github.com/jcmturner/gokrb5/v8@v8.4.4/pac/pac_type.go (about)

     1  package pac
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  
     9  	"github.com/jcmturner/gokrb5/v8/crypto"
    10  	"github.com/jcmturner/gokrb5/v8/iana/keyusage"
    11  	"github.com/jcmturner/gokrb5/v8/types"
    12  	"github.com/jcmturner/rpc/v2/mstypes"
    13  )
    14  
    15  const (
    16  	infoTypeKerbValidationInfo     uint32 = 1
    17  	infoTypeCredentials            uint32 = 2
    18  	infoTypePACServerSignatureData uint32 = 6
    19  	infoTypePACKDCSignatureData    uint32 = 7
    20  	infoTypePACClientInfo          uint32 = 10
    21  	infoTypeS4UDelegationInfo      uint32 = 11
    22  	infoTypeUPNDNSInfo             uint32 = 12
    23  	infoTypePACClientClaimsInfo    uint32 = 13
    24  	infoTypePACDeviceInfo          uint32 = 14
    25  	infoTypePACDeviceClaimsInfo    uint32 = 15
    26  )
    27  
    28  // PACType implements: https://msdn.microsoft.com/en-us/library/cc237950.aspx
    29  type PACType struct {
    30  	CBuffers           uint32
    31  	Version            uint32
    32  	Buffers            []InfoBuffer
    33  	Data               []byte
    34  	KerbValidationInfo *KerbValidationInfo
    35  	CredentialsInfo    *CredentialsInfo
    36  	ServerChecksum     *SignatureData
    37  	KDCChecksum        *SignatureData
    38  	ClientInfo         *ClientInfo
    39  	S4UDelegationInfo  *S4UDelegationInfo
    40  	UPNDNSInfo         *UPNDNSInfo
    41  	ClientClaimsInfo   *ClientClaimsInfo
    42  	DeviceInfo         *DeviceInfo
    43  	DeviceClaimsInfo   *DeviceClaimsInfo
    44  	ZeroSigData        []byte
    45  }
    46  
    47  // InfoBuffer implements the PAC Info Buffer: https://msdn.microsoft.com/en-us/library/cc237954.aspx
    48  type InfoBuffer struct {
    49  	ULType       uint32 // A 32-bit unsigned integer in little-endian format that describes the type of data present in the buffer contained at Offset.
    50  	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.
    51  	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.
    52  }
    53  
    54  // Unmarshal bytes into the PACType struct
    55  func (pac *PACType) Unmarshal(b []byte) (err error) {
    56  	pac.Data = b
    57  	zb := make([]byte, len(b), len(b))
    58  	copy(zb, b)
    59  	pac.ZeroSigData = zb
    60  	r := mstypes.NewReader(bytes.NewReader(b))
    61  	pac.CBuffers, err = r.Uint32()
    62  	if err != nil {
    63  		return
    64  	}
    65  	pac.Version, err = r.Uint32()
    66  	if err != nil {
    67  		return
    68  	}
    69  	buf := make([]InfoBuffer, pac.CBuffers, pac.CBuffers)
    70  	for i := range buf {
    71  		buf[i].ULType, err = r.Uint32()
    72  		if err != nil {
    73  			return
    74  		}
    75  		buf[i].CBBufferSize, err = r.Uint32()
    76  		if err != nil {
    77  			return
    78  		}
    79  		buf[i].Offset, err = r.Uint64()
    80  		if err != nil {
    81  			return
    82  		}
    83  	}
    84  	pac.Buffers = buf
    85  	return nil
    86  }
    87  
    88  // ProcessPACInfoBuffers processes the PAC Info Buffers.
    89  // https://msdn.microsoft.com/en-us/library/cc237954.aspx
    90  func (pac *PACType) ProcessPACInfoBuffers(key types.EncryptionKey, l *log.Logger) error {
    91  	for _, buf := range pac.Buffers {
    92  		p := make([]byte, buf.CBBufferSize, buf.CBBufferSize)
    93  		copy(p, pac.Data[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)])
    94  		switch buf.ULType {
    95  		case infoTypeKerbValidationInfo:
    96  			if pac.KerbValidationInfo != nil {
    97  				//Must ignore subsequent buffers of this type
    98  				continue
    99  			}
   100  			var k KerbValidationInfo
   101  			err := k.Unmarshal(p)
   102  			if err != nil {
   103  				return fmt.Errorf("error processing KerbValidationInfo: %v", err)
   104  			}
   105  			pac.KerbValidationInfo = &k
   106  		case infoTypeCredentials:
   107  			// Currently PAC parsing is only useful on the service side in gokrb5
   108  			// The CredentialsInfo are only useful when gokrb5 has implemented RFC4556 and only applied on the client side.
   109  			// Skipping CredentialsInfo - will be revisited under RFC4556 implementation.
   110  			continue
   111  			//if pac.CredentialsInfo != nil {
   112  			//	//Must ignore subsequent buffers of this type
   113  			//	continue
   114  			//}
   115  			//var k CredentialsInfo
   116  			//err := k.Unmarshal(p, key) // The encryption key used is the AS reply key only available to the client.
   117  			//if err != nil {
   118  			//	return fmt.Errorf("error processing CredentialsInfo: %v", err)
   119  			//}
   120  			//pac.CredentialsInfo = &k
   121  		case infoTypePACServerSignatureData:
   122  			if pac.ServerChecksum != nil {
   123  				//Must ignore subsequent buffers of this type
   124  				continue
   125  			}
   126  			var k SignatureData
   127  			zb, err := k.Unmarshal(p)
   128  			copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb)
   129  			if err != nil {
   130  				return fmt.Errorf("error processing ServerChecksum: %v", err)
   131  			}
   132  			pac.ServerChecksum = &k
   133  		case infoTypePACKDCSignatureData:
   134  			if pac.KDCChecksum != nil {
   135  				//Must ignore subsequent buffers of this type
   136  				continue
   137  			}
   138  			var k SignatureData
   139  			zb, err := k.Unmarshal(p)
   140  			copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb)
   141  			if err != nil {
   142  				return fmt.Errorf("error processing KDCChecksum: %v", err)
   143  			}
   144  			pac.KDCChecksum = &k
   145  		case infoTypePACClientInfo:
   146  			if pac.ClientInfo != nil {
   147  				//Must ignore subsequent buffers of this type
   148  				continue
   149  			}
   150  			var k ClientInfo
   151  			err := k.Unmarshal(p)
   152  			if err != nil {
   153  				return fmt.Errorf("error processing ClientInfo: %v", err)
   154  			}
   155  			pac.ClientInfo = &k
   156  		case infoTypeS4UDelegationInfo:
   157  			if pac.S4UDelegationInfo != nil {
   158  				//Must ignore subsequent buffers of this type
   159  				continue
   160  			}
   161  			var k S4UDelegationInfo
   162  			err := k.Unmarshal(p)
   163  			if err != nil {
   164  				l.Printf("could not process S4U_DelegationInfo: %v", err)
   165  				continue
   166  			}
   167  			pac.S4UDelegationInfo = &k
   168  		case infoTypeUPNDNSInfo:
   169  			if pac.UPNDNSInfo != nil {
   170  				//Must ignore subsequent buffers of this type
   171  				continue
   172  			}
   173  			var k UPNDNSInfo
   174  			err := k.Unmarshal(p)
   175  			if err != nil {
   176  				l.Printf("could not process UPN_DNSInfo: %v", err)
   177  				continue
   178  			}
   179  			pac.UPNDNSInfo = &k
   180  		case infoTypePACClientClaimsInfo:
   181  			if pac.ClientClaimsInfo != nil || len(p) < 1 {
   182  				//Must ignore subsequent buffers of this type
   183  				continue
   184  			}
   185  			var k ClientClaimsInfo
   186  			err := k.Unmarshal(p)
   187  			if err != nil {
   188  				l.Printf("could not process ClientClaimsInfo: %v", err)
   189  				continue
   190  			}
   191  			pac.ClientClaimsInfo = &k
   192  		case infoTypePACDeviceInfo:
   193  			if pac.DeviceInfo != nil {
   194  				//Must ignore subsequent buffers of this type
   195  				continue
   196  			}
   197  			var k DeviceInfo
   198  			err := k.Unmarshal(p)
   199  			if err != nil {
   200  				l.Printf("could not process DeviceInfo: %v", err)
   201  				continue
   202  			}
   203  			pac.DeviceInfo = &k
   204  		case infoTypePACDeviceClaimsInfo:
   205  			if pac.DeviceClaimsInfo != nil {
   206  				//Must ignore subsequent buffers of this type
   207  				continue
   208  			}
   209  			var k DeviceClaimsInfo
   210  			err := k.Unmarshal(p)
   211  			if err != nil {
   212  				l.Printf("could not process DeviceClaimsInfo: %v", err)
   213  				continue
   214  			}
   215  			pac.DeviceClaimsInfo = &k
   216  		}
   217  	}
   218  
   219  	if ok, err := pac.verify(key); !ok {
   220  		return err
   221  	}
   222  
   223  	return nil
   224  }
   225  
   226  func (pac *PACType) verify(key types.EncryptionKey) (bool, error) {
   227  	if pac.KerbValidationInfo == nil {
   228  		return false, errors.New("PAC Info Buffers does not contain a KerbValidationInfo")
   229  	}
   230  	if pac.ServerChecksum == nil {
   231  		return false, errors.New("PAC Info Buffers does not contain a ServerChecksum")
   232  	}
   233  	if pac.KDCChecksum == nil {
   234  		return false, errors.New("PAC Info Buffers does not contain a KDCChecksum")
   235  	}
   236  	if pac.ClientInfo == nil {
   237  		return false, errors.New("PAC Info Buffers does not contain a ClientInfo")
   238  	}
   239  	etype, err := crypto.GetChksumEtype(int32(pac.ServerChecksum.SignatureType))
   240  	if err != nil {
   241  		return false, err
   242  	}
   243  	if ok := etype.VerifyChecksum(key.KeyValue,
   244  		pac.ZeroSigData,
   245  		pac.ServerChecksum.Signature,
   246  		keyusage.KERB_NON_KERB_CKSUM_SALT); !ok {
   247  		return false, errors.New("PAC service checksum verification failed")
   248  	}
   249  
   250  	return true, nil
   251  }