浏览代码

fixed client PA-REQ-ENC-PA-REP checksum validation

Jonathan Turner 9 年之前
父节点
当前提交
fa6fe72439
共有 4 个文件被更改,包括 34 次插入34 次删除
  1. 0 1
      README.md
  2. 1 5
      client/ASExchange.go
  3. 31 26
      messages/KDCRep.go
  4. 2 2
      messages/KDCRep_test.go

+ 0 - 1
README.md

@@ -129,4 +129,3 @@ If authentication succeeds then the request's context will have the following va
 | Golang's ASN1 package cannot marshal into a GeneralString | Yes - using https://github.com/jcmturner/asn1 | https://github.com/golang/go/issues/18832 |
 | Golang's ASN1 package cannot marshal into slice of strings and pass stringtype parameter tags to members | Yes - using https://github.com/jcmturner/asn1 | https://github.com/golang/go/issues/18834 |
 | Golang's ASN1 package cannot marshal with application tags | Yes | |
-| Client's verification of PA-REQ-ENC-PA-REP PA Data checksum fails | Yes - check currently disabled | |

+ 1 - 5
client/ASExchange.go

@@ -70,11 +70,7 @@ func (cl *Client) ASExchange() error {
 		}
 		return krberr
 	}
-	err = ar.DecryptEncPart(cl.Credentials)
-	if err != nil {
-		return fmt.Errorf("Error decrypting EncPart of AS_REP: %v", err)
-	}
-	if ok, err := ar.IsValid(cl.Config, a); !ok {
+	if ok, err := ar.IsValid(cl.Config, cl.Credentials, a); !ok {
 		return fmt.Errorf("AS_REP is not valid: %v", err)
 	}
 	cl.Session = &Session{

+ 31 - 26
messages/KDCRep.go

@@ -10,6 +10,7 @@ import (
 	"github.com/jcmturner/gokrb5/config"
 	"github.com/jcmturner/gokrb5/credentials"
 	"github.com/jcmturner/gokrb5/crypto"
+	"github.com/jcmturner/gokrb5/crypto/engine"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
 	"github.com/jcmturner/gokrb5/iana/keyusage"
 	"github.com/jcmturner/gokrb5/iana/msgtype"
@@ -146,39 +147,39 @@ func (e *EncKDCRepPart) Unmarshal(b []byte) error {
 }
 
 // Decrypt the encrypted part of an AS_REP.
-func (k *ASRep) DecryptEncPart(c *credentials.Credentials) error {
+func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) {
 	var key types.EncryptionKey
 	var err error
 	if c.HasKeytab() {
 		key, err = c.Keytab.GetEncryptionKey(k.CName.NameString, k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
 		if err != nil {
-			return fmt.Errorf("Could not get key from keytab: %v", err)
+			return key, fmt.Errorf("Could not get key from keytab: %v", err)
 		}
 	}
 	if c.HasPassword() {
 		key, _, err = crypto.GetKeyFromPassword(c.Password, k.CName, k.CRealm, k.EncPart.EType, k.PAData)
 		if err != nil {
-			return fmt.Errorf("Could not derive key from password: %v", err)
+			return key, fmt.Errorf("Could not derive key from password: %v", err)
 		}
 	}
 	if !c.HasKeytab() && !c.HasPassword() {
-		return errors.New("No secret available in credentials to preform decryption")
+		return key, errors.New("No secret available in credentials to preform decryption")
 	}
 	b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART)
 	if err != nil {
-		return fmt.Errorf("Error decrypting KDC_REP EncPart: %v", err)
+		return key, fmt.Errorf("Error decrypting KDC_REP EncPart: %v", err)
 	}
 	var denc EncKDCRepPart
 	err = denc.Unmarshal(b)
 	if err != nil {
-		return fmt.Errorf("Error unmarshalling encrypted part: %v", err)
+		return key, fmt.Errorf("Error unmarshalling encrypted part: %v", err)
 	}
 	k.DecryptedEncPart = denc
-	return nil
+	return key, nil
 }
 
 // Check validity of AS_REP message.
-func (k *ASRep) IsValid(cfg *config.Config, asReq ASReq) (bool, error) {
+func (k *ASRep) IsValid(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) {
 	//Ref RFC 4120 Section 3.1.5
 	if k.CName.NameType != asReq.ReqBody.CName.NameType || k.CName.NameString == nil {
 		return false, fmt.Errorf("CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
@@ -191,6 +192,10 @@ func (k *ASRep) IsValid(cfg *config.Config, asReq ASReq) (bool, error) {
 	if k.CRealm != asReq.ReqBody.Realm {
 		return false, fmt.Errorf("CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm)
 	}
+	key, err := k.DecryptEncPart(creds)
+	if err != nil {
+		return false, fmt.Errorf("Error decrypting EncPart of AS_REP: %v", err)
+	}
 	if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce {
 		return false, errors.New("Possible replay attack, nonce in response does not match that in request")
 	}
@@ -214,28 +219,28 @@ func (k *ASRep) IsValid(cfg *config.Config, asReq ASReq) (bool, error) {
 	if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew {
 		return false, fmt.Errorf("Clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds())
 	}
+	// RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
 	if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) {
 		if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
 			return false, errors.New("KDC did not respond appropriately to FAST negotiation")
 		}
-		//TODO figure out how to check hash and put back
-		//for _, pa := range k.DecryptedEncPart.EncPAData {
-		//	if pa.PADataType == patype.PA_REQ_ENC_PA_REP {
-		//		var pafast types.PAReqEncPARep
-		//		err := pafast.Unmarshal(pa.PADataValue)
-		//		if err != nil {
-		//			return false, fmt.Errorf("KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP: %v", err)
-		//		}
-		//		etype, err := crypto.GetChksumEtype(pafast.ChksumType)
-		//		if err != nil {
-		//			return false, fmt.Errorf("KDC FAST negotiation response error, %v", err)
-		//		}
-		//		ab, _ := asReq.Marshal()
-		//		if !engine.VerifyChecksum(k.DecryptedEncPart.Key.KeyValue, pafast.Chksum, ab, keyusage.KEY_USAGE_AS_REQ, etype) {
-		//			return false, errors.New("KDC FAST negotiation response checksum invalid")
-		//		}
-		//	}
-		//}
+		for _, pa := range k.DecryptedEncPart.EncPAData {
+			if pa.PADataType == patype.PA_REQ_ENC_PA_REP {
+				var pafast types.PAReqEncPARep
+				err := pafast.Unmarshal(pa.PADataValue)
+				if err != nil {
+					return false, fmt.Errorf("KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP: %v", err)
+				}
+				etype, err := crypto.GetChksumEtype(pafast.ChksumType)
+				if err != nil {
+					return false, fmt.Errorf("KDC FAST negotiation response error, %v", err)
+				}
+				ab, _ := asReq.Marshal()
+				if !engine.VerifyChecksum(key.KeyValue, pafast.Chksum, ab, keyusage.KEY_USAGE_AS_REQ, etype) {
+					return false, errors.New("KDC FAST negotiation response checksum invalid")
+				}
+			}
+		}
 	}
 	return true, nil
 }

+ 2 - 2
messages/KDCRep_test.go

@@ -250,7 +250,7 @@ func TestUnmarshalASRepDecodeAndDecrypt(t *testing.T) {
 		t.Fatalf("keytab parse error: %v\n", err)
 	}
 	cred := credentials.NewCredentials(test_user, test_realm)
-	err = asRep.DecryptEncPart(cred.WithKeytab(kt))
+	_, err = asRep.DecryptEncPart(cred.WithKeytab(kt))
 	if err != nil {
 		t.Fatalf("Decryption of AS_REP EncPart failed: %v", err)
 	}
@@ -290,7 +290,7 @@ func TestUnmarshalASRepDecodeAndDecrypt_withPassword(t *testing.T) {
 	assert.Equal(t, etypeID.AES256_CTS_HMAC_SHA1_96, asRep.EncPart.EType, "Etype of encrypted part not as expected")
 	assert.Equal(t, 0, asRep.EncPart.KVNO, "Encrypted part KVNO not as expected")
 	cred := credentials.NewCredentials(test_user, test_realm)
-	err = asRep.DecryptEncPart(cred.WithPassword(test_user_password))
+	_, err = asRep.DecryptEncPart(cred.WithPassword(test_user_password))
 	if err != nil {
 		t.Fatalf("Decryption of AS_REP EncPart failed: %v", err)
 	}