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
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '1.0.4'.freeze
2
+ VERSION = '2.0.2'.freeze
3
3
  end
@@ -6,8 +6,8 @@ require 'ruby_smb/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'ruby_smb'
8
8
  spec.version = RubySMB::VERSION
9
- spec.authors = ['David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
10
- spec.email = ['DMaloney@rapid7.com', 'egypt@metasploit.com', 'dev_mohanty@rapid7.com', 'paristvinternet-github@yahoo.com']
9
+ spec.authors = ['Metasploit Hackers', 'David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
10
+ spec.email = ['msfdev@metasploit.com']
11
11
  spec.summary = 'A pure Ruby implementation of the SMB Protocol Family'
12
12
  spec.description = ''
13
13
  spec.homepage = 'https://github.com/rapid7/ruby_smb'
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.platform = Gem::Platform::RUBY
27
27
  end
28
28
 
29
- spec.required_ruby_version = '>= 2.2.0'
29
+ spec.required_ruby_version = '>= 2.5'
30
30
 
31
31
  spec.add_development_dependency 'bundler'
32
32
  spec.add_development_dependency 'fivemat'
@@ -36,4 +36,6 @@ Gem::Specification.new do |spec|
36
36
  spec.add_runtime_dependency 'rubyntlm'
37
37
  spec.add_runtime_dependency 'windows_error'
38
38
  spec.add_runtime_dependency 'bindata'
39
+ spec.add_runtime_dependency 'openssl-ccm'
40
+ spec.add_runtime_dependency 'openssl-cmac'
39
41
  end
@@ -6,11 +6,66 @@ RSpec.describe RubySMB::Client do
6
6
  let(:username) { 'msfadmin' }
7
7
  let(:password) { 'msfpasswd' }
8
8
  subject(:client) { described_class.new(dispatcher, username: username, password: password) }
9
- let(:smb1_client) { described_class.new(dispatcher, smb2: false, username: username, password: password) }
10
- let(:smb2_client) { described_class.new(dispatcher, smb1: false, username: username, password: password) }
9
+ let(:smb1_client) { described_class.new(dispatcher, smb2: false, smb3: false, username: username, password: password) }
10
+ let(:smb2_client) { described_class.new(dispatcher, smb1: false, smb3: false, username: username, password: password) }
11
+ let(:smb3_client) { described_class.new(dispatcher, smb1: false, smb2: false, username: username, password: password) }
11
12
  let(:empty_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
12
13
  let(:error_packet) { RubySMB::SMB2::Packet::ErrorPacket.new }
13
14
 
15
+ it { is_expected.to respond_to :dispatcher }
16
+ it { is_expected.to respond_to :domain }
17
+ it { is_expected.to respond_to :local_workstation }
18
+ it { is_expected.to respond_to :ntlm_client }
19
+ it { is_expected.to respond_to :password }
20
+ it { is_expected.to respond_to :peer_native_os }
21
+ it { is_expected.to respond_to :peer_native_lm }
22
+ it { is_expected.to respond_to :primary_domain }
23
+ it { is_expected.to respond_to :default_name }
24
+ it { is_expected.to respond_to :default_domain }
25
+ it { is_expected.to respond_to :dns_host_name }
26
+ it { is_expected.to respond_to :dns_domain_name }
27
+ it { is_expected.to respond_to :dns_tree_name }
28
+ it { is_expected.to respond_to :os_version }
29
+ it { is_expected.to respond_to :dialect }
30
+ it { is_expected.to respond_to :sequence_counter }
31
+ it { is_expected.to respond_to :session_id }
32
+ it { is_expected.to respond_to :signing_required }
33
+ it { is_expected.to respond_to :smb1 }
34
+ it { is_expected.to respond_to :smb2 }
35
+ it { is_expected.to respond_to :smb3 }
36
+ it { is_expected.to respond_to :smb2_message_id }
37
+ it { is_expected.to respond_to :username }
38
+ it { is_expected.to respond_to :user_id }
39
+ it { is_expected.to respond_to :max_buffer_size }
40
+ it { is_expected.to respond_to :server_max_buffer_size }
41
+ it { is_expected.to respond_to :server_max_write_size }
42
+ it { is_expected.to respond_to :server_max_read_size }
43
+ it { is_expected.to respond_to :server_max_transact_size }
44
+ it { is_expected.to respond_to :preauth_integrity_hash_algorithm }
45
+ it { is_expected.to respond_to :preauth_integrity_hash_value }
46
+ it { is_expected.to respond_to :encryption_algorithm }
47
+ it { is_expected.to respond_to :client_encryption_key }
48
+ it { is_expected.to respond_to :server_encryption_key }
49
+ it { is_expected.to respond_to :session_encrypt_data }
50
+ it { is_expected.to respond_to :server_encryption_algorithms }
51
+ it { is_expected.to respond_to :server_compression_algorithms }
52
+ it { is_expected.to respond_to :negotiated_smb_version }
53
+ it { is_expected.to respond_to :session_key }
54
+ it { is_expected.to respond_to :tree_connects }
55
+ it { is_expected.to respond_to :open_files }
56
+ it { is_expected.to respond_to :use_ntlmv2 }
57
+ it { is_expected.to respond_to :usentlm2_session }
58
+ it { is_expected.to respond_to :send_lm }
59
+ it { is_expected.to respond_to :use_lanman_key }
60
+ it { is_expected.to respond_to :send_ntlm }
61
+ it { is_expected.to respond_to :spnopt }
62
+ it { is_expected.to respond_to :evasion_opts }
63
+ it { is_expected.to respond_to :native_os }
64
+ it { is_expected.to respond_to :native_lm }
65
+ it { is_expected.to respond_to :verify_signature }
66
+ it { is_expected.to respond_to :auth_user }
67
+ it { is_expected.to respond_to :last_file_id }
68
+
14
69
  describe '#initialize' do
15
70
  it 'should raise an ArgumentError without a valid dispatcher' do
16
71
  expect { described_class.new(nil) }.to raise_error(ArgumentError)
@@ -26,14 +81,21 @@ RSpec.describe RubySMB::Client do
26
81
 
27
82
  it 'accepts an argument to disable smb1 support' do
28
83
  expect(smb2_client.smb1).to be false
84
+ expect(smb3_client.smb1).to be false
29
85
  end
30
86
 
31
87
  it 'accepts an argument to disable smb2 support' do
32
88
  expect(smb1_client.smb2).to be false
89
+ expect(smb3_client.smb2).to be false
90
+ end
91
+
92
+ it 'accepts an argument to disable smb3 support' do
93
+ expect(smb1_client.smb3).to be false
94
+ expect(smb2_client.smb3).to be false
33
95
  end
34
96
 
35
- it 'raises an exception if both SMB1 and SMB2 are disabled' do
36
- expect { described_class.new(dispatcher, smb1: false, smb2: false, username: username, password: password) }.to raise_error(ArgumentError, 'You must enable at least one Protocol')
97
+ it 'raises an exception if SMB1, SMB2 and SMB3 are disabled' do
98
+ expect { described_class.new(dispatcher, smb1: false, smb2: false, smb3: false, username: username, password: password) }.to raise_error(ArgumentError, 'You must enable at least one Protocol')
37
99
  end
38
100
 
39
101
  it 'sets the username attribute' do
@@ -44,6 +106,11 @@ RSpec.describe RubySMB::Client do
44
106
  expect(client.password).to eq password
45
107
  end
46
108
 
109
+ it 'sets the session_encrypt_data attribute' do
110
+ client = described_class.new(dispatcher, username: username, password: password, always_encrypt: true)
111
+ expect(client.session_encrypt_data).to eq true
112
+ end
113
+
47
114
  it 'creates an NTLM client' do
48
115
  expect(client.ntlm_client).to be_a Net::NTLM::Client
49
116
  end
@@ -58,7 +125,7 @@ RSpec.describe RubySMB::Client do
58
125
  expect(opt[:workstation]).to eq(local_workstation)
59
126
  expect(opt[:domain]).to eq(domain)
60
127
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
61
- Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000
128
+ Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000 ^ Net::NTLM::FLAGS[:OEM]
62
129
  expect(opt[:flags]).to eq(flags)
63
130
  end
64
131
 
@@ -76,28 +143,280 @@ RSpec.describe RubySMB::Client do
76
143
  end
77
144
  end
78
145
 
146
+ describe '#echo' do
147
+ let(:response) { double('Echo Response') }
148
+ before :example do
149
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
150
+ end
151
+
152
+ context 'with SMB1' do
153
+ it 'calls #smb1_echo with the expected arguments' do
154
+ allow(smb1_client).to receive(:smb1_echo).and_return(response)
155
+ count = 3
156
+ data = 'testing...'
157
+ smb1_client.echo(count: count, data: data)
158
+ expect(smb1_client).to have_received(:smb1_echo).with(count: count, data: data)
159
+ end
160
+ end
161
+
162
+ context 'with SMB2' do
163
+ it 'calls #smb2_echo without arguments' do
164
+ allow(smb2_client).to receive(:smb2_echo).and_return(response)
165
+ smb2_client.echo
166
+ expect(smb2_client).to have_received(:smb2_echo)
167
+ end
168
+ end
169
+
170
+ context 'with SMB3' do
171
+ it 'calls #smb2_echo without arguments' do
172
+ allow(smb3_client).to receive(:smb2_echo).and_return(response)
173
+ smb3_client.echo
174
+ expect(smb3_client).to have_received(:smb2_echo)
175
+ end
176
+ end
177
+
178
+ it 'returns the expected status code' do
179
+ allow(smb2_client).to receive(:smb2_echo).and_return(response)
180
+ expect(smb2_client.echo).to eq(WindowsError::NTStatus::STATUS_SUCCESS)
181
+ end
182
+ end
183
+
79
184
  describe '#send_recv' do
80
185
  let(:smb1_request) { RubySMB::SMB1::Packet::TreeConnectRequest.new }
81
186
  let(:smb2_request) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
82
187
 
83
188
  before(:each) do
84
- expect(dispatcher).to receive(:send_packet).and_return(nil)
85
- expect(dispatcher).to receive(:recv_packet).and_return('A')
189
+ allow(client).to receive(:is_status_pending?).and_return(false)
190
+ allow(dispatcher).to receive(:send_packet).and_return(nil)
191
+ allow(dispatcher).to receive(:recv_packet).and_return('A')
86
192
  end
87
193
 
88
- it 'checks the packet version' do
89
- expect(smb1_request).to receive(:packet_smb_version).and_call_original
90
- client.send_recv(smb1_request)
194
+ context 'when signing' do
195
+ it 'calls #smb1_sign if it is an SMB1 packet' do
196
+ expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
197
+ client.send_recv(smb1_request)
198
+ end
199
+
200
+ context 'with an SMB2 packet' do
201
+ it 'does not sign a SessionSetupRequest packet' do
202
+ expect(smb2_client).to_not receive(:smb2_sign)
203
+ expect(smb2_client).to_not receive(:smb3_sign)
204
+ client.send_recv(RubySMB::SMB2::Packet::SessionSetupRequest.new)
205
+ end
206
+
207
+ it 'calls #smb2_sign if it is an SMB2 client' do
208
+ allow(smb2_client).to receive(:is_status_pending?).and_return(false)
209
+ expect(smb2_client).to receive(:smb2_sign).with(smb2_request).and_call_original
210
+ smb2_client.send_recv(smb2_request)
211
+ end
212
+
213
+ it 'calls #smb3_sign if it is an SMB3 client' do
214
+ allow(smb3_client).to receive(:is_status_pending?).and_return(false)
215
+ expect(smb3_client).to receive(:smb3_sign).with(smb2_request).and_call_original
216
+ smb3_client.send_recv(smb2_request)
217
+ end
218
+ end
91
219
  end
92
220
 
93
- it 'calls #smb1_sign if it is an SMB1 packet' do
94
- expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
221
+ it 'sends the expected packet and gets the response' do
222
+ expect(dispatcher).to receive(:send_packet).with(smb1_request)
223
+ expect(dispatcher).to receive(:recv_packet)
95
224
  client.send_recv(smb1_request)
96
225
  end
97
226
 
98
- it 'calls #smb2_sign if it is an SMB2 packet' do
99
- expect(client).to receive(:smb2_sign).with(smb2_request).and_call_original
100
- client.send_recv(smb2_request)
227
+ context 'with SMB1' do
228
+ it 'does not check if it is a STATUS_PENDING response' do
229
+ expect(smb1_client).to_not receive(:is_status_pending?)
230
+ smb1_client.send_recv(smb1_request)
231
+ end
232
+ end
233
+
234
+ context 'with SMB2' do
235
+ context 'when receiving a STATUS_PENDING response' do
236
+ it 'waits 1 second and reads/decrypts again' do
237
+ allow(smb2_client).to receive(:is_status_pending?).and_return(true, false)
238
+ expect(smb2_client).to receive(:sleep).with(1)
239
+ expect(dispatcher).to receive(:recv_packet).twice
240
+ smb2_client.send_recv(smb2_request)
241
+ end
242
+ end
243
+ end
244
+
245
+ context 'with SMB3 and encryption' do
246
+ before :example do
247
+ smb3_client.dialect = '0x0300'
248
+ allow(smb3_client).to receive(:is_status_pending?).and_return(false)
249
+ end
250
+
251
+ context 'with a SessionSetupRequest' do
252
+ it 'does not encrypt/decrypt' do
253
+ request = RubySMB::SMB2::Packet::SessionSetupRequest.new
254
+ expect(smb3_client).to_not receive(:send_encrypt).with(request)
255
+ expect(smb3_client).to_not receive(:recv_encrypt)
256
+ expect(dispatcher).to receive(:send_packet).with(request)
257
+ expect(dispatcher).to receive(:recv_packet)
258
+ smb3_client.send_recv(request)
259
+ end
260
+ end
261
+
262
+ context 'with a NegotiateRequest' do
263
+ it 'does not encrypt/decrypt' do
264
+ request = RubySMB::SMB2::Packet::NegotiateRequest.new
265
+ expect(smb3_client).to_not receive(:send_encrypt).with(request)
266
+ expect(smb3_client).to_not receive(:recv_encrypt)
267
+ expect(dispatcher).to receive(:send_packet).with(request)
268
+ expect(dispatcher).to receive(:recv_packet)
269
+ smb3_client.send_recv(request)
270
+ end
271
+ end
272
+
273
+ it 'encrypts and decrypts' do
274
+ expect(smb3_client).to receive(:send_encrypt).with(smb2_request)
275
+ expect(smb3_client).to receive(:recv_encrypt)
276
+ smb3_client.send_recv(smb2_request)
277
+ end
278
+
279
+ context 'when receiving a STATUS_PENDING response' do
280
+ it 'waits 1 second and reads/decrypts again' do
281
+ allow(smb3_client).to receive(:is_status_pending?).and_return(true, false)
282
+ expect(smb3_client).to receive(:sleep).with(1)
283
+ expect(smb3_client).to receive(:send_encrypt).with(smb2_request)
284
+ expect(smb3_client).to receive(:recv_encrypt).twice
285
+ smb3_client.send_recv(smb2_request)
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ describe '#is_status_pending?' do
292
+ let(:response) {
293
+ res = RubySMB::SMB2::Packet::SessionSetupRequest.new
294
+ res.smb2_header.nt_status= WindowsError::NTStatus::STATUS_PENDING.value
295
+ res.smb2_header.flags.async_command = 1
296
+ res
297
+ }
298
+
299
+ it 'returns true when the response has a STATUS_PENDING status code and the async_command flag set' do
300
+ expect(client.is_status_pending?(response.to_binary_s)).to be true
301
+ end
302
+
303
+ it 'returns false when the response has a STATUS_PENDING status code and the async_command flag not set' do
304
+ response.smb2_header.flags.async_command = 0
305
+ expect(client.is_status_pending?(response.to_binary_s)).to be false
306
+ end
307
+
308
+ it 'returns false when the response has no STATUS_PENDING status code but the async_command flag set' do
309
+ response.smb2_header.nt_status= WindowsError::NTStatus::STATUS_SUCCESS.value
310
+ expect(client.is_status_pending?(response.to_binary_s)).to be false
311
+ end
312
+ end
313
+
314
+ describe '#can_be_encrypted?' do
315
+ it 'returns true if the packet can be encrypted' do
316
+ packet = RubySMB::SMB2::Packet::TreeConnectRequest.new
317
+ expect(client.can_be_encrypted?(packet)).to be true
318
+ end
319
+
320
+ it 'returns false if it is an SMB1 packet' do
321
+ packet = RubySMB::SMB1::Packet::LogoffRequest.new
322
+ expect(client.can_be_encrypted?(packet)).to be false
323
+ end
324
+
325
+ [RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].each do |klass|
326
+ it "returns false if the packet is a #{klass}" do
327
+ packet = klass.new
328
+ expect(client.can_be_encrypted?(packet)).to be false
329
+ end
330
+ end
331
+ end
332
+
333
+ describe '#encryption_supported?' do
334
+ ['0x0300', '0x0302', '0x0311'].each do |dialect|
335
+ it "returns true if the dialect is #{dialect}" do
336
+ client.dialect = dialect
337
+ expect(client.encryption_supported?).to be true
338
+ end
339
+ end
340
+
341
+ it "returns false if the dialect does not support encryption" do
342
+ client.dialect = '0x0202'
343
+ expect(client.encryption_supported?).to be false
344
+ end
345
+ end
346
+
347
+ describe '#send_encrypt' do
348
+ let(:packet) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
349
+ before :example do
350
+ allow(dispatcher).to receive(:send_packet)
351
+ client.dialect = '0x0300'
352
+ end
353
+
354
+ it 'creates a Transform request' do
355
+ expect(client).to receive(:smb3_encrypt).with(packet.to_binary_s)
356
+ client.send_encrypt(packet)
357
+ end
358
+
359
+ it 'raises an EncryptionError exception if an error occurs while encrypting' do
360
+ allow(client).to receive(:smb3_encrypt).and_raise(RubySMB::Error::RubySMBError.new('Error'))
361
+ expect { client.send_encrypt(packet) }.to raise_error(
362
+ RubySMB::Error::EncryptionError,
363
+ "Error while encrypting #{packet.class.name} packet (SMB 0x0300): Error"
364
+ )
365
+ end
366
+
367
+ it 'sends the encrypted packet' do
368
+ encrypted_packet = double('Encrypted packet')
369
+ allow(client).to receive(:smb3_encrypt).and_return(encrypted_packet)
370
+ client.send_encrypt(packet)
371
+ expect(dispatcher).to have_received(:send_packet).with(encrypted_packet)
372
+ end
373
+ end
374
+
375
+ describe '#recv_encrypt' do
376
+ let(:packet) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
377
+ before :example do
378
+ allow(dispatcher).to receive(:recv_packet).and_return(packet.to_binary_s)
379
+ client.dialect = '0x0300'
380
+ allow(client).to receive(:smb3_decrypt)
381
+ end
382
+
383
+ it 'reads the response packet' do
384
+ client.recv_encrypt
385
+ expect(dispatcher).to have_received(:recv_packet)
386
+ end
387
+
388
+ it 'raises an EncryptionError exception if an error occurs while receiving the response' do
389
+ allow(dispatcher).to receive(:recv_packet).and_raise(RubySMB::Error::CommunicationError)
390
+ expect { client.recv_encrypt }.to raise_error(
391
+ RubySMB::Error::EncryptionError,
392
+ 'Communication error with the remote host: RubySMB::Error::CommunicationError. '\
393
+ 'The server supports encryption but was not able to handle the encrypted request.'
394
+ )
395
+ end
396
+
397
+ it 'parses the response as a Transform response packet' do
398
+ expect(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).with(packet.to_binary_s)
399
+ client.recv_encrypt
400
+ end
401
+
402
+ it 'raises an InvalidPacket exception if an error occurs while parsing the response' do
403
+ allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_raise(IOError)
404
+ expect { client.recv_encrypt }.to raise_error(RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet')
405
+ end
406
+
407
+ it 'decrypts the Transform response packet' do
408
+ transform = double('Transform header packet')
409
+ allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_return(transform)
410
+ client.recv_encrypt
411
+ expect(client).to have_received(:smb3_decrypt).with(transform)
412
+ end
413
+
414
+ it 'raises an EncryptionError exception if an error occurs while decrypting' do
415
+ allow(client).to receive(:smb3_decrypt).and_raise(RubySMB::Error::RubySMBError)
416
+ expect { client.recv_encrypt }.to raise_error(
417
+ RubySMB::Error::EncryptionError,
418
+ 'Error while decrypting RubySMB::SMB2::Packet::TransformHeader packet (SMB 0x0300}): RubySMB::Error::RubySMBError'
419
+ )
101
420
  end
102
421
  end
103
422
 
@@ -240,6 +559,44 @@ RSpec.describe RubySMB::Client do
240
559
  expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
241
560
  end
242
561
  end
562
+
563
+ context 'with SMB3' do
564
+ let(:raw_response) { double('Raw response') }
565
+ let(:logoff_response) {
566
+ RubySMB::SMB2::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB2::Commands::LOGOFF} )
567
+ }
568
+ before :example do
569
+ allow(smb3_client).to receive(:send_recv).and_return(raw_response)
570
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
571
+ allow(smb3_client).to receive(:wipe_state!)
572
+ end
573
+
574
+ it 'creates a LogoffRequest packet' do
575
+ expect(RubySMB::SMB2::Packet::LogoffRequest).to receive(:new).and_call_original
576
+ smb3_client.logoff!
577
+ end
578
+
579
+ it 'calls #send_recv' do
580
+ expect(smb3_client).to receive(:send_recv)
581
+ smb3_client.logoff!
582
+ end
583
+
584
+ it 'reads the raw response as a LogoffResponse packet' do
585
+ expect(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).with(raw_response)
586
+ smb3_client.logoff!
587
+ end
588
+
589
+ it 'raise an InvalidPacket exception when the response is an error packet' do
590
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB2::Packet::ErrorPacket.new)
591
+ expect {smb3_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
592
+ end
593
+
594
+ it 'raise an InvalidPacket exception when the response is not a LOGOFF command' do
595
+ logoff_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
596
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
597
+ expect {smb3_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
598
+ end
599
+ end
243
600
  end
244
601
 
245
602
  context 'NetBIOS Session Service' do
@@ -365,7 +722,8 @@ RSpec.describe RubySMB::Client do
365
722
  smb1_extended_response.to_binary_s
366
723
  }
367
724
 
368
- let(:smb2_response) { RubySMB::SMB2::Packet::NegotiateResponse.new }
725
+ let(:smb2_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x200) }
726
+ let(:smb3_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x300) }
369
727
 
370
728
  describe '#smb1_negotiate_request' do
371
729
  it 'returns an SMB1 Negotiate Request packet' do
@@ -373,33 +731,158 @@ RSpec.describe RubySMB::Client do
373
731
  end
374
732
 
375
733
  it 'sets the default SMB1 Dialect' do
376
- expect(client.smb1_negotiate_request.dialects).to include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT)
734
+ expect(client.smb1_negotiate_request.dialects).to include(
735
+ buffer_format: 2,
736
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT
737
+ )
377
738
  end
378
739
 
379
740
  it 'sets the SMB2.02 dialect if SMB2 support is enabled' do
380
- expect(client.smb1_negotiate_request.dialects).to include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT)
741
+ expect(client.smb1_negotiate_request.dialects).to include(
742
+ buffer_format: 2,
743
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT
744
+ )
381
745
  end
382
746
 
383
747
  it 'excludes the SMB2.02 Dialect if SMB2 support is disabled' do
384
- expect(smb1_client.smb1_negotiate_request.dialects).to_not include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT)
748
+ expect(smb1_client.smb1_negotiate_request.dialects).to_not include(
749
+ buffer_format: 2,
750
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT
751
+ )
385
752
  end
386
753
 
387
754
  it 'excludes the default SMB1 Dialect if SMB1 support is disabled' do
388
- expect(smb2_client.smb1_negotiate_request.dialects).to_not include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT)
755
+ expect(smb2_client.smb1_negotiate_request.dialects).to_not include(
756
+ buffer_format: 2,
757
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT
758
+ )
759
+ end
760
+
761
+ it 'sets the SMB wildcard dialect if SMB2 support is enabled' do
762
+ expect(client.smb1_negotiate_request.dialects).to include(
763
+ buffer_format: 2,
764
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_WILDCARD
765
+ )
766
+ end
767
+
768
+ it 'sets the SMB wildcard dialect if SMB3 support is enabled' do
769
+ expect(smb3_client.smb1_negotiate_request.dialects).to include(
770
+ buffer_format: 2,
771
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_WILDCARD
772
+ )
773
+ end
774
+
775
+ it 'excludes the SMB wildcard dialect if both SMB2 and SMB3 supports are disabled' do
776
+ expect(smb1_client.smb1_negotiate_request.dialects).to_not include(
777
+ buffer_format: 2,
778
+ dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_WILDCARD
779
+ )
389
780
  end
390
781
  end
391
782
 
392
- describe '#smb2_negotiate_request' do
783
+ describe '#smb2_3_negotiate_request' do
393
784
  it 'return an SMB2 Negotiate Request packet' do
394
- expect(client.smb2_negotiate_request).to be_a(RubySMB::SMB2::Packet::NegotiateRequest)
785
+ expect(client.smb2_3_negotiate_request).to be_a(RubySMB::SMB2::Packet::NegotiateRequest)
786
+ end
787
+
788
+ it 'sets the default SMB2 Dialect if SMB2 support is enabled' do
789
+ expect(client.smb2_3_negotiate_request.dialects).to include(
790
+ *(RubySMB::Client::SMB2_DIALECT_DEFAULT.map {|d| d.to_i(16)})
791
+ )
395
792
  end
396
793
 
397
- it 'sets the default SMB2 Dialect' do
398
- expect(client.smb2_negotiate_request.dialects).to include(RubySMB::Client::SMB2_DIALECT_DEFAULT)
794
+ it 'does not set the default SMB2 Dialect if SMB2 support is disabled' do
795
+ expect(smb3_client.smb2_3_negotiate_request.dialects).to_not include(
796
+ *(RubySMB::Client::SMB2_DIALECT_DEFAULT.map {|d| d.to_i(16)})
797
+ )
399
798
  end
400
799
 
401
800
  it 'sets the Message ID to 0' do
402
- expect(client.smb2_negotiate_request.smb2_header.message_id).to eq 0
801
+ expect(client.smb2_3_negotiate_request.smb2_header.message_id).to eq 0
802
+ end
803
+
804
+ it 'adds SMB3 dialects if if SMB3 support is enabled' do
805
+ expect(client.smb2_3_negotiate_request.dialects).to include(
806
+ *(RubySMB::Client::SMB3_DIALECT_DEFAULT.map {|d| d.to_i(16)})
807
+ )
808
+ end
809
+
810
+ it 'does not set the default SMB3 Dialect if SMB3 support is disabled' do
811
+ expect(smb2_client.smb2_3_negotiate_request.dialects).to_not include(
812
+ *(RubySMB::Client::SMB3_DIALECT_DEFAULT.map {|d| d.to_i(16)})
813
+ )
814
+ end
815
+ end
816
+
817
+ describe '#add_smb3_to_negotiate_request' do
818
+ let(:negotiate_request) { RubySMB::SMB2::Packet::NegotiateRequest.new }
819
+
820
+ it 'adds the default SMB3 dialects' do
821
+ expect(client.add_smb3_to_negotiate_request(negotiate_request).dialects).to include(
822
+ *(RubySMB::Client::SMB3_DIALECT_DEFAULT.map {|d| d.to_i(16)})
823
+ )
824
+ end
825
+
826
+ it 'raises the expected exception when the dialects is not an array of strings' do
827
+ dialects = ['0x0300', 0x0302, '0x0311']
828
+ expect { client.add_smb3_to_negotiate_request(negotiate_request, dialects) }.to raise_error(ArgumentError)
829
+ end
830
+
831
+ it 'sets encryption capability flag' do
832
+ expect(client.add_smb3_to_negotiate_request(negotiate_request).capabilities.encryption).to eq(1)
833
+ end
834
+
835
+ context 'when the negotiate packet includes the 0x0311 dialect' do
836
+ before :example do
837
+ client.add_smb3_to_negotiate_request(negotiate_request, ['0x0311'])
838
+ end
839
+
840
+ it 'adds 3 Negotiate Contexts' do
841
+ expect(negotiate_request.negotiate_context_info.negotiate_context_count).to eq(3)
842
+ end
843
+
844
+ it 'adds a Preauth Integrity Negotiate Context with the expected hash algorithms' do
845
+ nc = negotiate_request.negotiate_context_list.select do |n|
846
+ n.context_type == RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
847
+ end
848
+ expect(nc.length).to eq(1)
849
+ expect(nc.first.data.hash_algorithms).to eq([RubySMB::SMB2::PreauthIntegrityCapabilities::SHA_512])
850
+ end
851
+
852
+ it 'adds Encryption Negotiate Contexts with the expected encryption algorithms' do
853
+ nc = negotiate_request.negotiate_context_list.select do |n|
854
+ n.context_type == RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
855
+ end
856
+ expect(nc.length).to eq(1)
857
+ expect(nc.first.data.ciphers).to eq(
858
+ [
859
+ RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM,
860
+ RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
861
+ ]
862
+ )
863
+ end
864
+
865
+ it 'adds Compression Negotiate Contexts with the expected compression algorithms' do
866
+ nc = negotiate_request.negotiate_context_list.select do |n|
867
+ n.context_type == RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
868
+ end
869
+ expect(nc.length).to eq(1)
870
+ expect(nc.first.data.compression_algorithms).to eq(
871
+ [
872
+ RubySMB::SMB2::CompressionCapabilities::LZNT1,
873
+ RubySMB::SMB2::CompressionCapabilities::LZ77,
874
+ RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman,
875
+ RubySMB::SMB2::CompressionCapabilities::Pattern_V1
876
+ ]
877
+ )
878
+ end
879
+ end
880
+
881
+ context 'when the negotiate packet does not include the 0x0311 dialect' do
882
+ it 'does not add any Negotiate Context' do
883
+ client.add_smb3_to_negotiate_request(negotiate_request, ['0x0300', '0x0302'])
884
+ expect(negotiate_request.negotiate_context_list?). to be false
885
+ end
403
886
  end
404
887
  end
405
888
 
@@ -414,10 +897,15 @@ RSpec.describe RubySMB::Client do
414
897
  client.negotiate_request
415
898
  end
416
899
 
417
- it 'calls #smb2_negotiate_request if SMB2 is enabled' do
418
- expect(smb2_client).to receive(:smb2_negotiate_request)
900
+ it 'calls #smb2_3_negotiate_request if SMB2 is enabled' do
901
+ expect(smb2_client).to receive(:smb2_3_negotiate_request)
419
902
  smb2_client.negotiate_request
420
903
  end
904
+
905
+ it 'calls #smb2_3_negotiate_request if SMB3 is enabled' do
906
+ expect(smb3_client).to receive(:smb2_3_negotiate_request)
907
+ smb3_client.negotiate_request
908
+ end
421
909
  end
422
910
 
423
911
  describe '#negotiate_response' do
@@ -464,12 +952,28 @@ RSpec.describe RubySMB::Client do
464
952
  end
465
953
  end
466
954
 
467
- context 'with SMB1 and SMB2 enabled' do
955
+ context 'with only SMB3' do
956
+ it 'returns a properly formed packet' do
957
+ expect(smb3_client.negotiate_response(smb2_response.to_binary_s)).to eq smb2_response
958
+ end
959
+
960
+ it 'raises an exception if the Response is invalid' do
961
+ expect { smb3_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
962
+ end
963
+
964
+ it 'considers the response invalid if it is not an actual Negotiate Response' do
965
+ bogus_response = smb2_response
966
+ bogus_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
967
+ expect { smb3_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
968
+ end
969
+ end
970
+
971
+ context 'with SMB1, SMB2 and SMB3 enabled' do
468
972
  it 'returns an SMB1 NegotiateResponse if it looks like SMB1' do
469
973
  expect(client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
470
974
  end
471
975
 
472
- it 'returns an SMB2 NegotiateResponse if it looks like SMB2' do
976
+ it 'returns an SMB2 NegotiateResponse if it looks like SMB2 or SMB3' do
473
977
  expect(client.negotiate_response(smb2_response.to_binary_s)).to eq smb2_response
474
978
  end
475
979
  end
@@ -477,9 +981,10 @@ RSpec.describe RubySMB::Client do
477
981
 
478
982
  describe '#parse_negotiate_response' do
479
983
  context 'when SMB1 was Negotiated' do
480
- it 'turns off SMB2 support' do
984
+ it 'turns off SMB2 and SMB3 support' do
481
985
  client.parse_negotiate_response(smb1_extended_response)
482
986
  expect(client.smb2).to be false
987
+ expect(client.smb3).to be false
483
988
  end
484
989
 
485
990
  it 'sets whether or not signing is required' do
@@ -502,12 +1007,18 @@ RSpec.describe RubySMB::Client do
502
1007
  it 'returns the string \'SMB1\'' do
503
1008
  expect(client.parse_negotiate_response(smb1_extended_response)).to eq ('SMB1')
504
1009
  end
1010
+
1011
+ it 'sets #negotiated_smb_version to 1' do
1012
+ client.parse_negotiate_response(smb1_extended_response)
1013
+ expect(client.negotiated_smb_version).to eq(1)
1014
+ end
505
1015
  end
506
1016
 
507
1017
  context 'when SMB2 was negotiated' do
508
- it 'turns off SMB1 support' do
1018
+ it 'turns off SMB1 and SMB3 support' do
509
1019
  client.parse_negotiate_response(smb2_response)
510
1020
  expect(client.smb1).to be false
1021
+ expect(client.smb3).to be false
511
1022
  end
512
1023
 
513
1024
  it 'sets whether or not signing is required' do
@@ -526,9 +1037,122 @@ RSpec.describe RubySMB::Client do
526
1037
  expect(client.parse_negotiate_response(smb2_response)).to eq ('SMB2')
527
1038
  end
528
1039
  end
1040
+
1041
+ context 'when SMB3 was negotiated' do
1042
+ it 'turns off SMB1 and SMB2 support' do
1043
+ client.parse_negotiate_response(smb3_response)
1044
+ expect(client.smb1).to be false
1045
+ expect(client.smb2).to be false
1046
+ end
1047
+
1048
+ it 'sets whether or not signing is required' do
1049
+ smb3_response.security_mode.signing_required = 1
1050
+ client.parse_negotiate_response(smb3_response)
1051
+ expect(client.signing_required).to be true
1052
+ end
1053
+
1054
+ it 'sets #dialect to the negotiated dialect' do
1055
+ client.parse_negotiate_response(smb3_response)
1056
+ expect(client.dialect).to eq '0x0300'
1057
+ end
1058
+
1059
+ it 'returns the string \'SMB2\'' do
1060
+ expect(client.parse_negotiate_response(smb3_response)).to eq ('SMB3')
1061
+ end
1062
+
1063
+ context 'when the server supports encryption' do
1064
+ before :example do
1065
+ smb3_response.capabilities.encryption = 1
1066
+ end
1067
+
1068
+ it 'sets the expected encryption algorithm' do
1069
+ client.parse_negotiate_response(smb3_response)
1070
+ expect(client.encryption_algorithm).to eq(RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM])
1071
+ end
1072
+
1073
+ it 'keeps session encryption enabled if it was already' do
1074
+ client.session_encrypt_data = true
1075
+ client.parse_negotiate_response(smb3_response)
1076
+ expect(client.session_encrypt_data).to be true
1077
+ end
1078
+
1079
+ it 'keeps session encryption disabled if it was already' do
1080
+ client.session_encrypt_data = false
1081
+ client.parse_negotiate_response(smb3_response)
1082
+ expect(client.session_encrypt_data).to be false
1083
+ end
1084
+ end
1085
+
1086
+ context 'when the server does not support encryption' do
1087
+ before :example do
1088
+ smb3_response.capabilities.encryption = 0
1089
+ end
1090
+
1091
+ it 'disables session encryption if it was already enabled' do
1092
+ client.session_encrypt_data = true
1093
+ client.parse_negotiate_response(smb3_response)
1094
+ expect(client.session_encrypt_data).to be false
1095
+ end
1096
+
1097
+ it 'keeps session encryption disabled if it was already' do
1098
+ client.session_encrypt_data = false
1099
+ client.parse_negotiate_response(smb3_response)
1100
+ expect(client.session_encrypt_data).to be false
1101
+ end
1102
+ end
1103
+ end
1104
+
1105
+ context 'when the response contains the SMB2 wildcard revision number dialect' do
1106
+ it 'only turns off SMB1 support' do
1107
+ smb2_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x02ff)
1108
+ client.parse_negotiate_response(smb2_response)
1109
+ expect(client.smb1).to be false
1110
+ expect(client.smb2).to be true
1111
+ expect(client.smb3).to be true
1112
+ end
1113
+ end
1114
+
1115
+ context 'when the negotiation failed' do
1116
+ context 'with a STATUS_NOT_SUPPORTED status code' do
1117
+ before :example do
1118
+ error_packet.smb2_header.nt_status = WindowsError::NTStatus::STATUS_NOT_SUPPORTED.value
1119
+ end
1120
+
1121
+ it 'raises the expected exception with SMB2' do
1122
+ expect { smb2_client.parse_negotiate_response(error_packet) }.to raise_error(
1123
+ RubySMB::Error::NegotiationFailure,
1124
+ 'Unable to negotiate with remote host, SMB2 not supported'
1125
+ )
1126
+ end
1127
+
1128
+ it 'raises the expected exception with SMB3' do
1129
+ expect { smb3_client.parse_negotiate_response(error_packet) }.to raise_error(
1130
+ RubySMB::Error::NegotiationFailure,
1131
+ 'Unable to negotiate with remote host, SMB3 not supported'
1132
+ )
1133
+ end
1134
+ end
1135
+
1136
+ context 'with an unknown status code' do
1137
+ it 'raises the expected exception' do
1138
+ expect { client.parse_negotiate_response(empty_packet) }.to raise_error(
1139
+ RubySMB::Error::NegotiationFailure,
1140
+ 'Unable to negotiate with remote host'
1141
+ )
1142
+ end
1143
+ end
1144
+ end
529
1145
  end
530
1146
 
531
1147
  describe '#negotiate' do
1148
+ let(:request_packet) { client.smb1_negotiate_request }
1149
+ before :example do
1150
+ allow(client).to receive(:negotiate_request)
1151
+ allow(client).to receive(:send_recv)
1152
+ allow(client).to receive(:negotiate_response)
1153
+ allow(client).to receive(:parse_negotiate_response)
1154
+ end
1155
+
532
1156
  it 'calls the backing methods' do
533
1157
  expect(client).to receive(:negotiate_request)
534
1158
  expect(client).to receive(:send_recv)
@@ -537,18 +1161,247 @@ RSpec.describe RubySMB::Client do
537
1161
  client.negotiate
538
1162
  end
539
1163
 
540
- it 'sets the response-packet #dialects array with the dialects sent in the request' do
541
- request_packet = client.smb1_negotiate_request
542
- allow(client).to receive(:negotiate_request).and_return(request_packet)
543
- allow(client).to receive(:send_recv)
544
- allow(client).to receive(:negotiate_response).and_return(smb1_extended_response)
545
- expect(smb1_extended_response).to receive(:dialects=).with(request_packet.dialects)
546
- client.negotiate
1164
+ context 'with SMB1' do
1165
+ it 'sets the response-packet #dialects array with the dialects sent in the request' do
1166
+ request_packet = client.smb1_negotiate_request
1167
+ allow(client).to receive(:negotiate_request).and_return(request_packet)
1168
+ allow(client).to receive(:negotiate_response).and_return(smb1_extended_response)
1169
+ expect(smb1_extended_response).to receive(:dialects=).with(request_packet.dialects)
1170
+ client.negotiate
1171
+ end
1172
+ end
1173
+
1174
+ context "with 0x0311 dialect" do
1175
+ it 'calls #parse_negotiate_response and updates the preauth hash' do
1176
+ client.dialect = '0x0311'
1177
+ request_packet = client.smb2_3_negotiate_request
1178
+ allow(client).to receive(:negotiate_request).and_return(request_packet)
1179
+ allow(client).to receive(:negotiate_response).and_return(smb3_response)
1180
+ expect(client).to receive(:parse_negotiate_response).with(smb3_response)
1181
+ expect(client).to receive(:update_preauth_hash).with(request_packet)
1182
+ expect(client).to receive(:update_preauth_hash).with(smb3_response)
1183
+ client.negotiate
1184
+ end
1185
+ end
1186
+
1187
+ context 'with a wildcard revision number response' do
1188
+ before :example do
1189
+ client.dialect = '0x02ff'
1190
+ allow(client).to receive(:smb2_message_id=) do
1191
+ client.dialect = '0x0202'
1192
+ end
1193
+ end
1194
+
1195
+ it 'increments the message ID' do
1196
+ expect(client).to receive(:smb2_message_id=).with(1)
1197
+ client.negotiate
1198
+ end
1199
+
1200
+ it 're-negotiates' do
1201
+ expect(client).to receive(:negotiate_request).twice
1202
+ expect(client).to receive(:send_recv).twice
1203
+ expect(client).to receive(:negotiate_response).twice
1204
+ expect(client).to receive(:parse_negotiate_response).twice
1205
+ client.negotiate
1206
+ end
547
1207
  end
548
1208
 
549
- it 'raise the expected exception if an error occurs' do
550
- allow(client).to receive(:send_recv).and_raise(RubySMB::Error::InvalidPacket)
551
- expect { client.negotiate }.to raise_error(RubySMB::Error::NegotiationFailure)
1209
+ context 'when an error occurs' do
1210
+ before :example do
1211
+ allow(client).to receive(:negotiate_request).and_return(request_packet)
1212
+ allow(client).to receive(:send_recv).and_raise(RubySMB::Error::InvalidPacket)
1213
+ client.smb1 = false
1214
+ client.smb2 = false
1215
+ client.smb3 = false
1216
+ end
1217
+
1218
+ context 'with SMB1' do
1219
+ let(:request_packet) { client.smb1_negotiate_request }
1220
+
1221
+ it 'raise the expected exception' do
1222
+ client.smb1 = true
1223
+ expect { client.negotiate }.to raise_error(
1224
+ RubySMB::Error::NegotiationFailure,
1225
+ "Unable to negotiate SMB1 with the remote host: RubySMB::Error::InvalidPacket"
1226
+ )
1227
+ end
1228
+ end
1229
+
1230
+ context 'with SMB2' do
1231
+ let(:request_packet) { client.smb2_3_negotiate_request }
1232
+
1233
+ it 'raise the expected exception' do
1234
+ client.smb2 = true
1235
+ expect { client.negotiate }.to raise_error(
1236
+ RubySMB::Error::NegotiationFailure,
1237
+ "Unable to negotiate SMB2 with the remote host: RubySMB::Error::InvalidPacket"
1238
+ )
1239
+ end
1240
+ end
1241
+
1242
+ context 'with SMB3' do
1243
+ let(:request_packet) { client.smb2_3_negotiate_request }
1244
+
1245
+ it 'raise the expected exception' do
1246
+ client.smb3 = true
1247
+ expect { client.negotiate }.to raise_error(
1248
+ RubySMB::Error::NegotiationFailure,
1249
+ "Unable to negotiate SMB3 with the remote host: RubySMB::Error::InvalidPacket"
1250
+ )
1251
+ end
1252
+ end
1253
+ end
1254
+
1255
+ describe '#parse_smb3_capabilities' do
1256
+ let(:request_packet) { client.smb2_3_negotiate_request }
1257
+ let(:smb3_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311) }
1258
+ let(:nc_encryption) do
1259
+ nc = RubySMB::SMB2::NegotiateContext.new(
1260
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1261
+ )
1262
+ nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM
1263
+ nc
1264
+ end
1265
+ let(:nc_integrity) do
1266
+ nc = RubySMB::SMB2::NegotiateContext.new(
1267
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
1268
+ )
1269
+ nc.data.hash_algorithms << RubySMB::SMB2::PreauthIntegrityCapabilities::SHA_512
1270
+ nc
1271
+ end
1272
+
1273
+ before :example do
1274
+ allow(smb3_client).to receive(:update_preauth_hash)
1275
+ smb3_response.add_negotiate_context(nc_encryption)
1276
+ smb3_response.add_negotiate_context(nc_integrity)
1277
+ end
1278
+
1279
+ context 'when selecting the integrity hash algorithm' do
1280
+ context 'with one algorithm' do
1281
+ it 'selects the expected algorithm' do
1282
+ smb3_client.parse_smb3_capabilities(smb3_response)
1283
+ expect(smb3_client.preauth_integrity_hash_algorithm).to eq('SHA512')
1284
+ end
1285
+ end
1286
+
1287
+ context 'with multiple algorithms' do
1288
+ it 'selects the first algorithm' do
1289
+ nc = smb3_response.find_negotiate_context(
1290
+ RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
1291
+ )
1292
+ nc.data.hash_algorithms << 3
1293
+ smb3_client.parse_smb3_capabilities(smb3_response)
1294
+ expect(smb3_client.preauth_integrity_hash_algorithm).to eq('SHA512')
1295
+ end
1296
+ end
1297
+
1298
+ context 'without integrity negotiate context' do
1299
+ it 'raises the expected exception' do
1300
+ smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
1301
+ smb3_response.add_negotiate_context(nc_encryption)
1302
+ expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
1303
+ RubySMB::Error::EncryptionError,
1304
+ 'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
1305
+ )
1306
+ end
1307
+ end
1308
+
1309
+ context 'with an unknown integrity hash algorithm' do
1310
+ it 'raises the expected exception' do
1311
+ smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
1312
+ smb3_response.add_negotiate_context(nc_encryption)
1313
+ nc = RubySMB::SMB2::NegotiateContext.new(
1314
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
1315
+ )
1316
+ nc.data.hash_algorithms << 5
1317
+ smb3_response.add_negotiate_context(nc)
1318
+ expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
1319
+ RubySMB::Error::EncryptionError,
1320
+ 'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
1321
+ )
1322
+ end
1323
+ end
1324
+ end
1325
+
1326
+ context 'when selecting the encryption algorithm' do
1327
+ context 'with one algorithm' do
1328
+ it 'selects the expected algorithm' do
1329
+ smb3_client.parse_smb3_capabilities(smb3_response)
1330
+ expect(smb3_client.encryption_algorithm).to eq('AES-128-CCM')
1331
+ end
1332
+ end
1333
+
1334
+ context 'with multiple algorithms' do
1335
+ it 'selects the AES-128-GCM algorithm if included' do
1336
+ nc = smb3_response.find_negotiate_context(
1337
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1338
+ )
1339
+ nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
1340
+ smb3_client.parse_smb3_capabilities(smb3_response)
1341
+ expect(smb3_client.encryption_algorithm).to eq('AES-128-GCM')
1342
+ end
1343
+
1344
+ it 'selects the first algorithm if AES-128-GCM is not included' do
1345
+ nc = smb3_response.find_negotiate_context(
1346
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1347
+ )
1348
+ nc.data.ciphers << 3
1349
+ smb3_client.parse_smb3_capabilities(smb3_response)
1350
+ expect(smb3_client.encryption_algorithm).to eq('AES-128-CCM')
1351
+ end
1352
+
1353
+ it 'keep tracks of the server supported algorithms' do
1354
+ nc = smb3_response.find_negotiate_context(
1355
+ RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1356
+ )
1357
+ nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
1358
+ smb3_client.parse_smb3_capabilities(smb3_response)
1359
+ expect(smb3_client.server_encryption_algorithms).to eq([1, 2])
1360
+ end
1361
+ end
1362
+
1363
+ context 'without encryption context' do
1364
+ it 'raises the expected exception' do
1365
+ smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
1366
+ smb3_response.add_negotiate_context(nc_integrity)
1367
+ expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
1368
+ RubySMB::Error::EncryptionError,
1369
+ 'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
1370
+ )
1371
+ end
1372
+ end
1373
+
1374
+ context 'with an unknown encryption algorithm' do
1375
+ it 'raises the expected exception' do
1376
+ smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
1377
+ smb3_response.add_negotiate_context(nc_integrity)
1378
+ nc = RubySMB::SMB2::NegotiateContext.new(
1379
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1380
+ )
1381
+ nc.data.ciphers << 14
1382
+ smb3_response.add_negotiate_context(nc)
1383
+ expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
1384
+ RubySMB::Error::EncryptionError,
1385
+ 'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
1386
+ )
1387
+ end
1388
+ end
1389
+ end
1390
+
1391
+ context 'when selecting the compression algorithm' do
1392
+ it 'keep tracks of the server supported algorithms' do
1393
+ nc = RubySMB::SMB2::NegotiateContext.new(
1394
+ context_type: RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
1395
+ )
1396
+ nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZNT1
1397
+ nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77
1398
+ nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman
1399
+ nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::Pattern_V1
1400
+ smb3_response.add_negotiate_context(nc)
1401
+ smb3_client.parse_smb3_capabilities(smb3_response)
1402
+ expect(smb3_client.server_compression_algorithms).to eq([1, 2, 3, 4])
1403
+ end
1404
+ end
552
1405
  end
553
1406
  end
554
1407
  end
@@ -875,6 +1728,39 @@ RSpec.describe RubySMB::Client do
875
1728
  smb2_client.smb2_authenticate
876
1729
  expect(smb2_client.os_version).to eq '6.1.7601'
877
1730
  end
1731
+
1732
+ ['0x0202', '0x0210', '0x0300', '0x0302'].each do |dialect|
1733
+ it "does not update the preauth hash with dialect #{dialect}" do
1734
+ smb2_client.dialect = dialect
1735
+ expect(smb2_client).to_not receive(:update_preauth_hash)
1736
+ smb2_client.smb2_authenticate
1737
+ end
1738
+ end
1739
+
1740
+ it "updates the preauth hash with dialect 0x0311" do
1741
+ smb2_client.dialect = '0x0311'
1742
+ expect(smb2_client).to receive(:update_preauth_hash).with(response_packet)
1743
+ smb2_client.smb2_authenticate
1744
+ end
1745
+
1746
+ context 'when setting the session_encrypt_data parameter' do
1747
+ before :example do
1748
+ smb2_client.smb3 = true
1749
+ smb2_client.session_encrypt_data = false
1750
+ end
1751
+
1752
+ it 'sets the session_encrypt_data parameter to true if the server requires encryption' do
1753
+ final_response_packet.session_flags.encrypt_data = 1
1754
+ smb2_client.smb2_authenticate
1755
+ expect(smb2_client.session_encrypt_data).to be true
1756
+ end
1757
+
1758
+ it 'does not set the session_encrypt_data parameter if the server does not require encryption' do
1759
+ final_response_packet.session_flags.encrypt_data = 0
1760
+ smb2_client.smb2_authenticate
1761
+ expect(smb2_client.session_encrypt_data).to be false
1762
+ end
1763
+ end
878
1764
  end
879
1765
 
880
1766
  describe '#smb2_ntlmssp_negotiate_packet' do
@@ -890,20 +1776,34 @@ RSpec.describe RubySMB::Client do
890
1776
  smb2_client.smb2_ntlmssp_negotiate_packet
891
1777
  end
892
1778
 
893
- it 'sets the message ID in the packet header to 1' do
894
- expect(smb2_client.smb2_ntlmssp_negotiate_packet.smb2_header.message_id).to eq 1
895
- end
896
-
897
- it 'increments client#smb2_message_id' do
898
- expect { smb2_client.smb2_ntlmssp_negotiate_packet }.to change(smb2_client, :smb2_message_id).to(2)
1779
+ it 'enables signing' do
1780
+ expect(smb2_client.smb2_ntlmssp_negotiate_packet.security_mode.signing_enabled).to eq 1
899
1781
  end
900
1782
  end
901
1783
 
902
1784
  describe '#smb2_ntlmssp_negotiate' do
1785
+ before :example do
1786
+ allow(smb2_client).to receive(:smb2_ntlmssp_negotiate_packet).and_return(negotiate_packet)
1787
+ allow(smb2_client).to receive(:send_recv)
1788
+ end
1789
+
903
1790
  it 'sends the request packet and receives a response' do
904
- expect(smb2_client).to receive(:smb2_ntlmssp_negotiate_packet).and_return(negotiate_packet)
905
- expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
906
- expect(dispatcher).to receive(:recv_packet)
1791
+ expect(smb2_client).to receive(:smb2_ntlmssp_negotiate_packet)
1792
+ expect(smb2_client).to receive(:send_recv).with(negotiate_packet)
1793
+ smb2_client.smb2_ntlmssp_negotiate
1794
+ end
1795
+
1796
+ ['0x0202', '0x0210', '0x0300', '0x0302'].each do |dialect|
1797
+ it "does not update the preauth hash with dialect #{dialect}" do
1798
+ smb2_client.dialect = dialect
1799
+ expect(smb2_client).to_not receive(:update_preauth_hash)
1800
+ smb2_client.smb2_ntlmssp_negotiate
1801
+ end
1802
+ end
1803
+
1804
+ it "updates the preauth hash with dialect 0x0311" do
1805
+ smb2_client.dialect = '0x0311'
1806
+ expect(smb2_client).to receive(:update_preauth_hash).with(negotiate_packet)
907
1807
  smb2_client.smb2_ntlmssp_negotiate
908
1808
  end
909
1809
  end
@@ -961,13 +1861,35 @@ RSpec.describe RubySMB::Client do
961
1861
  it 'sets the session ID on the request packet' do
962
1862
  expect(smb2_client.smb2_ntlmssp_auth_packet(type3_message, session_id).smb2_header.session_id).to eq session_id
963
1863
  end
1864
+
1865
+ it 'enables signing' do
1866
+ expect(smb2_client.smb2_ntlmssp_auth_packet(type3_message, session_id).security_mode.signing_enabled).to eq 1
1867
+ end
964
1868
  end
965
1869
 
966
1870
  describe '#smb2_ntlmssp_authenticate' do
1871
+ before :example do
1872
+ allow(smb2_client).to receive(:smb2_ntlmssp_auth_packet).and_return(negotiate_packet)
1873
+ allow(smb2_client).to receive(:send_recv)
1874
+ end
1875
+
967
1876
  it 'sends the request packet and receives a response' do
968
- expect(smb2_client).to receive(:smb2_ntlmssp_auth_packet).and_return(negotiate_packet)
969
- expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
970
- expect(dispatcher).to receive(:recv_packet)
1877
+ expect(smb2_client).to receive(:smb2_ntlmssp_auth_packet)
1878
+ expect(smb2_client).to receive(:send_recv).with(negotiate_packet)
1879
+ smb2_client.smb2_ntlmssp_authenticate(type3_message, session_id)
1880
+ end
1881
+
1882
+ ['0x0202', '0x0210', '0x0300', '0x0302'].each do |dialect|
1883
+ it "does not update the preauth hash with dialect #{dialect}" do
1884
+ smb2_client.dialect = dialect
1885
+ expect(smb2_client).to_not receive(:update_preauth_hash)
1886
+ smb2_client.smb2_ntlmssp_authenticate(type3_message, session_id)
1887
+ end
1888
+ end
1889
+
1890
+ it "updates the preauth hash with dialect 0x0311" do
1891
+ smb2_client.dialect = '0x0311'
1892
+ expect(smb2_client).to receive(:update_preauth_hash).with(negotiate_packet)
971
1893
  smb2_client.smb2_ntlmssp_authenticate(type3_message, session_id)
972
1894
  end
973
1895
  end
@@ -1103,6 +2025,108 @@ RSpec.describe RubySMB::Client do
1103
2025
  end
1104
2026
  end
1105
2027
  end
2028
+
2029
+ describe '#smb3_sign' do
2030
+ context 'if signing is required and we have a session key' do
2031
+ let(:request) {
2032
+ packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
2033
+ packet.smb2_header.flags.signed = 1
2034
+ packet.smb2_header.signature = "\x00" * 16
2035
+ packet
2036
+ }
2037
+ let(:session_key) { 'Session Key' }
2038
+ before :example do
2039
+ smb3_client.session_key = session_key
2040
+ smb3_client.signing_required = true
2041
+ end
2042
+
2043
+ ['0x0300', '0x0302'].each do |dialect|
2044
+ context "with #{dialect} dialect" do
2045
+ it 'generates the signing key based on the session key and specific strings, and sign the packet with CMAC' do
2046
+ smb3_client.dialect = dialect
2047
+ fake_hash = "\x34\xc0\x40\xfe\x87\xcf\x49\x3d\x37\x87\x52\xd0\xd5\xf5\xfb\x86".b
2048
+ signing_key = RubySMB::Crypto::KDF.counter_mode(session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
2049
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(session_key, "SMB2AESCMAC\x00", "SmbSign\x00").and_call_original
2050
+ expect(OpenSSL::CMAC).to receive(:digest).with('AES', signing_key, request.to_binary_s).and_call_original
2051
+ expect(smb3_client.smb3_sign(request).smb2_header.signature).to eq fake_hash
2052
+ end
2053
+ end
2054
+ end
2055
+
2056
+ context "with 0x0311 dialect" do
2057
+ it 'generates the signing key based on the session key, the preauth integrity hash and specific strings, and sign the packet with CMAC' do
2058
+ smb3_client.dialect = '0x0311'
2059
+ preauth_integrity_hash_value = 'Preauth Integrity Hash'
2060
+ fake_hash = "\x0e\x49\x6f\x8e\x74\x7c\xf2\xa0\x88\x5e\x9d\x54\xff\x0d\x0d\xfa".b
2061
+ smb3_client.preauth_integrity_hash_value = preauth_integrity_hash_value
2062
+ signing_key = RubySMB::Crypto::KDF.counter_mode(session_key, "SMBSigningKey\x00", preauth_integrity_hash_value)
2063
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(session_key, "SMBSigningKey\x00", preauth_integrity_hash_value).and_call_original
2064
+ expect(OpenSSL::CMAC).to receive(:digest).with('AES', signing_key, request.to_binary_s).and_call_original
2065
+ expect(smb3_client.smb3_sign(request).smb2_header.signature).to eq fake_hash
2066
+ end
2067
+ end
2068
+
2069
+ context 'with an incompatible dialect' do
2070
+ it 'raises the expected exception' do
2071
+ smb3_client.dialect = '0x0202'
2072
+ expect { smb3_client.smb3_sign(request) }.to raise_error(
2073
+ RubySMB::Error::SigningError,
2074
+ 'Dialect is incompatible with SMBv3 signing'
2075
+ )
2076
+ end
2077
+ end
2078
+ end
2079
+
2080
+ context 'if signing is not required but it is a TreeConnectRequest and we have a session key' do
2081
+ let(:request) {
2082
+ packet = RubySMB::SMB2::Packet::TreeConnectRequest.new
2083
+ packet.smb2_header.flags.signed = 1
2084
+ packet.smb2_header.signature = "\x00" * 16
2085
+ packet
2086
+ }
2087
+ let(:session_key) { 'Session Key' }
2088
+ before :example do
2089
+ smb3_client.session_key = session_key
2090
+ smb3_client.signing_required = false
2091
+ end
2092
+
2093
+ ['0x0300', '0x0302'].each do |dialect|
2094
+ context "with #{dialect} dialect" do
2095
+ it 'generates the signing key based on the session key and specific strings, and sign the packet with CMAC' do
2096
+ smb3_client.dialect = dialect
2097
+ fake_hash = "\x34\x9e\x28\xb9\x50\x08\x34\x31\xc0\x83\x9d\xba\x56\xa5\x70\xa4".b
2098
+ signing_key = RubySMB::Crypto::KDF.counter_mode(session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
2099
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(session_key, "SMB2AESCMAC\x00", "SmbSign\x00").and_call_original
2100
+ expect(OpenSSL::CMAC).to receive(:digest).with('AES', signing_key, request.to_binary_s).and_call_original
2101
+ expect(smb3_client.smb3_sign(request).smb2_header.signature).to eq fake_hash
2102
+ end
2103
+ end
2104
+ end
2105
+
2106
+ context "with 0x0311 dialect" do
2107
+ it 'generates the signing key based on the session key, the preauth integrity hash and specific strings, and sign the packet with CMAC' do
2108
+ smb3_client.dialect = '0x0311'
2109
+ preauth_integrity_hash_value = 'Preauth Integrity Hash'
2110
+ fake_hash = "\x83\xd9\x31\x39\x60\x46\xbe\x1e\x29\x34\xc8\xcf\x8c\x8e\xb4\x73".b
2111
+ smb3_client.preauth_integrity_hash_value = preauth_integrity_hash_value
2112
+ signing_key = RubySMB::Crypto::KDF.counter_mode(session_key, "SMBSigningKey\x00", preauth_integrity_hash_value)
2113
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(session_key, "SMBSigningKey\x00", preauth_integrity_hash_value).and_call_original
2114
+ expect(OpenSSL::CMAC).to receive(:digest).with('AES', signing_key, request.to_binary_s).and_call_original
2115
+ expect(smb3_client.smb3_sign(request).smb2_header.signature).to eq fake_hash
2116
+ end
2117
+ end
2118
+
2119
+ context 'with an incompatible dialect' do
2120
+ it 'raises the expected exception' do
2121
+ smb3_client.dialect = '0x0202'
2122
+ expect { smb3_client.smb3_sign(request) }.to raise_error(
2123
+ RubySMB::Error::SigningError,
2124
+ 'Dialect is incompatible with SMBv3 signing'
2125
+ )
2126
+ end
2127
+ end
2128
+ end
2129
+ end
1106
2130
  end
1107
2131
 
1108
2132
  context '#increment_smb_message_id' do
@@ -1156,7 +2180,10 @@ RSpec.describe RubySMB::Client do
1156
2180
 
1157
2181
  it 'raises an UnexpectedStatusCode exception if we do not get STATUS_SUCCESS' do
1158
2182
  response.smb_header.nt_status = 0xc0000015
1159
- expect { smb1_client.smb1_tree_from_response(path, response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
2183
+ expect { smb1_client.smb1_tree_from_response(path, response) }.to raise_error(
2184
+ RubySMB::Error::UnexpectedStatusCode,
2185
+ 'The server responded with an unexpected status code: STATUS_NONEXISTENT_SECTOR'
2186
+ )
1160
2187
  end
1161
2188
 
1162
2189
  it 'creates a new Tree from itself, the share path, and the response packet' do
@@ -1177,11 +2204,14 @@ RSpec.describe RubySMB::Client do
1177
2204
  }
1178
2205
 
1179
2206
  describe '#smb2_tree_connect' do
1180
- it 'builds and sends a TreeconnectRequest for the supplied share' do
2207
+ it 'builds and sends the expected TreeconnectRequest for the supplied share' do
1181
2208
  allow(RubySMB::SMB2::Packet::TreeConnectRequest).to receive(:new).and_return(request)
1182
- modified_request = request
1183
- modified_request.encode_path(path)
1184
- expect(smb2_client).to receive(:send_recv).with(modified_request).and_return(response.to_binary_s)
2209
+ expect(smb2_client).to receive(:send_recv) do |req|
2210
+ expect(req).to eq(request)
2211
+ expect(req.smb2_header.tree_id).to eq(65_535)
2212
+ expect(req.path).to eq(path.encode('UTF-16LE'))
2213
+ response.to_binary_s
2214
+ end
1185
2215
  smb2_client.smb2_tree_connect(path)
1186
2216
  end
1187
2217
 
@@ -1200,11 +2230,20 @@ RSpec.describe RubySMB::Client do
1200
2230
 
1201
2231
  it 'raises an UnexpectedStatusCode exception if we do not get STATUS_SUCCESS' do
1202
2232
  response.smb2_header.nt_status = 0xc0000015
1203
- expect { smb2_client.smb2_tree_from_response(path, response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
2233
+ expect { smb2_client.smb2_tree_from_response(path, response) }.to raise_error(
2234
+ RubySMB::Error::UnexpectedStatusCode,
2235
+ 'The server responded with an unexpected status code: STATUS_NONEXISTENT_SECTOR'
2236
+ )
1204
2237
  end
1205
2238
 
1206
2239
  it 'creates a new Tree from itself, the share path, and the response packet' do
1207
- expect(RubySMB::SMB2::Tree).to receive(:new).with(client: smb2_client, share: path, response: response)
2240
+ expect(RubySMB::SMB2::Tree).to receive(:new).with(client: smb2_client, share: path, response: response, encrypt: false)
2241
+ smb2_client.smb2_tree_from_response(path, response)
2242
+ end
2243
+
2244
+ it 'creates a new with encryption set if the response requires it' do
2245
+ response.share_flags.encrypt = 1
2246
+ expect(RubySMB::SMB2::Tree).to receive(:new).with(client: smb2_client, share: path, response: response, encrypt: true)
1208
2247
  smb2_client.smb2_tree_from_response(path, response)
1209
2248
  end
1210
2249
  end
@@ -1301,7 +2340,7 @@ RSpec.describe RubySMB::Client do
1301
2340
  end
1302
2341
 
1303
2342
  it 'raise an InvalidPacket exception when the response is not valid' do
1304
- echo_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
2343
+ echo_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
1305
2344
  allow(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1306
2345
  expect { smb1_client.echo }.to raise_error(RubySMB::Error::InvalidPacket)
1307
2346
  end
@@ -1325,5 +2364,346 @@ RSpec.describe RubySMB::Client do
1325
2364
  end
1326
2365
  end
1327
2366
 
2367
+ context 'Winreg' do
2368
+ describe '#connect_to_winreg' do
2369
+ let(:host) { '1.2.3.4' }
2370
+ let(:share) { "\\\\#{host}\\IPC$" }
2371
+ let(:ipc_tree) { double('IPC$ tree') }
2372
+ let(:named_pipe) { double('Named pipe') }
2373
+ before :example do
2374
+ allow(ipc_tree).to receive_messages(
2375
+ :share => share,
2376
+ :open_file => named_pipe
2377
+ )
2378
+ allow(client).to receive(:tree_connect).and_return(ipc_tree)
2379
+ end
2380
+
2381
+ context 'when the client is already connected to the IPC$ share' do
2382
+ before :example do
2383
+ client.tree_connects << ipc_tree
2384
+ allow(ipc_tree).to receive(:share).and_return(share)
2385
+ end
2386
+
2387
+ it 'does not connect to the already connected tree' do
2388
+ client.connect_to_winreg(host)
2389
+ expect(client).to_not have_received(:tree_connect)
2390
+ end
2391
+ end
2392
+
2393
+ it 'calls #tree_connect' do
2394
+ client.connect_to_winreg(host)
2395
+ expect(client).to have_received(:tree_connect).with(share)
2396
+ end
2397
+
2398
+ it 'open \'winreg\' file on the IPC$ Tree' do
2399
+ client.connect_to_winreg(host)
2400
+ expect(ipc_tree).to have_received(:open_file).with(filename: "winreg", write: true, read: true)
2401
+ end
2402
+
2403
+ it 'returns the expected opened named pipe' do
2404
+ expect(client.connect_to_winreg(host)).to eq(named_pipe)
2405
+ end
2406
+
2407
+ context 'when a block is given' do
2408
+ before :example do
2409
+ allow(named_pipe).to receive(:close)
2410
+ end
2411
+
2412
+ it 'yields the expected named_pipe' do
2413
+ client.connect_to_winreg(host) do |np|
2414
+ expect(np).to eq(named_pipe)
2415
+ end
2416
+ end
2417
+
2418
+ it 'closes the named pipe' do
2419
+ client.connect_to_winreg(host) { |np| }
2420
+ expect(named_pipe).to have_received(:close)
2421
+ end
2422
+
2423
+ it 'returns the block return value' do
2424
+ result = double('Result')
2425
+ expect(client.connect_to_winreg(host) { |np| result }).to eq(result)
2426
+ end
2427
+ end
2428
+ end
2429
+
2430
+ describe '#has_registry_key?' do
2431
+ let(:host) { '1.2.3.4' }
2432
+ let(:key) { 'HKLM\\Registry\\Key' }
2433
+ let(:named_pipe) { double('Named pipe') }
2434
+ let(:result) { double('Result') }
2435
+ before :example do
2436
+ allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
2437
+ allow(named_pipe).to receive(:has_registry_key?).and_return(result)
2438
+ end
2439
+
2440
+ it 'calls #connect_to_winreg to wrap the main logic around' do
2441
+ client.has_registry_key?(host, key)
2442
+ expect(client).to have_received(:connect_to_winreg).with(host)
2443
+ end
2444
+
2445
+ it 'calls Pipe #has_registry_key?' do
2446
+ client.has_registry_key?(host, key)
2447
+ expect(named_pipe).to have_received(:has_registry_key?).with(key)
2448
+ end
2449
+ end
2450
+
2451
+ describe '#read_registry_key_value' do
2452
+ let(:host) { '1.2.3.4' }
2453
+ let(:key) { 'HKLM\\Registry\\Key' }
2454
+ let(:value_name) { 'Value' }
2455
+ let(:named_pipe) { double('Named pipe') }
2456
+ let(:result) { double('Result') }
2457
+ before :example do
2458
+ allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
2459
+ allow(named_pipe).to receive(:read_registry_key_value).and_return(result)
2460
+ end
2461
+
2462
+ it 'calls #connect_to_winreg to wrap the main logic around' do
2463
+ client.read_registry_key_value(host, key, value_name)
2464
+ expect(client).to have_received(:connect_to_winreg).with(host)
2465
+ end
2466
+
2467
+ it 'calls Pipe #read_registry_key_value' do
2468
+ client.read_registry_key_value(host, key, value_name)
2469
+ expect(named_pipe).to have_received(:read_registry_key_value).with(key, value_name)
2470
+ end
2471
+ end
2472
+
2473
+ describe '#enum_registry_key' do
2474
+ let(:host) { '1.2.3.4' }
2475
+ let(:key) { 'HKLM\\Registry\\Key' }
2476
+ let(:named_pipe) { double('Named pipe') }
2477
+ let(:result) { double('Result') }
2478
+ before :example do
2479
+ allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
2480
+ allow(named_pipe).to receive(:enum_registry_key).and_return(result)
2481
+ end
2482
+
2483
+ it 'calls #connect_to_winreg to wrap the main logic around' do
2484
+ client.enum_registry_key(host, key)
2485
+ expect(client).to have_received(:connect_to_winreg).with(host)
2486
+ end
2487
+
2488
+ it 'calls Pipe #enum_registry_key' do
2489
+ client.enum_registry_key(host, key)
2490
+ expect(named_pipe).to have_received(:enum_registry_key).with(key)
2491
+ end
2492
+ end
2493
+
2494
+ describe '#enum_registry_values' do
2495
+ let(:host) { '1.2.3.4' }
2496
+ let(:key) { 'HKLM\\Registry\\Key' }
2497
+ let(:named_pipe) { double('Named pipe') }
2498
+ let(:result) { double('Result') }
2499
+ before :example do
2500
+ allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
2501
+ allow(named_pipe).to receive(:enum_registry_values).and_return(result)
2502
+ end
2503
+
2504
+ it 'calls #connect_to_winreg to wrap the main logic around' do
2505
+ client.enum_registry_values(host, key)
2506
+ expect(client).to have_received(:connect_to_winreg).with(host)
2507
+ end
2508
+
2509
+ it 'calls Pipe #enum_registry_values' do
2510
+ client.enum_registry_values(host, key)
2511
+ expect(named_pipe).to have_received(:enum_registry_values).with(key)
2512
+ end
2513
+ end
2514
+ end
2515
+
2516
+ describe '#update_preauth_hash' do
2517
+ it 'raises an EncryptionError exception if the preauth integrity hash algorithm is not known' do
2518
+ expect { client.update_preauth_hash('Test') }.to raise_error(
2519
+ RubySMB::Error::EncryptionError,
2520
+ 'Cannot compute the Preauth Integrity Hash value: Preauth Integrity Hash Algorithm is nil'
2521
+ )
2522
+ end
2523
+
2524
+ it 'computes the hash value' do
2525
+ packet = RubySMB::SMB2::Packet::EchoRequest.new
2526
+ data = 'Previous hash'
2527
+ algo = RubySMB::SMB2::PreauthIntegrityCapabilities::HASH_ALGORITM_MAP[
2528
+ RubySMB::SMB2::PreauthIntegrityCapabilities::SHA_512
2529
+ ]
2530
+ client.preauth_integrity_hash_algorithm = algo
2531
+ client.preauth_integrity_hash_value = data
2532
+ hash = OpenSSL::Digest.digest(algo, data + packet.to_binary_s)
2533
+ client.update_preauth_hash(packet)
2534
+ expect(client.preauth_integrity_hash_value).to eq(hash)
2535
+ end
2536
+ end
2537
+
2538
+ context 'Encryption' do
2539
+ describe '#smb3_encrypt' do
2540
+ let(:transform_packet) { double('TransformHeader packet') }
2541
+ let(:session_key) { "\x5c\x00\x4a\x3b\xf0\xa2\x4f\x75\x4c\xb2\x74\x0a\xcf\xc4\x8e\x1a".b }
2542
+ let(:data) { RubySMB::SMB2::Packet::TreeConnectRequest.new.to_binary_s }
2543
+
2544
+ before :example do
2545
+ allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:new).and_return(transform_packet)
2546
+ allow(transform_packet).to receive(:encrypt)
2547
+ client.session_key = session_key
2548
+ end
2549
+
2550
+ it 'does not generate a new client encryption key if it already exists' do
2551
+ client.client_encryption_key = 'key'
2552
+ expect(RubySMB::Crypto::KDF).to_not receive(:counter_mode)
2553
+ expect(client.client_encryption_key).to eq('key')
2554
+ client.smb3_encrypt(data)
2555
+ end
2556
+
2557
+ ['0x0300', '0x0302'].each do |dialect|
2558
+ context "with #{dialect} dialect" do
2559
+ before :example do
2560
+ client.dialect = dialect
2561
+ end
2562
+
2563
+ it 'generates the client encryption key with the expected parameters' do
2564
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(
2565
+ session_key,
2566
+ "SMB2AESCCM\x00",
2567
+ "ServerIn \x00"
2568
+ ).and_call_original
2569
+ client.smb3_encrypt(data)
2570
+ end
2571
+ end
2572
+ end
2573
+
2574
+ context 'with 0x0311 dialect' do
2575
+ it 'generates the client encryption key with the expected parameters' do
2576
+ client.preauth_integrity_hash_value = ''
2577
+ client.dialect = '0x0311'
2578
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(
2579
+ session_key,
2580
+ "SMBC2SCipherKey\x00",
2581
+ ''
2582
+ ).and_call_original
2583
+ client.smb3_encrypt(data)
2584
+ end
2585
+ end
2586
+
2587
+ it 'raises the expected exception if the dialect is incompatible' do
2588
+ client.dialect = '0x0202'
2589
+ expect { client.smb3_encrypt(data) }.to raise_error(RubySMB::Error::EncryptionError)
2590
+ end
2591
+
2592
+ it 'creates a TransformHeader packet and encrypt the data' do
2593
+ client.dialect = '0x0300'
2594
+ client.encryption_algorithm = 'AES-128-CCM'
2595
+ client.session_id = 123
2596
+ client.smb3_encrypt(data)
2597
+ expect(RubySMB::SMB2::Packet::TransformHeader).to have_received(:new).with(flags: 1, session_id: 123)
2598
+ expect(transform_packet).to have_received(:encrypt).with(data, client.client_encryption_key, algorithm: 'AES-128-CCM')
2599
+ end
2600
+
2601
+ it 'generates the expected client encryption key with 0x0302 dialect' do
2602
+ client.dialect = '0x0302'
2603
+ expected_enc_key =
2604
+ "\xa4\xfa\x23\xc1\xb0\x65\x84\xce\x47\x08\x5b\xe0\x64\x98\xd7\x87".b
2605
+ client.smb3_encrypt(data)
2606
+ expect(client.client_encryption_key).to eq expected_enc_key
2607
+ end
2608
+
2609
+ it 'generates the expected client encryption key with 0x0311 dialect' do
2610
+ client.dialect = '0x0311'
2611
+ client.session_key =
2612
+ "\x5c\x00\x4a\x3b\xf0\xa2\x4f\x75\x4c\xb2\x74\x0a\xcf\xc4\x8e\x1a".b
2613
+ client.preauth_integrity_hash_value =
2614
+ "\x57\x77\x7d\x47\xc2\xa9\xc8\x23\x6e\x8a\xfa\x39\xe8\x77\x2f\xb0\xb6"\
2615
+ "\x01\xba\x85\x58\x77\xf5\x01\xa0\xf0\x31\x69\x6a\x64\x49\x1c\x61\xdb"\
2616
+ "\x57\x34\x19\x1b\x80\x33\x9a\xfa\x1d\x6c\x3f\xca\x44\x68\x78\x5b\xb9"\
2617
+ "\xda\x41\xfa\x83\xe5\xa9\x6f\xcf\x44\xbc\xe5\x26\x6e".b
2618
+ expected_enc_key =
2619
+ "\xc7\x4e\xfe\x4d\x15\x48\x5b\x0b\x71\x45\x49\x26\x8a\xd9\x6c\xaa".b
2620
+ client.smb3_encrypt(data)
2621
+ expect(client.client_encryption_key).to eq expected_enc_key
2622
+ end
2623
+ end
2624
+
2625
+ describe '#smb3_decrypt' do
2626
+ let(:transform_packet) { double('TransformHeader packet') }
2627
+ let(:session_key) { "\x5c\x00\x4a\x3b\xf0\xa2\x4f\x75\x4c\xb2\x74\x0a\xcf\xc4\x8e\x1a".b }
2628
+
2629
+ before :example do
2630
+ allow(transform_packet).to receive(:decrypt)
2631
+ client.session_key = session_key
2632
+ end
2633
+
2634
+ it 'does not generate a new server encryption key if it already exists' do
2635
+ client.server_encryption_key = 'key'
2636
+ expect(RubySMB::Crypto::KDF).to_not receive(:counter_mode)
2637
+ expect(client.server_encryption_key).to eq('key')
2638
+ client.smb3_decrypt(transform_packet)
2639
+ end
2640
+
2641
+ ['0x0300', '0x0302'].each do |dialect|
2642
+ context "with #{dialect} dialect" do
2643
+ before :example do
2644
+ client.dialect = dialect
2645
+ end
2646
+
2647
+ it 'generates the client encryption key with the expected parameters' do
2648
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(
2649
+ session_key,
2650
+ "SMB2AESCCM\x00",
2651
+ "ServerOut\x00"
2652
+ ).and_call_original
2653
+ client.smb3_decrypt(transform_packet)
2654
+ end
2655
+ end
2656
+ end
2657
+
2658
+ context 'with 0x0311 dialect' do
2659
+ it 'generates the client encryption key with the expected parameters' do
2660
+ client.preauth_integrity_hash_value = ''
2661
+ client.dialect = '0x0311'
2662
+ expect(RubySMB::Crypto::KDF).to receive(:counter_mode).with(
2663
+ session_key,
2664
+ "SMBS2CCipherKey\x00",
2665
+ ''
2666
+ ).and_call_original
2667
+ client.smb3_decrypt(transform_packet)
2668
+ end
2669
+ end
2670
+
2671
+ it 'raises the expected exception if the dialect is incompatible' do
2672
+ client.dialect = '0x0202'
2673
+ expect { client.smb3_decrypt(transform_packet) }.to raise_error(RubySMB::Error::EncryptionError)
2674
+ end
2675
+
2676
+ it 'creates a TransformHeader packet and encrypt the data' do
2677
+ client.dialect = '0x0300'
2678
+ client.encryption_algorithm = 'AES-128-CCM'
2679
+ client.session_id = 123
2680
+ client.smb3_decrypt(transform_packet)
2681
+ expect(transform_packet).to have_received(:decrypt).with(client.server_encryption_key, algorithm: 'AES-128-CCM')
2682
+ end
2683
+
2684
+ it 'generates the expected server encryption key with 0x0302 dialect' do
2685
+ client.dialect = '0x0302'
2686
+ expected_enc_key =
2687
+ "\x65\x21\xd3\x6d\xe9\xe3\x5a\x66\x09\x61\xae\x3e\xc6\x49\x6b\xdf".b
2688
+ client.smb3_decrypt(transform_packet)
2689
+ expect(client.server_encryption_key).to eq expected_enc_key
2690
+ end
2691
+
2692
+ it 'generates the expected server encryption key with 0x0311 dialect' do
2693
+ client.dialect = '0x0311'
2694
+ client.session_key =
2695
+ "\x5c\x00\x4a\x3b\xf0\xa2\x4f\x75\x4c\xb2\x74\x0a\xcf\xc4\x8e\x1a".b
2696
+ client.preauth_integrity_hash_value =
2697
+ "\x57\x77\x7d\x47\xc2\xa9\xc8\x23\x6e\x8a\xfa\x39\xe8\x77\x2f\xb0\xb6"\
2698
+ "\x01\xba\x85\x58\x77\xf5\x01\xa0\xf0\x31\x69\x6a\x64\x49\x1c\x61\xdb"\
2699
+ "\x57\x34\x19\x1b\x80\x33\x9a\xfa\x1d\x6c\x3f\xca\x44\x68\x78\x5b\xb9"\
2700
+ "\xda\x41\xfa\x83\xe5\xa9\x6f\xcf\x44\xbc\xe5\x26\x6e".b
2701
+ expected_enc_key =
2702
+ "\x8c\x2c\x31\x15\x66\xba\xa9\xab\xcf\xb2\x47\x8d\x72\xd5\xd7\x4a".b
2703
+ client.smb3_decrypt(transform_packet)
2704
+ expect(client.server_encryption_key).to eq expected_enc_key
2705
+ end
2706
+ end
2707
+ end
1328
2708
  end
1329
2709