123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- package client
- import (
- "encoding/json"
- "errors"
- "sort"
- "sync"
- "time"
- "github.com/jcmturner/gokrb5/v8/messages"
- "github.com/jcmturner/gokrb5/v8/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 {
- SPN string
- Ticket messages.Ticket `json:"-"`
- AuthTime time.Time
- StartTime time.Time
- EndTime time.Time
- RenewTill time.Time
- SessionKey types.EncryptionKey `json:"-"`
- }
- // 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
- }
- // JSON returns information about the cached service tickets in a JSON format.
- func (c *Cache) JSON() (string, error) {
- c.mux.RLock()
- defer c.mux.RUnlock()
- var es []CacheEntry
- keys := make([]string, 0, len(c.Entries))
- for k := range c.Entries {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- es = append(es, c.Entries[k])
- }
- b, err := json.MarshalIndent(&es, "", " ")
- if err != nil {
- return "", err
- }
- return string(b), nil
- }
- // 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{
- SPN: spn,
- 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
- }
|