ruby_smb 1.0.4 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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