258 lines
8.3 KiB
Go
258 lines
8.3 KiB
Go
package integratedauth
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/md5"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
type AuthenticatorWithEPA interface {
|
|
SetChannelBinding(*ChannelBindings)
|
|
}
|
|
|
|
type ChannelBindingsType uint32
|
|
const (
|
|
ChannelBindingsTypeTLSExporter = 0
|
|
ChannelBindingsTypeTLSUnique = 1
|
|
ChannelBindingsTypeTLSServerEndPoint = 2
|
|
ChannelBindingsTypeEmpty = 3
|
|
)
|
|
|
|
const (
|
|
// https://datatracker.ietf.org/doc/rfc9266/
|
|
TLS_EXPORTER_PREFIX = "tls-exporter:"
|
|
TLS_EXPORTER_EKM_LABEL = "EXPORTER-Channel-Binding"
|
|
TLS_EXPORTER_EKM_LENGTH = 32
|
|
// https://www.rfc-editor.org/rfc/rfc5801.html#section-5.2
|
|
TLS_UNIQUE_PREFIX = "tls-unique:"
|
|
TLS_SERVER_END_POINT_PREFIX = "tls-server-end-point:"
|
|
)
|
|
|
|
// gss_channel_bindings_struct: https://docs.oracle.com/cd/E19683-01/816-1331/overview-52/index.html
|
|
// gss_buffer_desc: https://docs.oracle.com/cd/E19683-01/816-1331/reference-21/index.html
|
|
type ChannelBindings struct {
|
|
Type ChannelBindingsType
|
|
InitiatorAddrType uint32
|
|
InitiatorAddress []byte
|
|
AcceptorAddrType uint32
|
|
AcceptorAddress []byte
|
|
ApplicationData []byte
|
|
}
|
|
|
|
// SEC_CHANNEL_BINDINGS: https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sec_channel_bindings
|
|
type SEC_CHANNEL_BINDINGS struct {
|
|
DwInitiatorAddrType uint32
|
|
CbInitiatorLength uint32
|
|
DwInitiatorOffset uint32
|
|
DwAcceptorAddrType uint32
|
|
CbAcceptorLength uint32
|
|
DwAcceptorOffset uint32
|
|
CbApplicationDataLength uint32
|
|
DwApplicationDataOffset uint32
|
|
Data []byte
|
|
}
|
|
|
|
var EmptyChannelBindings = &ChannelBindings{
|
|
Type: ChannelBindingsTypeEmpty,
|
|
InitiatorAddrType: 0,
|
|
InitiatorAddress: nil,
|
|
AcceptorAddrType: 0,
|
|
AcceptorAddress: nil,
|
|
ApplicationData: nil,
|
|
}
|
|
|
|
// ToBytes converts a ChannelBindings struct to a byte slice as it would be gss_channel_bindings_struct structure in GSSAPI.
|
|
// Returns:
|
|
// - a byte slice
|
|
func (cb *ChannelBindings) ToBytes() []byte {
|
|
binarylength := 4 + 4 + 4 + 4 + 4 + uint32(len(cb.InitiatorAddress)+len(cb.AcceptorAddress)+len(cb.ApplicationData))
|
|
i := 0
|
|
bytes := make([]byte, binarylength)
|
|
binary.LittleEndian.PutUint32(bytes[i:i+4], cb.InitiatorAddrType)
|
|
i += 4
|
|
binary.LittleEndian.PutUint32(bytes[i:i+4], uint32(len(cb.InitiatorAddress)))
|
|
i += 4
|
|
if len(cb.InitiatorAddress) > 0 {
|
|
copy(bytes[i:i+len(cb.InitiatorAddress)], cb.InitiatorAddress)
|
|
i += len(cb.InitiatorAddress)
|
|
}
|
|
binary.LittleEndian.PutUint32(bytes[i:i+4], cb.AcceptorAddrType)
|
|
i += 4
|
|
binary.LittleEndian.PutUint32(bytes[i:i+4], uint32(len(cb.AcceptorAddress)))
|
|
i += 4
|
|
if len(cb.AcceptorAddress) > 0 {
|
|
copy(bytes[i:i+len(cb.AcceptorAddress)], cb.AcceptorAddress)
|
|
i += len(cb.AcceptorAddress)
|
|
}
|
|
binary.LittleEndian.PutUint32(bytes[i:i+4], uint32(len(cb.ApplicationData)))
|
|
i += 4
|
|
if len(cb.ApplicationData) > 0 {
|
|
copy(bytes[i:i+len(cb.ApplicationData)], cb.ApplicationData)
|
|
i += len(cb.ApplicationData)
|
|
}
|
|
// Print bytes in hexdump -C style for debugging
|
|
return bytes
|
|
}
|
|
|
|
// Md5Hash calculates the MD5 hash of the ChannelBindings struct
|
|
// Returns:
|
|
// - a byte slice
|
|
func (cb *ChannelBindings) Md5Hash() []byte {
|
|
if cb.Type == ChannelBindingsTypeEmpty {
|
|
// generate a slice with zeros
|
|
zeros := make([]byte, 16)
|
|
return zeros
|
|
}
|
|
hash := md5.New()
|
|
hash.Write(cb.ToBytes())
|
|
return hash.Sum(nil)
|
|
}
|
|
|
|
// AsSSPI_SEC_CHANNEL_BINDINGS converts a ChannelBindings struct to a SEC_CHANNEL_BINDINGS struct
|
|
// Returns:
|
|
// - a SEC_CHANNEL_BINDINGS struct
|
|
func (cb *ChannelBindings) AsSSPI_SEC_CHANNEL_BINDINGS() *SEC_CHANNEL_BINDINGS {
|
|
initiatorOffset := uint32(32)
|
|
acceptorOffset := initiatorOffset + uint32(len(cb.InitiatorAddress))
|
|
applicationDataOffset := acceptorOffset + uint32(len(cb.AcceptorAddress))
|
|
c := &SEC_CHANNEL_BINDINGS{
|
|
DwInitiatorAddrType: cb.InitiatorAddrType,
|
|
CbInitiatorLength: uint32(len(cb.InitiatorAddress)),
|
|
DwInitiatorOffset: initiatorOffset,
|
|
DwAcceptorAddrType: cb.AcceptorAddrType,
|
|
CbAcceptorLength: uint32(len(cb.AcceptorAddress)),
|
|
DwAcceptorOffset: acceptorOffset,
|
|
CbApplicationDataLength: uint32(len(cb.ApplicationData)),
|
|
DwApplicationDataOffset: applicationDataOffset,
|
|
}
|
|
data := make([]byte, c.CbInitiatorLength+c.CbAcceptorLength+c.CbApplicationDataLength)
|
|
var i uint32 = 0
|
|
if c.CbInitiatorLength > 0 {
|
|
copy(data[i:i+c.CbInitiatorLength], cb.InitiatorAddress)
|
|
i += c.CbInitiatorLength
|
|
}
|
|
if c.CbAcceptorLength > 0 {
|
|
copy(data[i:i+c.CbAcceptorLength], cb.AcceptorAddress)
|
|
i += c.CbAcceptorLength
|
|
}
|
|
if c.CbApplicationDataLength > 0 {
|
|
copy(data[i:i+c.CbApplicationDataLength], cb.ApplicationData)
|
|
i += c.CbApplicationDataLength
|
|
}
|
|
c.Data = data
|
|
return c
|
|
}
|
|
|
|
// ToBytes converts a SEC_CHANNEL_BINDINGS struct to a byte slice, that can be use in SSPI InitializeSecurityContext function.
|
|
// Returns:
|
|
// - a byte slice
|
|
func (cb *SEC_CHANNEL_BINDINGS) ToBytes() []byte {
|
|
bytes := make([]byte, 32+len(cb.Data))
|
|
binary.LittleEndian.PutUint32(bytes[0:4], cb.DwInitiatorAddrType)
|
|
binary.LittleEndian.PutUint32(bytes[4:8], cb.CbInitiatorLength)
|
|
binary.LittleEndian.PutUint32(bytes[8:12], cb.DwInitiatorOffset)
|
|
binary.LittleEndian.PutUint32(bytes[12:16], cb.DwAcceptorAddrType)
|
|
binary.LittleEndian.PutUint32(bytes[16:20], cb.CbAcceptorLength)
|
|
binary.LittleEndian.PutUint32(bytes[20:24], cb.DwAcceptorOffset)
|
|
binary.LittleEndian.PutUint32(bytes[24:28], cb.CbApplicationDataLength)
|
|
binary.LittleEndian.PutUint32(bytes[28:32], cb.DwApplicationDataOffset)
|
|
copy(bytes[32:32+len(cb.Data)], cb.Data)
|
|
|
|
return bytes
|
|
}
|
|
|
|
// GenerateCBTFromTLSUnique generates a ChannelBindings struct from a TLS unique value
|
|
// Adds tls-unique: prefix to the TLS unique value.
|
|
// Parameters:
|
|
// - tlsUnique: the TLS unique value
|
|
// Returns:
|
|
// - a ChannelBindings struct
|
|
func GenerateCBTFromTLSUnique(tlsUnique []byte) (*ChannelBindings, error) {
|
|
if len(tlsUnique) == 0 {
|
|
return nil, fmt.Errorf("tlsUnique is empty")
|
|
}
|
|
return &ChannelBindings{
|
|
Type: ChannelBindingsTypeTLSUnique,
|
|
InitiatorAddrType: 0,
|
|
InitiatorAddress: nil,
|
|
AcceptorAddrType: 0,
|
|
AcceptorAddress: nil,
|
|
ApplicationData: append([]byte(TLS_UNIQUE_PREFIX), tlsUnique...),
|
|
}, nil
|
|
}
|
|
|
|
// GenerateCBTFromTLSConnState generates a ChannelBindings struct from a TLS connection state
|
|
// If the TLS version is TLS 1.3, it generates a ChannelBindings struct from the TLS exporter key.
|
|
// If the TLS version is not TLS 1.3, it generates a ChannelBindings struct from the TLS unique value.
|
|
// Parameters:
|
|
// - state: the TLS connection state
|
|
// Returns:
|
|
// - a ChannelBindings struct
|
|
func GenerateCBTFromTLSConnState(state tls.ConnectionState) (*ChannelBindings, error) {
|
|
switch state.Version {
|
|
case tls.VersionTLS13:
|
|
// We don't support generating Channel Bindings from TLS 1.3 yet
|
|
return nil, nil
|
|
default:
|
|
return GenerateCBTFromTLSUnique(state.TLSUnique)
|
|
}
|
|
}
|
|
|
|
// GenerateCBTFromTLSExporter generates a ChannelBindings struct from a TLS exporter key
|
|
// Parameters:
|
|
// - exporterKey: the TLS exporter key
|
|
// Returns:
|
|
// - a ChannelBindings struct
|
|
func GenerateCBTFromTLSExporter(exporterKey []byte) (*ChannelBindings, error) {
|
|
if len(exporterKey) == 0 {
|
|
return nil, fmt.Errorf("exporterKey is empty")
|
|
}
|
|
|
|
return &ChannelBindings{
|
|
Type: ChannelBindingsTypeTLSExporter,
|
|
InitiatorAddrType: 0,
|
|
InitiatorAddress: nil,
|
|
AcceptorAddrType: 0,
|
|
AcceptorAddress: nil,
|
|
ApplicationData: append([]byte(TLS_EXPORTER_PREFIX), exporterKey...),
|
|
}, nil
|
|
}
|
|
|
|
// GenerateCBTFromServerCert generates a ChannelBindings struct from a server certificate
|
|
// Calculates the hash of the server certificate as described in 4.2 section of RFC5056.
|
|
// Parameters:
|
|
// - cert: the server certificate
|
|
// Returns:
|
|
// - a ChannelBindings struct
|
|
func GenerateCBTFromServerCert(cert *x509.Certificate) *ChannelBindings {
|
|
if cert == nil {
|
|
return nil
|
|
}
|
|
var certHash []byte
|
|
var hashType crypto.Hash
|
|
switch cert.SignatureAlgorithm {
|
|
case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.SHA256WithRSAPSS:
|
|
hashType = crypto.SHA256
|
|
case x509.SHA384WithRSA, x509.ECDSAWithSHA384, x509.SHA384WithRSAPSS:
|
|
hashType = crypto.SHA384
|
|
case x509.SHA512WithRSA, x509.ECDSAWithSHA512, x509.SHA512WithRSAPSS:
|
|
hashType = crypto.SHA512
|
|
default:
|
|
hashType = crypto.SHA256
|
|
}
|
|
h := hashType.New()
|
|
_, _ = h.Write(cert.Raw)
|
|
certHash = h.Sum(nil)
|
|
return &ChannelBindings{
|
|
Type: ChannelBindingsTypeTLSServerEndPoint,
|
|
InitiatorAddrType: 0,
|
|
InitiatorAddress: nil,
|
|
AcceptorAddrType: 0,
|
|
AcceptorAddress: nil,
|
|
ApplicationData: append([]byte(TLS_SERVER_END_POINT_PREFIX), certHash...),
|
|
}
|
|
}
|