github.com/jcmturner/gokrb5/v8@v8.4.4/client/client_integration_test.go (about)

     1  package client_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"errors"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"os/exec"
    11  	"os/user"
    12  	"runtime"
    13  	"testing"
    14  	"time"
    15  
    16  	"fmt"
    17  	"github.com/jcmturner/gokrb5/v8/client"
    18  	"github.com/jcmturner/gokrb5/v8/config"
    19  	"github.com/jcmturner/gokrb5/v8/credentials"
    20  	"github.com/jcmturner/gokrb5/v8/iana/etypeID"
    21  	"github.com/jcmturner/gokrb5/v8/keytab"
    22  	"github.com/jcmturner/gokrb5/v8/spnego"
    23  	"github.com/jcmturner/gokrb5/v8/test"
    24  	"github.com/jcmturner/gokrb5/v8/test/testdata"
    25  	"github.com/stretchr/testify/assert"
    26  	"strings"
    27  	"sync"
    28  )
    29  
    30  func TestClient_SuccessfulLogin_Keytab(t *testing.T) {
    31  	test.Integration(t)
    32  
    33  	addr := os.Getenv("TEST_KDC_ADDR")
    34  	if addr == "" {
    35  		addr = testdata.KDC_IP_TEST_GOKRB5
    36  	}
    37  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
    38  	kt := keytab.New()
    39  	kt.Unmarshal(b)
    40  	c, _ := config.NewFromString(testdata.KRB5_CONF)
    41  	var tests = []string{
    42  		testdata.KDC_PORT_TEST_GOKRB5,
    43  		testdata.KDC_PORT_TEST_GOKRB5_OLD,
    44  		testdata.KDC_PORT_TEST_GOKRB5_LASTEST,
    45  	}
    46  	for _, tst := range tests {
    47  		c.Realms[0].KDC = []string{addr + ":" + tst}
    48  		cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
    49  
    50  		err := cl.Login()
    51  		if err != nil {
    52  			t.Errorf("error on logging in with KDC %s: %v\n", tst, err)
    53  		}
    54  	}
    55  }
    56  
    57  func TestClient_SuccessfulLogin_Password(t *testing.T) {
    58  	test.Integration(t)
    59  
    60  	addr := os.Getenv("TEST_KDC_ADDR")
    61  	if addr == "" {
    62  		addr = testdata.KDC_IP_TEST_GOKRB5
    63  	}
    64  	c, _ := config.NewFromString(testdata.KRB5_CONF)
    65  	var tests = []string{
    66  		testdata.KDC_PORT_TEST_GOKRB5,
    67  		testdata.KDC_PORT_TEST_GOKRB5_OLD,
    68  		testdata.KDC_PORT_TEST_GOKRB5_LASTEST,
    69  	}
    70  	for _, tst := range tests {
    71  		c.Realms[0].KDC = []string{addr + ":" + tst}
    72  		cl := client.NewWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue", c)
    73  
    74  		err := cl.Login()
    75  		if err != nil {
    76  			t.Errorf("error on logging in with KDC %s: %v\n", tst, err)
    77  		}
    78  	}
    79  }
    80  
    81  func TestClient_SuccessfulLogin_TCPOnly(t *testing.T) {
    82  	test.Integration(t)
    83  
    84  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
    85  	kt := keytab.New()
    86  	kt.Unmarshal(b)
    87  	c, _ := config.NewFromString(testdata.KRB5_CONF)
    88  	addr := os.Getenv("TEST_KDC_ADDR")
    89  	if addr == "" {
    90  		addr = testdata.KDC_IP_TEST_GOKRB5
    91  	}
    92  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
    93  	c.LibDefaults.UDPPreferenceLimit = 1
    94  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
    95  
    96  	err := cl.Login()
    97  	if err != nil {
    98  		t.Fatalf("error on login: %v\n", err)
    99  	}
   100  }
   101  
   102  func TestClient_ASExchange_TGSExchange_EncTypes_Keytab(t *testing.T) {
   103  	test.Integration(t)
   104  
   105  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   106  	kt := keytab.New()
   107  	kt.Unmarshal(b)
   108  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   109  	addr := os.Getenv("TEST_KDC_ADDR")
   110  	if addr == "" {
   111  		addr = testdata.KDC_IP_TEST_GOKRB5
   112  	}
   113  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5_LASTEST}
   114  	var tests = []string{
   115  		"des3-cbc-sha1-kd",
   116  		"aes128-cts-hmac-sha1-96",
   117  		"aes256-cts-hmac-sha1-96",
   118  		"aes128-cts-hmac-sha256-128",
   119  		"aes256-cts-hmac-sha384-192",
   120  		"rc4-hmac",
   121  	}
   122  	for _, tst := range tests {
   123  		c.LibDefaults.DefaultTktEnctypes = []string{tst}
   124  		c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[tst]}
   125  		c.LibDefaults.DefaultTGSEnctypes = []string{tst}
   126  		c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[tst]}
   127  		cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   128  
   129  		err := cl.Login()
   130  		if err != nil {
   131  			t.Errorf("error on login using enctype %s: %v\n", tst, err)
   132  		}
   133  		tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
   134  		if err != nil {
   135  			t.Errorf("error in TGS exchange using enctype %s: %v", tst, err)
   136  		}
   137  		assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", tst)
   138  		assert.Equal(t, etypeID.ETypesByName[tst], key.KeyType, "Key is not for enctype %s", tst)
   139  	}
   140  }
   141  
   142  func TestClient_ASExchange_TGSExchange_EncTypes_Password(t *testing.T) {
   143  	test.Integration(t)
   144  
   145  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   146  	addr := os.Getenv("TEST_KDC_ADDR")
   147  	if addr == "" {
   148  		addr = testdata.KDC_IP_TEST_GOKRB5
   149  	}
   150  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5_LASTEST}
   151  	var tests = []string{
   152  		"des3-cbc-sha1-kd",
   153  		"aes128-cts-hmac-sha1-96",
   154  		"aes256-cts-hmac-sha1-96",
   155  		"aes128-cts-hmac-sha256-128",
   156  		"aes256-cts-hmac-sha384-192",
   157  		"rc4-hmac",
   158  	}
   159  	for _, tst := range tests {
   160  		c.LibDefaults.DefaultTktEnctypes = []string{tst}
   161  		c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[tst]}
   162  		c.LibDefaults.DefaultTGSEnctypes = []string{tst}
   163  		c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[tst]}
   164  		cl := client.NewWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue", c)
   165  
   166  		err := cl.Login()
   167  		if err != nil {
   168  			t.Errorf("error on login using enctype %s: %v\n", tst, err)
   169  		}
   170  		tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
   171  		if err != nil {
   172  			t.Errorf("error in TGS exchange using enctype %s: %v", tst, err)
   173  		}
   174  		assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", tst)
   175  		assert.Equal(t, etypeID.ETypesByName[tst], key.KeyType, "Key is not for enctype %s", tst)
   176  	}
   177  }
   178  
   179  func TestClient_FailedLogin(t *testing.T) {
   180  	test.Integration(t)
   181  
   182  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5_WRONGPASSWD)
   183  	kt := keytab.New()
   184  	kt.Unmarshal(b)
   185  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   186  	addr := os.Getenv("TEST_KDC_ADDR")
   187  	if addr == "" {
   188  		addr = testdata.KDC_IP_TEST_GOKRB5
   189  	}
   190  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   191  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   192  
   193  	err := cl.Login()
   194  	if err == nil {
   195  		t.Fatal("login with incorrect password did not error")
   196  	}
   197  }
   198  
   199  func TestClient_SuccessfulLogin_UserRequiringPreAuth(t *testing.T) {
   200  	test.Integration(t)
   201  
   202  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER2_TEST_GOKRB5)
   203  	kt := keytab.New()
   204  	kt.Unmarshal(b)
   205  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   206  	addr := os.Getenv("TEST_KDC_ADDR")
   207  	if addr == "" {
   208  		addr = testdata.KDC_IP_TEST_GOKRB5
   209  	}
   210  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   211  	cl := client.NewWithKeytab("testuser2", "TEST.GOKRB5", kt, c)
   212  
   213  	err := cl.Login()
   214  	if err != nil {
   215  		t.Fatalf("error on login: %v\n", err)
   216  	}
   217  }
   218  
   219  func TestClient_SuccessfulLogin_UserRequiringPreAuth_TCPOnly(t *testing.T) {
   220  	test.Integration(t)
   221  
   222  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER2_TEST_GOKRB5)
   223  	kt := keytab.New()
   224  	kt.Unmarshal(b)
   225  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   226  	addr := os.Getenv("TEST_KDC_ADDR")
   227  	if addr == "" {
   228  		addr = testdata.KDC_IP_TEST_GOKRB5
   229  	}
   230  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   231  	c.LibDefaults.UDPPreferenceLimit = 1
   232  	cl := client.NewWithKeytab("testuser2", "TEST.GOKRB5", kt, c)
   233  
   234  	err := cl.Login()
   235  	if err != nil {
   236  		t.Fatalf("error on login: %v\n", err)
   237  	}
   238  }
   239  
   240  func TestClient_NetworkTimeout(t *testing.T) {
   241  	test.Integration(t)
   242  
   243  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   244  	kt := keytab.New()
   245  	kt.Unmarshal(b)
   246  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   247  	c.Realms[0].KDC = []string{testdata.KDC_IP_TEST_GOKRB5_BADADDR + ":88"}
   248  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   249  
   250  	err := cl.Login()
   251  	if err == nil {
   252  		t.Fatal("login with incorrect KDC address did not error")
   253  	}
   254  }
   255  
   256  func TestClient_NetworkTryNextKDC(t *testing.T) {
   257  	test.Integration(t)
   258  
   259  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   260  	kt := keytab.New()
   261  	kt.Unmarshal(b)
   262  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   263  	addr := os.Getenv("TEST_KDC_ADDR")
   264  	if addr == "" {
   265  		addr = testdata.KDC_IP_TEST_GOKRB5
   266  	}
   267  	// Two out fo three times this should fail the first time.
   268  	// So will run login twice to expect at least once the first time it will be to a bad KDC
   269  	c.Realms[0].KDC = []string{testdata.KDC_IP_TEST_GOKRB5_BADADDR + ":88",
   270  		testdata.KDC_IP_TEST_GOKRB5_BADADDR + ":88",
   271  		addr + ":" + testdata.KDC_PORT_TEST_GOKRB5,
   272  	}
   273  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   274  
   275  	err := cl.Login()
   276  	if err != nil {
   277  		t.Fatal("login failed")
   278  	}
   279  	err = cl.Login()
   280  	if err != nil {
   281  		t.Fatal("login failed")
   282  	}
   283  }
   284  
   285  func TestClient_GetServiceTicket(t *testing.T) {
   286  	test.Integration(t)
   287  
   288  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   289  	kt := keytab.New()
   290  	kt.Unmarshal(b)
   291  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   292  	addr := os.Getenv("TEST_KDC_ADDR")
   293  	if addr == "" {
   294  		addr = testdata.KDC_IP_TEST_GOKRB5
   295  	}
   296  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   297  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   298  
   299  	err := cl.Login()
   300  	if err != nil {
   301  		t.Fatalf("error on login: %v\n", err)
   302  	}
   303  	spn := "HTTP/host.test.gokrb5"
   304  	tkt, key, err := cl.GetServiceTicket(spn)
   305  	if err != nil {
   306  		t.Fatalf("error getting service ticket: %v\n", err)
   307  	}
   308  	assert.Equal(t, spn, tkt.SName.PrincipalNameString())
   309  	assert.Equal(t, int32(18), key.KeyType)
   310  
   311  	//Check cache use - should get the same values back again
   312  	tkt2, key2, err := cl.GetServiceTicket(spn)
   313  	if err != nil {
   314  		t.Fatalf("error getting service ticket: %v\n", err)
   315  	}
   316  	assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
   317  	assert.Equal(t, key.KeyValue, key2.KeyValue)
   318  }
   319  
   320  func TestClient_GetServiceTicket_CanonicalizeTrue(t *testing.T) {
   321  	test.Integration(t)
   322  
   323  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   324  	kt := keytab.New()
   325  	kt.Unmarshal(b)
   326  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   327  	c.LibDefaults.Canonicalize = true
   328  	addr := os.Getenv("TEST_KDC_ADDR")
   329  	if addr == "" {
   330  		addr = testdata.KDC_IP_TEST_GOKRB5
   331  	}
   332  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   333  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   334  
   335  	err := cl.Login()
   336  	if err != nil {
   337  		t.Fatalf("error on login: %v\n", err)
   338  	}
   339  	spn := "HTTP/host.test.gokrb5"
   340  	tkt, key, err := cl.GetServiceTicket(spn)
   341  	if err != nil {
   342  		t.Fatalf("error getting service ticket: %v\n", err)
   343  	}
   344  	assert.Equal(t, spn, tkt.SName.PrincipalNameString())
   345  	assert.Equal(t, int32(18), key.KeyType)
   346  
   347  	//Check cache use - should get the same values back again
   348  	tkt2, key2, err := cl.GetServiceTicket(spn)
   349  	if err != nil {
   350  		t.Fatalf("error getting service ticket: %v\n", err)
   351  	}
   352  	assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
   353  	assert.Equal(t, key.KeyValue, key2.KeyValue)
   354  }
   355  
   356  func TestClient_GetServiceTicket_InvalidSPN(t *testing.T) {
   357  	test.Integration(t)
   358  
   359  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   360  	kt := keytab.New()
   361  	kt.Unmarshal(b)
   362  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   363  	addr := os.Getenv("TEST_KDC_ADDR")
   364  	if addr == "" {
   365  		addr = testdata.KDC_IP_TEST_GOKRB5
   366  	}
   367  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   368  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   369  
   370  	err := cl.Login()
   371  	if err != nil {
   372  		t.Fatalf("error on login: %v\n", err)
   373  	}
   374  	spn := "host.test.gokrb5"
   375  	_, _, err = cl.GetServiceTicket(spn)
   376  	assert.NotNil(t, err, "Expected unknown principal error")
   377  	assert.True(t, strings.Contains(err.Error(), "KDC_ERR_S_PRINCIPAL_UNKNOWN"), "Error text not as expected")
   378  }
   379  
   380  func TestClient_GetServiceTicket_OlderKDC(t *testing.T) {
   381  	test.Integration(t)
   382  
   383  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   384  	kt := keytab.New()
   385  	kt.Unmarshal(b)
   386  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   387  	addr := os.Getenv("TEST_KDC_ADDR")
   388  	if addr == "" {
   389  		addr = testdata.KDC_IP_TEST_GOKRB5
   390  	}
   391  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5_OLD}
   392  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   393  
   394  	err := cl.Login()
   395  	if err != nil {
   396  		t.Fatalf("error on login: %v\n", err)
   397  	}
   398  	spn := "HTTP/host.test.gokrb5"
   399  	tkt, key, err := cl.GetServiceTicket(spn)
   400  	if err != nil {
   401  		t.Fatalf("error getting service ticket: %v\n", err)
   402  	}
   403  	assert.Equal(t, spn, tkt.SName.PrincipalNameString())
   404  	assert.Equal(t, int32(18), key.KeyType)
   405  }
   406  
   407  func TestMultiThreadedClientUse(t *testing.T) {
   408  	test.Integration(t)
   409  
   410  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   411  	kt := keytab.New()
   412  	kt.Unmarshal(b)
   413  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   414  	addr := os.Getenv("TEST_KDC_ADDR")
   415  	if addr == "" {
   416  		addr = testdata.KDC_IP_TEST_GOKRB5
   417  	}
   418  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   419  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   420  
   421  	var wg sync.WaitGroup
   422  	wg.Add(5)
   423  	for i := 0; i < 5; i++ {
   424  		go func() {
   425  			defer wg.Done()
   426  			err := cl.Login()
   427  			if err != nil {
   428  				panic(err)
   429  			}
   430  		}()
   431  	}
   432  	wg.Wait()
   433  
   434  	var wg2 sync.WaitGroup
   435  	wg2.Add(5)
   436  	for i := 0; i < 5; i++ {
   437  		go func() {
   438  			defer wg2.Done()
   439  			err := spnegoGet(cl)
   440  			if err != nil {
   441  				panic(err)
   442  			}
   443  		}()
   444  	}
   445  	wg2.Wait()
   446  }
   447  
   448  func spnegoGet(cl *client.Client) error {
   449  	url := os.Getenv("TEST_HTTP_URL")
   450  	if url == "" {
   451  		url = testdata.TEST_HTTP_URL
   452  	}
   453  	r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
   454  	httpResp, err := http.DefaultClient.Do(r)
   455  	if err != nil {
   456  		return fmt.Errorf("request error: %v\n", err)
   457  	}
   458  	if httpResp.StatusCode != http.StatusUnauthorized {
   459  		return errors.New("did not get unauthorized code when no SPNEGO header set")
   460  	}
   461  	err = spnego.SetSPNEGOHeader(cl, r, "HTTP/host.test.gokrb5")
   462  	if err != nil {
   463  		return fmt.Errorf("error setting client SPNEGO header: %v", err)
   464  	}
   465  	httpResp, err = http.DefaultClient.Do(r)
   466  	if err != nil {
   467  		return fmt.Errorf("request error: %v\n", err)
   468  	}
   469  	if httpResp.StatusCode != http.StatusOK {
   470  		return errors.New("did not get OK code when SPNEGO header set")
   471  	}
   472  	return nil
   473  }
   474  
   475  func TestNewFromCCache(t *testing.T) {
   476  	test.Integration(t)
   477  
   478  	b, err := hex.DecodeString(testdata.CCACHE_TEST)
   479  	if err != nil {
   480  		t.Fatalf("error decoding test data")
   481  	}
   482  	cc := new(credentials.CCache)
   483  	err = cc.Unmarshal(b)
   484  	if err != nil {
   485  		t.Fatal("error getting test CCache")
   486  	}
   487  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   488  	addr := os.Getenv("TEST_KDC_ADDR")
   489  	if addr == "" {
   490  		addr = testdata.KDC_IP_TEST_GOKRB5
   491  	}
   492  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   493  	cl, err := client.NewFromCCache(cc, c)
   494  	if err != nil {
   495  		t.Fatalf("error creating client from CCache: %v", err)
   496  	}
   497  	if ok, err := cl.IsConfigured(); !ok {
   498  		t.Fatalf("client was not configured from CCache: %v", err)
   499  	}
   500  }
   501  
   502  // Login to the TEST.GOKRB5 domain and request service ticket for resource in the RESDOM.GOKRB5 domain.
   503  // There is a trust between the two domains.
   504  func TestClient_GetServiceTicket_Trusted_Resource_Domain(t *testing.T) {
   505  	test.Integration(t)
   506  
   507  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   508  	kt := keytab.New()
   509  	kt.Unmarshal(b)
   510  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   511  
   512  	addr := os.Getenv("TEST_KDC_ADDR")
   513  	if addr == "" {
   514  		addr = testdata.KDC_IP_TEST_GOKRB5
   515  	}
   516  	for i, r := range c.Realms {
   517  		if r.Realm == "TEST.GOKRB5" {
   518  			c.Realms[i].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   519  		}
   520  		if r.Realm == "RESDOM.GOKRB5" {
   521  			c.Realms[i].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5_RESDOM}
   522  		}
   523  	}
   524  
   525  	c.LibDefaults.DefaultRealm = "TEST.GOKRB5"
   526  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   527  	c.LibDefaults.DefaultTktEnctypes = []string{"aes256-cts-hmac-sha1-96"}
   528  	c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
   529  	c.LibDefaults.DefaultTGSEnctypes = []string{"aes256-cts-hmac-sha1-96"}
   530  	c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
   531  
   532  	err := cl.Login()
   533  
   534  	if err != nil {
   535  		t.Fatalf("error on login: %v\n", err)
   536  	}
   537  	spn := "HTTP/host.resdom.gokrb5"
   538  	tkt, key, err := cl.GetServiceTicket(spn)
   539  	if err != nil {
   540  		t.Fatalf("error getting service ticket: %v\n", err)
   541  	}
   542  	assert.Equal(t, spn, tkt.SName.PrincipalNameString())
   543  	assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], key.KeyType)
   544  
   545  	b, _ = hex.DecodeString(testdata.KEYTAB_SYSHTTP_RESDOM_GOKRB5)
   546  	skt := keytab.New()
   547  	skt.Unmarshal(b)
   548  	err = tkt.DecryptEncPart(skt, nil)
   549  	if err != nil {
   550  		t.Errorf("error decrypting ticket with service keytab: %v", err)
   551  	}
   552  }
   553  
   554  const (
   555  	kinitCmd = "kinit"
   556  	kvnoCmd  = "kvno"
   557  	spn      = "HTTP/host.test.gokrb5"
   558  )
   559  
   560  func login() error {
   561  	file, err := os.Create("/etc/krb5.conf")
   562  	if err != nil {
   563  		return fmt.Errorf("cannot open krb5.conf: %v", err)
   564  	}
   565  	defer file.Close()
   566  	fmt.Fprintf(file, testdata.KRB5_CONF)
   567  
   568  	cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5")
   569  
   570  	stdinR, stdinW := io.Pipe()
   571  	stderrR, stderrW := io.Pipe()
   572  	cmd.Stdin = stdinR
   573  	cmd.Stderr = stderrW
   574  
   575  	err = cmd.Start()
   576  	if err != nil {
   577  		return fmt.Errorf("could not start %s command: %v", kinitCmd, err)
   578  	}
   579  
   580  	go func() {
   581  		io.WriteString(stdinW, "passwordvalue")
   582  		stdinW.Close()
   583  	}()
   584  	errBuf := new(bytes.Buffer)
   585  	go func() {
   586  		io.Copy(errBuf, stderrR)
   587  		stderrR.Close()
   588  	}()
   589  
   590  	err = cmd.Wait()
   591  	if err != nil {
   592  		return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes()))
   593  	}
   594  	return nil
   595  }
   596  
   597  func getServiceTkt() error {
   598  	cmd := exec.Command(kvnoCmd, spn)
   599  	err := cmd.Start()
   600  	if err != nil {
   601  		return fmt.Errorf("could not start %s command: %v", kvnoCmd, err)
   602  	}
   603  	err = cmd.Wait()
   604  	if err != nil {
   605  		return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err)
   606  	}
   607  	return nil
   608  }
   609  
   610  func loadCCache() (*credentials.CCache, error) {
   611  	usr, _ := user.Current()
   612  	cpath := "/tmp/krb5cc_" + usr.Uid
   613  	return credentials.LoadCCache(cpath)
   614  }
   615  
   616  func TestGetServiceTicketFromCCacheTGT(t *testing.T) {
   617  	test.Privileged(t)
   618  
   619  	err := login()
   620  	if err != nil {
   621  		t.Fatalf("error logging in with kinit: %v", err)
   622  	}
   623  	c, err := loadCCache()
   624  	if err != nil {
   625  		t.Errorf("error loading CCache: %v", err)
   626  	}
   627  	cfg, _ := config.NewFromString(testdata.KRB5_CONF)
   628  	addr := os.Getenv("TEST_KDC_ADDR")
   629  	if addr == "" {
   630  		addr = testdata.KDC_IP_TEST_GOKRB5
   631  	}
   632  	cfg.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   633  	cl, err := client.NewFromCCache(c, cfg)
   634  	if err != nil {
   635  		t.Fatalf("error generating client from ccache: %v", err)
   636  	}
   637  	spn := "HTTP/host.test.gokrb5"
   638  	tkt, key, err := cl.GetServiceTicket(spn)
   639  	if err != nil {
   640  		t.Fatalf("error getting service ticket: %v\n", err)
   641  	}
   642  	assert.Equal(t, spn, tkt.SName.PrincipalNameString())
   643  	assert.Equal(t, int32(18), key.KeyType)
   644  
   645  	//Check cache use - should get the same values back again
   646  	tkt2, key2, err := cl.GetServiceTicket(spn)
   647  	if err != nil {
   648  		t.Fatalf("error getting service ticket: %v\n", err)
   649  	}
   650  	assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
   651  	assert.Equal(t, key.KeyValue, key2.KeyValue)
   652  
   653  	url := os.Getenv("TEST_HTTP_URL")
   654  	if url == "" {
   655  		url = testdata.TEST_HTTP_URL
   656  	}
   657  	r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
   658  	err = spnego.SetSPNEGOHeader(cl, r, "HTTP/host.test.gokrb5")
   659  	if err != nil {
   660  		t.Fatalf("error setting client SPNEGO header: %v", err)
   661  	}
   662  	httpResp, err := http.DefaultClient.Do(r)
   663  	if err != nil {
   664  		t.Fatalf("request error: %v\n", err)
   665  	}
   666  	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
   667  }
   668  
   669  func TestGetServiceTicketFromCCacheWithoutKDC(t *testing.T) {
   670  	test.Privileged(t)
   671  
   672  	err := login()
   673  	if err != nil {
   674  		t.Fatalf("error logging in with kinit: %v", err)
   675  	}
   676  	err = getServiceTkt()
   677  	if err != nil {
   678  		t.Fatalf("error getting service ticket: %v", err)
   679  	}
   680  	c, err := loadCCache()
   681  	if err != nil {
   682  		t.Errorf("error loading CCache: %v", err)
   683  	}
   684  	cfg, _ := config.NewFromString("...")
   685  	cl, err := client.NewFromCCache(c, cfg)
   686  	if err != nil {
   687  		t.Fatalf("error generating client from ccache: %v", err)
   688  	}
   689  	url := os.Getenv("TEST_HTTP_URL")
   690  	if url == "" {
   691  		url = testdata.TEST_HTTP_URL
   692  	}
   693  	r, _ := http.NewRequest("GET", url+"/modgssapi/index.html", nil)
   694  	err = spnego.SetSPNEGOHeader(cl, r, "HTTP/host.test.gokrb5")
   695  	if err != nil {
   696  		t.Fatalf("error setting client SPNEGO header: %v", err)
   697  	}
   698  	httpResp, err := http.DefaultClient.Do(r)
   699  	if err != nil {
   700  		t.Fatalf("request error: %v\n", err)
   701  	}
   702  	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
   703  }
   704  
   705  func TestClient_ChangePasswd(t *testing.T) {
   706  	test.Integration(t)
   707  
   708  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   709  	kt := keytab.New()
   710  	kt.Unmarshal(b)
   711  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   712  	addr := os.Getenv("TEST_KDC_ADDR")
   713  	if addr == "" {
   714  		addr = testdata.KDC_IP_TEST_GOKRB5
   715  	}
   716  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5}
   717  	c.Realms[0].KPasswdServer = []string{addr + ":464"}
   718  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   719  
   720  	ok, err := cl.ChangePasswd("newpassword")
   721  	if err != nil {
   722  		t.Fatalf("error changing password: %v", err)
   723  	}
   724  	assert.True(t, ok, "password was not changed")
   725  
   726  	cl = client.NewWithPassword("testuser1", "TEST.GOKRB5", "newpassword", c)
   727  	ok, err = cl.ChangePasswd(testdata.TESTUSER_PASSWORD)
   728  	if err != nil {
   729  		t.Fatalf("error changing password: %v", err)
   730  	}
   731  	assert.True(t, ok, "password was not changed back")
   732  
   733  	cl = client.NewWithPassword("testuser1", "TEST.GOKRB5", testdata.TESTUSER_PASSWORD, c)
   734  	err = cl.Login()
   735  	if err != nil {
   736  		t.Fatalf("Could not log back in after reverting password: %v", err)
   737  	}
   738  }
   739  
   740  func TestClient_Destroy(t *testing.T) {
   741  	test.Integration(t)
   742  
   743  	addr := os.Getenv("TEST_KDC_ADDR")
   744  	if addr == "" {
   745  		addr = testdata.KDC_IP_TEST_GOKRB5
   746  	}
   747  	b, _ := hex.DecodeString(testdata.KEYTAB_TESTUSER1_TEST_GOKRB5)
   748  	kt := keytab.New()
   749  	kt.Unmarshal(b)
   750  	c, _ := config.NewFromString(testdata.KRB5_CONF)
   751  	c.Realms[0].KDC = []string{addr + ":" + testdata.KDC_PORT_TEST_GOKRB5_SHORTTICKETS}
   752  	cl := client.NewWithKeytab("testuser1", "TEST.GOKRB5", kt, c)
   753  
   754  	err := cl.Login()
   755  	if err != nil {
   756  		t.Fatalf("error on login: %v\n", err)
   757  	}
   758  	spn := "HTTP/host.test.gokrb5"
   759  	_, _, err = cl.GetServiceTicket(spn)
   760  	if err != nil {
   761  		t.Fatalf("error getting service ticket: %v\n", err)
   762  	}
   763  	n := runtime.NumGoroutine()
   764  	time.Sleep(time.Second * 60)
   765  	cl.Destroy()
   766  	time.Sleep(time.Second * 5)
   767  	assert.True(t, runtime.NumGoroutine() < n, "auto-renewal goroutine was not stopped when client destroyed")
   768  	is, _ := cl.IsConfigured()
   769  	assert.False(t, is, "client is still configured after it was destroyed")
   770  }