Преглед изворни кода

fix failing pre-auth on TGT renewal when client's prefered etype is not
supported by the KDC

Jonathan Turner пре 7 година
родитељ
комит
cbdf1c7592

+ 8 - 3
client/ASExchange.go

@@ -27,7 +27,7 @@ func (cl *Client) ASExchange(realm string, ASReq messages.ASReq, referral int) (
 	if err != nil {
 	if err != nil {
 		if e, ok := err.(messages.KRBError); ok {
 		if e, ok := err.(messages.KRBError); ok {
 			switch e.ErrorCode {
 			switch e.ErrorCode {
-			case errorcode.KDC_ERR_PREAUTH_REQUIRED:
+			case errorcode.KDC_ERR_PREAUTH_REQUIRED, errorcode.KDC_ERR_PREAUTH_FAILED:
 				// From now on assume this client will need to do this pre-auth and set the PAData
 				// From now on assume this client will need to do this pre-auth and set the PAData
 				cl.GoKrb5Conf.AssumePAEncTimestampRequired = true
 				cl.GoKrb5Conf.AssumePAEncTimestampRequired = true
 				err = setPAData(cl, e, &ASReq)
 				err = setPAData(cl, e, &ASReq)
@@ -81,8 +81,12 @@ func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) erro
 		}
 		}
 		var et etype.EType
 		var et etype.EType
 		if krberr.ErrorCode == 0 {
 		if krberr.ErrorCode == 0 {
-			// This is not in response to an error from the KDC. It is preemptive
-			et, err = crypto.GetEtype(ASReq.ReqBody.EType[0]) // Take the first as preference
+			etn := cl.GoKrb5Conf.preAuthEType
+			if etn == 0 {
+				etn = int32(cl.Config.LibDefaults.PreferredPreauthTypes[0])
+			}
+			// This is not in response to an error from the KDC. It is preemptive or renewal
+			et, err = crypto.GetEtype(etn) // Take the first as preference
 			if err != nil {
 			if err != nil {
 				return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
 				return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
 			}
 			}
@@ -92,6 +96,7 @@ func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) erro
 			if err != nil {
 			if err != nil {
 				return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
 				return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
 			}
 			}
+			cl.GoKrb5Conf.preAuthEType = et.GetETypeID()
 		}
 		}
 		key, err := cl.Key(et, krberr)
 		key, err := cl.Key(et, krberr)
 		if err != nil {
 		if err != nil {

+ 1 - 0
client/client.go

@@ -32,6 +32,7 @@ type Client struct {
 type Config struct {
 type Config struct {
 	DisablePAFXFast              bool
 	DisablePAFXFast              bool
 	AssumePAEncTimestampRequired bool
 	AssumePAEncTimestampRequired bool
+	preAuthEType                 int32
 }
 }
 
 
 // NewClientWithPassword creates a new client from a password credential.
 // NewClientWithPassword creates a new client from a password credential.

+ 11 - 3
client/client_integration_test.go

@@ -708,17 +708,18 @@ func TestClient_ChangePasswd(t *testing.T) {
 	assert.True(t, ok, "password was not changed back")
 	assert.True(t, ok, "password was not changed back")
 }
 }
 
 
-func TestClient_AutoRenew_Goroutine_Count(t *testing.T) {
+func TestClient_AutoRenew_Goroutine(t *testing.T) {
 	// Tests that the auto renew of client credentials is not spawning goroutines out of control.
 	// Tests that the auto renew of client credentials is not spawning goroutines out of control.
 	addr := os.Getenv("TEST_KDC_ADDR")
 	addr := os.Getenv("TEST_KDC_ADDR")
 	if addr == "" {
 	if addr == "" {
 		addr = testdata.TEST_KDC_ADDR
 		addr = testdata.TEST_KDC_ADDR
 	}
 	}
-	b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
+	b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
 	kt, _ := keytab.Parse(b)
 	kt, _ := keytab.Parse(b)
 	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
 	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
 	c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
 	c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
-	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
+	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.
+	cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
 	cl.WithConfig(c)
 	cl.WithConfig(c)
 
 
 	err := cl.Login()
 	err := cl.Login()
@@ -728,6 +729,13 @@ func TestClient_AutoRenew_Goroutine_Count(t *testing.T) {
 	n := runtime.NumGoroutine()
 	n := runtime.NumGoroutine()
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
 		time.Sleep(time.Second * 20)
 		time.Sleep(time.Second * 20)
+		sess, err := cl.sessionFromRealm("TEST.GOKRB5")
+		if err != nil {
+			t.Errorf("could not get client's session: %v", err)
+		}
+		if !sess.valid() {
+			t.Fatalf("session auto update failed")
+		}
 		if runtime.NumGoroutine() > n {
 		if runtime.NumGoroutine() > n {
 			t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine())
 			t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine())
 		}
 		}

+ 10 - 0
client/session.go

@@ -59,6 +59,16 @@ func (s *session) destroy() {
 	s.sessionKeyExpiration = s.endTime
 	s.sessionKeyExpiration = s.endTime
 }
 }
 
 
+func (s *session) valid() bool {
+	s.mux.RLock()
+	defer s.mux.RUnlock()
+	t := time.Now().UTC()
+	if t.Before(s.endTime) && s.authTime.Before(t) {
+		return true
+	}
+	return false
+}
+
 // AddSession adds a session for a realm with a TGT to the client's session cache.
 // 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.
 // A goroutine is started to automatically renew the TGT before expiry.
 func (cl *Client) AddSession(tgt messages.Ticket, dep messages.EncKDCRepPart) {
 func (cl *Client) AddSession(tgt messages.Ticket, dep messages.EncKDCRepPart) {

+ 1 - 1
testenv/docker/krb5kdc-shorttickets/kdc.conf

@@ -10,7 +10,7 @@
   acl_file = /var/kerberos/krb5kdc/kadm5.acl
   acl_file = /var/kerberos/krb5kdc/kadm5.acl
   dict_file = /usr/share/dict/words
   dict_file = /usr/share/dict/words
   admin_keytab = /opt/krb5/data/kadm5.keytab
   admin_keytab = /opt/krb5/data/kadm5.keytab
-  supported_enctypes = aes128-cts-hmac-sha1-96:normal aes256-cts-hmac-sha1-96:normal aes128-cts-hmac-sha256-128:normal aes256-cts-hmac-sha384-192:normal des3-cbc-sha1-kd:normal rc4-hmac:normal
+  supported_enctypes = aes128-cts-hmac-sha1-96:normal aes256-cts-hmac-sha1-96:normal aes128-cts-hmac-sha256-128:normal aes256-cts-hmac-sha384-192:normal rc4-hmac:normal
   default_principal_flags = +renewable
   default_principal_flags = +renewable
  }
  }