瀏覽代碼

renewing service tickets

Jonathan Turner 9 年之前
父節點
當前提交
1bb2c3f32b
共有 6 個文件被更改,包括 127 次插入82 次删除
  1. 27 5
      client/TGSExchange.go
  2. 48 44
      client/cache.go
  3. 17 6
      client/session.go
  4. 11 7
      debug.go
  5. 13 13
      messages/APReq.go
  6. 11 7
      messages/KDCReq.go

+ 27 - 5
client/TGSExchange.go

@@ -6,16 +6,18 @@ import (
 	"github.com/jcmturner/gokrb5/iana/nametype"
 	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/types"
+	"os"
 	"strings"
+	"time"
 )
 
 // 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, renewal bool) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
+func (cl *Client) TGSExchange(spn types.PrincipalName, tkt types.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")
 	}
-	tgsReq, err = messages.NewTGSReq(cl.Credentials.Username, cl.Config, cl.Session.TGT, cl.Session.SessionKey, spn, renewal)
+	tgsReq, err = messages.NewTGSReq(cl.Credentials.Username, cl.Config, tkt, sessionKey, spn, renewal)
 	if err != nil {
 		return tgsReq, tgsRep, fmt.Errorf("Error generating New TGS_REQ: %v", err)
 	}
@@ -31,10 +33,11 @@ func (cl *Client) TGSExchange(spn types.PrincipalName, renewal bool) (tgsReq mes
 	if err != nil {
 		return tgsReq, tgsRep, fmt.Errorf("Error unmarshalling TGS_REP: %v", err)
 	}
-	err = tgsRep.DecryptEncPart(cl.Session.SessionKey)
+	err = tgsRep.DecryptEncPart(sessionKey)
 	if err != nil {
 		return tgsReq, tgsRep, fmt.Errorf("Error decrypting EncPart of TGS_REP: %v", err)
 	}
+	fmt.Fprintf(os.Stderr, "TGSRep: %+v\n", tgsRep)
 	if ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok {
 		return tgsReq, tgsRep, fmt.Errorf("TGS_REP is not valid: %v", err)
 	}
@@ -45,15 +48,34 @@ func (cl *Client) TGSExchange(spn types.PrincipalName, renewal bool) (tgsReq mes
 // 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) error {
+	if _, ok := cl.GetCachedTicket(spn); ok {
+		// Already a valid ticket in the cache
+		return nil
+	}
+	// Ensure TGT still valid
+	if time.Now().After(cl.Session.EndTime) {
+		err := cl.updateTGT()
+		if err != nil {
+			return err
+		}
+	}
 	s := strings.Split(spn, "/")
 	princ := types.PrincipalName{
 		NameType:   nametype.KRB_NT_PRINCIPAL,
 		NameString: s,
 	}
-	_, tgsRep, err := cl.TGSExchange(princ, false)
+	_, tgsRep, err := cl.TGSExchange(princ, cl.Session.TGT, cl.Session.SessionKey, false)
 	if err != nil {
 		return err
 	}
-	cl.Cache.AddEntry(tgsRep.Ticket, tgsRep.DecryptedEncPart.AuthTime, tgsRep.DecryptedEncPart.EndTime, tgsRep.DecryptedEncPart.RenewTill)
+	cl.Cache.AddEntry(
+		tgsRep.Ticket,
+		tgsRep.DecryptedEncPart.AuthTime,
+		tgsRep.DecryptedEncPart.EndTime,
+		tgsRep.DecryptedEncPart.RenewTill,
+		tgsRep.DecryptedEncPart.Key,
+	)
+	e, _ := cl.Cache.GetEntry(spn)
+	fmt.Fprintf(os.Stderr, "ServiceTkt: %+v", e)
 	return nil
 }

+ 48 - 44
client/cache.go

@@ -1,7 +1,9 @@
 package client
 
 import (
+	"fmt"
 	"github.com/jcmturner/gokrb5/types"
+	"os"
 	"strings"
 	"time"
 )
@@ -13,10 +15,11 @@ type Cache struct {
 
 // Ticket cache entry.
 type CacheEntry struct {
-	Ticket    types.Ticket
-	AuthTime  time.Time
-	EndTime   time.Time
-	RenewTill time.Time
+	Ticket     types.Ticket
+	AuthTime   time.Time
+	EndTime    time.Time
+	RenewTill  time.Time
+	SessionKey types.EncryptionKey
 }
 
 // Create a new client ticket cache.
@@ -32,56 +35,57 @@ func (c *Cache) GetEntry(spn string) (CacheEntry, bool) {
 	return e, ok
 }
 
+// Add a ticket to the cache.
+func (c *Cache) AddEntry(tkt types.Ticket, authTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry {
+	spn := strings.Join(tkt.SName.NameString, "/")
+	(*c).Entries[spn] = CacheEntry{
+		Ticket:     tkt,
+		AuthTime:   authTime,
+		EndTime:    endTime,
+		RenewTill:  renewTill,
+		SessionKey: sessionKey,
+	}
+	return c.Entries[spn]
+}
+
+// Remove the cache entry for the defined SPN.
+func (c *Cache) RemoveEntry(spn string) {
+	delete(c.Entries, spn)
+}
+
 // Get a ticket from the cache for the SPN.
 // Only a ticket that is currently valid will be returned.
-func (c *Cache) GetTicket(spn string) (types.Ticket, bool) {
-	if e, ok := c.GetEntry(spn); ok {
+func (cl *Client) GetCachedTicket(spn string) (types.Ticket, bool) {
+	if e, ok := cl.Cache.GetEntry(spn); ok {
 		//If within time window of ticket return it
 		if time.Now().After(e.AuthTime) && time.Now().Before(e.EndTime) {
 			return e.Ticket, true
+		} else if time.Now().Before(e.RenewTill) {
+			e, err := cl.RenewTicket(e)
+			if err != nil {
+				return e.Ticket, false
+			}
+			return e.Ticket, true
 		}
 	}
 	var tkt types.Ticket
 	return tkt, false
 }
 
-// Add a ticket to the cache.
-func (c *Cache) AddEntry(tkt types.Ticket, authTime, endTime, renewTill time.Time) {
-	(*c).Entries[strings.Join(tkt.SName.NameString, "/")] = CacheEntry{
-		Ticket:    tkt,
-		AuthTime:  authTime,
-		EndTime:   endTime,
-		RenewTill: renewTill,
+// Renew a cache entry ticket
+func (cl *Client) RenewTicket(e CacheEntry) (CacheEntry, error) {
+	spn := e.Ticket.SName
+	_, tgsRep, err := cl.TGSExchange(spn, e.Ticket, e.SessionKey, true)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Renew err: %+v\n", err)
+		return e, err
 	}
+	e = cl.Cache.AddEntry(
+		tgsRep.Ticket,
+		tgsRep.DecryptedEncPart.AuthTime,
+		tgsRep.DecryptedEncPart.EndTime,
+		tgsRep.DecryptedEncPart.RenewTill,
+		tgsRep.DecryptedEncPart.Key,
+	)
+	return e, nil
 }
-
-// Remove the cache entry for the defined SPN.
-func (c *Cache) RemoveEntry(spn string) {
-	delete(c.Entries, spn)
-}
-
-// Renew a ticket in the cache for the specified SPN.
-//func (c *Cache) RenewEntry(spn string) error {
-//	if e, ok := c.GetEntry(spn); ok {
-//		return e.Renew()
-//	}
-//	return fmt.Errorf("No entry for this SPN: %s", spn)
-//}
-
-// Enable background auto renew of the ticket for the specified SPN.
-//func (cl *Client) EnableAutoRenew(spn string) {
-//	go func() {
-//		for {
-//
-//		}
-//	}()
-//}
-
-// Renew the cache entry.
-//func (e *CacheEntry) Renew() error {
-//	if time.Now().After(e.RenewTill) {
-//		return errors.New("Past renew till time. Cannot renew.")
-//	}
-//	//TODO put renew action here
-//	return nil
-//}

+ 17 - 6
client/session.go

@@ -21,7 +21,7 @@ func (cl *Client) RenewTGT() error {
 		NameType:   nametype.KRB_NT_SRV_INST,
 		NameString: []string{"krbtgt", cl.Session.TGT.Realm},
 	}
-	_, tgsRep, err := cl.TGSExchange(spn, true)
+	_, tgsRep, err := cl.TGSExchange(spn, cl.Session.TGT, cl.Session.SessionKey, true)
 	if err != nil {
 		return err
 	}
@@ -45,11 +45,22 @@ func (cl *Client) EnableAutoSessionRenewal() {
 				return
 			}
 			time.Sleep(w)
-			if time.Now().Before(cl.Session.RenewTill) {
-				cl.RenewTGT()
-			} else {
-				cl.Login()
-			}
+			cl.updateTGT()
 		}
 	}()
 }
+
+func (cl *Client) updateTGT() error {
+	if time.Now().Before(cl.Session.RenewTill) {
+		err := cl.RenewTGT()
+		if err != nil {
+			return err
+		}
+	} else {
+		err := cl.Login()
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}

+ 11 - 7
debug.go

@@ -22,7 +22,8 @@ const krb5conf = `[libdefaults]
   default_realm = TEST.GOKRB5
   dns_lookup_realm = false
   dns_lookup_kdc = false
-  ticket_lifetime = 24h
+  ticket_lifetime = 3m
+  renew_lifetime = 7m
   forwardable = yes
   default_tkt_enctypes = aes256-cts-hmac-sha1-96
 
@@ -43,7 +44,7 @@ const pa149rep = "6b8202f3308202efa003020105a10302010ba22e302c302aa103020113a223
 func main() {
 
 	TestTGSReq()
-	time.Sleep(time.Duration(3) * time.Hour)
+	//time.Sleep(time.Duration(3) * time.Hour)
 }
 
 func NoPA() {
@@ -136,7 +137,7 @@ func AS() {
 func TestTGSReq() {
 	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
 	kt, _ := keytab.Parse(b)
-	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
+	c, _ := config.NewConfigFromString(krb5conf)
 	cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
 	cl.WithConfig(c)
 
@@ -144,9 +145,12 @@ func TestTGSReq() {
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error on AS_REQ: %v\n", err)
 	}
-	err = cl.GetServiceTicket("HTTP/host.test.gokrb5")
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error on TGS_REQ: %v\n", err)
-	}
 	cl.EnableAutoSessionRenewal()
+	for {
+		err = cl.GetServiceTicket("HTTP/host.test.gokrb5")
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error on TGS_REQ: %v\n", err)
+		}
+		time.Sleep(time.Duration(1) * time.Minute)
+	}
 }

+ 13 - 13
messages/APReq.go

@@ -4,13 +4,13 @@ import (
 	"errors"
 	"fmt"
 	"github.com/jcmturner/asn1"
+	"github.com/jcmturner/gokrb5/asn1tools"
+	"github.com/jcmturner/gokrb5/crypto"
+	"github.com/jcmturner/gokrb5/iana"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
+	"github.com/jcmturner/gokrb5/iana/keyusage"
 	"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 {
@@ -43,17 +43,17 @@ type APReq struct {
 	Authenticator types.EncryptedData `asn1:"explicit,tag:4"`
 }
 
-func NewAPReq(TGT types.Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
+func NewAPReq(tkt 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: types.NewKrbFlags(),
-		Ticket: TGT,
+		PVNO:          iana.PVNO,
+		MsgType:       msgtype.KRB_AP_REQ,
+		APOptions:     types.NewKrbFlags(),
+		Ticket:        tkt,
 		Authenticator: ed,
 	}
 	return a, nil
@@ -90,9 +90,9 @@ func (a *APReq) Unmarshal(b []byte) error {
 
 func (a *APReq) Marshal() ([]byte, error) {
 	m := marshalAPReq{
-		PVNO:    a.PVNO,
-		MsgType: a.MsgType,
-		APOptions: a.APOptions,
+		PVNO:          a.PVNO,
+		MsgType:       a.MsgType,
+		APOptions:     a.APOptions,
 		Authenticator: a.Authenticator,
 	}
 	var b []byte
@@ -112,4 +112,4 @@ func (a *APReq) Marshal() ([]byte, error) {
 	}
 	mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ)
 	return mk, nil
-}
+}

+ 11 - 7
messages/KDCReq.go

@@ -99,7 +99,8 @@ func NewASReq(c *config.Config, username string) ASReq {
 					NameType:   nametype.KRB_NT_SRV_INST,
 					NameString: []string{"krbtgt", c.LibDefaults.Default_realm},
 				},
-				Till:  t.Add(c.LibDefaults.Ticket_lifetime),
+				//Till:  t.Add(c.LibDefaults.Ticket_lifetime),
+				Till:  t.Add(time.Duration(24) * time.Hour),
 				Nonce: nonce,
 				EType: c.LibDefaults.Default_tkt_enctype_ids,
 			},
@@ -117,11 +118,13 @@ func NewASReq(c *config.Config, username string) ASReq {
 	if c.LibDefaults.Renew_lifetime != 0 {
 		types.SetFlag(&a.ReqBody.KDCOptions, types.Renewable)
 		a.ReqBody.RTime = t.Add(c.LibDefaults.Renew_lifetime)
+		a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
+
 	}
 	return a
 }
 
-func NewTGSReq(username string, c *config.Config, TGT types.Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
+func NewTGSReq(username string, c *config.Config, tkt types.Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
 	nonce := int(rand.Int31())
 	t := time.Now()
 	a := TGSReq{
@@ -132,9 +135,10 @@ func NewTGSReq(username string, c *config.Config, TGT types.Ticket, sessionKey t
 				KDCOptions: types.NewKrbFlags(),
 				Realm:      c.ResolveRealm(spn.NameString[len(spn.NameString)-1]),
 				SName:      spn,
-				Till:       t.Add(c.LibDefaults.Ticket_lifetime),
-				Nonce:      nonce,
-				EType:      c.LibDefaults.Default_tgs_enctype_ids,
+				//Till:       t.Add(c.LibDefaults.Ticket_lifetime),
+				Till:  t.Add(time.Duration(2) * time.Minute),
+				Nonce: nonce,
+				EType: c.LibDefaults.Default_tgs_enctype_ids,
 			},
 			Renewal: renewal,
 		},
@@ -148,7 +152,7 @@ func NewTGSReq(username string, c *config.Config, TGT types.Ticket, sessionKey t
 	if c.LibDefaults.Proxiable {
 		types.SetFlag(&a.ReqBody.KDCOptions, types.Proxiable)
 	}
-	if c.LibDefaults.Renew_lifetime != 0 {
+	if c.LibDefaults.Renew_lifetime > time.Duration(0) {
 		types.SetFlag(&a.ReqBody.KDCOptions, types.Renewable)
 		a.ReqBody.RTime = t.Add(c.LibDefaults.Renew_lifetime)
 	}
@@ -172,7 +176,7 @@ func NewTGSReq(username string, c *config.Config, TGT types.Ticket, sessionKey t
 		CksumType: etype.GetHashID(),
 		Checksum:  cb,
 	}
-	apReq, err := NewAPReq(TGT, sessionKey, auth)
+	apReq, err := NewAPReq(tkt, sessionKey, auth)
 	apb, err := apReq.Marshal()
 	if err != nil {
 		return a, fmt.Errorf("Error marshalling AP_REQ for pre-authentication data: %v", err)