ruby_smb 2.0.10 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/examples/auth_capture.rb +71 -0
- data/lib/ruby_smb/client/negotiation.rb +1 -1
- data/lib/ruby_smb/client.rb +9 -8
- data/lib/ruby_smb/dialect.rb +45 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
- data/lib/ruby_smb/gss/provider.rb +35 -0
- data/lib/ruby_smb/gss.rb +56 -63
- data/lib/ruby_smb/ntlm.rb +45 -0
- data/lib/ruby_smb/server/server_client/negotiation.rb +155 -0
- data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
- data/lib/ruby_smb/server/server_client.rb +163 -0
- data/lib/ruby_smb/server.rb +54 -0
- data/lib/ruby_smb/signing.rb +59 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
- data/lib/ruby_smb/smb2.rb +3 -1
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +2 -1
- data/spec/lib/ruby_smb/client_spec.rb +7 -9
- data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
- data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
- data/spec/lib/ruby_smb/server_spec.rb +32 -0
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +25 -3
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/client/signing.rb +0 -64
@@ -209,6 +209,8 @@ RSpec.describe RubySMB::Client do
|
|
209
209
|
|
210
210
|
context 'when signing' do
|
211
211
|
it 'calls #smb1_sign if it is an SMB1 packet' do
|
212
|
+
allow(client).to receive(:signing_required).and_return(true)
|
213
|
+
allow(client).to receive(:session_key).and_return(Random.new.bytes(16))
|
212
214
|
expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
|
213
215
|
client.send_recv(smb1_request)
|
214
216
|
end
|
@@ -223,15 +225,11 @@ RSpec.describe RubySMB::Client do
|
|
223
225
|
|
224
226
|
it 'calls #smb2_sign if it is an SMB2 client' do
|
225
227
|
allow(smb2_client).to receive(:is_status_pending?).and_return(false)
|
228
|
+
allow(smb2_client).to receive(:signing_required).and_return(true)
|
229
|
+
allow(smb2_client).to receive(:session_key).and_return(Random.new.bytes(16))
|
226
230
|
expect(smb2_client).to receive(:smb2_sign).with(smb2_request).and_call_original
|
227
231
|
smb2_client.send_recv(smb2_request)
|
228
232
|
end
|
229
|
-
|
230
|
-
it 'calls #smb3_sign if it is an SMB3 client' do
|
231
|
-
allow(smb3_client).to receive(:is_status_pending?).and_return(false)
|
232
|
-
expect(smb3_client).to receive(:smb3_sign).with(smb2_request).and_call_original
|
233
|
-
smb3_client.send_recv(smb2_request)
|
234
|
-
end
|
235
233
|
end
|
236
234
|
end
|
237
235
|
|
@@ -2087,7 +2085,7 @@ RSpec.describe RubySMB::Client do
|
|
2087
2085
|
it 'generates the HMAC based on the packet and the NTLM session key and signs the packet with it' do
|
2088
2086
|
smb2_client.session_key = 'foo'
|
2089
2087
|
smb2_client.signing_required = true
|
2090
|
-
expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest
|
2088
|
+
expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest), smb2_client.session_key, request1.to_binary_s).and_return(fake_hmac)
|
2091
2089
|
expect(smb2_client.smb2_sign(request1).smb2_header.signature).to eq fake_hmac
|
2092
2090
|
end
|
2093
2091
|
end
|
@@ -2187,7 +2185,7 @@ RSpec.describe RubySMB::Client do
|
|
2187
2185
|
smb3_client.dialect = '0x0202'
|
2188
2186
|
expect { smb3_client.smb3_sign(request) }.to raise_error(
|
2189
2187
|
RubySMB::Error::SigningError,
|
2190
|
-
'Dialect is incompatible with SMBv3 signing'
|
2188
|
+
'Dialect "0x0202" is incompatible with SMBv3 signing'
|
2191
2189
|
)
|
2192
2190
|
end
|
2193
2191
|
end
|
@@ -2237,7 +2235,7 @@ RSpec.describe RubySMB::Client do
|
|
2237
2235
|
smb3_client.dialect = '0x0202'
|
2238
2236
|
expect { smb3_client.smb3_sign(request) }.to raise_error(
|
2239
2237
|
RubySMB::Error::SigningError,
|
2240
|
-
'Dialect is incompatible with SMBv3 signing'
|
2238
|
+
'Dialect "0x0202" is incompatible with SMBv3 signing'
|
2241
2239
|
)
|
2242
2240
|
end
|
2243
2241
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe RubySMB::Gss::Provider::NTLM::Account do
|
2
|
+
let(:username) { 'RubySMB' }
|
3
|
+
let(:password) { 'password' }
|
4
|
+
let(:domain) { 'WORKGROUP' }
|
5
|
+
subject(:account) { RubySMB::Gss::Provider::NTLM::Account.new(username, password, domain) }
|
6
|
+
|
7
|
+
it { is_expected.to respond_to :username }
|
8
|
+
it { is_expected.to respond_to :password }
|
9
|
+
it { is_expected.to respond_to :domain }
|
10
|
+
|
11
|
+
it 'sets the username correct' do
|
12
|
+
expect(account.username).to eq username
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets the password correctly' do
|
16
|
+
expect(account.password).to eq password
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'sets the domain correctly' do
|
20
|
+
expect(account.domain).to eq domain
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#to_s' do
|
24
|
+
it 'converts to a string' do
|
25
|
+
expect(account.to_s).to be_a String
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'formats the username and domain correctly' do
|
29
|
+
expect(account.to_s).to eq "#{domain}\\#{username}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
|
2
|
+
let(:username) { 'RubySMB' }
|
3
|
+
let(:domain) { 'WORKGROUP' }
|
4
|
+
let(:password) { 'password' }
|
5
|
+
let(:provider) { RubySMB::Gss::Provider::NTLM.new.tap { |provider| provider.put_account(username, password, domain: domain) } }
|
6
|
+
let(:authenticator) { described_class.new(provider, nil) }
|
7
|
+
let(:type1_msg) do
|
8
|
+
Net::NTLM::Message::Type1.new.tap do |msg|
|
9
|
+
msg.domain = domain
|
10
|
+
end
|
11
|
+
end
|
12
|
+
let(:type3_msg) do
|
13
|
+
Net::NTLM::Message::Type2.new.response(user: username, password: '', domain: domain)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#initialize' do
|
17
|
+
it 'defaults to a null session key' do
|
18
|
+
expect(authenticator.session_key).to be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'defaults to a null server challenge' do
|
22
|
+
expect(authenticator.server_challenge).to be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#process' do
|
27
|
+
it 'should handle an empty GSS buffer' do
|
28
|
+
result = authenticator.process
|
29
|
+
expect(result).to be_a RubySMB::Gss::Provider::Result
|
30
|
+
expect(result.nt_status).to eq WindowsError::NTStatus::STATUS_SUCCESS
|
31
|
+
expect(result.buffer).to_not be_empty
|
32
|
+
expect(result.identity).to be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should handle an embedded NTLM type 1 message' do
|
36
|
+
expect(authenticator).to receive(:process_ntlm_type1).and_call_original
|
37
|
+
result = authenticator.process(RubySMB::Gss.gss_type1(type1_msg.serialize))
|
38
|
+
expect(result).to be_a RubySMB::Gss::Provider::Result
|
39
|
+
expect(result.nt_status).to eq WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
|
40
|
+
expect(result.buffer).to_not be_empty
|
41
|
+
expect(result.identity).to be_nil
|
42
|
+
expect(authenticator.session_key).to be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should handle an embedded NTLM type 3 message' do
|
46
|
+
authenticator.server_challenge = Random.new.bytes(8)
|
47
|
+
expect(authenticator).to receive(:process_ntlm_type3).and_call_original
|
48
|
+
result = authenticator.process(RubySMB::Gss.gss_type3(type3_msg.serialize))
|
49
|
+
expect(result).to be_a RubySMB::Gss::Provider::Result
|
50
|
+
expect(result.nt_status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
|
51
|
+
expect(result.buffer).to be_nil
|
52
|
+
expect(result.identity).to be_nil
|
53
|
+
expect(authenticator.session_key).to be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#process_ntlm_type1' do
|
58
|
+
it 'should process a NTLM type 1 message and return a type2 message' do
|
59
|
+
expect(authenticator.process_ntlm_type1(type1_msg)).to be_a Net::NTLM::Message::Type2
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#process_ntlm_type3' do
|
64
|
+
it 'should process a NTLM type 3 message and return an error code' do
|
65
|
+
expect(authenticator.process_ntlm_type3(type3_msg)).to be_a WindowsError::ErrorCode
|
66
|
+
expect(authenticator.process_ntlm_type3(type3_msg)).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#reset!' do
|
71
|
+
it 'should clear the server challenge' do
|
72
|
+
authenticator.instance_variable_set(:@server_challenge, Random.new.bytes(8))
|
73
|
+
authenticator.reset!
|
74
|
+
expect(authenticator.instance_variable_get(:@server_challenge)).to be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should clear the session key' do
|
78
|
+
authenticator.instance_variable_set(:@session_key, Random.new.bytes(16))
|
79
|
+
authenticator.reset!
|
80
|
+
expect(authenticator.instance_variable_get(:@session_key)).to be_nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'a full Net-NTLMv2 authentication exchange' do
|
85
|
+
let(:type2_msg) { authenticator.process_ntlm_type1(type1_msg)}
|
86
|
+
|
87
|
+
it 'should respond to a correct password with STATUS_SUCCESS' do
|
88
|
+
type3_msg = type2_msg.response({user: username, domain: domain, password: password}, ntlmv2: true)
|
89
|
+
type3_msg.user.force_encoding('UTF-16LE')
|
90
|
+
type3_msg.domain.force_encoding('UTF-16LE')
|
91
|
+
expect(authenticator.process_ntlm_type3(type3_msg)).to eq WindowsError::NTStatus::STATUS_SUCCESS
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should respond to an incorrect password with STATUS_LOGON_FAILURE' do
|
95
|
+
type3_msg = type2_msg.response({user: username, domain: domain, password: password + rand(0x41..0x5b).chr}, ntlmv2: true)
|
96
|
+
type3_msg.user.force_encoding('UTF-16LE')
|
97
|
+
type3_msg.domain.force_encoding('UTF-16LE')
|
98
|
+
expect(authenticator.process_ntlm_type3(type3_msg)).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe RubySMB::Gss::Provider::NTLM::OSVersion do
|
2
|
+
subject(:os_version) { RubySMB::Gss::Provider::NTLM::OSVersion.new }
|
3
|
+
|
4
|
+
it { is_expected.to respond_to :major }
|
5
|
+
it { is_expected.to respond_to :minor }
|
6
|
+
it { is_expected.to respond_to :build }
|
7
|
+
it { is_expected.to respond_to :ntlm_revision }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
it 'defaults to an NTLM revision of 15' do
|
11
|
+
expect(os_version.ntlm_revision).to eq 15
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#read' do
|
16
|
+
it 'reads a packed version correctly' do
|
17
|
+
# Version 6.1 (Build 7601); NTLM Current Revision 15
|
18
|
+
os_version = RubySMB::Gss::Provider::NTLM::OSVersion.read("\x06\x01\x1d\xb1\x00\x00\x00\x0f")
|
19
|
+
expect(os_version.major).to eq 6
|
20
|
+
expect(os_version.minor).to eq 1
|
21
|
+
expect(os_version.build).to eq 7601
|
22
|
+
expect(os_version.ntlm_revision).to eq 15
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#to_s' do
|
27
|
+
it 'creates a string representation of the OS version' do
|
28
|
+
expect(os_version.to_s).to be_a String
|
29
|
+
expect(os_version.to_s).to match /Version \d+\.\d+ \(Build \d+\); NTLM Current Revision \d+/
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RubySMB::Gss::Provider::NTLM do
|
4
|
+
let(:provider) { described_class.new }
|
5
|
+
|
6
|
+
it { is_expected.to respond_to :allow_anonymous }
|
7
|
+
it { is_expected.to respond_to :default_domain }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
it 'defaults to false for allowing anonymous access' do
|
11
|
+
expect(provider.allow_anonymous).to be false
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'defaults to a default domain of WORKGROUP' do
|
15
|
+
expect(provider.default_domain).to eq 'WORKGROUP'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'defaults to a random challenge generator' do
|
19
|
+
expect(provider.generate_server_challenge).to_not eq provider.generate_server_challenge
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#generate_server_challenge' do
|
24
|
+
it 'generates a valid 8-byte challenge' do
|
25
|
+
challenge = provider.generate_server_challenge
|
26
|
+
expect(challenge).to be_a String
|
27
|
+
expect(challenge.length).to eq 8
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should take a generator block' do
|
31
|
+
random_challenge = Random.new.bytes(8)
|
32
|
+
provider.generate_server_challenge do
|
33
|
+
random_challenge
|
34
|
+
end
|
35
|
+
expect(provider.generate_server_challenge).to eq random_challenge
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#get_account' do
|
40
|
+
let(:username) { 'RubySMB' }
|
41
|
+
let(:password) { 'password' }
|
42
|
+
let(:domain) { 'WORKGROUP' }
|
43
|
+
|
44
|
+
context 'when getting accounts' do
|
45
|
+
before(:each) do
|
46
|
+
provider.put_account(username, password)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should return nil for an unknown account' do
|
50
|
+
account = provider.get_account('Spencer')
|
51
|
+
expect(account).to be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should work with a case sensitive name' do
|
55
|
+
account = provider.get_account(username)
|
56
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
57
|
+
expect(account.username).to eq username
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should work with a case insensitive name' do
|
61
|
+
account = provider.get_account(username.downcase)
|
62
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
63
|
+
expect(account.username).to eq username
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should work with a case sensitive domain' do
|
67
|
+
account = provider.get_account(username, domain: domain)
|
68
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
69
|
+
expect(account.domain).to eq domain
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should work with a case insensitive domain' do
|
73
|
+
account = provider.get_account(username, domain: domain.downcase)
|
74
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
75
|
+
expect(account.domain).to eq domain
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should work with the special . domain' do
|
79
|
+
account = provider.get_account(username, domain: '.')
|
80
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
81
|
+
expect(account.domain).to eq domain
|
82
|
+
end
|
83
|
+
|
84
|
+
# UTF-16LE is optionally used for encoding some Net-NTLM message fields, the #get_account method should handle it
|
85
|
+
# transparently
|
86
|
+
it 'should work with a UTF16-LE name' do
|
87
|
+
account = provider.get_account(username.encode('UTF-16LE'))
|
88
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
89
|
+
expect(account.username).to eq username
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should work with a UTF16-LE domain' do
|
93
|
+
account = provider.get_account(username, domain: domain.encode('UTF-16LE'))
|
94
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
95
|
+
expect(account.domain).to eq domain
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when putting accounts' do
|
100
|
+
it 'should accept new accounts with the default domain' do
|
101
|
+
provider.put_account(username, password)
|
102
|
+
end
|
103
|
+
|
104
|
+
after(:each) do
|
105
|
+
account = provider.get_account(username, domain: domain)
|
106
|
+
expect(account).to be_a RubySMB::Gss::Provider::NTLM::Account
|
107
|
+
expect(account.username).to eq username
|
108
|
+
expect(account.password).to eq password
|
109
|
+
expect(account.domain).to eq domain
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
RSpec.describe RubySMB::Server::ServerClient do
|
2
|
+
let(:server) { RubySMB::Server.new(server_sock: ::TCPServer.new(0)) }
|
3
|
+
let(:sock) { double('Socket', peeraddr: '192.168.1.5') }
|
4
|
+
let(:dispatcher) { RubySMB::Dispatcher::Socket.new(sock) }
|
5
|
+
subject(:server_client) { described_class.new(server, dispatcher) }
|
6
|
+
|
7
|
+
it { is_expected.to respond_to :dialect }
|
8
|
+
it { is_expected.to respond_to :identity }
|
9
|
+
it { is_expected.to respond_to :state }
|
10
|
+
it { is_expected.to respond_to :session_key }
|
11
|
+
|
12
|
+
describe '#disconnect!' do
|
13
|
+
it 'closes the socket' do
|
14
|
+
expect(dispatcher.tcp_socket).to receive(:close).with(no_args).and_return(nil)
|
15
|
+
server_client.disconnect!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#initialize' do
|
20
|
+
it 'starts in the negotiate state' do
|
21
|
+
expect(server_client.state).to eq :negotiate
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'starts without a dialect' do
|
25
|
+
expect(server_client.dialect).to be_nil
|
26
|
+
expect(server_client.metadialect).to be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'starts without an identity' do
|
30
|
+
expect(server_client.identity).to be_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'starts without a session_key' do
|
34
|
+
expect(server_client.session_key).to be_nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'creates a new authenticator instance' do
|
38
|
+
expect(server.gss_provider).to receive(:new_authenticator).and_call_original
|
39
|
+
described_class.new(server, dispatcher)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#process_gss' do
|
44
|
+
before(:each) do
|
45
|
+
expect(server_client.instance_eval { @gss_authenticator }).to receive(:process).and_call_original
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should handle an empty GSS buffer' do
|
49
|
+
result = server_client.process_gss
|
50
|
+
expect(result).to be_a RubySMB::Gss::Provider::Result
|
51
|
+
expect(result.nt_status).to eq WindowsError::NTStatus::STATUS_SUCCESS
|
52
|
+
expect(result.buffer).to_not be_empty
|
53
|
+
expect(result.identity).to be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#recv_packet' do
|
58
|
+
it 'receives a new packet from the dispatcher' do
|
59
|
+
expect(dispatcher).to receive(:recv_packet).with(no_args)
|
60
|
+
server_client.recv_packet
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#run' do
|
65
|
+
let(:packet) { Random.new.bytes(16) }
|
66
|
+
before(:each) do
|
67
|
+
expect(server_client).to receive(:recv_packet).and_return(packet)
|
68
|
+
# this hook should ensure that the dispatcher loop returns after processing a single request
|
69
|
+
expect(dispatcher.tcp_socket).to receive(:closed?).and_return(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'calls #handle_negotiate when the state is negotiate' do
|
73
|
+
expect(server_client).to receive(:handle_negotiate).with(packet).and_return(nil)
|
74
|
+
server_client.instance_eval { @state = :negotiate }
|
75
|
+
server_client.run
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'calls #handle_session_setup when the state is session_setup' do
|
79
|
+
expect(server_client).to receive(:handle_session_setup).with(packet).and_return(nil)
|
80
|
+
server_client.instance_eval { @state = :session_setup }
|
81
|
+
server_client.run
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'calls #authenticated when the state is authenticated' do
|
85
|
+
expect(server_client).to receive(:handle_authenticated).with(packet).and_return(nil)
|
86
|
+
server_client.instance_eval { @state = :authenticated }
|
87
|
+
server_client.run
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#send_packet' do
|
92
|
+
let(:packet) { RubySMB::GenericPacket.new }
|
93
|
+
|
94
|
+
before(:each) do
|
95
|
+
expect(dispatcher).to receive(:send_packet).with(packet).and_return(nil)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'sends a packet to the dispatcher' do
|
99
|
+
server_client.send_packet(packet)
|
100
|
+
end
|
101
|
+
|
102
|
+
%w{ 0x0202 0x0210 0x0300 0x0302 0x0311 }.each do |dialect|
|
103
|
+
context "when the dialect is #{dialect}" do
|
104
|
+
before(:each) do
|
105
|
+
server_client.instance_eval { @dialect = dialect }
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'and the state is authenticated' do
|
109
|
+
before(:each) do
|
110
|
+
server_client.instance_eval { @state = :authenticated }
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'and the identity is anonymous' do
|
114
|
+
before(:each) do
|
115
|
+
server_client.instance_eval { @identity = RubySMB::Gss::Provider::IDENTITY_ANONYMOUS }
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not sign packets' do
|
119
|
+
expect(server_client).to_not receive(:smb2_sign)
|
120
|
+
expect(server_client).to_not receive(:smb3_sign)
|
121
|
+
server_client.send_packet(packet)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'and the identity is not anonymous' do
|
126
|
+
before(:each) do
|
127
|
+
server_client.instance_eval { @identity = 'WORKGROUP\RubySMB'; @session_key = Random.new.bytes(16) }
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'does sign packets' do
|
131
|
+
packet = RubySMB::GenericPacket.new
|
132
|
+
dialect_family = RubySMB::Dialect[dialect].family
|
133
|
+
if dialect_family == RubySMB::Dialect::FAMILY_SMB2
|
134
|
+
expect(server_client).to receive(:smb2_sign).with(packet).and_return(packet)
|
135
|
+
expect(server_client).to_not receive(:smb3_sign)
|
136
|
+
elsif dialect_family == RubySMB::Dialect::FAMILY_SMB3
|
137
|
+
expect(server_client).to receive(:smb3_sign).with(packet).and_return(packet)
|
138
|
+
expect(server_client).to_not receive(:smb2_sign)
|
139
|
+
end
|
140
|
+
server_client.send_packet(packet)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#update_preauth_hash' do
|
149
|
+
it 'raises an EncryptionError exception if the preauth integrity hash algorithm is not known' do
|
150
|
+
expect { server_client.update_preauth_hash('Test') }.to raise_error(
|
151
|
+
RubySMB::Error::EncryptionError,
|
152
|
+
'Cannot compute the Preauth Integrity Hash value: Preauth Integrity Hash Algorithm is nil'
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|