-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support Extended Protection for Authentication (aka Channel binding)
- Loading branch information
Showing
12 changed files
with
305 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ language: ruby | |
rvm: | ||
- 1.9.3 | ||
- 1.9.2 | ||
- 1.8.7 | ||
- 2.0.0 | ||
- rbx-19mode | ||
- rbx-18mode | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
module Net | ||
module NTLM | ||
class ChannelBinding | ||
|
||
# Creates a ChannelBinding used for Extended Protection Authentication | ||
# @see http://blogs.msdn.com/b/openspecification/archive/2013/03/26/ntlm-and-channel-binding-hash-aka-exteneded-protection-for-authentication.aspx | ||
# | ||
# @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing | ||
# the outer TLS channel | ||
# @return [NTLM::ChannelBinding] A ChannelBinding holding a token that can be | ||
# embedded in a {Type3} message | ||
def self.create(outer_channel) | ||
new(outer_channel) | ||
end | ||
|
||
# @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing | ||
# the outer TLS channel | ||
def initialize(outer_channel) | ||
@channel = outer_channel | ||
@unique_prefix = 'tls-server-end-point' | ||
@initiator_addtype = 0 | ||
@initiator_address_length = 0 | ||
@acceptor_addrtype = 0 | ||
@acceptor_address_length = 0 | ||
end | ||
|
||
attr_reader :channel, :unique_prefix, :initiator_addtype | ||
attr_reader :initiator_address_length, :acceptor_addrtype | ||
attr_reader :acceptor_address_length | ||
|
||
# Returns a channel binding hash acceptable for use as a AV_PAIR MsvAvChannelBindings | ||
# field value as specified in the NTLM protocol | ||
# | ||
# @return [String] MD5 hash of gss_channel_bindings_struct | ||
def channel_binding_token | ||
@channel_binding_token ||= OpenSSL::Digest::MD5.new(gss_channel_bindings_struct).digest | ||
end | ||
|
||
def gss_channel_bindings_struct | ||
@gss_channel_bindings_struct ||= begin | ||
token = [initiator_addtype].pack('I') | ||
token << [initiator_address_length].pack('I') | ||
token << [acceptor_addrtype].pack('I') | ||
token << [acceptor_address_length].pack('I') | ||
token << [application_data.length].pack('I') | ||
token << application_data | ||
token | ||
end | ||
end | ||
|
||
def channel_hash | ||
@channel_hash ||= OpenSSL::Digest::SHA256.new(channel.to_der) | ||
end | ||
|
||
def application_data | ||
@application_data ||= begin | ||
data = unique_prefix | ||
data << ':' | ||
data << channel_hash.digest | ||
data | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module Net | ||
module Ntlm | ||
class NtlmError < StandardError; end | ||
|
||
class InvalidTargetDataError < NtlmError | ||
attr_reader :data | ||
|
||
def initialize(msg, data) | ||
@data = data | ||
super(msg) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
module Net | ||
module NTLM | ||
|
||
# Represents a list of AV_PAIR structures | ||
# @see https://msdn.microsoft.com/en-us/library/cc236646.aspx | ||
class TargetInfo | ||
|
||
# Allowed AvId values for an AV_PAIR | ||
MSV_AV_EOL = "\x00\x00".freeze | ||
MSV_AV_NB_COMPUTER_NAME = "\x01\x00".freeze | ||
MSV_AV_NB_DOMAIN_NAME = "\x02\x00".freeze | ||
MSV_AV_DNS_COMPUTER_NAME = "\x03\x00".freeze | ||
MSV_AV_DNS_DOMAIN_NAME = "\x04\x00".freeze | ||
MSV_AV_DNS_TREE_NAME = "\x05\x00".freeze | ||
MSV_AV_FLAGS = "\x06\x00".freeze | ||
MSV_AV_TIMESTAMP = "\x07\x00".freeze | ||
MSV_AV_SINGLE_HOST = "\x08\x00".freeze | ||
MSV_AV_TARGET_NAME = "\x09\x00".freeze | ||
MSV_AV_CHANNEL_BINDINGS = "\x0A\x00".freeze | ||
|
||
# @param av_pair_sequence [String] AV_PAIR list from challenge message | ||
def initialize(av_pair_sequence) | ||
@av_pairs = read_pairs(av_pair_sequence) | ||
end | ||
|
||
attr_reader :av_pairs | ||
|
||
def to_s | ||
result = '' | ||
av_pairs.each do |k,v| | ||
result << k | ||
result << [v.length].pack('S') | ||
result << v | ||
end | ||
result << Net::NTLM::TargetInfo::MSV_AV_EOL | ||
result << [0].pack('S') | ||
result.force_encoding(Encoding::ASCII_8BIT) | ||
end | ||
|
||
private | ||
|
||
VALID_PAIR_ID = [ | ||
MSV_AV_EOL, | ||
MSV_AV_NB_COMPUTER_NAME, | ||
MSV_AV_NB_DOMAIN_NAME, | ||
MSV_AV_DNS_COMPUTER_NAME, | ||
MSV_AV_DNS_DOMAIN_NAME, | ||
MSV_AV_DNS_TREE_NAME, | ||
MSV_AV_FLAGS, | ||
MSV_AV_TIMESTAMP, | ||
MSV_AV_SINGLE_HOST, | ||
MSV_AV_TARGET_NAME, | ||
MSV_AV_CHANNEL_BINDINGS | ||
].freeze | ||
|
||
def read_pairs(av_pair_sequence) | ||
offset = 0 | ||
result = {} | ||
return result if av_pair_sequence.nil? | ||
|
||
until offset >= av_pair_sequence.length | ||
id = av_pair_sequence[offset..offset+1] | ||
|
||
unless VALID_PAIR_ID.include?(id) | ||
raise Net::Ntlm::InvalidTargetDataError.new( | ||
"Invalid AvId #{to_hex(id)} in AV_PAIR structure", | ||
av_pair_sequence | ||
) | ||
end | ||
|
||
length = av_pair_sequence[offset+2..offset+3].unpack('S')[0].to_i | ||
if length > 0 | ||
value = av_pair_sequence[offset+4..offset+4+length-1] | ||
result[id] = value | ||
end | ||
|
||
offset += 4 + length | ||
end | ||
|
||
result | ||
end | ||
|
||
def to_hex(str) | ||
return nil if str.nil? | ||
str.bytes.map {|b| '0x' + b.to_s(16).rjust(2,'0').upcase}.join('-') | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
require 'spec_helper' | ||
|
||
describe Net::NTLM::ChannelBinding do | ||
let(:certificates_path) { 'spec/support/certificates' } | ||
let(:sha_256_path) { File.join(certificates_path, 'sha_256_hash.pem') } | ||
let(:sha_256_cert) { OpenSSL::X509::Certificate.new(File.read(sha_256_path)) } | ||
let(:cert_hash) { "\x04\x0E\x56\x28\xEC\x4A\x98\x29\x91\x70\x73\x62\x03\x7B\xB2\x3C".force_encoding(Encoding::ASCII_8BIT) } | ||
|
||
subject { Net::NTLM::ChannelBinding.create(sha_256_cert) } | ||
|
||
describe '#channel_binding_token' do | ||
|
||
it 'returns the correct hash' do | ||
expect(subject.channel_binding_token).to eq cert_hash | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
require 'spec_helper' | ||
|
||
describe Net::NTLM::TargetInfo do | ||
let(:key1) { Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME } | ||
let(:value1) { 'some data' } | ||
let(:key2) { Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME } | ||
let(:value2) { 'some other data' } | ||
let(:data) do | ||
dt = key1.dup | ||
dt << [value1.length].pack('S') | ||
dt << value1 | ||
dt << key2.dup | ||
dt << [value2.length].pack('S') | ||
dt << value2 | ||
dt << Net::NTLM::TargetInfo::MSV_AV_EOL | ||
dt << [0].pack('S') | ||
dt.force_encoding(Encoding::ASCII_8BIT) | ||
end | ||
|
||
subject { Net::NTLM::TargetInfo.new(data) } | ||
|
||
describe 'invalid data' do | ||
|
||
context 'invalid pair id' do | ||
let(:data) { "\xFF\x00" } | ||
|
||
it 'returns an error' do | ||
expect{subject}.to raise_error Net::Ntlm::InvalidTargetDataError | ||
end | ||
end | ||
end | ||
|
||
describe '#av_pairs' do | ||
|
||
it 'returns the pair values with the given keys' do | ||
expect(subject.av_pairs[key1]).to eq value1 | ||
expect(subject.av_pairs[key2]).to eq value2 | ||
end | ||
|
||
context "target data is nil" do | ||
subject { Net::NTLM::TargetInfo.new(nil) } | ||
|
||
it 'returns the pair values with the given keys' do | ||
expect(subject.av_pairs).to be_empty | ||
end | ||
end | ||
end | ||
|
||
describe '#to_s' do | ||
let(:data) do | ||
dt = key1.dup | ||
dt << [value1.length].pack('S') | ||
dt << value1 | ||
dt << key2.dup | ||
dt << [value2.length].pack('S') | ||
dt << value2 | ||
dt.force_encoding(Encoding::ASCII_8BIT) | ||
end | ||
let(:new_key) { Net::NTLM::TargetInfo::MSV_AV_CHANNEL_BINDINGS } | ||
let(:new_value) { 'bindings' } | ||
let(:new_data) do | ||
dt = data | ||
dt << new_key | ||
dt << [new_value.length].pack('S') | ||
dt << new_value | ||
dt << Net::NTLM::TargetInfo::MSV_AV_EOL | ||
dt << [0].pack('S') | ||
dt.force_encoding(Encoding::ASCII_8BIT) | ||
end | ||
|
||
it 'returns bytes with any new data added' do | ||
subject.av_pairs[new_key] = new_value | ||
expect(subject.to_s).to eq new_data | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIDKjCCAhKgAwIBAgIQV0qwsk2MCoxI2Do7IQ6eQzANBgkqhkiG9w0BAQsFADAa | ||
MRgwFgYDVQQDDA8xOTIuMTY4LjEzNy4xNjEwHhcNMTYwMTI3MjIyMzA5WhcNMTcw | ||
MTI3MjI0MzA5WjAaMRgwFgYDVQQDDA8xOTIuMTY4LjEzNy4xNjEwggEiMA0GCSqG | ||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+bGWZQFYjF+bV1WJ1L/MGVNmJR89aJ44Z | ||
rKI/IXKFdbn5wjQPWng/DcaHR6xtMXQkc22boe58GK/uzl84ofbRa6qtboa5djdZ | ||
9CGsd4Yf6CnVz4mhKSi+BnLi80ydhIRByxoX5bGcCSW6dixR5XiNMaMKzhCjQ+of | ||
TU+PBNt7doXB7p0mO4AZz42v4rorRiPNasETj6wlKhFKCMvPLePTwphCgCQsLvgG | ||
NQKtFD7TXvrZwplPSeCPhnzd1vHoZMisMn8ZVQ5dAfSEGGkPkOLO0htbUbdaNMoU | ||
DPyo7Bu62Q/dqqo1MNbMYM5Ilw8mxe4drOs9UupH0eMovFhVMO0LAgMBAAGjbDBq | ||
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw | ||
GgYDVR0RBBMwEYIPMTkyLjE2OC4xMzcuMTYxMB0GA1UdDgQWBBSLuqyHonSmdm8m | ||
9R+z2obO/X3/+TANBgkqhkiG9w0BAQsFAAOCAQEAH4pDGBclTHrwF+Bkbfj81ibK | ||
E2SJSHbdhSx6YCsR28jXUOESfaik5ZPPMXscJqVc1FPpsU9clPFnGiAf0Kt48gsR | ||
twfrRSGgJRv1ZgQyJ4dEQkXbQf2+8uY25Rv4kkFDSvPrE6E9o9Jf9bjqefUYski1 | ||
YoYdWzgrh/2qoNhnM34wizZgE1bWYbWA9MlUuWH9q/OBEx9uP/K53SXOR7DRzYcY | ||
Kg1Z7hV86nvc0WutjEadgdtvJ7eUlg8vAWZqWo5SIdp69l0OEWUlHiaRsPImS5Hd | ||
pX3W8n0wHCxBSntDww7U3SHg6DrYf72taBIQW7xFf63S37yLP4CNss68GqPdyQ== | ||
-----END CERTIFICATE----- |