Browse Source

hostaddress items

Jonathan Turner 9 years ago
parent
commit
7a9007a2ed
6 changed files with 115 additions and 30 deletions
  1. 0 1
      crypto/aes/aes.go
  2. 1 3
      iana/asnAppTag/constants.go
  3. 6 2
      messages/KDCRep.go
  4. 0 16
      messages/KDCRep_test.go
  5. 38 8
      service/http.go
  6. 70 0
      types/HostAddress.go

+ 0 - 1
crypto/aes/aes.go

@@ -124,7 +124,6 @@ func encryptCTS(key, iv, message []byte, e etype.EType) ([]byte, []byte, error)
 	ct = append(ct, lb...)
 	ct = append(ct, pb...)
 	return lb, ct[:l], nil
-	//TODO do we need to add the hash to the end?
 }
 
 func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {

+ 1 - 3
iana/asnAppTag/constants.go

@@ -20,6 +20,4 @@ const (
 	EncKrbPrivPart = 28
 	EncKrbCredPart = 29
 	KRBError       = 30
-)
-
-//TODO review if we want to consolidate with the MsgTypes in the dictionary
+)

+ 6 - 2
messages/KDCRep.go

@@ -206,7 +206,9 @@ func (k *ASRep) IsValid(cfg *config.Config, asReq ASReq) (bool, error) {
 		return false, fmt.Errorf("SRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
 	}
 	if len(asReq.ReqBody.Addresses) > 0 {
-		//TODO compare if address list is the same
+		if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) {
+			return false, errors.New("Addresses listed in the AS_REP does not match those listed in the AS_REQ")
+		}
 	}
 	t := time.Now().UTC()
 	if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew {
@@ -289,7 +291,9 @@ func (k *TGSRep) IsValid(cfg *config.Config, tgsReq TGSReq) (bool, error) {
 		return false, fmt.Errorf("SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
 	}
 	if len(tgsReq.ReqBody.Addresses) > 0 {
-		//TODO compare if address list is the same
+		if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) {
+			return false, errors.New("Addresses listed in the TGS_REP does not match those listed in the TGS_REQ")
+		}
 	}
 	if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
 		if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {

+ 0 - 16
messages/KDCRep_test.go

@@ -265,14 +265,6 @@ func TestUnmarshalASRepDecodeAndDecrypt(t *testing.T) {
 	assert.Equal(t, test_realm, asRep.DecryptedEncPart.SRealm, "Service realm not as expected")
 	assert.Equal(t, 2, asRep.DecryptedEncPart.SName.NameType, "Name type for AS_REP not as expected")
 	assert.Equal(t, []string{"krbtgt", test_realm}, asRep.DecryptedEncPart.SName.NameString, "Service name string not as expected")
-	//t.Log("Finished testing ecrypted parts of AS REP")
-
-	//TODO should we be able to decrypt this part with the client key?
-	/*s, err = etype.Decrypt(key, asRep.Ticket.EncPart.Cipher)
-	if err != nil {
-		t.Fatalf("Error decrypting ticket encrypted part: %v\n", err)
-	}
-	t.Logf("Decypted Ticket EncPart %+v", s)*/
 }
 
 func TestUnmarshalASRepDecodeAndDecrypt_withPassword(t *testing.T) {
@@ -313,14 +305,6 @@ func TestUnmarshalASRepDecodeAndDecrypt_withPassword(t *testing.T) {
 	assert.Equal(t, test_realm, asRep.DecryptedEncPart.SRealm, "Service realm not as expected")
 	assert.Equal(t, 2, asRep.DecryptedEncPart.SName.NameType, "Name type for AS_REP not as expected")
 	assert.Equal(t, []string{"krbtgt", test_realm}, asRep.DecryptedEncPart.SName.NameString, "Service name string not as expected")
-	//t.Log("Finished testing ecrypted parts of AS REP")
-
-	//TODO should we be able to decrypt this part with the client key?
-	/*s, err = etype.Decrypt(key, asRep.Ticket.EncPart.Cipher)
-	if err != nil {
-		t.Fatalf("Error decrypting ticket encrypted part: %v\n", err)
-	}
-	t.Logf("Decypted Ticket EncPart %+v", s)*/
 }
 
 //func TestKDCRep_Validate(t *testing.T) {

+ 38 - 8
service/http.go

@@ -15,6 +15,7 @@ import (
 	"net/http"
 	"strings"
 	"time"
+	"net"
 )
 
 const (
@@ -75,7 +76,7 @@ func SPNEGOKRB5Authenticate(f http.Handler, ktab keytab.Keytab, l *log.Logger) h
 			rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error unmarshalling the authenticator: %v", r.RemoteAddr, err))
 			return
 		}
-		if ok, err := validateAPREQ(a, mt.APReq); ok {
+		if ok, err := validateAPREQ(a, mt.APReq, r); ok {
 			cnameStr := a.CName.GetPrincipalNameString()
 			ctx := r.Context()
 			ctx = context.WithValue(ctx, "cname", cnameStr)
@@ -94,18 +95,47 @@ func SPNEGOKRB5Authenticate(f http.Handler, ktab keytab.Keytab, l *log.Logger) h
 }
 
 // Validate the AP_REQ provided in the SPNEGO NegTokenInit.
-func validateAPREQ(a types.Authenticator, APReq messages.APReq) (bool, error) {
+func validateAPREQ(a types.Authenticator, APReq messages.APReq, r *http.Request) (bool, error) {
 	// Check CName in Authenticator is the same as that in the ticket
 	if !a.CName.Equal(APReq.Ticket.DecryptedEncPart.CName) {
 		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket")
 		return false, err
 	}
-	// TODO client address check
-	//The addresses in the ticket (if any) are then
-	//searched for an address matching the operating-system reported
-	//address of the client.  If no match is found or the server insists on
-	//ticket addresses but none are present in the ticket, the
-	//KRB_AP_ERR_BADADDR error is returned.
+	if len(APReq.Ticket.DecryptedEncPart.CAddr) > 0 {
+		//The addresses in the ticket (if any) are then
+		//searched for an address matching the operating-system reported
+		//address of the client.  If no match is found or the server insists on
+		//ticket addresses but none are present in the ticket, the
+		//KRB_AP_ERR_BADADDR error is returned.
+		cAddr, _, err := net.SplitHostPort(r.RemoteAddr)
+		if err != nil {
+			err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Invalid format of client address.")
+			return false, err
+		}
+		ip := net.ParseIP(cAddr)
+		hb, err := ip.MarshalText()
+		if err != nil {
+			err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Could not marshal client's address into bytes.")
+			return false, err
+		}
+		var ht int
+		if ip.To4() != nil {
+			ht = types.AddrType_IPv4
+		} else if ip.To16() != nil {
+			ht = types.AddrType_IPv6
+		} else {
+			err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Could not determine client's address type.")
+			return false, err
+		}
+		h := types.HostAddress{
+			AddrType: ht,
+			Address: hb,
+		}
+		if !types.HostAddressesContains(APReq.Ticket.DecryptedEncPart.CAddr, h) {
+			err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Client address not within the list contained in the service ticket")
+			return false, err
+		}
+	}
 
 	// Check the clock skew between the client and the service server
 	ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)

+ 70 - 0
types/HostAddress.go

@@ -4,6 +4,7 @@ package types
 // Section: 5.2.5
 
 import (
+	"bytes"
 	"github.com/jcmturner/asn1"
 )
 
@@ -31,6 +32,18 @@ address
 	This field encodes a single address of type addr-type.
 */
 
+const (
+	AddrType_IPv4            = 2
+	AddrType_Directional     = 3
+	AddrType_ChaosNet        = 5
+	AddrType_XNS             = 6
+	AddrType_ISO             = 7
+	AddrType_DECNET_Phase_IV = 12
+	AddrType_AppleTalk_DDP   = 16
+	AddrType_NetBios         = 20
+	AddrType_IPv6            = 24
+)
+
 type HostAddresses []HostAddress
 
 type HostAddress struct {
@@ -43,3 +56,60 @@ func (h *HostAddress) GetAddress() (string, error) {
 	_, err := asn1.Unmarshal(h.Address, &b)
 	return string(b), err
 }
+
+func HostAddressesEqual(h, a []HostAddress) bool {
+	if len(h) != len(a) {
+		return false
+	}
+	for _, e := range a {
+		var found bool
+		found = false
+		for _, i := range h {
+			if e.Equal(i) {
+				found = true
+				break
+			}
+		}
+		if !found {
+			return false
+		}
+	}
+	return true
+}
+
+func HostAddressesContains(h []HostAddress, a HostAddress) bool {
+	for _, e := range h {
+		if e.Equal(a) {
+			return true
+		}
+	}
+	return false
+}
+
+func (h *HostAddress) Equal(a HostAddress) bool {
+	if h.AddrType != a.AddrType {
+		return false
+	}
+	return bytes.Equal(h.Address, a.Address)
+}
+
+func (h *HostAddresses) Contains(a HostAddress) bool {
+	for _, e := range *h {
+		if e.Equal(a) {
+			return true
+		}
+	}
+	return false
+}
+
+func (h *HostAddresses) Equal(a []HostAddress) bool {
+	if len(*h) != len(a) {
+		return false
+	}
+	for _, e := range a {
+		if !h.Contains(e) {
+			return false
+		}
+	}
+	return true
+}