| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- 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
- }
|