252 lines
7.4 KiB
Go
252 lines
7.4 KiB
Go
|
package pac
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
|
||
|
"github.com/jcmturner/gokrb5/v8/crypto"
|
||
|
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
|
||
|
"github.com/jcmturner/gokrb5/v8/types"
|
||
|
"github.com/jcmturner/rpc/v2/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
|
||
|
}
|