Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the necessary functionalities for implementing a GSS-API server #532

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion v8/client/TGSExchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.Encryptio
realm = cl.Credentials.Realm()
}

tgt, skey, err := cl.sessionTGT(realm)
tgt, _, skey, err := cl.sessionTGT(realm)
if err != nil {
return tkt, skey, err
}
Expand Down
49 changes: 44 additions & 5 deletions v8/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (cl *Client) IsConfigured() (bool, error) {
}
// Client needs to have either a password, keytab or a session already (later when loading from CCache)
if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
authTime, _, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
authTime, _, _, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
if err != nil || authTime.IsZero() {
return false, errors.New("client has neither a keytab nor a password set and no session")
}
Expand All @@ -169,7 +169,7 @@ func (cl *Client) Login() error {
return err
}
if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
_, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
_, _, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "no user credentials available and error getting any existing session")
}
Expand All @@ -193,7 +193,7 @@ func (cl *Client) Login() error {

// AffirmLogin will only perform an AS exchange with the KDC if the client does not already have a TGT.
func (cl *Client) AffirmLogin() error {
_, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
_, _, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
if err != nil || time.Now().UTC().After(endTime) {
err := cl.Login()
if err != nil {
Expand All @@ -208,14 +208,14 @@ func (cl *Client) realmLogin(realm string) error {
if realm == cl.Credentials.Domain() {
return cl.Login()
}
_, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
_, _, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
if err != nil || time.Now().UTC().After(endTime) {
err := cl.Login()
if err != nil {
return fmt.Errorf("could not get valid TGT for client's realm: %v", err)
}
}
tgt, skey, err := cl.sessionTGT(cl.Credentials.Domain())
tgt, _, skey, err := cl.sessionTGT(cl.Credentials.Domain())
if err != nil {
return err
}
Expand Down Expand Up @@ -243,6 +243,45 @@ func (cl *Client) Destroy() {
cl.Log("client destroyed")
}

func (cl *Client) GetCCache() (*credentials.CCache, error) {
tgt, flags, skey, err := cl.sessionTGT(cl.Credentials.Realm())
if err != nil {
return nil, err
}
authTime, startTime, endTime, renewTime, _, err := cl.sessionTimes(cl.Credentials.Realm())
if err != nil {
return nil, fmt.Errorf("failed to get session times while getting cache (%w)", err)
}
tgtMar, err := tgt.Marshal()
if err != nil {
return nil, fmt.Errorf("failed to marshal tft while getting cache (%w)", err)
}
tgtCred := credentials.Credential{
Client: credentials.Principal{
Realm: cl.Credentials.Realm(),
PrincipalName: cl.Credentials.CName(),
},
Server: credentials.Principal{
Realm: tgt.Realm,
PrincipalName: tgt.SName,
},
Key: skey,
AuthTime: authTime,
StartTime: startTime,
EndTime: endTime,
RenewTill: renewTime,
TicketFlags: flags,
Ticket: tgtMar,
}
creds := []credentials.Credential{tgtCred}
ccache := credentials.CCacheFromCredentials(creds)
ccache.DefaultPrincipal = credentials.Principal{
Realm: cl.Credentials.Realm(),
PrincipalName: cl.Credentials.CName(),
}
return ccache, nil
}

// Diagnostics runs a set of checks that the client is properly configured and writes details to the io.Writer provided.
func (cl *Client) Diagnostics(w io.Writer) error {
cl.Print(w)
Expand Down
30 changes: 20 additions & 10 deletions v8/client/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"sync"
"time"

"github.com/jcmturner/gofork/encoding/asn1"

"github.com/jcmturner/gokrb5/v8/iana/nametype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/messages"
Expand Down Expand Up @@ -64,9 +66,11 @@ func (s *sessions) get(realm string) (*session, bool) {
type session struct {
realm string
authTime time.Time
startTime time.Time
endTime time.Time
renewTill time.Time
tgt messages.Ticket
tgtFlags asn1.BitString
sessionKey types.EncryptionKey
sessionKeyExpiration time.Time
cancel chan bool
Expand All @@ -77,6 +81,7 @@ type session struct {
type jsonSession struct {
Realm string
AuthTime time.Time
StartTime time.Time
EndTime time.Time
RenewTill time.Time
SessionKeyExpiration time.Time
Expand All @@ -93,9 +98,11 @@ func (cl *Client) addSession(tgt messages.Ticket, dep messages.EncKDCRepPart) {
s := &session{
realm: realm,
authTime: dep.AuthTime,
startTime: dep.StartTime,
endTime: dep.EndTime,
renewTill: dep.RenewTill,
tgt: tgt,
tgtFlags: dep.Flags,
sessionKey: dep.Key,
sessionKeyExpiration: dep.KeyExpiration,
}
Expand All @@ -109,9 +116,11 @@ func (s *session) update(tgt messages.Ticket, dep messages.EncKDCRepPart) {
s.mux.Lock()
defer s.mux.Unlock()
s.authTime = dep.AuthTime
s.startTime = dep.StartTime
s.endTime = dep.EndTime
s.renewTill = dep.RenewTill
s.tgt = tgt
s.tgtFlags = dep.Flags
s.sessionKey = dep.Key
s.sessionKeyExpiration = dep.KeyExpiration
}
Expand Down Expand Up @@ -140,17 +149,17 @@ func (s *session) valid() bool {
}

// tgtDetails is a thread safe way to get the session's realm, TGT and session key values
func (s *session) tgtDetails() (string, messages.Ticket, types.EncryptionKey) {
func (s *session) tgtDetails() (string, messages.Ticket, asn1.BitString, types.EncryptionKey) {
s.mux.RLock()
defer s.mux.RUnlock()
return s.realm, s.tgt, s.sessionKey
return s.realm, s.tgt, s.tgtFlags, s.sessionKey
}

// timeDetails is a thread safe way to get the session's validity time values
func (s *session) timeDetails() (string, time.Time, time.Time, time.Time, time.Time) {
func (s *session) timeDetails() (string, time.Time, time.Time, time.Time, time.Time, time.Time) {
s.mux.RLock()
defer s.mux.RUnlock()
return s.realm, s.authTime, s.endTime, s.renewTill, s.sessionKeyExpiration
return s.realm, s.startTime, s.authTime, s.endTime, s.renewTill, s.sessionKeyExpiration
}

// JSON return information about the held sessions in a JSON format.
Expand All @@ -164,10 +173,11 @@ func (s *sessions) JSON() (string, error) {
}
sort.Strings(keys)
for _, k := range keys {
r, at, et, rt, kt := s.Entries[k].timeDetails()
r, st, at, et, rt, kt := s.Entries[k].timeDetails()
j := jsonSession{
Realm: r,
AuthTime: at,
StartTime: st,
EndTime: et,
RenewTill: rt,
SessionKeyExpiration: kt,
Expand Down Expand Up @@ -217,7 +227,7 @@ func (cl *Client) enableAutoSessionRenewal(s *session) {

// renewTGT renews the client's TGT session.
func (cl *Client) renewTGT(s *session) error {
realm, tgt, skey := s.tgtDetails()
realm, tgt, _, skey := s.tgtDetails()
spn := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", realm},
Expand Down Expand Up @@ -266,7 +276,7 @@ func (cl *Client) ensureValidSession(realm string) error {
}

// sessionTGTDetails is a thread safe way to get the TGT and session key values for a realm
func (cl *Client) sessionTGT(realm string) (tgt messages.Ticket, sessionKey types.EncryptionKey, err error) {
func (cl *Client) sessionTGT(realm string) (tgt messages.Ticket, flags asn1.BitString, sessionKey types.EncryptionKey, err error) {
err = cl.ensureValidSession(realm)
if err != nil {
return
Expand All @@ -276,18 +286,18 @@ func (cl *Client) sessionTGT(realm string) (tgt messages.Ticket, sessionKey type
err = fmt.Errorf("could not find TGT session for %s", realm)
return
}
_, tgt, sessionKey = s.tgtDetails()
_, tgt, flags, sessionKey = s.tgtDetails()
return
}

// sessionTimes provides the timing information with regards to a session for the realm specified.
func (cl *Client) sessionTimes(realm string) (authTime, endTime, renewTime, sessionExp time.Time, err error) {
func (cl *Client) sessionTimes(realm string) (authTime, startTime, endTime, renewTime, sessionExp time.Time, err error) {
s, ok := cl.sessions.get(realm)
if !ok {
err = fmt.Errorf("could not find TGT session for %s", realm)
return
}
_, authTime, endTime, renewTime, sessionExp = s.timeDetails()
_, startTime, authTime, endTime, renewTime, sessionExp = s.timeDetails()
return
}

Expand Down
14 changes: 9 additions & 5 deletions v8/client/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package client
import (
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
"sync"
Expand Down Expand Up @@ -55,12 +55,12 @@ func TestMultiThreadedClientSession(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
tgt, _, err := cl.sessionTGT("TEST.GOKRB5")
tgt, _, _, err := cl.sessionTGT("TEST.GOKRB5")
if err != nil || tgt.Realm != "TEST.GOKRB5" {
t.Logf("error getting session: %v", err)
}
_, _, _, r, _ := cl.sessionTimes("TEST.GOKRB5")
fmt.Fprintf(io.Discard, "%v", r)
_, _, _, _, r, _ := cl.sessionTimes("TEST.GOKRB5")
fmt.Fprintf(ioutil.Discard, "%v", r)
}()
time.Sleep(time.Second)
}
Expand Down Expand Up @@ -90,7 +90,7 @@ func TestClient_AutoRenew_Goroutine(t *testing.T) {
n := runtime.NumGoroutine()
for i := 0; i < 24; i++ {
time.Sleep(time.Second * 5)
_, endTime, _, _, err := cl.sessionTimes("TEST.GOKRB5")
_, _, endTime, _, _, err := cl.sessionTimes("TEST.GOKRB5")
if err != nil {
t.Errorf("could not get client's session: %v", err)
}
Expand Down Expand Up @@ -123,6 +123,7 @@ func TestSessions_JSON(t *testing.T) {
e := &session{
realm: realm,
authTime: time.Unix(int64(0+i), 0).UTC(),
startTime: time.Unix(int64(1+i), 0).UTC(),
endTime: time.Unix(int64(10+i), 0).UTC(),
renewTill: time.Unix(int64(20+i), 0).UTC(),
sessionKeyExpiration: time.Unix(int64(30+i), 0).UTC(),
Expand All @@ -137,20 +138,23 @@ func TestSessions_JSON(t *testing.T) {
{
"Realm": "test0",
"AuthTime": "1970-01-01T00:00:00Z",
"StartTime": "1970-01-01T00:00:01Z",
"EndTime": "1970-01-01T00:00:10Z",
"RenewTill": "1970-01-01T00:00:20Z",
"SessionKeyExpiration": "1970-01-01T00:00:30Z"
},
{
"Realm": "test1",
"AuthTime": "1970-01-01T00:00:01Z",
"StartTime": "1970-01-01T00:00:02Z",
"EndTime": "1970-01-01T00:00:11Z",
"RenewTill": "1970-01-01T00:00:21Z",
"SessionKeyExpiration": "1970-01-01T00:00:31Z"
},
{
"Realm": "test2",
"AuthTime": "1970-01-01T00:00:02Z",
"StartTime": "1970-01-01T00:00:03Z",
"EndTime": "1970-01-01T00:00:12Z",
"RenewTill": "1970-01-01T00:00:22Z",
"SessionKeyExpiration": "1970-01-01T00:00:32Z"
Expand Down
Loading