1package client
2
3import (
4	"encoding/hex"
5	"fmt"
6	"io/ioutil"
7	"os"
8	"runtime"
9	"sync"
10	"testing"
11	"time"
12
13	"github.com/jcmturner/gokrb5/v8/config"
14	"github.com/jcmturner/gokrb5/v8/iana/etypeID"
15	"github.com/jcmturner/gokrb5/v8/keytab"
16	"github.com/jcmturner/gokrb5/v8/test"
17	"github.com/jcmturner/gokrb5/v8/test/testdata"
18	"github.com/stretchr/testify/assert"
19)
20
21func TestMultiThreadedClientSession(t *testing.T) {
22	test.Integration(t)
23
24	b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
25	kt := keytab.New()
26	kt.Unmarshal(b)
27	c, _ := config.NewFromString(testdata.TEST_KRB5CONF)
28	addr := os.Getenv("TEST_KDC_ADDR")
29	if addr == "" {
30		addr = testdata.TEST_KDC_ADDR
31	}
32	c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
33	cl := NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
34	err := cl.Login()
35	if err != nil {
36		t.Fatalf("failed to log in: %v", err)
37	}
38
39	s, ok := cl.sessions.get("TEST.GOKRB5")
40	if !ok {
41		t.Fatal("error initially getting session")
42	}
43	go func() {
44		for {
45			err := cl.renewTGT(s)
46			if err != nil {
47				t.Logf("error renewing TGT: %v", err)
48			}
49			time.Sleep(time.Millisecond * 100)
50		}
51	}()
52
53	var wg sync.WaitGroup
54	wg.Add(10)
55	for i := 0; i < 10; i++ {
56		go func() {
57			defer wg.Done()
58			tgt, _, err := cl.sessionTGT("TEST.GOKRB5")
59			if err != nil || tgt.Realm != "TEST.GOKRB5" {
60				t.Logf("error getting session: %v", err)
61			}
62			_, _, _, r, _ := cl.sessionTimes("TEST.GOKRB5")
63			fmt.Fprintf(ioutil.Discard, "%v", r)
64		}()
65		time.Sleep(time.Second)
66	}
67	wg.Wait()
68}
69
70func TestClient_AutoRenew_Goroutine(t *testing.T) {
71	test.Integration(t)
72
73	// Tests that the auto renew of client credentials is not spawning goroutines out of control.
74	addr := os.Getenv("TEST_KDC_ADDR")
75	if addr == "" {
76		addr = testdata.TEST_KDC_ADDR
77	}
78	b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
79	kt := keytab.New()
80	kt.Unmarshal(b)
81	c, _ := config.NewFromString(testdata.TEST_KRB5CONF)
82	c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
83	c.LibDefaults.PreferredPreauthTypes = []int{int(etypeID.DES3_CBC_SHA1_KD)} // a preauth etype the KDC does not support. Test this does not cause renewal to fail.
84	cl := NewWithKeytab("testuser2", "TEST.GOKRB5", kt, c)
85
86	err := cl.Login()
87	if err != nil {
88		t.Errorf("error on logging in: %v\n", err)
89	}
90	n := runtime.NumGoroutine()
91	for i := 0; i < 24; i++ {
92		time.Sleep(time.Second * 5)
93		_, endTime, _, _, err := cl.sessionTimes("TEST.GOKRB5")
94		if err != nil {
95			t.Errorf("could not get client's session: %v", err)
96		}
97		if time.Now().UTC().After(endTime) {
98			t.Fatalf("session auto update failed")
99		}
100		spn := "HTTP/host.test.gokrb5"
101		tkt, key, err := cl.GetServiceTicket(spn)
102		if err != nil {
103			t.Fatalf("error getting service ticket: %v\n", err)
104		}
105		b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
106		skt := keytab.New()
107		skt.Unmarshal(b)
108		tkt.DecryptEncPart(skt, nil)
109		assert.Equal(t, spn, tkt.SName.PrincipalNameString())
110		assert.Equal(t, int32(18), key.KeyType)
111		if runtime.NumGoroutine() > n {
112			t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine())
113		}
114	}
115}
116