瀏覽代碼

tgs ex work

Jonathan Turner 9 年之前
父節點
當前提交
f6b747e444

+ 7 - 5
README.md

@@ -4,6 +4,8 @@ This is work in progress and does not yet work...
 
 [![GoDoc](https://godoc.org/github.com/jcmturner/gokrb5?status.svg)](https://godoc.org/github.com/jcmturner/gokrb5)
 
+## Compatibility
+Go version 1.8+ is needed.
 
 ## References
 ### RFCs
@@ -23,12 +25,12 @@ This is work in progress and does not yet work...
 * https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
 
 ## Thanks
-* Greg Hudson from the MIT Consortium for Kerberos and Internet Trust for providing useful test vectors.
+* Greg Hudson from the MIT Consortium for Kerberos and Internet Trust for providing useful advice.
 
 ## Known Issues
 | Issue | Worked around? | References |
 |-------|-------------|------------|
-| Cannot unmarshal into slice of asn1.RawValue | Yes | https://github.com/golang/go/issues/17321 |
-| Cannot marshal into a GeneralString | Yes - using https://github.com/jcmturner/asn1 | https://github.com/golang/go/issues/18832 |
-| 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 |
-| Cannot marshal with application tags | Yes | |
+| Golang's ASN1 package cannot unmarshal into slice of asn1.RawValue | Yes | https://github.com/golang/go/issues/17321 |
+| 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 | |

+ 1 - 1
config/krb5conf.go

@@ -375,7 +375,7 @@ func (d *DomainRealm) parseLines(lines []string) error {
 		}
 		p := strings.Split(line, "=")
 		domain := strings.Replace(strings.ToLower(p[0]), " ", "", -1)
-		realm := strings.Replace(p[1], " ", "", -1)
+		realm := strings.Replace(strings.ToUpper(p[1]), " ", "", -1)
 		d.addMapping(domain, realm)
 	}
 	return nil

+ 11 - 2
crypto/EncryptionEngine.go

@@ -15,6 +15,7 @@ import (
 
 type EType interface {
 	GetETypeID() int
+	GetHashID() int
 	GetKeyByteSize() int                                        // See "protocol key format" for defined values
 	GetKeySeedBitLength() int                                   // key-generation seed length, k
 	GetDefaultStringToKeyParams() string                        // default string-to-key parameters (s2kparams)
@@ -215,8 +216,8 @@ func GetKeyFromPassword(passwd string, cn types.PrincipalName, realm string, ety
 	return key, etype, nil
 }
 
-func GetIntegrityHash(pt, key []byte, usage uint32, etype EType) ([]byte, error) {
-	k, err := etype.DeriveKey(key, GetUsageKi(usage))
+func getHash(pt, key []byte, usage []byte, etype EType) ([]byte, error) {
+	k, err := etype.DeriveKey(key, usage)
 	if err != nil {
 		return nil, fmt.Errorf("Unable to derive key for checksum: %v", err)
 	}
@@ -225,6 +226,14 @@ func GetIntegrityHash(pt, key []byte, usage uint32, etype EType) ([]byte, error)
 	return mac.Sum(nil)[:etype.GetHMACBitLength()/8], nil
 }
 
+func GetChecksumHash(pt, key []byte, usage uint32, etype EType) ([]byte, error) {
+	return getHash(pt, key, GetUsageKc(usage), etype)
+}
+
+func GetIntegrityHash(pt, key []byte, usage uint32, etype EType) ([]byte, error) {
+	return getHash(pt, key, GetUsageKi(usage), etype)
+}
+
 func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype EType) bool {
 	//The ciphertext output is the concatenation of the output of the basic
 	//encryption function E and a (possibly truncated) HMAC using the

+ 7 - 1
crypto/aes128-cts-hmac-sha1-96.go

@@ -4,6 +4,8 @@ import (
 	"crypto/aes"
 	"crypto/sha1"
 	"hash"
+	"github.com/jcmturner/gokrb5/iana/chksumtype"
+	"github.com/jcmturner/gokrb5/iana/etype"
 )
 
 // RFC 3962
@@ -56,7 +58,11 @@ type Aes128CtsHmacSha96 struct {
 }
 
 func (e Aes128CtsHmacSha96) GetETypeID() int {
-	return 17
+	return etype.AES128_CTS_HMAC_SHA1_96
+}
+
+func (e Aes128CtsHmacSha96) GetHashID() int {
+	return chksumtype.HMAC_SHA1_96_AES128
 }
 
 func (e Aes128CtsHmacSha96) GetKeyByteSize() int {

+ 7 - 1
crypto/aes256-cts-hmac-sha1-96.go

@@ -4,6 +4,8 @@ import (
 	"crypto/aes"
 	"crypto/sha1"
 	"hash"
+	"github.com/jcmturner/gokrb5/iana/etype"
+	"github.com/jcmturner/gokrb5/iana/chksumtype"
 )
 
 // RFC 3962
@@ -56,7 +58,11 @@ type Aes256CtsHmacSha96 struct {
 }
 
 func (e Aes256CtsHmacSha96) GetETypeID() int {
-	return 18
+	return etype.AES256_CTS_HMAC_SHA1_96
+}
+
+func (e Aes256CtsHmacSha96) GetHashID() int {
+	return chksumtype.HMAC_SHA1_96_AES256
 }
 
 func (e Aes256CtsHmacSha96) GetKeyByteSize() int {

+ 7 - 1
crypto/des3-cbc-sha1-kd.go

@@ -7,6 +7,8 @@ import (
 	"errors"
 	"fmt"
 	"hash"
+	"github.com/jcmturner/gokrb5/iana/chksumtype"
+	"github.com/jcmturner/gokrb5/iana/etype"
 )
 
 //RFC: 3961 Section 6.3
@@ -50,7 +52,11 @@ type Des3CbcSha1Kd struct {
 }
 
 func (e Des3CbcSha1Kd) GetETypeID() int {
-	return 16
+	return etype.DES3_CBC_SHA1_KD
+}
+
+func (e Des3CbcSha1Kd) GetHashID() int {
+	return chksumtype.HMAC_SHA1_DES3_KD
 }
 
 func (e Des3CbcSha1Kd) GetKeyByteSize() int {

+ 71 - 0
messages/APReq.go

@@ -7,6 +7,10 @@ import (
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
 	"github.com/jcmturner/gokrb5/iana/msgtype"
 	"github.com/jcmturner/gokrb5/types"
+	"github.com/jcmturner/gokrb5/iana"
+	"github.com/jcmturner/gokrb5/crypto"
+	"github.com/jcmturner/gokrb5/asn1tools"
+	"github.com/jcmturner/gokrb5/iana/keyusage"
 )
 
 /*AP-REQ          ::= [APPLICATION 14] SEQUENCE {
@@ -39,6 +43,47 @@ type APReq struct {
 	Authenticator types.EncryptedData `asn1:"explicit,tag:4"`
 }
 
+func NewAPReq(TGT types.Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
+	var a APReq
+	ed, err := encryptAuthenticator(auth, sessionKey)
+	if err != nil {
+		return a, fmt.Errorf("Error creating authenticator for AP_REQ: %v", err)
+	}
+	a = APReq{
+		PVNO:    iana.PVNO,
+		MsgType: msgtype.KRB_AP_REQ,
+		APOptions: asn1.BitString{},
+		Ticket: TGT,
+		Authenticator: ed,
+	}
+	return a, nil
+}
+
+func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey) (types.EncryptedData, error) {
+	var ed types.EncryptedData
+	etype, err := crypto.GetEtype(sessionKey.KeyType)
+	if err != nil {
+		return ed, fmt.Errorf("Error getting etype to encrypt authenticator: %v", err)
+	}
+	m, err := a.Marshal()
+	if err != nil {
+		return ed, fmt.Errorf("Error marshalling authenticator: %v", err)
+	}
+	k, err := etype.DeriveKey(sessionKey.KeyValue, crypto.GetUsageKe(uint32(keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR)))
+	if err != nil {
+		return ed, fmt.Errorf("Error deriving key for authenticator: %v", err)
+	}
+	_, b, err := etype.Encrypt(k, m)
+	if err != nil {
+		return ed, fmt.Errorf("Error encrypting authenticator: %v", err)
+	}
+	ed = types.EncryptedData{
+		EType: sessionKey.KeyType,
+		Cipher: b,
+	}
+	return ed, nil
+}
+
 func (a *APReq) Unmarshal(b []byte) error {
 	var m marshalAPReq
 	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ))
@@ -58,3 +103,29 @@ func (a *APReq) Unmarshal(b []byte) error {
 	}
 	return nil
 }
+
+func (a *APReq) Marshal() ([]byte, error) {
+	m := marshalAPReq{
+		PVNO:    a.PVNO,
+		MsgType: a.MsgType,
+		APOptions: a.APOptions,
+		Authenticator: a.Authenticator,
+	}
+	var b []byte
+	b, err := a.Ticket.Marshal()
+	if err != nil {
+		return b, err
+	}
+	m.Ticket = asn1.RawValue{
+		Class:      2,
+		IsCompound: true,
+		Tag:        3,
+		Bytes:      b,
+	}
+	mk, err := asn1.Marshal(m)
+	if err != nil {
+		return mk, fmt.Errorf("Error marshalling AP_REQ: %v", err)
+	}
+	mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ)
+	return mk, nil
+}

+ 30 - 8
messages/KDCReq.go

@@ -17,6 +17,8 @@ import (
 	"math/rand"
 	"time"
 	"strings"
+	"github.com/jcmturner/gokrb5/iana/keyusage"
+	"github.com/jcmturner/gokrb5/crypto"
 )
 
 type marshalKDCReq struct {
@@ -111,19 +113,13 @@ func NewASReq(c *config.Config, username string) ASReq {
 	return a
 }
 
-func NewTGSReq(c *config.Config, spn string) TGSReq {
-	pas := types.PADataSequence{
-		types.PAData{
-			PADataType: patype.PA_REQ_ENC_PA_REP,
-		},
-	}
+func NewTGSReq(username string, c *config.Config, TGT types.Ticket, sessionKey types.EncryptionKey, spn string) (TGSReq, error) {
 	nonce := int(rand.Int31())
 	t := time.Now()
 	s := strings.Split(spn, "/")
 	a := TGSReq{
 		PVNO:    iana.PVNO,
 		MsgType: msgtype.KRB_TGS_REQ,
-		PAData:  pas,
 		ReqBody: KDCReqBody{
 			KDCOptions: c.LibDefaults.Kdc_default_options,
 			Realm:      c.ResolveRealm(s[len(s)-1]),
@@ -148,7 +144,33 @@ func NewTGSReq(c *config.Config, spn string) TGSReq {
 	if c.LibDefaults.Renew_lifetime != 0 {
 		a.ReqBody.RTime = t.Add(c.LibDefaults.Renew_lifetime)
 	}
-	return a
+	b, err := a.ReqBody.Marshal()
+	if err != nil {
+		return a, fmt.Errorf("Error marshalling request body: %v", err)
+	}
+	auth := types.NewAuthenticator(c.LibDefaults.Default_realm, username)
+	etype, err := crypto.GetEtype(sessionKey.KeyType)
+	if err != nil {
+		return a, fmt.Errorf("Error getting etype to encrypt authenticator: %v", err)
+	}
+	cb, err := crypto.GetChecksumHash(b, sessionKey.KeyValue, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM, etype)
+	auth.Cksum = types.Checksum {
+		CksumType: etype.GetHashID(),
+		Checksum: cb,
+	}
+	apReq, err := NewAPReq(TGT, sessionKey, auth)
+	apb, err := apReq.Marshal()
+	if err != nil {
+		return a, fmt.Errorf("Error marshalling AP_REQ for pre-authentication data: %v", err)
+	}
+	pas := types.PADataSequence{
+		types.PAData{
+			PADataType: patype.PA_TGS_REQ,
+			PADataValue: apb,
+		},
+	}
+	a.PAData = pas
+	return a, nil
 }
 
 func (k *ASReq) Unmarshal(b []byte) error {

+ 27 - 0
types/Authenticator.go

@@ -3,8 +3,11 @@ package types
 import (
 	"fmt"
 	"github.com/jcmturner/asn1"
+	"github.com/jcmturner/gokrb5/iana"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
+	"github.com/jcmturner/gokrb5/iana/nametype"
 	"time"
+	"github.com/jcmturner/gokrb5/asn1tools"
 )
 
 /*Authenticator   ::= [APPLICATION 2] SEQUENCE  {
@@ -39,7 +42,31 @@ type Authenticator struct {
 	AuthorizationData AuthorizationData `asn1:"explicit,optional,tag:8"`
 }
 
+func NewAuthenticator(realm, username string) Authenticator {
+	t := time.Now()
+	return Authenticator{
+		AVNO:   iana.PVNO,
+		CRealm: realm,
+		CName: PrincipalName{
+			NameType:   nametype.KRB_NT_PRINCIPAL,
+			NameString: []string{username},
+		},
+		Cksum: Checksum{},
+		Cusec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)),
+		CTime: t,
+	}
+}
+
 func (a *Authenticator) Unmarshal(b []byte) error {
 	_, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.Authenticator))
 	return err
 }
+
+func (a *Authenticator) Marshal() ([]byte, error) {
+	b, err := asn1.Marshal(*a)
+	if err != nil {
+		return nil, err
+	}
+	b = asn1tools.AddASNAppTag(b, asnAppTag.Authenticator)
+	return b, nil
+}