github.com/jcmturner/gokrb5/v8@v8.4.4/messages/KDCRep.go (about) 1 package messages 2 3 // Reference: https://www.ietf.org/rfc/rfc4120.txt 4 // Section: 5.4.2 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/jcmturner/gofork/encoding/asn1" 11 "github.com/jcmturner/gokrb5/v8/asn1tools" 12 "github.com/jcmturner/gokrb5/v8/config" 13 "github.com/jcmturner/gokrb5/v8/credentials" 14 "github.com/jcmturner/gokrb5/v8/crypto" 15 "github.com/jcmturner/gokrb5/v8/iana/asnAppTag" 16 "github.com/jcmturner/gokrb5/v8/iana/flags" 17 "github.com/jcmturner/gokrb5/v8/iana/keyusage" 18 "github.com/jcmturner/gokrb5/v8/iana/msgtype" 19 "github.com/jcmturner/gokrb5/v8/iana/patype" 20 "github.com/jcmturner/gokrb5/v8/krberror" 21 "github.com/jcmturner/gokrb5/v8/types" 22 ) 23 24 type marshalKDCRep struct { 25 PVNO int `asn1:"explicit,tag:0"` 26 MsgType int `asn1:"explicit,tag:1"` 27 PAData types.PADataSequence `asn1:"explicit,optional,tag:2"` 28 CRealm string `asn1:"generalstring,explicit,tag:3"` 29 CName types.PrincipalName `asn1:"explicit,tag:4"` 30 // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag 31 Ticket asn1.RawValue `asn1:"explicit,tag:5"` 32 EncPart types.EncryptedData `asn1:"explicit,tag:6"` 33 } 34 35 // KDCRepFields represents the KRB_KDC_REP fields. 36 type KDCRepFields struct { 37 PVNO int 38 MsgType int 39 PAData []types.PAData 40 CRealm string 41 CName types.PrincipalName 42 Ticket Ticket 43 EncPart types.EncryptedData 44 DecryptedEncPart EncKDCRepPart 45 } 46 47 // ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2. 48 type ASRep struct { 49 KDCRepFields 50 } 51 52 // TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2. 53 type TGSRep struct { 54 KDCRepFields 55 } 56 57 // EncKDCRepPart is the encrypted part of KRB_KDC_REP. 58 type EncKDCRepPart struct { 59 Key types.EncryptionKey `asn1:"explicit,tag:0"` 60 LastReqs []LastReq `asn1:"explicit,tag:1"` 61 Nonce int `asn1:"explicit,tag:2"` 62 KeyExpiration time.Time `asn1:"generalized,explicit,optional,tag:3"` 63 Flags asn1.BitString `asn1:"explicit,tag:4"` 64 AuthTime time.Time `asn1:"generalized,explicit,tag:5"` 65 StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 66 EndTime time.Time `asn1:"generalized,explicit,tag:7"` 67 RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"` 68 SRealm string `asn1:"generalstring,explicit,tag:9"` 69 SName types.PrincipalName `asn1:"explicit,tag:10"` 70 CAddr []types.HostAddress `asn1:"explicit,optional,tag:11"` 71 EncPAData types.PADataSequence `asn1:"explicit,optional,tag:12"` 72 } 73 74 // LastReq part of KRB_KDC_REP. 75 type LastReq struct { 76 LRType int32 `asn1:"explicit,tag:0"` 77 LRValue time.Time `asn1:"generalized,explicit,tag:1"` 78 } 79 80 // Unmarshal bytes b into the ASRep struct. 81 func (k *ASRep) Unmarshal(b []byte) error { 82 var m marshalKDCRep 83 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP)) 84 if err != nil { 85 return processUnmarshalReplyError(b, err) 86 } 87 if m.MsgType != msgtype.KRB_AS_REP { 88 return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, m.MsgType) 89 } 90 //Process the raw ticket within 91 tkt, err := unmarshalTicket(m.Ticket.Bytes) 92 if err != nil { 93 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within AS_REP") 94 } 95 k.KDCRepFields = KDCRepFields{ 96 PVNO: m.PVNO, 97 MsgType: m.MsgType, 98 PAData: m.PAData, 99 CRealm: m.CRealm, 100 CName: m.CName, 101 Ticket: tkt, 102 EncPart: m.EncPart, 103 } 104 return nil 105 } 106 107 // Marshal ASRep struct. 108 func (k *ASRep) Marshal() ([]byte, error) { 109 m := marshalKDCRep{ 110 PVNO: k.PVNO, 111 MsgType: k.MsgType, 112 PAData: k.PAData, 113 CRealm: k.CRealm, 114 CName: k.CName, 115 EncPart: k.EncPart, 116 } 117 b, err := k.Ticket.Marshal() 118 if err != nil { 119 return []byte{}, err 120 } 121 m.Ticket = asn1.RawValue{ 122 Class: asn1.ClassContextSpecific, 123 IsCompound: true, 124 Tag: 5, 125 Bytes: b, 126 } 127 mk, err := asn1.Marshal(m) 128 if err != nil { 129 return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REP") 130 } 131 mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREP) 132 return mk, nil 133 } 134 135 // Unmarshal bytes b into the TGSRep struct. 136 func (k *TGSRep) Unmarshal(b []byte) error { 137 var m marshalKDCRep 138 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP)) 139 if err != nil { 140 return processUnmarshalReplyError(b, err) 141 } 142 if m.MsgType != msgtype.KRB_TGS_REP { 143 return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, m.MsgType) 144 } 145 //Process the raw ticket within 146 tkt, err := unmarshalTicket(m.Ticket.Bytes) 147 if err != nil { 148 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP") 149 } 150 k.KDCRepFields = KDCRepFields{ 151 PVNO: m.PVNO, 152 MsgType: m.MsgType, 153 PAData: m.PAData, 154 CRealm: m.CRealm, 155 CName: m.CName, 156 Ticket: tkt, 157 EncPart: m.EncPart, 158 } 159 return nil 160 } 161 162 // Marshal TGSRep struct. 163 func (k *TGSRep) Marshal() ([]byte, error) { 164 m := marshalKDCRep{ 165 PVNO: k.PVNO, 166 MsgType: k.MsgType, 167 PAData: k.PAData, 168 CRealm: k.CRealm, 169 CName: k.CName, 170 EncPart: k.EncPart, 171 } 172 b, err := k.Ticket.Marshal() 173 if err != nil { 174 return []byte{}, err 175 } 176 m.Ticket = asn1.RawValue{ 177 Class: asn1.ClassContextSpecific, 178 IsCompound: true, 179 Tag: 5, 180 Bytes: b, 181 } 182 mk, err := asn1.Marshal(m) 183 if err != nil { 184 return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REP") 185 } 186 mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREP) 187 return mk, nil 188 } 189 190 // Unmarshal bytes b into encrypted part of KRB_KDC_REP. 191 func (e *EncKDCRepPart) Unmarshal(b []byte) error { 192 _, err := asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart)) 193 if err != nil { 194 // Try using tag 26 195 // Ref: RFC 4120 - mentions that some implementations use application tag number 26 wether or not the reply is 196 // a AS-REP or a TGS-REP. 197 _, err = asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart)) 198 if err != nil { 199 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP") 200 } 201 } 202 return nil 203 } 204 205 // Marshal encrypted part of KRB_KDC_REP. 206 func (e *EncKDCRepPart) Marshal() ([]byte, error) { 207 b, err := asn1.Marshal(*e) 208 if err != nil { 209 return b, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AS_REP encpart") 210 } 211 b = asn1tools.AddASNAppTag(b, asnAppTag.EncASRepPart) 212 return b, nil 213 } 214 215 // DecryptEncPart decrypts the encrypted part of an AS_REP. 216 func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) { 217 var key types.EncryptionKey 218 var err error 219 if c.HasKeytab() { 220 key, _, err = c.Keytab().GetEncryptionKey(k.CName, k.CRealm, k.EncPart.KVNO, k.EncPart.EType) 221 if err != nil { 222 return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part") 223 } 224 } 225 if c.HasPassword() { 226 key, _, err = crypto.GetKeyFromPassword(c.Password(), k.CName, k.CRealm, k.EncPart.EType, k.PAData) 227 if err != nil { 228 return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part") 229 } 230 } 231 if !c.HasKeytab() && !c.HasPassword() { 232 return key, krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to perform decryption of AS_REP encrypted part") 233 } 234 b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART) 235 if err != nil { 236 return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part") 237 } 238 var denc EncKDCRepPart 239 err = denc.Unmarshal(b) 240 if err != nil { 241 return key, krberror.Errorf(err, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP") 242 } 243 k.DecryptedEncPart = denc 244 return key, nil 245 } 246 247 // Verify checks the validity of AS_REP message. 248 func (k *ASRep) Verify(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) { 249 //Ref RFC 4120 Section 3.1.5 250 if !k.CName.Equal(asReq.ReqBody.CName) { 251 return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName) 252 } 253 if k.CRealm != asReq.ReqBody.Realm { 254 return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm) 255 } 256 key, err := k.DecryptEncPart(creds) 257 if err != nil { 258 return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting EncPart of AS_REP") 259 } 260 if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce { 261 return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request") 262 } 263 if !k.DecryptedEncPart.SName.Equal(asReq.ReqBody.SName) { 264 return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", asReq.ReqBody.SName, k.DecryptedEncPart.SName) 265 } 266 if k.DecryptedEncPart.SRealm != asReq.ReqBody.Realm { 267 return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.DecryptedEncPart.SRealm) 268 } 269 if len(asReq.ReqBody.Addresses) > 0 { 270 if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) { 271 return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ") 272 } 273 } 274 t := time.Now().UTC() 275 if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew { 276 return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds()) 277 } 278 // RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11 279 if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) { 280 if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) { 281 return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation") 282 } 283 for _, pa := range k.DecryptedEncPart.EncPAData { 284 if pa.PADataType == patype.PA_REQ_ENC_PA_REP { 285 var pafast types.PAReqEncPARep 286 err := pafast.Unmarshal(pa.PADataValue) 287 if err != nil { 288 return false, krberror.Errorf(err, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP") 289 } 290 etype, err := crypto.GetChksumEtype(pafast.ChksumType) 291 if err != nil { 292 return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response error") 293 } 294 ab, _ := asReq.Marshal() 295 if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) { 296 return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response checksum invalid") 297 } 298 } 299 } 300 } 301 return true, nil 302 } 303 304 // DecryptEncPart decrypts the encrypted part of an TGS_REP. 305 func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error { 306 b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.TGS_REP_ENCPART_SESSION_KEY) 307 if err != nil { 308 return krberror.Errorf(err, krberror.DecryptingError, "error decrypting TGS_REP EncPart") 309 } 310 var denc EncKDCRepPart 311 err = denc.Unmarshal(b) 312 if err != nil { 313 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part") 314 } 315 k.DecryptedEncPart = denc 316 return nil 317 } 318 319 // Verify checks the validity of the TGS_REP message. 320 func (k *TGSRep) Verify(cfg *config.Config, tgsReq TGSReq) (bool, error) { 321 if !k.CName.Equal(tgsReq.ReqBody.CName) { 322 return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName) 323 } 324 if k.Ticket.Realm != tgsReq.ReqBody.Realm { 325 return false, krberror.NewErrorf(krberror.KRBMsgError, "realm in response ticket does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.Ticket.Realm) 326 } 327 if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce { 328 return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request") 329 } 330 //if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil { 331 // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName) 332 //} 333 //for i := range k.Ticket.SName.NameString { 334 // if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] { 335 // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName) 336 // } 337 //} 338 //if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil { 339 // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName) 340 //} 341 //for i := range k.DecryptedEncPart.SName.NameString { 342 // if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] { 343 // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName) 344 // } 345 //} 346 if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm { 347 return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm) 348 } 349 if len(k.DecryptedEncPart.CAddr) > 0 { 350 if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) { 351 return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ") 352 } 353 } 354 if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew { 355 if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew { 356 return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", cfg.LibDefaults.Clockskew.Seconds()) 357 } 358 } 359 return true, nil 360 }