blob: 07b4a01dc9c10eb7845c7a5c9e186db1932ce7bf [file] [log] [blame]
Scott Bakered4efab2020-01-13 19:12:25 -08001package client
2
3import (
4 "errors"
5 "sync"
6 "time"
7
8 "gopkg.in/jcmturner/gokrb5.v7/messages"
9 "gopkg.in/jcmturner/gokrb5.v7/types"
10)
11
12// Cache for service tickets held by the client.
13type Cache struct {
14 Entries map[string]CacheEntry
15 mux sync.RWMutex
16}
17
18// CacheEntry holds details for a cache entry.
19type CacheEntry struct {
20 Ticket messages.Ticket
21 AuthTime time.Time
22 StartTime time.Time
23 EndTime time.Time
24 RenewTill time.Time
25 SessionKey types.EncryptionKey
26}
27
28// NewCache creates a new client ticket cache instance.
29func NewCache() *Cache {
30 return &Cache{
31 Entries: map[string]CacheEntry{},
32 }
33}
34
35// getEntry returns a cache entry that matches the SPN.
36func (c *Cache) getEntry(spn string) (CacheEntry, bool) {
37 c.mux.RLock()
38 defer c.mux.RUnlock()
39 e, ok := (*c).Entries[spn]
40 return e, ok
41}
42
43// addEntry adds a ticket to the cache.
44func (c *Cache) addEntry(tkt messages.Ticket, authTime, startTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry {
45 spn := tkt.SName.PrincipalNameString()
46 c.mux.Lock()
47 defer c.mux.Unlock()
48 (*c).Entries[spn] = CacheEntry{
49 Ticket: tkt,
50 AuthTime: authTime,
51 StartTime: startTime,
52 EndTime: endTime,
53 RenewTill: renewTill,
54 SessionKey: sessionKey,
55 }
56 return c.Entries[spn]
57}
58
59// clear deletes all the cache entries
60func (c *Cache) clear() {
61 c.mux.Lock()
62 defer c.mux.Unlock()
63 for k := range c.Entries {
64 delete(c.Entries, k)
65 }
66}
67
68// RemoveEntry removes the cache entry for the defined SPN.
69func (c *Cache) RemoveEntry(spn string) {
70 c.mux.Lock()
71 defer c.mux.Unlock()
72 delete(c.Entries, spn)
73}
74
75// GetCachedTicket returns a ticket from the cache for the SPN.
76// Only a ticket that is currently valid will be returned.
77func (cl *Client) GetCachedTicket(spn string) (messages.Ticket, types.EncryptionKey, bool) {
78 if e, ok := cl.cache.getEntry(spn); ok {
79 //If within time window of ticket return it
80 if time.Now().UTC().After(e.StartTime) && time.Now().UTC().Before(e.EndTime) {
81 cl.Log("ticket received from cache for %s", spn)
82 return e.Ticket, e.SessionKey, true
83 } else if time.Now().UTC().Before(e.RenewTill) {
84 e, err := cl.renewTicket(e)
85 if err != nil {
86 return e.Ticket, e.SessionKey, false
87 }
88 return e.Ticket, e.SessionKey, true
89 }
90 }
91 var tkt messages.Ticket
92 var key types.EncryptionKey
93 return tkt, key, false
94}
95
96// renewTicket renews a cache entry ticket.
97// To renew from outside the client package use GetCachedTicket
98func (cl *Client) renewTicket(e CacheEntry) (CacheEntry, error) {
99 spn := e.Ticket.SName
100 _, _, err := cl.TGSREQGenerateAndExchange(spn, e.Ticket.Realm, e.Ticket, e.SessionKey, true)
101 if err != nil {
102 return e, err
103 }
104 e, ok := cl.cache.getEntry(e.Ticket.SName.PrincipalNameString())
105 if !ok {
106 return e, errors.New("ticket was not added to cache")
107 }
108 cl.Log("ticket renewed for %s (EndTime: %v)", spn.PrincipalNameString(), e.EndTime)
109 return e, nil
110}