blob: 183607976314049b2fd5fb8b35eb868cd2135c05 [file] [log] [blame]
khenaidoo7d3c5582021-08-11 18:09:44 -04001package messages
2
3import (
4 "fmt"
5 "time"
6
7 "github.com/jcmturner/gofork/encoding/asn1"
8 "github.com/jcmturner/gokrb5/v8/asn1tools"
9 "github.com/jcmturner/gokrb5/v8/crypto"
10 "github.com/jcmturner/gokrb5/v8/iana"
11 "github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
12 "github.com/jcmturner/gokrb5/v8/iana/errorcode"
13 "github.com/jcmturner/gokrb5/v8/iana/keyusage"
14 "github.com/jcmturner/gokrb5/v8/iana/msgtype"
15 "github.com/jcmturner/gokrb5/v8/keytab"
16 "github.com/jcmturner/gokrb5/v8/krberror"
17 "github.com/jcmturner/gokrb5/v8/types"
18)
19
20type marshalAPReq struct {
21 PVNO int `asn1:"explicit,tag:0"`
22 MsgType int `asn1:"explicit,tag:1"`
23 APOptions asn1.BitString `asn1:"explicit,tag:2"`
24 // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
25 Ticket asn1.RawValue `asn1:"explicit,tag:3"`
26 EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"`
27}
28
29// APReq implements RFC 4120 KRB_AP_REQ: https://tools.ietf.org/html/rfc4120#section-5.5.1.
30type APReq struct {
31 PVNO int `asn1:"explicit,tag:0"`
32 MsgType int `asn1:"explicit,tag:1"`
33 APOptions asn1.BitString `asn1:"explicit,tag:2"`
34 Ticket Ticket `asn1:"explicit,tag:3"`
35 EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"`
36 Authenticator types.Authenticator `asn1:"optional"`
37}
38
39// NewAPReq generates a new KRB_AP_REQ struct.
40func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
41 var a APReq
42 ed, err := encryptAuthenticator(auth, sessionKey, tkt)
43 if err != nil {
44 return a, krberror.Errorf(err, krberror.KRBMsgError, "error creating Authenticator for AP_REQ")
45 }
46 a = APReq{
47 PVNO: iana.PVNO,
48 MsgType: msgtype.KRB_AP_REQ,
49 APOptions: types.NewKrbFlags(),
50 Ticket: tkt,
51 EncryptedAuthenticator: ed,
52 }
53 return a, nil
54}
55
56// Encrypt Authenticator
57func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) {
58 var ed types.EncryptedData
59 m, err := a.Marshal()
60 if err != nil {
61 return ed, krberror.Errorf(err, krberror.EncodingError, "marshaling error of EncryptedData form of Authenticator")
62 }
63 usage := authenticatorKeyUsage(tkt.SName)
64 ed, err = crypto.GetEncryptedData(m, sessionKey, uint32(usage), tkt.EncPart.KVNO)
65 if err != nil {
66 return ed, krberror.Errorf(err, krberror.EncryptingError, "error encrypting Authenticator")
67 }
68 return ed, nil
69}
70
71// DecryptAuthenticator decrypts the Authenticator within the AP_REQ.
72// sessionKey may simply be the key within the decrypted EncPart of the ticket within the AP_REQ.
73func (a *APReq) DecryptAuthenticator(sessionKey types.EncryptionKey) error {
74 usage := authenticatorKeyUsage(a.Ticket.SName)
75 ab, e := crypto.DecryptEncPart(a.EncryptedAuthenticator, sessionKey, uint32(usage))
76 if e != nil {
77 return fmt.Errorf("error decrypting authenticator: %v", e)
78 }
79 err := a.Authenticator.Unmarshal(ab)
80 if err != nil {
81 return fmt.Errorf("error unmarshaling authenticator: %v", err)
82 }
83 return nil
84}
85
86func authenticatorKeyUsage(pn types.PrincipalName) int {
87 if pn.NameString[0] == "krbtgt" {
88 return keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR
89 }
90 return keyusage.AP_REQ_AUTHENTICATOR
91}
92
93// Unmarshal bytes b into the APReq struct.
94func (a *APReq) Unmarshal(b []byte) error {
95 var m marshalAPReq
96 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ))
97 if err != nil {
98 return krberror.Errorf(err, krberror.EncodingError, "unmarshal error of AP_REQ")
99 }
100 if m.MsgType != msgtype.KRB_AP_REQ {
101 return NewKRBError(types.PrincipalName{}, "", errorcode.KRB_AP_ERR_MSG_TYPE, errorcode.Lookup(errorcode.KRB_AP_ERR_MSG_TYPE))
102 }
103 a.PVNO = m.PVNO
104 a.MsgType = m.MsgType
105 a.APOptions = m.APOptions
106 a.EncryptedAuthenticator = m.EncryptedAuthenticator
107 a.Ticket, err = unmarshalTicket(m.Ticket.Bytes)
108 if err != nil {
109 return krberror.Errorf(err, krberror.EncodingError, "unmarshaling error of Ticket within AP_REQ")
110 }
111 return nil
112}
113
114// Marshal APReq struct.
115func (a *APReq) Marshal() ([]byte, error) {
116 m := marshalAPReq{
117 PVNO: a.PVNO,
118 MsgType: a.MsgType,
119 APOptions: a.APOptions,
120 EncryptedAuthenticator: a.EncryptedAuthenticator,
121 }
122 var b []byte
123 b, err := a.Ticket.Marshal()
124 if err != nil {
125 return b, err
126 }
127 m.Ticket = asn1.RawValue{
128 Class: asn1.ClassContextSpecific,
129 IsCompound: true,
130 Tag: 3,
131 Bytes: b,
132 }
133 mk, err := asn1.Marshal(m)
134 if err != nil {
135 return mk, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP_REQ")
136 }
137 mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ)
138 return mk, nil
139}
140
141// Verify an AP_REQ using service's keytab, spn and max acceptable clock skew duration.
142// The service ticket encrypted part and authenticator will be decrypted as part of this operation.
143func (a *APReq) Verify(kt *keytab.Keytab, d time.Duration, cAddr types.HostAddress, snameOverride *types.PrincipalName) (bool, error) {
144 // Decrypt ticket's encrypted part with service key
145 //TODO decrypt with service's session key from its TGT is use-to-user. Need to figure out how to get TGT.
146 //if types.IsFlagSet(&a.APOptions, flags.APOptionUseSessionKey) {
147 // err := a.Ticket.Decrypt(tgt.DecryptedEncPart.Key)
148 // if err != nil {
149 // return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of ticket provided using session key")
150 // }
151 //} else {
152 // err := a.Ticket.DecryptEncPart(*kt, &a.Ticket.SName)
153 // if err != nil {
154 // return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
155 // }
156 //}
157 sname := &a.Ticket.SName
158 if snameOverride != nil {
159 sname = snameOverride
160 }
161 err := a.Ticket.DecryptEncPart(kt, sname)
162 if err != nil {
163 return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
164 }
165
166 // Check time validity of ticket
167 ok, err := a.Ticket.Valid(d)
168 if err != nil || !ok {
169 return ok, err
170 }
171
172 // Check client's address is listed in the client addresses in the ticket
173 if len(a.Ticket.DecryptedEncPart.CAddr) > 0 {
174 //If client addresses are present check if any of them match the source IP that sent the APReq
175 //If there is no match return KRB_AP_ERR_BADADDR error.
176 if !types.HostAddressesContains(a.Ticket.DecryptedEncPart.CAddr, cAddr) {
177 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "client address not within the list contained in the service ticket")
178 }
179 }
180
181 // Decrypt authenticator with session key from ticket's encrypted part
182 err = a.DecryptAuthenticator(a.Ticket.DecryptedEncPart.Key)
183 if err != nil {
184 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BAD_INTEGRITY, "could not decrypt authenticator")
185 }
186
187 // Check CName in authenticator is the same as that in the ticket
188 if !a.Authenticator.CName.Equal(a.Ticket.DecryptedEncPart.CName) {
189 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket")
190 }
191
192 // Check the clock skew between the client and the service server
193 ct := a.Authenticator.CTime.Add(time.Duration(a.Authenticator.Cusec) * time.Microsecond)
194 t := time.Now().UTC()
195 if t.Sub(ct) > d || ct.Sub(t) > d {
196 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("clock skew with client too large. greater than %v seconds", d))
197 }
198 return true, nil
199}