1package pac
2
3import (
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
15const (
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
29type 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
48type 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
55func (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
90func (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
226func (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}
252