Repository: jcmturner/gokrb5
Pull request #72 (2017-12-11 21:32:30)

refactor GSSAPI mechtoken
custom ap options

update for error handling


Patch:
diff --git a/gssapi/NegotiationToken.go b/gssapi/NegotiationToken.go
index 002f588..910d87a 100644
--- a/gssapi/NegotiationToken.go
+++ b/gssapi/NegotiationToken.go
@@ -133,12 +133,16 @@ func (n *NegTokenResp) Marshal() ([]byte, error) {
 
 // NewNegTokenInitKrb5 creates new Init negotiation token for Kerberos 5
 func NewNegTokenInitKrb5(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) {
-	mt, err := NewKRB5APREQMechToken(creds, tkt, sessionKey)
+	mt, err := NewAPREQMechToken(creds, tkt, sessionKey, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}, []int{})
 	if err != nil {
-		return NegTokenInit{}, fmt.Errorf("Error getting MechToken; %v", err)
+		return NegTokenInit{}, fmt.Errorf("error getting MechToken; %v", err)
+	}
+	mtb, err := mt.Marshal()
+	if err != nil {
+		return NegTokenInit{}, fmt.Errorf("error marshalling MechToken; %v", err)
 	}
 	return NegTokenInit{
 		MechTypes: []asn1.ObjectIdentifier{MechTypeOIDKRB5},
-		MechToken: mt,
+		MechToken: mtb,
 	}, nil
 }
diff --git a/gssapi/krb5Token.go b/gssapi/krb5Token.go
index 55fd12d..1763084 100644
--- a/gssapi/krb5Token.go
+++ b/gssapi/krb5Token.go
@@ -3,6 +3,7 @@ package gssapi
 import (
 	"encoding/binary"
 	"encoding/hex"
+	"errors"
 	"fmt"
 	"github.com/jcmturner/asn1"
 	"gopkg.in/jcmturner/gokrb5.v2/asn1tools"
@@ -28,7 +29,7 @@ const (
 	GSS_C_INTEG_FLAG    = 32
 )
 
-// MechToken implementation for GSSAPI
+// MechToken implementation for GSSAPI.
 type MechToken struct {
 	OID      asn1.ObjectIdentifier
 	TokID    []byte
@@ -37,7 +38,32 @@ type MechToken struct {
 	KRBError messages.KRBError
 }
 
-// Unmarshal a MechToken
+// Marshal a MechToken into a slice of bytes.
+func (m *MechToken) Marshal() ([]byte, error) {
+	// Create the header
+	b, _ := asn1.Marshal(m.OID)
+	b = append(b, m.TokID...)
+	var tb []byte
+	var err error
+	switch hex.EncodeToString(m.TokID) {
+	case TOK_ID_KRB_AP_REQ:
+		tb, err = m.APReq.Marshal()
+		if err != nil {
+			return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err)
+		}
+	case TOK_ID_KRB_AP_REP:
+		return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5")
+	case TOK_ID_KRB_ERROR:
+		return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5")
+	}
+	if err != nil {
+		return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err)
+	}
+	b = append(b, tb...)
+	return asn1tools.AddASNAppTag(b, 0), nil
+}
+
+// Unmarshal a MechToken.
 func (m *MechToken) Unmarshal(b []byte) error {
 	var oid asn1.ObjectIdentifier
 	r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
@@ -72,7 +98,7 @@ func (m *MechToken) Unmarshal(b []byte) error {
 	return nil
 }
 
-// IsAPReq tests if the MechToken contains an AP_REQ
+// IsAPReq tests if the MechToken contains an AP_REQ.
 func (m *MechToken) IsAPReq() bool {
 	if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REQ {
 		return true
@@ -80,7 +106,7 @@ func (m *MechToken) IsAPReq() bool {
 	return false
 }
 
-// IsAPRep tests if the MechToken contains an AP_REP
+// IsAPRep tests if the MechToken contains an AP_REP.
 func (m *MechToken) IsAPRep() bool {
 	if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REP {
 		return true
@@ -88,7 +114,7 @@ func (m *MechToken) IsAPRep() bool {
 	return false
 }
 
-// IsKRBError tests if the MechToken contains an KRB_ERROR
+// IsKRBError tests if the MechToken contains an KRB_ERROR.
 func (m *MechToken) IsKRBError() bool {
 	if hex.EncodeToString(m.TokID) == TOK_ID_KRB_ERROR {
 		return true
@@ -96,16 +122,16 @@ func (m *MechToken) IsKRBError() bool {
 	return false
 }
 
-// NewKRB5APREQMechToken creates new kerberos AP_REQ MechToken
-func NewKRB5APREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) ([]byte, error) {
-	// Create the header
-	b, _ := asn1.Marshal(MechTypeOIDKRB5)
+// NewAPREQMechToken creates new Kerberos AP_REQ MechToken.
+func NewAPREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (MechToken, error) {
+	var m MechToken
+	m.OID = MechTypeOIDKRB5
 	tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
-	b = append(b, tb...)
-	// Add the token
-	auth, err := newAuthenticator(creds, sessionKey.KeyType)
+	m.TokID = tb
+
+	auth, err := NewAuthenticator(creds, sessionKey.KeyType, GSSAPIFlags)
 	if err != nil {
-		return []byte{}, err
+		return m, err
 	}
 	APReq, err := messages.NewAPReq(
 		tkt,
@@ -113,31 +139,31 @@ func NewKRB5APREQMechToken(creds credentials.Credentials, tkt messages.Ticket, s
 		auth,
 	)
 	if err != nil {
-		return []byte{}, fmt.Errorf("could not create new AP_REQ: %v", err)
+		return m, err
 	}
-	tb, err = APReq.Marshal()
-	if err != nil {
-		return []byte{}, fmt.Errorf("could not marshal AP_REQ: %v", err)
+	for _, o := range APOptions {
+		types.SetFlag(&APReq.APOptions, o)
 	}
-	b = append(b, tb...)
-	return asn1tools.AddASNAppTag(b, 0), nil
+	m.APReq = APReq
+	tb, err = APReq.Marshal()
+	return m, nil
 }
 
-// Create new kerberos authenticator for kerberos MechToken
-func newAuthenticator(creds credentials.Credentials, keyType int) (types.Authenticator, error) {
+// NewAuthenticator creates a new kerberos authenticator for kerberos MechToken
+func NewAuthenticator(creds credentials.Credentials, keyType int, flags []int) (types.Authenticator, error) {
 	//RFC 4121 Section 4.1.1
 	auth, err := types.NewAuthenticator(creds.Realm, creds.CName)
 	if err != nil {
-		return auth, krberror.Errorf(err, krberror.KRBMsgError, "Error generating new authenticator")
+		return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
 	}
 	etype, err := crypto.GetEtype(keyType)
 	if err != nil {
-		return auth, krberror.Errorf(err, krberror.KRBMsgError, "Error generating new authenticator")
+		return auth, krberror.Errorf(err, krberror.KRBMsgError, "error getting etype for authenticator")
 	}
 	auth.GenerateSeqNumberAndSubKey(keyType, etype.GetKeyByteSize())
 	auth.Cksum = types.Checksum{
 		CksumType: chksumtype.GSSAPI,
-		Checksum:  newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}),
+		Checksum:  newAuthenticatorChksum(flags),
 	}
 	return auth, nil
 }
@@ -180,3 +206,30 @@ Octet        Name      Description
 28..(n-1)    Deleg   A KRB_CRED message (n = Dlgth + 28) [optional].
 n..last      Exts    Extensions [optional].
 */
+
+// NewKRB5APREQMechToken (DEPRECATED - use NewAPREQMechToken and then call Marshal() on the MechToken instead) creates new kerberos AP_REQ MechToken.
+func NewKRB5APREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) ([]byte, error) {
+	// Create the header
+	b, _ := asn1.Marshal(MechTypeOIDKRB5)
+	tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
+	b = append(b, tb...)
+	// Add the token
+	auth, err := NewAuthenticator(creds, sessionKey.KeyType, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG})
+	if err != nil {
+		return []byte{}, err
+	}
+	APReq, err := messages.NewAPReq(
+		tkt,
+		sessionKey,
+		auth,
+	)
+	if err != nil {
+		return []byte{}, fmt.Errorf("could not create new AP_REQ: %v", err)
+	}
+	tb, err = APReq.Marshal()
+	if err != nil {
+		return []byte{}, fmt.Errorf("could not marshal AP_REQ: %v", err)
+	}
+	b = append(b, tb...)
+	return asn1tools.AddASNAppTag(b, 0), nil
+}


Test patch:
diff --git a/gssapi/krb5Token_test.go b/gssapi/krb5Token_test.go
index 33fecfe..c2ed3c3 100644
--- a/gssapi/krb5Token_test.go
+++ b/gssapi/krb5Token_test.go
@@ -47,7 +47,7 @@ func TestMechToken_newAuthenticator(t *testing.T) {
 	creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM)
 	creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING
 	etypeID := 18
-	a, err := newAuthenticator(creds, etypeID)
+	a, err := NewAuthenticator(creds, etypeID, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG})
 	if err != nil {
 		t.Fatalf("Error creating authenticator: %v", err)
 	}
@@ -66,7 +66,7 @@ func TestMechToken_newAuthenticator(t *testing.T) {
 	}))
 }
 
-func TestNewKRB5APREQMechToken(t *testing.T) {
+func TestNewAPREQMechToken_and_Marshal(t *testing.T) {
 	creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM)
 	creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING
 
@@ -86,8 +86,14 @@ func TestNewKRB5APREQMechToken(t *testing.T) {
 		KeyValue: make([]byte, 32),
 	}
 
-	mb, err := NewKRB5APREQMechToken(creds, tkt, key)
-	var mt MechToken
+	mt, err := NewAPREQMechToken(creds, tkt, key, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}, []int{})
+	if err != nil {
+		t.Fatalf("Error creating MechToken: %v", err)
+	}
+	mb, err := mt.Marshal()
+	if err != nil {
+		t.Fatalf("Error unmarshalling MechToken: %v", err)
+	}
 	err = mt.Unmarshal(mb)
 	if err != nil {
 		t.Fatalf("Error unmarshalling MechToken: %v", err)