blob: f75ddc4e1ce9e3bf3222295c4099b289a6d75cf2 [file] [log] [blame]
Scott Bakered4efab2020-01-13 19:12:25 -08001package messages
2
3// Reference: https://www.ietf.org/rfc/rfc4120.txt
4// Section: 5.4.1
5
6import (
7 "crypto/rand"
8 "fmt"
9 "math"
10 "math/big"
11 "time"
12
13 "github.com/jcmturner/gofork/encoding/asn1"
14 "gopkg.in/jcmturner/gokrb5.v7/asn1tools"
15 "gopkg.in/jcmturner/gokrb5.v7/config"
16 "gopkg.in/jcmturner/gokrb5.v7/crypto"
17 "gopkg.in/jcmturner/gokrb5.v7/iana"
18 "gopkg.in/jcmturner/gokrb5.v7/iana/asnAppTag"
19 "gopkg.in/jcmturner/gokrb5.v7/iana/flags"
20 "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
21 "gopkg.in/jcmturner/gokrb5.v7/iana/msgtype"
22 "gopkg.in/jcmturner/gokrb5.v7/iana/nametype"
23 "gopkg.in/jcmturner/gokrb5.v7/iana/patype"
24 "gopkg.in/jcmturner/gokrb5.v7/krberror"
25 "gopkg.in/jcmturner/gokrb5.v7/types"
26)
27
28type marshalKDCReq struct {
29 PVNO int `asn1:"explicit,tag:1"`
30 MsgType int `asn1:"explicit,tag:2"`
31 PAData types.PADataSequence `asn1:"explicit,optional,tag:3"`
32 ReqBody asn1.RawValue `asn1:"explicit,tag:4"`
33}
34
35// KDCReqFields represents the KRB_KDC_REQ fields.
36type KDCReqFields struct {
37 PVNO int
38 MsgType int
39 PAData types.PADataSequence
40 ReqBody KDCReqBody
41 Renewal bool
42}
43
44// ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
45type ASReq struct {
46 KDCReqFields
47}
48
49// TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
50type TGSReq struct {
51 KDCReqFields
52}
53
54type marshalKDCReqBody struct {
55 KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
56 CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
57 Realm string `asn1:"generalstring,explicit,tag:2"`
58 SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
59 From time.Time `asn1:"generalized,explicit,optional,tag:4"`
60 Till time.Time `asn1:"generalized,explicit,tag:5"`
61 RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
62 Nonce int `asn1:"explicit,tag:7"`
63 EType []int32 `asn1:"explicit,tag:8"`
64 Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
65 EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
66 // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
67 AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"`
68}
69
70// KDCReqBody implements the KRB_KDC_REQ request body.
71type KDCReqBody struct {
72 KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
73 CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
74 Realm string `asn1:"generalstring,explicit,tag:2"`
75 SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
76 From time.Time `asn1:"generalized,explicit,optional,tag:4"`
77 Till time.Time `asn1:"generalized,explicit,tag:5"`
78 RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
79 Nonce int `asn1:"explicit,tag:7"`
80 EType []int32 `asn1:"explicit,tag:8"`
81 Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
82 EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
83 AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"`
84}
85
86// NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request.
87func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
88 sname := types.PrincipalName{
89 NameType: nametype.KRB_NT_SRV_INST,
90 NameString: []string{"krbtgt", realm},
91 }
92 return NewASReq(realm, c, cname, sname)
93}
94
95// NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request.
96func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
97 sname := types.PrincipalName{
98 NameType: nametype.KRB_NT_PRINCIPAL,
99 NameString: []string{"kadmin", "changepw"},
100 }
101 return NewASReq(realm, c, cname, sname)
102}
103
104// NewASReq generates a new KRB_AS_REQ struct for a given SNAME.
105func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) {
106 nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
107 if err != nil {
108 return ASReq{}, err
109 }
110 t := time.Now().UTC()
111 // Copy the default options to make this thread safe
112 kopts := types.NewKrbFlags()
113 copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes)
114 kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength
115 a := ASReq{
116 KDCReqFields{
117 PVNO: iana.PVNO,
118 MsgType: msgtype.KRB_AS_REQ,
119 PAData: types.PADataSequence{},
120 ReqBody: KDCReqBody{
121 KDCOptions: kopts,
122 Realm: realm,
123 CName: cname,
124 SName: sname,
125 Till: t.Add(c.LibDefaults.TicketLifetime),
126 Nonce: int(nonce.Int64()),
127 EType: c.LibDefaults.DefaultTktEnctypeIDs,
128 },
129 },
130 }
131 if c.LibDefaults.Forwardable {
132 types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
133 }
134 if c.LibDefaults.Canonicalize {
135 types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
136 }
137 if c.LibDefaults.Proxiable {
138 types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
139 }
140 if c.LibDefaults.RenewLifetime != 0 {
141 types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
142 a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
143 a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
144 }
145 if !c.LibDefaults.NoAddresses {
146 ha, err := types.LocalHostAddresses()
147 if err != nil {
148 return a, fmt.Errorf("could not get local addresses: %v", err)
149 }
150 ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
151 a.ReqBody.Addresses = ha
152 }
153 return a, nil
154}
155
156// NewTGSReq generates a new KRB_TGS_REQ struct.
157func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tgt Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool) (TGSReq, error) {
158 a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
159 if err != nil {
160 return a, err
161 }
162 err = a.setPAData(tgt, sessionKey)
163 return a, err
164}
165
166// NewUser2UserTGSReq returns a TGS-REQ suitable for user-to-user authentication (https://tools.ietf.org/html/rfc4120#section-3.7)
167func NewUser2UserTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, clientTGT Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool, verifyingTGT Ticket) (TGSReq, error) {
168 a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
169 if err != nil {
170 return a, err
171 }
172 a.ReqBody.AdditionalTickets = []Ticket{verifyingTGT}
173 types.SetFlag(&a.ReqBody.KDCOptions, flags.EncTktInSkey)
174 err = a.setPAData(clientTGT, sessionKey)
175 return a, err
176}
177
178// tgsReq populates the fields for a TGS_REQ
179func tgsReq(cname, sname types.PrincipalName, kdcRealm string, renewal bool, c *config.Config) (TGSReq, error) {
180 nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
181 if err != nil {
182 return TGSReq{}, err
183 }
184 t := time.Now().UTC()
185 k := KDCReqFields{
186 PVNO: iana.PVNO,
187 MsgType: msgtype.KRB_TGS_REQ,
188 ReqBody: KDCReqBody{
189 KDCOptions: types.NewKrbFlags(),
190 Realm: kdcRealm,
191 CName: cname, // Add the CName to make validation of the reply easier
192 SName: sname,
193 Till: t.Add(c.LibDefaults.TicketLifetime),
194 Nonce: int(nonce.Int64()),
195 EType: c.LibDefaults.DefaultTGSEnctypeIDs,
196 },
197 Renewal: renewal,
198 }
199 if c.LibDefaults.Forwardable {
200 types.SetFlag(&k.ReqBody.KDCOptions, flags.Forwardable)
201 }
202 if c.LibDefaults.Canonicalize {
203 types.SetFlag(&k.ReqBody.KDCOptions, flags.Canonicalize)
204 }
205 if c.LibDefaults.Proxiable {
206 types.SetFlag(&k.ReqBody.KDCOptions, flags.Proxiable)
207 }
208 if c.LibDefaults.RenewLifetime > time.Duration(0) {
209 types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
210 k.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
211 }
212 if !c.LibDefaults.NoAddresses {
213 ha, err := types.LocalHostAddresses()
214 if err != nil {
215 return TGSReq{}, fmt.Errorf("could not get local addresses: %v", err)
216 }
217 ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
218 k.ReqBody.Addresses = ha
219 }
220 if renewal {
221 types.SetFlag(&k.ReqBody.KDCOptions, flags.Renew)
222 types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
223 }
224 return TGSReq{
225 k,
226 }, nil
227}
228
229func (k *TGSReq) setPAData(tgt Ticket, sessionKey types.EncryptionKey) error {
230 // Marshal the request and calculate checksum
231 b, err := k.ReqBody.Marshal()
232 if err != nil {
233 return krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body")
234 }
235 etype, err := crypto.GetEtype(sessionKey.KeyType)
236 if err != nil {
237 return krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator")
238 }
239 cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM)
240 if err != nil {
241 return krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash")
242 }
243
244 // Form PAData for TGS_REQ
245 // Create authenticator
246 auth, err := types.NewAuthenticator(tgt.Realm, k.ReqBody.CName)
247 if err != nil {
248 return krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
249 }
250 auth.Cksum = types.Checksum{
251 CksumType: etype.GetHashID(),
252 Checksum: cb,
253 }
254 // Create AP_REQ
255 apReq, err := NewAPReq(tgt, sessionKey, auth)
256 if err != nil {
257 return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ")
258 }
259 apb, err := apReq.Marshal()
260 if err != nil {
261 return krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data")
262 }
263 k.PAData = types.PADataSequence{
264 types.PAData{
265 PADataType: patype.PA_TGS_REQ,
266 PADataValue: apb,
267 },
268 }
269 return nil
270}
271
272// Unmarshal bytes b into the ASReq struct.
273func (k *ASReq) Unmarshal(b []byte) error {
274 var m marshalKDCReq
275 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))
276 if err != nil {
277 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ")
278 }
279 expectedMsgType := msgtype.KRB_AS_REQ
280 if m.MsgType != expectedMsgType {
281 return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
282 }
283 var reqb KDCReqBody
284 err = reqb.Unmarshal(m.ReqBody.Bytes)
285 if err != nil {
286 return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body")
287 }
288 k.MsgType = m.MsgType
289 k.PAData = m.PAData
290 k.PVNO = m.PVNO
291 k.ReqBody = reqb
292 return nil
293}
294
295// Unmarshal bytes b into the TGSReq struct.
296func (k *TGSReq) Unmarshal(b []byte) error {
297 var m marshalKDCReq
298 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ))
299 if err != nil {
300 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ")
301 }
302 expectedMsgType := msgtype.KRB_TGS_REQ
303 if m.MsgType != expectedMsgType {
304 return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
305 }
306 var reqb KDCReqBody
307 err = reqb.Unmarshal(m.ReqBody.Bytes)
308 if err != nil {
309 return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body")
310 }
311 k.MsgType = m.MsgType
312 k.PAData = m.PAData
313 k.PVNO = m.PVNO
314 k.ReqBody = reqb
315 return nil
316}
317
318// Unmarshal bytes b into the KRB_KDC_REQ body struct.
319func (k *KDCReqBody) Unmarshal(b []byte) error {
320 var m marshalKDCReqBody
321 _, err := asn1.Unmarshal(b, &m)
322 if err != nil {
323 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body")
324 }
325 k.KDCOptions = m.KDCOptions
326 if len(k.KDCOptions.Bytes) < 4 {
327 tb := make([]byte, 4-len(k.KDCOptions.Bytes))
328 k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...)
329 k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8
330 }
331 k.CName = m.CName
332 k.Realm = m.Realm
333 k.SName = m.SName
334 k.From = m.From
335 k.Till = m.Till
336 k.RTime = m.RTime
337 k.Nonce = m.Nonce
338 k.EType = m.EType
339 k.Addresses = m.Addresses
340 k.EncAuthData = m.EncAuthData
341 if len(m.AdditionalTickets.Bytes) > 0 {
342 k.AdditionalTickets, err = unmarshalTicketsSequence(m.AdditionalTickets)
343 if err != nil {
344 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets")
345 }
346 }
347 return nil
348}
349
350// Marshal ASReq struct.
351func (k *ASReq) Marshal() ([]byte, error) {
352 m := marshalKDCReq{
353 PVNO: k.PVNO,
354 MsgType: k.MsgType,
355 PAData: k.PAData,
356 }
357 b, err := k.ReqBody.Marshal()
358 if err != nil {
359 var mk []byte
360 return mk, err
361 }
362 m.ReqBody = asn1.RawValue{
363 Class: asn1.ClassContextSpecific,
364 IsCompound: true,
365 Tag: 4,
366 Bytes: b,
367 }
368 mk, err := asn1.Marshal(m)
369 if err != nil {
370 return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
371 }
372 mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ)
373 return mk, nil
374}
375
376// Marshal TGSReq struct.
377func (k *TGSReq) Marshal() ([]byte, error) {
378 m := marshalKDCReq{
379 PVNO: k.PVNO,
380 MsgType: k.MsgType,
381 PAData: k.PAData,
382 }
383 b, err := k.ReqBody.Marshal()
384 if err != nil {
385 var mk []byte
386 return mk, err
387 }
388 m.ReqBody = asn1.RawValue{
389 Class: asn1.ClassContextSpecific,
390 IsCompound: true,
391 Tag: 4,
392 Bytes: b,
393 }
394 mk, err := asn1.Marshal(m)
395 if err != nil {
396 return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
397 }
398 mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ)
399 return mk, nil
400}
401
402// Marshal KRB_KDC_REQ body struct.
403func (k *KDCReqBody) Marshal() ([]byte, error) {
404 var b []byte
405 m := marshalKDCReqBody{
406 KDCOptions: k.KDCOptions,
407 CName: k.CName,
408 Realm: k.Realm,
409 SName: k.SName,
410 From: k.From,
411 Till: k.Till,
412 RTime: k.RTime,
413 Nonce: k.Nonce,
414 EType: k.EType,
415 Addresses: k.Addresses,
416 EncAuthData: k.EncAuthData,
417 }
418 rawtkts, err := MarshalTicketSequence(k.AdditionalTickets)
419 if err != nil {
420 return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets")
421 }
422 //The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody
423 rawtkts.Tag = 11
424 if len(rawtkts.Bytes) > 0 {
425 m.AdditionalTickets = rawtkts
426 }
427 b, err = asn1.Marshal(m)
428 if err != nil {
429 return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body")
430 }
431 return b, nil
432}