Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/ruby_smb/gss/provider/ntlm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def process_ntlm_type1(type1_msg)

@server_challenge = @provider.generate_server_challenge
msg.challenge = @server_challenge.unpack1('Q<') # 64-bit unsigned, little endian (uint64_t)
target_info = Net::NTLM::TargetInfo.new('')
target_info = Net::NTLM::TargetInfo.new(nil)
target_info.av_pairs.merge!({
Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => @provider.netbios_domain.encode('UTF-16LE').b,
Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => @provider.netbios_hostname.encode('UTF-16LE').b,
Expand Down
4 changes: 3 additions & 1 deletion lib/ruby_smb/peer_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ module PeerInfo
# Extract and store useful information about the peer/server from the
# NTLM Type 2 (challenge) TargetInfo fields.
#
# @param target_info_str [String] the Target Info string
# @param target_info_str [String, nil] the Target Info string
def store_target_info(target_info_str)
return if target_info_str.nil? || target_info_str.empty?

target_info = Net::NTLM::TargetInfo.new(target_info_str)
{
Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => :@default_name,
Expand Down
37 changes: 37 additions & 0 deletions spec/lib/ruby_smb/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,15 @@
expect(smb1_client.session_key).to eq ntlm_client.session_key
end

it 'continues authentication when the server advertises empty target info' do
challenge = Net::NTLM::Message.decode64(type2_string)
challenge.set_flag(:TARGET_INFO)
challenge.target_info = ''.b
allow(smb1_client).to receive(:smb1_type2_message).and_return(challenge.encode64)

expect(smb1_client.smb1_authenticate).to eq WindowsError::NTStatus::STATUS_SUCCESS
end

it 'stores the OS version number from the challenge message' do
smb1_client.smb1_authenticate
expect(smb1_client.os_version).to eq '6.1.7601'
Expand Down Expand Up @@ -1860,6 +1869,15 @@
expect(smb2_client.session_key).to eq ntlm_client.session_key
end

it 'continues authentication when the server advertises empty target info' do
challenge = Net::NTLM::Message.decode64(type2_string)
challenge.set_flag(:TARGET_INFO)
challenge.target_info = ''.b
allow(smb2_client).to receive(:smb2_type2_message).and_return(challenge.encode64)

expect(smb2_client.smb2_authenticate).to eq WindowsError::NTStatus::STATUS_SUCCESS
end

it 'stores the OS version number from the challenge message' do
smb2_client.smb2_authenticate
expect(smb2_client.os_version).to eq '6.1.7601'
Expand Down Expand Up @@ -2068,6 +2086,25 @@
client.store_target_info(target_info_str)
end

it 'preserves existing peer information when given empty target info' do
client.store_target_info(target_info_str)
expect { client.store_target_info(''.b) }.to_not change {
[client.default_name, client.default_domain, client.dns_host_name, client.dns_domain_name, client.dns_tree_name]
}
end

it 'accepts nil target info' do
expect { client.store_target_info(nil) }.to_not raise_error
end

it 'accepts empty target info encoded with an end-of-list marker' do
expect { client.store_target_info("\x00\x00\x00\x00".b) }.to_not raise_error
end

it 'rejects nonempty invalid target info' do
expect { client.store_target_info("\xff\xff\x00\x00".b) }.to raise_error(Net::NTLM::InvalidTargetDataError)
end

it 'sets the expected Client\'s attribute' do
client.store_target_info(target_info_str)
expect(client.default_name).to eq 'TESTNAME'
Expand Down
8 changes: 8 additions & 0 deletions spec/lib/ruby_smb/dcerpc/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@
expect(auth_client.process_ntlm_type2(type2_message)).to start_with("NTLMSSP\x00\x03\x00\x00\x00")
end

it 'returns a type3 message when the server advertises empty target info' do
challenge = Net::NTLM::Message.parse(type2_message)
challenge.set_flag(:TARGET_INFO)
challenge.target_info = ''.b

expect(auth_client.process_ntlm_type2(challenge.serialize)).to start_with("NTLMSSP\x00\x03\x00\x00\x00")
end

it 'stores the session key' do
auth_client.process_ntlm_type2(type2_message)
expect(auth_client.instance_variable_get(:@session_key).size).to eq(16)
Expand Down
13 changes: 13 additions & 0 deletions spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@
it 'should process a NTLM type 1 message and return a type2 message' do
expect(authenticator.process_ntlm_type1(type1_msg)).to be_a Net::NTLM::Message::Type2
end

it 'includes the server names in the serialized target info' do
type2_msg = Net::NTLM::Message.parse(authenticator.process_ntlm_type1(type1_msg).serialize)
target_info = Net::NTLM::TargetInfo.new(type2_msg.target_info)

expect(target_info.av_pairs).to include(
Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => provider.netbios_domain.encode('UTF-16LE').b,
Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => provider.netbios_hostname.encode('UTF-16LE').b,
Net::NTLM::TargetInfo::MSV_AV_DNS_DOMAIN_NAME => provider.dns_domain.encode('UTF-16LE').b,
Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME => provider.dns_hostname.encode('UTF-16LE').b
)
expect(target_info.av_pairs[Net::NTLM::TargetInfo::MSV_AV_TIMESTAMP].bytesize).to eq 8
end
end

describe '#process_ntlm_type3' do
Expand Down
59 changes: 19 additions & 40 deletions spec/lib/ruby_smb/ntlm/client/session_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
AGwAbwBjAGEAbAADADgAVwBJAE4ALQAzAE0AUwBQADgASwAyAEwAQwBHAEMA
LgBtAHMAZgBsAGEAYgAuAGwAbwBjAGEAbAAHAAgAS6UAWjxl2AEAAAAA
}) }
subject(:client) { RubySMB::NTLM::Client.new('rubysmb', 'rubysmb', flags: RubySMB::NTLM::DEFAULT_CLIENT_FLAGS) }
let(:username) { 'rubysmb' }
let(:password) { 'rubysmb' }
let(:client) { RubySMB::NTLM::Client.new(username, password, flags: RubySMB::NTLM::DEFAULT_CLIENT_FLAGS) }
subject(:session) { described_class.new(client, message) }

describe '#authenticate!' do
Expand All @@ -17,72 +19,49 @@
session.authenticate!
end

it 'checks if it is anonymous' do
expect(session).to receive(:is_anonymous?).at_least(1).times.and_call_original
session.authenticate!
end

it 'returns a Type3 message' do
expect(session.authenticate!).to be_a Net::NTLM::Message::Type3
expect(session.authenticate!).to be_a Net::NTLM::Message
end

context 'when it is anonymous' do
before(:each) { allow(session).to receive(:is_anonymous?).and_return(true) }
after(:each) { session.authenticate! }
let(:username) { '' }
let(:password) { '' }

it 'uses the correct lm response' do
expect(session).to_not receive(:lmv2_resp)
expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
expect(params).to include :lm_response
expect(params[:lm_response]).to eq "\x00".b
method.call(params)
end
expect(session.authenticate!.lm_response).to eq "\x00".b
end

it 'uses the correct ntlm response' do
expect(session).to_not receive(:ntlmv2_resp)
expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
expect(params).to include :ntlm_response
expect(params[:ntlm_response]).to eq ''
method.call(params)
end
expect(session.authenticate!.ntlm_response).to eq ''
end
end

context 'when it is not anonymous' do
before(:each) { allow(session).to receive(:is_anonymous?).and_return(false) }
after(:each) { session.authenticate! }

it 'uses the correct lm response' do
expect(session).to receive(:lmv2_resp).and_call_original
expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
expect(params).to include :lm_response
expect(params[:lm_response].length).to be > 16
method.call(params)
end
expect(session.authenticate!.lm_response.length).to be > 16
end

it 'uses the correct ntlm response' do
expect(session).to receive(:ntlmv2_resp).and_call_original
expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
expect(params).to include :ntlm_response
expect(params[:ntlm_response].length).to be > 16
method.call(params)
end
expect(session.authenticate!.ntlm_response.length).to be > 16
end
end
end

describe '#calculate_user_session_key!' do
it 'returns an all zero key when it is anonymous' do
expect(session).to receive(:is_anonymous?).and_return(true)
expect(session.send(:calculate_user_session_key!)).to eq "\x00".b * 16
context 'when it is anonymous' do
let(:username) { '' }
let(:password) { '' }

it 'returns an all zero key' do
expect(session.send(:calculate_user_session_key!)).to eq "\x00".b * 16
end
end

it 'returns a session key' do
expect(session).to receive(:is_anonymous?).and_return(false)
expect(session.send(:calculate_user_session_key!)).to_not eq "\x00".b * 16
session_key = session.send(:calculate_user_session_key!)
expect(session_key.bytesize).to eq 16
expect(session_key).to_not eq "\x00".b * 16
end
end

Expand Down
Loading