Ver Fonte

fixed replay cache

Jonathan Turner há 9 anos atrás
pai
commit
1fc2c88364

+ 26 - 5
client/client_integration_test.go

@@ -9,6 +9,7 @@ import (
 	"github.com/jcmturner/gokrb5/keytab"
 	"github.com/jcmturner/gokrb5/testdata"
 	"github.com/stretchr/testify/assert"
+	"net/http"
 	"testing"
 )
 
@@ -136,12 +137,32 @@ func TestClient_GetServiceTicket_OlderKDC(t *testing.T) {
 	}
 	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
 	assert.Equal(t, 18, key.KeyType)
+}
 
-	//Check cache use - should get the same values back again
-	tkt2, key2, err := cl.GetServiceTicket(spn)
+func TestClient_SetSPNEGOHeader(t *testing.T) {
+	b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
+	kt, _ := keytab.Parse(b)
+	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
+	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
+	cl.WithConfig(c)
+
+	err := cl.Login()
 	if err != nil {
-		t.Fatalf("Error getting service ticket: %v\n", err)
+		t.Fatalf("Error on AS_REQ: %v\n", err)
 	}
-	assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
-	assert.Equal(t, key.KeyValue, key2.KeyValue)
+	r, _ := http.NewRequest("GET", "http://10.80.88.90/index.html", nil)
+	httpResp, err := http.DefaultClient.Do(r)
+	if err != nil {
+		t.Fatalf("Request error: %v\n", err)
+	}
+	assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
+	err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
+	if err != nil {
+		t.Fatalf("Error setting client SPNEGO header: %v", err)
+	}
+	httpResp, err = http.DefaultClient.Do(r)
+	if err != nil {
+		t.Fatalf("Request error: %v\n", err)
+	}
+	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
 }

+ 1 - 1
service/APExchange.go

@@ -64,7 +64,7 @@ func ValidateAPREQ(APReq messages.APReq, kt keytab.Keytab, cAddr string) (bool,
 
 	// Check for replay
 	rc := GetReplayCache(d)
-	if rc.IsReplay(d, APReq.Ticket.SName, a) {
+	if rc.IsReplay(APReq.Ticket.SName, a) {
 		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "Replay detected")
 		return false, creds, err
 	}

+ 2 - 2
service/cache.go

@@ -48,11 +48,11 @@ type replayCacheEntry struct {
 
 // Instance of the ServiceCache. This needs to be a singleton.
 var replayCache ServiceCache
+var once sync.Once
 
 // Get a pointer to the ServiceCache singleton.
 func GetReplayCache(d time.Duration) *ServiceCache {
 	// Create a singleton of the ReplayCache and start a background thread to regularly clean out old entries
-	var once sync.Once
 	once.Do(func() {
 		replayCache = make(ServiceCache)
 		go func() {
@@ -107,7 +107,7 @@ func (c *ServiceCache) ClearOldEntries(d time.Duration) {
 }
 
 // Check if the Authenticator provided is a replay within the duration defined. If this is not a replay add the entry to the cache for tracking.
-func (c *ServiceCache) IsReplay(d time.Duration, sname types.PrincipalName, a types.Authenticator) bool {
+func (c *ServiceCache) IsReplay(sname types.PrincipalName, a types.Authenticator) bool {
 	if ck, ok := (*c)[a.CName.GetPrincipalNameString()]; ok {
 		ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
 		if e, ok := ck.ReplayMap[ct]; ok {

+ 112 - 0
service/service_integration_test.go

@@ -0,0 +1,112 @@
+// +build integration
+// To turn on this test use -tags=integration in go test command
+
+package service
+
+import (
+	"encoding/hex"
+	"fmt"
+	"github.com/jcmturner/gokrb5/client"
+	"github.com/jcmturner/gokrb5/config"
+	"github.com/jcmturner/gokrb5/keytab"
+	"github.com/jcmturner/gokrb5/testdata"
+	"github.com/stretchr/testify/assert"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestService_SPNEGOKRB_NoAuthHeader(t *testing.T) {
+	s := httpServer()
+	defer s.Close()
+
+	cl := getClient()
+	err := cl.Login()
+	if err != nil {
+		t.Fatalf("Error on AS_REQ: %v\n", err)
+	}
+	r, _ := http.NewRequest("GET", s.URL, nil)
+	httpResp, err := http.DefaultClient.Do(r)
+	if err != nil {
+		t.Fatalf("Request error: %v\n", err)
+	}
+	assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
+	assert.Equal(t, "Negotiate", httpResp.Header.Get("WWW-Authenticate"), "Negitation header not set by server.")
+}
+
+func TestService_SPNEGOKRB_ValidUser(t *testing.T) {
+	s := httpServer()
+	defer s.Close()
+
+	cl := getClient()
+	err := cl.Login()
+	if err != nil {
+		t.Fatalf("Error on AS_REQ: %v\n", err)
+	}
+	r, _ := http.NewRequest("GET", s.URL, nil)
+	err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
+	if err != nil {
+		t.Fatalf("Error setting client SPNEGO header: %v", err)
+	}
+	httpResp, err := http.DefaultClient.Do(r)
+	if err != nil {
+		t.Fatalf("Request error: %v\n", err)
+	}
+	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
+}
+
+func TestService_SPNEGOKRB_Replay(t *testing.T) {
+	s := httpServer()
+	defer s.Close()
+
+	cl := getClient()
+	err := cl.Login()
+	if err != nil {
+		t.Fatalf("Error on AS_REQ: %v\n", err)
+	}
+	r, _ := http.NewRequest("GET", s.URL, nil)
+	err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
+	if err != nil {
+		t.Fatalf("Error setting client SPNEGO header: %v", err)
+	}
+
+	httpResp, err := http.DefaultClient.Do(r)
+	if err != nil {
+		t.Fatalf("Request error: %v\n", err)
+	}
+	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
+
+	// Do not Set the SPNEGO header again so this should try to replay the tokens
+	httpResp, err = http.DefaultClient.Do(r)
+	if err != nil {
+		t.Fatalf("Request error: %v\n", err)
+	}
+	assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.")
+}
+
+func getClient() client.Client {
+	b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
+	kt, _ := keytab.Parse(b)
+	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
+	cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
+	cl.WithConfig(c)
+	return cl
+}
+
+func httpServer() *httptest.Server {
+	l := log.New(ioutil.Discard, "GOKRB5 Service Tests: ", log.Ldate|log.Ltime|log.Lshortfile)
+	b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
+	kt, _ := keytab.Parse(b)
+	th := http.HandlerFunc(testAppHandler)
+	s := httptest.NewServer(SPNEGOKRB5Authenticate(th, kt, l))
+	return s
+}
+
+func testAppHandler(w http.ResponseWriter, r *http.Request) {
+	w.WriteHeader(http.StatusOK)
+	ctx := r.Context()
+	fmt.Fprintf(w, "<html>\nTEST.GOKRB5 Handler\nAuthenticed user: %s\nUser's realm: %s\n</html>", ctx.Value("cname").(string), ctx.Value("crealm").(string))
+	return
+}