ruby_smb 2.0.7 → 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/.github/workflows/verify.yml +57 -0
- data/README.md +0 -1
- data/examples/auth_capture.rb +71 -0
- data/lib/ruby_smb/client/negotiation.rb +11 -13
- data/lib/ruby_smb/client.rb +32 -27
- data/lib/ruby_smb/compression/lznt1.rb +164 -0
- data/lib/ruby_smb/compression.rb +7 -0
- data/lib/ruby_smb/dialect.rb +45 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.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/smb1/tree.rb +1 -1
- data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
- 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/tree.rb +1 -1
- data/lib/ruby_smb/smb2.rb +3 -1
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +24 -16
- data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
- 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/smb1/tree_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -5
- data/spec/spec_helper.rb +1 -1
- data.tar.gz.sig +3 -1
- metadata +30 -4
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -6
- data/lib/ruby_smb/client/signing.rb +0 -64
@@ -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
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe RubySMB::Server do
|
2
|
+
before(:each) do
|
3
|
+
allow(::TCPServer).to receive(:new).and_return(::TCPServer.new(0))
|
4
|
+
end
|
5
|
+
|
6
|
+
it { is_expected.to respond_to :dialects }
|
7
|
+
it { is_expected.to respond_to :gss_provider }
|
8
|
+
it { is_expected.to respond_to :guid }
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
it 'should bind to TCP port 445 by default' do
|
12
|
+
expect(::TCPServer).to receive(:new).with(445).and_return(::TCPServer.new(0))
|
13
|
+
described_class.new
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should create a new NTLM GSS provider by default' do
|
17
|
+
expect(RubySMB::Gss::Provider::NTLM).to receive(:new).and_call_original
|
18
|
+
described_class.new
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should generate a random 16-byte GUID' do
|
22
|
+
server_guid = described_class.new.guid
|
23
|
+
expect(server_guid).to be_a String
|
24
|
+
expect(server_guid.length).to eq 16
|
25
|
+
expect(server_guid).to_not eq described_class.new.guid
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should support some dialects' do
|
29
|
+
expect(described_class.new.dialects).to_not be_empty
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|