111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
|
package client
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||
|
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||
|
)
|
||
|
|
||
|
// Cache for service tickets held by the client.
|
||
|
type Cache struct {
|
||
|
Entries map[string]CacheEntry
|
||
|
mux sync.RWMutex
|
||
|
}
|
||
|
|
||
|
// CacheEntry holds details for a cache entry.
|
||
|
type CacheEntry struct {
|
||
|
Ticket messages.Ticket
|
||
|
AuthTime time.Time
|
||
|
StartTime time.Time
|
||
|
EndTime time.Time
|
||
|
RenewTill time.Time
|
||
|
SessionKey types.EncryptionKey
|
||
|
}
|
||
|
|
||
|
// NewCache creates a new client ticket cache instance.
|
||
|
func NewCache() *Cache {
|
||
|
return &Cache{
|
||
|
Entries: map[string]CacheEntry{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// getEntry returns a cache entry that matches the SPN.
|
||
|
func (c *Cache) getEntry(spn string) (CacheEntry, bool) {
|
||
|
c.mux.RLock()
|
||
|
defer c.mux.RUnlock()
|
||
|
e, ok := (*c).Entries[spn]
|
||
|
return e, ok
|
||
|
}
|
||
|
|
||
|
// addEntry adds a ticket to the cache.
|
||
|
func (c *Cache) addEntry(tkt messages.Ticket, authTime, startTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry {
|
||
|
spn := tkt.SName.PrincipalNameString()
|
||
|
c.mux.Lock()
|
||
|
defer c.mux.Unlock()
|
||
|
(*c).Entries[spn] = CacheEntry{
|
||
|
Ticket: tkt,
|
||
|
AuthTime: authTime,
|
||
|
StartTime: startTime,
|
||
|
EndTime: endTime,
|
||
|
RenewTill: renewTill,
|
||
|
SessionKey: sessionKey,
|
||
|
}
|
||
|
return c.Entries[spn]
|
||
|
}
|
||
|
|
||
|
// clear deletes all the cache entries
|
||
|
func (c *Cache) clear() {
|
||
|
c.mux.Lock()
|
||
|
defer c.mux.Unlock()
|
||
|
for k := range c.Entries {
|
||
|
delete(c.Entries, k)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RemoveEntry removes the cache entry for the defined SPN.
|
||
|
func (c *Cache) RemoveEntry(spn string) {
|
||
|
c.mux.Lock()
|
||
|
defer c.mux.Unlock()
|
||
|
delete(c.Entries, spn)
|
||
|
}
|
||
|
|
||
|
// GetCachedTicket returns a ticket from the cache for the SPN.
|
||
|
// Only a ticket that is currently valid will be returned.
|
||
|
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.StartTime) && time.Now().UTC().Before(e.EndTime) {
|
||
|
cl.Log("ticket received from cache for %s", spn)
|
||
|
return e.Ticket, e.SessionKey, true
|
||
|
} else if time.Now().UTC().Before(e.RenewTill) {
|
||
|
e, err := cl.renewTicket(e)
|
||
|
if err != nil {
|
||
|
return e.Ticket, e.SessionKey, false
|
||
|
}
|
||
|
return e.Ticket, e.SessionKey, true
|
||
|
}
|
||
|
}
|
||
|
var tkt messages.Ticket
|
||
|
var key types.EncryptionKey
|
||
|
return tkt, key, false
|
||
|
}
|
||
|
|
||
|
// renewTicket renews a cache entry ticket.
|
||
|
// To renew from outside the client package use GetCachedTicket
|
||
|
func (cl *Client) renewTicket(e CacheEntry) (CacheEntry, error) {
|
||
|
spn := e.Ticket.SName
|
||
|
_, _, err := cl.TGSREQGenerateAndExchange(spn, e.Ticket.Realm, e.Ticket, e.SessionKey, true)
|
||
|
if err != nil {
|
||
|
return e, err
|
||
|
}
|
||
|
e, ok := cl.cache.getEntry(e.Ticket.SName.PrincipalNameString())
|
||
|
if !ok {
|
||
|
return e, errors.New("ticket was not added to cache")
|
||
|
}
|
||
|
cl.Log("ticket renewed for %s (EndTime: %v)", spn.PrincipalNameString(), e.EndTime)
|
||
|
return e, nil
|
||
|
}
|