-
Notifications
You must be signed in to change notification settings - Fork 249
/
session.go
140 lines (126 loc) · 3.9 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package client
import (
"fmt"
"sync"
"time"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// Sessions keyed on the realm name
type sessions struct {
Entries map[string]*session
mux sync.RWMutex
}
// Client session struct.
type session struct {
Realm string
AuthTime time.Time
EndTime time.Time
RenewTill time.Time
TGT messages.Ticket
SessionKey types.EncryptionKey
SessionKeyExpiration time.Time
}
// AddSession adds a session for a realm with a TGT to the client's session cache.
// A goroutine is started to automatically renew the TGT before expiry.
func (cl *Client) AddSession(tkt messages.Ticket, dep messages.EncKDCRepPart) {
cl.sessions.mux.Lock()
defer cl.sessions.mux.Unlock()
s := &session{
Realm: tkt.SName.NameString[1],
AuthTime: dep.AuthTime,
EndTime: dep.EndTime,
RenewTill: dep.RenewTill,
TGT: tkt,
SessionKey: dep.Key,
SessionKeyExpiration: dep.KeyExpiration,
}
cl.sessions.Entries[tkt.SName.NameString[1]] = s
cl.enableAutoSessionRenewal(s)
}
// EnableAutoSessionRenewal turns on the automatic renewal for the client's TGT session.
func (cl *Client) enableAutoSessionRenewal(s *session) {
// TODO look into using a context here
go func(s *session) {
for {
//Wait until one minute before endtime
w := (s.EndTime.Sub(time.Now().UTC()) * 5) / 6
if w < 0 {
return
}
time.Sleep(w)
cl.updateSession(s)
}
}(s)
}
// RenewTGT renews the client's TGT session.
func (cl *Client) renewTGT(s *session) error {
spn := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", s.Realm},
}
_, tgsRep, err := cl.TGSExchange(spn, s.TGT.Realm, s.TGT, s.SessionKey, true, 0)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "Error renewing TGT")
}
s.AuthTime = tgsRep.DecryptedEncPart.AuthTime
s.AuthTime = tgsRep.DecryptedEncPart.AuthTime
s.EndTime = tgsRep.DecryptedEncPart.EndTime
s.RenewTill = tgsRep.DecryptedEncPart.RenewTill
s.TGT = tgsRep.Ticket
s.SessionKey = tgsRep.DecryptedEncPart.Key
s.SessionKeyExpiration = tgsRep.DecryptedEncPart.KeyExpiration
return nil
}
func (cl *Client) updateSession(s *session) error {
if time.Now().UTC().Before(s.RenewTill) {
err := cl.renewTGT(s)
if err != nil {
return err
}
} else {
err := cl.Login()
if err != nil {
return err
}
}
return nil
}
func (cl *Client) getSessionFromRemoteRealm(realm string) (*session, error) {
cl.sessions.mux.RLock()
sess, ok := cl.sessions.Entries[cl.Credentials.Realm]
cl.sessions.mux.RUnlock()
if !ok {
return nil, fmt.Errorf("client does not have a session for realm %s, login first", cl.Credentials.Realm)
}
spn := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", realm},
}
_, tgsRep, err := cl.TGSExchange(spn, cl.Credentials.Realm, sess.TGT, sess.SessionKey, false, 0)
if err != nil {
return nil, err
}
cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
cl.sessions.mux.RLock()
defer cl.sessions.mux.RUnlock()
return cl.sessions.Entries[realm], nil
}
// GetSessionFromRealm returns the session for the realm provided.
func (cl *Client) GetSessionFromRealm(realm string) (*session, error) {
cl.sessions.mux.RLock()
sess, ok := cl.sessions.Entries[realm]
cl.sessions.mux.RUnlock()
if !ok {
// Try to request TGT from trusted remote Realm
return cl.getSessionFromRemoteRealm(realm)
}
return sess, nil
}
// GetSessionFromPrincipalName returns the session for the realm of the principal provided.
func (cl *Client) GetSessionFromPrincipalName(spn types.PrincipalName) (*session, error) {
realm := cl.Config.ResolveRealm(spn.NameString[len(spn.NameString)-1])
return cl.GetSessionFromRealm(realm)
}