Przeglądaj źródła

http server spnego auth

Jonathan Turner 9 lat temu
rodzic
commit
05a18c7de2

+ 2 - 1
GSSAPI/NegotiationToken.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/jcmturner/asn1"
 	"github.com/jcmturner/gokrb5/config"
+	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/types"
 )
 
@@ -130,7 +131,7 @@ func (n *NegTokenResp) Marshal() ([]byte, error) {
 }
 
 // Create new Init negotiation token for Kerberos 5
-func NewNegTokenInitKrb5(c config.Config, cname types.PrincipalName, tkt types.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) {
+func NewNegTokenInitKrb5(c config.Config, cname types.PrincipalName, tkt messages.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) {
 	mt, err := NewKRB5APREQMechToken(c, cname, tkt, sessionKey)
 	if err != nil {
 		return NegTokenInit{}, fmt.Errorf("Error getting MechToken; %v", err)

+ 62 - 4
GSSAPI/krb5Token.go

@@ -28,15 +28,73 @@ const (
 )
 
 type MechToken struct {
-	TokID []byte
-	OID asn1.ObjectIdentifier
+	OID      asn1.ObjectIdentifier
+	TokID    []byte
+	APReq    messages.APReq
+	APRep    messages.APRep
+	KRBError messages.KRBError
+}
+
+func (m *MechToken) Unmarshal(b []byte) error {
+	var oid asn1.ObjectIdentifier
+	r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
+	if err != nil {
+		return fmt.Errorf("Error unmarshalling MechToken OID: %v", err)
+	}
+	m.OID = oid
+	m.TokID = r[0:2]
+	switch hex.EncodeToString(m.TokID) {
+	case TOK_ID_KRB_AP_REQ:
+		var a messages.APReq
+		err = a.Unmarshal(r[2:])
+		if err != nil {
+			return fmt.Errorf("Error unmarshalling MechToken AP_REQ: %v", err)
+		}
+		m.APReq = a
+	case TOK_ID_KRB_AP_REP:
+		var a messages.APRep
+		err = a.Unmarshal(r[2:])
+		if err != nil {
+			return fmt.Errorf("Error unmarshalling MechToken AP_REP: %v", err)
+		}
+		m.APRep = a
+	case TOK_ID_KRB_ERROR:
+		var a messages.KRBError
+		err = a.Unmarshal(r[2:])
+		if err != nil {
+			return fmt.Errorf("Error unmarshalling MechToken KRBError: %v", err)
+		}
+		m.KRBError = a
+	}
+	return nil
+}
+
+func (m *MechToken) IsAPReq() bool {
+	if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REQ {
+		return true
+	}
+	return false
+}
+
+func (m *MechToken) IsAPRep() bool {
+	if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REP {
+		return true
+	}
+	return false
+}
+
+func (m *MechToken) IsKRBError() bool {
+	if hex.EncodeToString(m.TokID) == TOK_ID_KRB_ERROR {
+		return true
+	}
+	return false
 }
 
 // Create new kerberos AP_REQ MechToken
-func NewKRB5APREQMechToken(c config.Config, cname types.PrincipalName, tkt types.Ticket, sessionKey types.EncryptionKey) ([]byte, error) {
+func NewKRB5APREQMechToken(c config.Config, cname types.PrincipalName, tkt messages.Ticket, sessionKey types.EncryptionKey) ([]byte, error) {
 	// Create the header
-	tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
 	b, _ := asn1.Marshal(MechTypeOID_Krb5)
+	tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
 	b = append(b, tb...)
 	// Add the token
 	APReq, err := messages.NewAPReq(

+ 17 - 1
GSSAPI/krb5Token_test.go

@@ -2,13 +2,16 @@ package GSSAPI
 
 import (
 	"encoding/hex"
+	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/testdata"
 	"github.com/jcmturner/gokrb5/types"
 	"testing"
 )
 
+const MechToken_Hex = "6082026306092a864886f71201020201006e8202523082024ea003020105a10302010ea20703050000000000a382015d6182015930820155a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382011830820114a003020112a103020103a28201060482010230621d868c97f30bf401e03bbffcd724bd9d067dce2afc31f71a356449b070cdafcc1ff372d0eb1e7a708b50c0152f3996c45b1ea312a803907fb97192d39f20cdcaea29876190f51de6e2b4a4df0460122ed97f363434e1e120b0e76c172b4424a536987152ac0b73013ab88af4b13a3fcdc63f739039dd46d839709cf5b51bb0ce6cb3af05fab3844caac280929955495235e9d0424f8a1fb9b4bd4f6bba971f40b97e9da60b9dabfcf0b1feebfca02c9a19b327a0004aa8e19192726cf347561fa8ac74afad5d6a264e50cf495b93aac86c77b2bc2d184234f6c2767dbea431485a25687b9044a20b601e968efaefffa1fc5283ff32aa6a53cb6c5cdd2eddcb26a481d73081d4a003020112a103020103a281c70481c4a1b29e420324f7edf9efae39df7bcaaf196a3160cf07e72f52a4ef8a965721b2f3343719c50699046e4fcc18ca26c2bfc7e4a9eddfc9d9cfc57ff2f6bdbbd1fc40ac442195bc669b9a0dbba12563b3e4cac9f4022fc01b8aa2d1ab84815bb078399ff7f4d5f9815eef896a0c7e3c049e6fd9932b97096cdb5861425b9d81753d0743212ded1a0fb55a00bf71a46be5ce5e1c8a5cc327b914347d9efcb6cb31ca363b1850d95c7b6c4c3cc6301615ad907318a0c5379d343610fab17eca9c7dc0a5a60658"
+
 func TestKrb5Token_NewAPREQ(t *testing.T) {
-	var tkt types.Ticket
+	var tkt messages.Ticket
 	b, err := hex.DecodeString(testdata.TestVectors["encode_krb5_ticket"])
 	if err != nil {
 		t.Fatalf("Test vector read error of %s: %v\n", "encode_krb5_ticket", err)
@@ -37,3 +40,16 @@ func TestKrb5Token_NewAPREQ(t *testing.T) {
 		t.Fatalf("Unmarshal error of %s: %v\n", "encode_krb5_keyblock", err)
 	}
 }
+
+func TestMechToken_Unmarshal(t *testing.T) {
+	b, err := hex.DecodeString(MechToken_Hex)
+	if err != nil {
+		t.Fatalf("Error decoding MechToken hex: %v", err)
+	}
+	var mt MechToken
+	err = mt.Unmarshal(b)
+	if err != nil {
+		t.Fatalf("Error unmarshalling MechToken: %v", err)
+	}
+	t.Logf("MechToken: %+v", mt)
+}

+ 1 - 1
client/ASExchange.go

@@ -45,7 +45,7 @@ func (cl *Client) ASExchange() error {
 				return fmt.Errorf("Error creating etype: %v", err)
 			}
 			//paEncTS, err := crypto.GetEncryptedData(paTSb, etype, cl.Config.LibDefaults.Default_realm, cl.Credentials.Username, cl.Credentials.Keytab, 1)
-			key, err := cl.Credentials.Keytab.GetEncryptionKey(cl.Credentials.Username, cl.Config.LibDefaults.Default_realm, 1, etype.GetETypeID())
+			key, err := cl.Credentials.Keytab.GetEncryptionKey(cl.Credentials.CName.NameString, cl.Config.LibDefaults.Default_realm, 1, etype.GetETypeID())
 			paEncTS, err := crypto.GetEncryptedData(paTSb, key, 0, 1)
 			if err != nil {
 				return fmt.Errorf("Error encrypting pre-authentication timestamp: %v", err)

+ 3 - 3
client/TGSExchange.go

@@ -12,7 +12,7 @@ import (
 
 // Perform a TGS exchange to retrieve a ticket to the specified SPN.
 // The ticket retrieved is added to the client's cache.
-func (cl *Client) TGSExchange(spn types.PrincipalName, tkt types.Ticket, sessionKey types.EncryptionKey, renewal bool) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
+func (cl *Client) TGSExchange(spn types.PrincipalName, tkt messages.Ticket, sessionKey types.EncryptionKey, renewal bool) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
 	if cl.Session == nil {
 		return tgsReq, tgsRep, errors.New("Error client does not have a session. Client needs to login first")
 	}
@@ -45,8 +45,8 @@ func (cl *Client) TGSExchange(spn types.PrincipalName, tkt types.Ticket, session
 // Make a request to get a service ticket for the SPN specified
 // SPN format: <SERVICE>/<FQDN> Eg. HTTP/www.example.com
 // The ticket will be added to the client's ticket cache
-func (cl *Client) GetServiceTicket(spn string) (types.Ticket, types.EncryptionKey, error) {
-	var tkt types.Ticket
+func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.EncryptionKey, error) {
+	var tkt messages.Ticket
 	var skey types.EncryptionKey
 	if tkt, skey, ok := cl.GetCachedTicket(spn); ok {
 		// Already a valid ticket in the cache

+ 5 - 4
client/cache.go

@@ -1,6 +1,7 @@
 package client
 
 import (
+	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/types"
 	"strings"
 	"time"
@@ -13,7 +14,7 @@ type Cache struct {
 
 // Ticket cache entry.
 type CacheEntry struct {
-	Ticket     types.Ticket
+	Ticket     messages.Ticket
 	AuthTime   time.Time
 	EndTime    time.Time
 	RenewTill  time.Time
@@ -34,7 +35,7 @@ func (c *Cache) GetEntry(spn string) (CacheEntry, bool) {
 }
 
 // Add a ticket to the cache.
-func (c *Cache) AddEntry(tkt types.Ticket, authTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry {
+func (c *Cache) AddEntry(tkt messages.Ticket, authTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry {
 	spn := strings.Join(tkt.SName.NameString, "/")
 	(*c).Entries[spn] = CacheEntry{
 		Ticket:     tkt,
@@ -53,7 +54,7 @@ func (c *Cache) RemoveEntry(spn string) {
 
 // Get a ticket from the cache for the SPN.
 // Only a ticket that is currently valid will be returned.
-func (cl *Client) GetCachedTicket(spn string) (types.Ticket, types.EncryptionKey, bool) {
+func (cl *Client) GetCachedTicket(spn string) (messages.Ticket, types.EncryptionKey, bool) {
 	if e, ok := cl.Cache.GetEntry(spn); ok {
 		//If within time window of ticket return it
 		if time.Now().UTC().After(e.AuthTime) && time.Now().UTC().Before(e.EndTime) {
@@ -66,7 +67,7 @@ func (cl *Client) GetCachedTicket(spn string) (types.Ticket, types.EncryptionKey
 			return e.Ticket, e.SessionKey, true
 		}
 	}
-	var tkt types.Ticket
+	var tkt messages.Ticket
 	var key types.EncryptionKey
 	return tkt, key, false
 }

+ 2 - 1
client/session.go

@@ -2,6 +2,7 @@ package client
 
 import (
 	"github.com/jcmturner/gokrb5/iana/nametype"
+	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/types"
 	"time"
 )
@@ -11,7 +12,7 @@ type Session struct {
 	AuthTime             time.Time
 	EndTime              time.Time
 	RenewTill            time.Time
-	TGT                  types.Ticket
+	TGT                  messages.Ticket
 	SessionKey           types.EncryptionKey
 	SessionKeyExpiration time.Time
 }

+ 9 - 4
keytab/keytab.go

@@ -44,16 +44,21 @@ func NewKeytab() Keytab {
 }
 
 // Get the EncryptionKey from the Keytab for the newest entry with the required kvno, etype and matching principal.
-func (kt *Keytab) GetEncryptionKey(username, realm string, kvno, etype int) (types.EncryptionKey, error) {
+func (kt *Keytab) GetEncryptionKey(nameString []string, realm string, kvno, etype int) (types.EncryptionKey, error) {
 	var key types.EncryptionKey
 	var t time.Time
 	for _, k := range kt.Entries {
 		if k.Principal.Realm == realm && int(k.Key.KeyType) == etype && (int(k.KVNO) == kvno || kvno == 0) && k.Timestamp.After(t) {
-			for _, n := range k.Principal.Components {
-				if n == username {
-					key = k.Key
+			p := true
+			for i, n := range k.Principal.Components {
+				if nameString[i] != n {
+					p = false
+					break
 				}
 			}
+			if p {
+				key = k.Key
+			}
 		}
 	}
 	if len(key.KeyValue) < 1 {

+ 4 - 4
messages/APReq.go

@@ -41,12 +41,12 @@ type APReq struct {
 	PVNO          int                 `asn1:"explicit,tag:0"`
 	MsgType       int                 `asn1:"explicit,tag:1"`
 	APOptions     asn1.BitString      `asn1:"explicit,tag:2"`
-	Ticket        types.Ticket        `asn1:"explicit,tag:3"`
+	Ticket        Ticket              `asn1:"explicit,tag:3"`
 	Authenticator types.EncryptedData `asn1:"explicit,tag:4"`
 }
 
 // Generate a new KRB_AP_REQ struct.
-func NewAPReq(tkt types.Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
+func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
 	var a APReq
 	ed, err := encryptAuthenticator(auth, sessionKey, tkt)
 	if err != nil {
@@ -63,7 +63,7 @@ func NewAPReq(tkt types.Ticket, sessionKey types.EncryptionKey, auth types.Authe
 }
 
 // Encrypt Authenticator
-func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt types.Ticket) (types.EncryptedData, error) {
+func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) {
 	var ed types.EncryptedData
 	m, err := a.Marshal()
 	if err != nil {
@@ -93,7 +93,7 @@ func (a *APReq) Unmarshal(b []byte) error {
 	a.MsgType = m.MsgType
 	a.APOptions = m.APOptions
 	a.Authenticator = m.Authenticator
-	a.Ticket, err = types.UnmarshalTicket(m.Ticket.Bytes)
+	a.Ticket, err = UnmarshalTicket(m.Ticket.Bytes)
 	if err != nil {
 		return fmt.Errorf("Error unmarshalling ticket in AP_REQ; %v", err)
 	}

+ 4 - 7
messages/KDCRep.go

@@ -36,7 +36,7 @@ type KDCRepFields struct {
 	PAData           []types.PAData
 	CRealm           string
 	CName            types.PrincipalName
-	Ticket           types.Ticket
+	Ticket           Ticket
 	EncPart          types.EncryptedData
 	DecryptedEncPart EncKDCRepPart
 }
@@ -85,7 +85,7 @@ func (k *ASRep) Unmarshal(b []byte) error {
 		return errors.New("Message ID does not indicate a KRB_AS_REP")
 	}
 	//Process the raw ticket within
-	tkt, err := types.UnmarshalTicket(m.Ticket.Bytes)
+	tkt, err := UnmarshalTicket(m.Ticket.Bytes)
 	if err != nil {
 		return err
 	}
@@ -112,7 +112,7 @@ func (k *TGSRep) Unmarshal(b []byte) error {
 		return errors.New("Message ID does not indicate a KRB_TGS_REP")
 	}
 	//Process the raw ticket within
-	tkt, err := types.UnmarshalTicket(m.Ticket.Bytes)
+	tkt, err := UnmarshalTicket(m.Ticket.Bytes)
 	if err != nil {
 		return err
 	}
@@ -150,10 +150,7 @@ func (k *ASRep) DecryptEncPart(c *credentials.Credentials) error {
 	var key types.EncryptionKey
 	var err error
 	if c.HasKeytab() {
-		if err != nil {
-			return fmt.Errorf("Error getting encryption type: %v", err)
-		}
-		key, err = c.Keytab.GetEncryptionKey(k.CName.NameString[0], k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
+		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)
 		}

+ 4 - 4
messages/KDCReq.go

@@ -76,7 +76,7 @@ type KDCReqBody struct {
 	EType             []int               `asn1:"explicit,tag:8"`
 	Addresses         []types.HostAddress `asn1:"explicit,optional,tag:9"`
 	EncAuthData       types.EncryptedData `asn1:"explicit,optional,tag:10"`
-	AdditionalTickets []types.Ticket      `asn1:"explicit,optional,tag:11"`
+	AdditionalTickets []Ticket            `asn1:"explicit,optional,tag:11"`
 }
 
 // Generate a new KRB_AS_REQ struct.
@@ -128,7 +128,7 @@ func NewASReq(c *config.Config, cname types.PrincipalName) ASReq {
 }
 
 // Generate a new KRB_TGS_REQ struct.
-func NewTGSReq(cname types.PrincipalName, c *config.Config, tkt types.Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
+func NewTGSReq(cname types.PrincipalName, c *config.Config, tkt Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
 	nonce := int(rand.Int31())
 	t := time.Now().UTC()
 	a := TGSReq{
@@ -264,7 +264,7 @@ func (k *KDCReqBody) Unmarshal(b []byte) error {
 	k.Addresses = m.Addresses
 	k.EncAuthData = m.EncAuthData
 	if len(m.AdditionalTickets.Bytes) > 0 {
-		k.AdditionalTickets, err = types.UnmarshalTicketsSequence(m.AdditionalTickets)
+		k.AdditionalTickets, err = UnmarshalTicketsSequence(m.AdditionalTickets)
 		if err != nil {
 			return fmt.Errorf("Error unmarshalling additional tickets: %v", err)
 		}
@@ -340,7 +340,7 @@ func (k *KDCReqBody) Marshal() ([]byte, error) {
 		Addresses:   k.Addresses,
 		EncAuthData: k.EncAuthData,
 	}
-	rawtkts, err := types.MarshalTicketSequence(k.AdditionalTickets)
+	rawtkts, err := MarshalTicketSequence(k.AdditionalTickets)
 	//The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody
 	rawtkts.Tag = 11
 	if err != nil {

+ 2 - 2
messages/KRBCred.go

@@ -22,7 +22,7 @@ type marshalKRBCred struct {
 type KRBCred struct {
 	PVNO             int
 	MsgType          int
-	Tickets          []types.Ticket
+	Tickets          []Ticket
 	EncPart          types.EncryptedData
 	DecryptedEncPart EncKrbCredPart
 }
@@ -67,7 +67,7 @@ func (k *KRBCred) Unmarshal(b []byte) error {
 	k.MsgType = m.MsgType
 	k.EncPart = m.EncPart
 	if len(m.Tickets.Bytes) > 0 {
-		k.Tickets, err = types.UnmarshalTicketsSequence(m.Tickets)
+		k.Tickets, err = UnmarshalTicketsSequence(m.Tickets)
 		if err != nil {
 			return fmt.Errorf("Error unmarshalling tickets within KRB_CRED: %v", err)
 		}

+ 39 - 17
types/Ticket.go → messages/Ticket.go

@@ -1,11 +1,14 @@
-// Kerberos 5 data types.
-package types
+package messages
 
 import (
 	"fmt"
 	"github.com/jcmturner/asn1"
 	"github.com/jcmturner/gokrb5/asn1tools"
+	"github.com/jcmturner/gokrb5/crypto"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
+	"github.com/jcmturner/gokrb5/iana/keyusage"
+	"github.com/jcmturner/gokrb5/keytab"
+	"github.com/jcmturner/gokrb5/types"
 	"time"
 )
 
@@ -13,24 +16,25 @@ import (
 // Section: 5.3
 
 type Ticket struct {
-	TktVNO  int           `asn1:"explicit,tag:0"`
-	Realm   string        `asn1:"generalstring,explicit,tag:1"`
-	SName   PrincipalName `asn1:"explicit,tag:2"`
-	EncPart EncryptedData `asn1:"explicit,tag:3"`
+	TktVNO           int                 `asn1:"explicit,tag:0"`
+	Realm            string              `asn1:"generalstring,explicit,tag:1"`
+	SName            types.PrincipalName `asn1:"explicit,tag:2"`
+	EncPart          types.EncryptedData `asn1:"explicit,tag:3"`
+	DecryptedEncPart EncTicketPart       `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
 }
 
 type EncTicketPart struct {
-	Flags             asn1.BitString    `asn1:"explicit,tag:0"`
-	Key               EncryptionKey     `asn1:"explicit,tag:1"`
-	CRealm            string            `asn1:"generalstring,explicit,tag:2"`
-	CName             PrincipalName     `asn1:"explicit,tag:3"`
-	Transited         TransitedEncoding `asn1:"explicit,tag:4"`
-	AuthTime          time.Time         `asn1:"generalized,explicit,tag:5"`
-	StartTime         time.Time         `asn1:"generalized,explicit,optional,tag:6"`
-	EndTime           time.Time         `asn1:"generalized,explicit,tag:7"`
-	RenewTill         time.Time         `asn1:"generalized,explicit,optional,tag:8"`
-	CAddr             HostAddresses     `asn1:"explicit,optional,tag:9"`
-	AuthorizationData AuthorizationData `asn1:"explicit,optional,tag:10"`
+	Flags             asn1.BitString          `asn1:"explicit,tag:0"`
+	Key               types.EncryptionKey     `asn1:"explicit,tag:1"`
+	CRealm            string                  `asn1:"generalstring,explicit,tag:2"`
+	CName             types.PrincipalName     `asn1:"explicit,tag:3"`
+	Transited         TransitedEncoding       `asn1:"explicit,tag:4"`
+	AuthTime          time.Time               `asn1:"generalized,explicit,tag:5"`
+	StartTime         time.Time               `asn1:"generalized,explicit,optional,tag:6"`
+	EndTime           time.Time               `asn1:"generalized,explicit,tag:7"`
+	RenewTill         time.Time               `asn1:"generalized,explicit,optional,tag:8"`
+	CAddr             types.HostAddresses     `asn1:"explicit,optional,tag:9"`
+	AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"`
 }
 
 type TransitedEncoding struct {
@@ -117,3 +121,21 @@ func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) {
 	//fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes)
 	return raw, nil
 }
+
+func (t *Ticket) DecryptEncPart(keytab keytab.Keytab) error {
+	key, err := keytab.GetEncryptionKey(t.SName.NameString, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
+	if err != nil {
+		return fmt.Errorf("Could not get key from keytab: %v", err)
+	}
+	b, err := crypto.DecryptEncPart(t.EncPart, key, keyusage.KDC_REP_TICKET)
+	if err != nil {
+		return fmt.Errorf("Error decrypting Ticket EncPart: %v", err)
+	}
+	var denc EncTicketPart
+	err = denc.Unmarshal(b)
+	if err != nil {
+		return fmt.Errorf("Error unmarshalling encrypted part: %v", err)
+	}
+	t.DecryptedEncPart = denc
+	return nil
+}

+ 4 - 4
types/Ticket_test.go → messages/Ticket_test.go

@@ -1,12 +1,12 @@
-package types
+package messages
 
 import (
 	"encoding/hex"
+	"fmt"
 	"github.com/jcmturner/gokrb5/testdata"
 	"github.com/stretchr/testify/assert"
 	"testing"
 	"time"
-	"fmt"
 )
 
 func TestUnmarshalTicket(t *testing.T) {
@@ -62,7 +62,7 @@ func TestUnmarshalEncTicketPart(t *testing.T) {
 		assert.Equal(t, 2, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1))
 		assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1))
 	}
-	for i, ele := range a.AuthorizationData{
+	for i, ele := range a.AuthorizationData {
 		assert.Equal(t, 1, ele.ADType, fmt.Sprintf("Authorization data type of element %d not as expected", i+1))
 		assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), ele.ADData, fmt.Sprintf("Authorization data of element %d not as expected", i+1))
 	}
@@ -110,4 +110,4 @@ func TestMarshalTicket(t *testing.T) {
 		t.Fatalf("Marshal of ticket errored: %v", err)
 	}
 	assert.Equal(t, b, mb, "Marshalled bytes not as expected")
-}
+}

+ 32 - 5
service/http.go

@@ -1,19 +1,23 @@
 package service
 
 import (
-	"net/http"
-	"strings"
 	"encoding/base64"
-	"github.com/jcmturner/gokrb5/GSSAPI"
 	"errors"
 	"fmt"
+	"github.com/jcmturner/gokrb5/GSSAPI"
+	"github.com/jcmturner/gokrb5/crypto"
+	"github.com/jcmturner/gokrb5/iana/keyusage"
+	"github.com/jcmturner/gokrb5/keytab"
+	"github.com/jcmturner/gokrb5/types"
+	"net/http"
+	"strings"
 )
 
-func SPNEGOHandler(w http.ResponseWriter, r *http.Request, ) {
+func SPNEGOHandler(w http.ResponseWriter, r *http.Request) {
 
 }
 
-func SPNEGOKRB5Authenticate(w http.ResponseWriter, r *http.Request) (bool, error) {
+func SPNEGOKRB5Authenticate(w http.ResponseWriter, r *http.Request, ktab keytab.Keytab) (bool, error) {
 	s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
 	if len(s) != 2 || s[0] != "Negotiate" {
 		// TODO set the NegTokenResp Negotiate header here on the w
@@ -31,6 +35,29 @@ func SPNEGOKRB5Authenticate(w http.ResponseWriter, r *http.Request) (bool, error
 	if nInit.MechTypes != GSSAPI.MechTypeOID_Krb5 {
 		return false, errors.New("OID of MechToken is not of type KRB5")
 	}
+	var mt GSSAPI.MechToken
+	err = mt.Unmarshal(nInit.MechToken)
+	if err != nil {
+		return false, fmt.Errorf("Error unmarshalling MechToken: %v", err)
+	}
+	if !mt.IsAPReq() {
+		return false, errors.New("MechToken does not contain an AP_REQ")
+	}
+	err = mt.APReq.Ticket.DecryptEncPart(ktab)
+	if err != nil {
+		return false, fmt.Errorf("Error decrypting the service ticket provided: %v", err)
+	}
+	sessionKey := mt.APReq.Ticket.DecryptedEncPart.Key
+	ab, err := crypto.DecryptEncPart(mt.APReq.Authenticator, sessionKey, keyusage.AP_REQ_AUTHENTICATOR)
+	if err != nil {
+		return false, fmt.Errorf("Error decrypting the authenticator provided: %v", err)
+	}
+	var a types.Authenticator
+	err = a.Unmarshal(ab)
+	if err != nil {
+		return false, fmt.Errorf("Error unmarshalling the authenticator: %v", err)
+	}
+	// TODO check timestamp within skew etc...
 
 	return true, nil
 }

+ 1 - 0
types/Authenticator.go

@@ -1,3 +1,4 @@
+// Kerberos 5 data types.
 package types
 
 import (