blob: c73fd0679d6832f69e7d862fe1ea9a0134828f2d [file] [log] [blame]
Scott Baker8461e152019-10-01 14:44:30 -07001package pac
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "log"
8
9 "gopkg.in/jcmturner/gokrb5.v7/crypto"
10 "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
11 "gopkg.in/jcmturner/gokrb5.v7/types"
12 "gopkg.in/jcmturner/rpc.v1/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}