blob: c73fd0679d6832f69e7d862fe1ea9a0134828f2d [file] [log] [blame]
package pac
import (
"bytes"
"errors"
"fmt"
"log"
"gopkg.in/jcmturner/gokrb5.v7/crypto"
"gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v7/types"
"gopkg.in/jcmturner/rpc.v1/mstypes"
)
const (
infoTypeKerbValidationInfo uint32 = 1
infoTypeCredentials uint32 = 2
infoTypePACServerSignatureData uint32 = 6
infoTypePACKDCSignatureData uint32 = 7
infoTypePACClientInfo uint32 = 10
infoTypeS4UDelegationInfo uint32 = 11
infoTypeUPNDNSInfo uint32 = 12
infoTypePACClientClaimsInfo uint32 = 13
infoTypePACDeviceInfo uint32 = 14
infoTypePACDeviceClaimsInfo uint32 = 15
)
// PACType implements: https://msdn.microsoft.com/en-us/library/cc237950.aspx
type PACType struct {
CBuffers uint32
Version uint32
Buffers []InfoBuffer
Data []byte
KerbValidationInfo *KerbValidationInfo
CredentialsInfo *CredentialsInfo
ServerChecksum *SignatureData
KDCChecksum *SignatureData
ClientInfo *ClientInfo
S4UDelegationInfo *S4UDelegationInfo
UPNDNSInfo *UPNDNSInfo
ClientClaimsInfo *ClientClaimsInfo
DeviceInfo *DeviceInfo
DeviceClaimsInfo *DeviceClaimsInfo
ZeroSigData []byte
}
// InfoBuffer implements the PAC Info Buffer: https://msdn.microsoft.com/en-us/library/cc237954.aspx
type InfoBuffer struct {
ULType uint32 // A 32-bit unsigned integer in little-endian format that describes the type of data present in the buffer contained at Offset.
CBBufferSize uint32 // A 32-bit unsigned integer in little-endian format that contains the size, in bytes, of the buffer in the PAC located at Offset.
Offset uint64 // A 64-bit unsigned integer in little-endian format that contains the offset to the beginning of the buffer, in bytes, from the beginning of the PACTYPE structure. The data offset MUST be a multiple of eight. The following sections specify the format of each type of element.
}
// Unmarshal bytes into the PACType struct
func (pac *PACType) Unmarshal(b []byte) (err error) {
pac.Data = b
zb := make([]byte, len(b), len(b))
copy(zb, b)
pac.ZeroSigData = zb
r := mstypes.NewReader(bytes.NewReader(b))
pac.CBuffers, err = r.Uint32()
if err != nil {
return
}
pac.Version, err = r.Uint32()
if err != nil {
return
}
buf := make([]InfoBuffer, pac.CBuffers, pac.CBuffers)
for i := range buf {
buf[i].ULType, err = r.Uint32()
if err != nil {
return
}
buf[i].CBBufferSize, err = r.Uint32()
if err != nil {
return
}
buf[i].Offset, err = r.Uint64()
if err != nil {
return
}
}
pac.Buffers = buf
return nil
}
// ProcessPACInfoBuffers processes the PAC Info Buffers.
// https://msdn.microsoft.com/en-us/library/cc237954.aspx
func (pac *PACType) ProcessPACInfoBuffers(key types.EncryptionKey, l *log.Logger) error {
for _, buf := range pac.Buffers {
p := make([]byte, buf.CBBufferSize, buf.CBBufferSize)
copy(p, pac.Data[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)])
switch buf.ULType {
case infoTypeKerbValidationInfo:
if pac.KerbValidationInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k KerbValidationInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing KerbValidationInfo: %v", err)
}
pac.KerbValidationInfo = &k
case infoTypeCredentials:
// Currently PAC parsing is only useful on the service side in gokrb5
// The CredentialsInfo are only useful when gokrb5 has implemented RFC4556 and only applied on the client side.
// Skipping CredentialsInfo - will be revisited under RFC4556 implementation.
continue
//if pac.CredentialsInfo != nil {
// //Must ignore subsequent buffers of this type
// continue
//}
//var k CredentialsInfo
//err := k.Unmarshal(p, key) // The encryption key used is the AS reply key only available to the client.
//if err != nil {
// return fmt.Errorf("error processing CredentialsInfo: %v", err)
//}
//pac.CredentialsInfo = &k
case infoTypePACServerSignatureData:
if pac.ServerChecksum != nil {
//Must ignore subsequent buffers of this type
continue
}
var k SignatureData
zb, err := k.Unmarshal(p)
copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb)
if err != nil {
return fmt.Errorf("error processing ServerChecksum: %v", err)
}
pac.ServerChecksum = &k
case infoTypePACKDCSignatureData:
if pac.KDCChecksum != nil {
//Must ignore subsequent buffers of this type
continue
}
var k SignatureData
zb, err := k.Unmarshal(p)
copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb)
if err != nil {
return fmt.Errorf("error processing KDCChecksum: %v", err)
}
pac.KDCChecksum = &k
case infoTypePACClientInfo:
if pac.ClientInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k ClientInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing ClientInfo: %v", err)
}
pac.ClientInfo = &k
case infoTypeS4UDelegationInfo:
if pac.S4UDelegationInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k S4UDelegationInfo
err := k.Unmarshal(p)
if err != nil {
l.Printf("could not process S4U_DelegationInfo: %v", err)
continue
}
pac.S4UDelegationInfo = &k
case infoTypeUPNDNSInfo:
if pac.UPNDNSInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k UPNDNSInfo
err := k.Unmarshal(p)
if err != nil {
l.Printf("could not process UPN_DNSInfo: %v", err)
continue
}
pac.UPNDNSInfo = &k
case infoTypePACClientClaimsInfo:
if pac.ClientClaimsInfo != nil || len(p) < 1 {
//Must ignore subsequent buffers of this type
continue
}
var k ClientClaimsInfo
err := k.Unmarshal(p)
if err != nil {
l.Printf("could not process ClientClaimsInfo: %v", err)
continue
}
pac.ClientClaimsInfo = &k
case infoTypePACDeviceInfo:
if pac.DeviceInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k DeviceInfo
err := k.Unmarshal(p)
if err != nil {
l.Printf("could not process DeviceInfo: %v", err)
continue
}
pac.DeviceInfo = &k
case infoTypePACDeviceClaimsInfo:
if pac.DeviceClaimsInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k DeviceClaimsInfo
err := k.Unmarshal(p)
if err != nil {
l.Printf("could not process DeviceClaimsInfo: %v", err)
continue
}
pac.DeviceClaimsInfo = &k
}
}
if ok, err := pac.verify(key); !ok {
return err
}
return nil
}
func (pac *PACType) verify(key types.EncryptionKey) (bool, error) {
if pac.KerbValidationInfo == nil {
return false, errors.New("PAC Info Buffers does not contain a KerbValidationInfo")
}
if pac.ServerChecksum == nil {
return false, errors.New("PAC Info Buffers does not contain a ServerChecksum")
}
if pac.KDCChecksum == nil {
return false, errors.New("PAC Info Buffers does not contain a KDCChecksum")
}
if pac.ClientInfo == nil {
return false, errors.New("PAC Info Buffers does not contain a ClientInfo")
}
etype, err := crypto.GetChksumEtype(int32(pac.ServerChecksum.SignatureType))
if err != nil {
return false, err
}
if ok := etype.VerifyChecksum(key.KeyValue,
pac.ZeroSigData,
pac.ServerChecksum.Signature,
keyusage.KERB_NON_KERB_CKSUM_SALT); !ok {
return false, errors.New("PAC service checksum verification failed")
}
return true, nil
}