Bladeren bron

refactor tidy

Jonathan Turner 9 jaren geleden
bovenliggende
commit
9ddac050ec
12 gewijzigde bestanden met toevoegingen van 224 en 166 verwijderingen
  1. 2 10
      GSSAPI/krb5Token.go
  2. 36 5
      README.md
  3. 15 5
      credentials/credentials.go
  4. 28 0
      iana/flags/constants.go
  5. 2 1
      messages/KDCRep.go
  6. 11 10
      messages/KDCReq.go
  7. 85 0
      service/APExchange.go
  8. 7 7
      service/cache.go
  9. 7 84
      service/http.go
  10. 19 6
      types/Authenticator.go
  11. 0 27
      types/KerberosFlags.go
  12. 12 11
      types/KerberosFlags_test.go

+ 2 - 10
GSSAPI/krb5Token.go

@@ -11,7 +11,6 @@ import (
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/types"
-	"math/rand"
 )
 
 const (
@@ -114,19 +113,12 @@ func NewKRB5APREQMechToken(creds credentials.Credentials, tkt messages.Ticket, s
 func newAuthenticator(creds credentials.Credentials, keyType int) types.Authenticator {
 	//RFC 4121 Section 4.1.1
 	auth := types.NewAuthenticator(creds.Realm, creds.CName)
+	etype, _ := crypto.GetEtype(keyType)
+	auth.GenerateSeqNumberAndSubKey(keyType, etype.GetKeyByteSize())
 	auth.Cksum = types.Checksum{
 		CksumType: chksumtype.GSSAPI,
 		Checksum:  newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}),
 	}
-	auth.SeqNumber = int(rand.Int31())
-	//Generate subkey value
-	etype, _ := crypto.GetEtype(keyType)
-	sk := make([]byte, etype.GetKeyByteSize())
-	rand.Read(sk)
-	auth.SubKey = types.EncryptionKey{
-		KeyType:  keyType,
-		KeyValue: sk,
-	}
 	return auth
 }
 

+ 36 - 5
README.md

@@ -1,4 +1,5 @@
 # gokrb5
+[![GoDoc](https://godoc.org/github.com/jcmturner/gokrb5?status.svg)](https://godoc.org/github.com/jcmturner/gokrb5)
 
 #### This is work in progress and may have some issues. Full testing is still required.
 
@@ -9,8 +10,6 @@ Currently the following is working/tested:
 * Client side support for authentication to HTTP servers that implement SPNEGO using Kerberos 5.
 * Service side handling for Kerberos SPNEGO seems to be working but not yet fully tested with a browser such as firefox or chrome.
 
-[![GoDoc](https://godoc.org/github.com/jcmturner/gokrb5?status.svg)](https://godoc.org/github.com/jcmturner/gokrb5)
-
 ## Implemented Encryption & Checksum Types
 The currently implemented encrytion types are:
 
@@ -59,22 +58,54 @@ err := cl.Login
 cl.EnableAutoSessionRenewal()
 ```
 #### Authenticate to a Service
-##### Native Kerberos
+##### Generic Kerberos
 Request a Serivce ticket for a Service Principal Name (SPN).
 This method will use the client's cache either returning a valid cached ticket, renewing a cached ticket with the KDC or requesting a new ticket from the KDC.
 Therefore the GetServiceTicket method can be continually used for the most efficient interaction with the KDC.
 ```go
 tkt, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
 ```
+The steps after this will be specifc to the application protocol but it will likely involve a client/server Authentication Protocol exchange (AP exchange).
+This will involve these steps:
+* Getting the service ticket and session key for the service the client is authenticating to:
+```go
+tkt, key, err := cl.GetServiceTicket(spnStr)
+```
+* Generate a new Authenticator and generate a sequence number and subkey:
+```go
+auth := types.NewAuthenticator(cl.Credentials.Realm, cl.Credentials.CName)
+etype, _ := crypto.GetEtype(key.KeyType)
+auth.GenerateSeqNumberAndSubKey(key.KeyType, etype.GetKeyByteSize())
+```
+* Set the checksum on the authenticator
+The checksum is an application specific value. Set as follows:
+```go
+auth.Cksum = types.Checksum{
+		CksumType: checksumIDint,
+		Checksum:  checksumBytesSlice,
+	}
+```
+
 ##### HTTP SPNEGO
 Create the HTTP request object and then call the client's SetSPNEGOHeader method passing the Service Principal Name (SPN)
 ```go
-r, _ := http.NewRequest("GET", "http://host.test.gokrb5/index.html", nil)
+r, _ := http.
+}NewRequest("GET", "http://host.test.gokrb5/index.html", nil)
 cl.SetSPNEGOHeader(r, "")
 HTTPResp, err := http.DefaultClient.Do(r)
 ```
 
-### Kerberos Web Service
+### Kerberised Service
+#### Validating Client Details
+To validate the AP_REQ sent by the client on the service side call this method:
+```go
+if ok, creds, err := ValidateAPREQ(mt.APReq, kt, r.RemoteAddr); ok {
+        // Perform application specifc actions
+        // creds object has details about the client identity
+}
+```
+
+#### Kerberos Web Service
 A HTTP handler wrapper can be used to implement Kerberos SPNEGO authentication for web services.
 To configure the wrapper the keytab for the SPN and a Logger are required:
 ```go

+ 15 - 5
credentials/credentials.go

@@ -11,11 +11,12 @@ import (
 // Contains either a keytab, password or both.
 // Keytabs are used over passwords if both are defined.
 type Credentials struct {
-	Username string
-	Realm    string
-	CName    types.PrincipalName
-	Keytab   keytab.Keytab
-	Password string
+	Username   string
+	Realm      string
+	CName      types.PrincipalName
+	Keytab     keytab.Keytab
+	Password   string
+	Attributes []string
 }
 
 // Create a new Credentials struct.
@@ -31,6 +32,15 @@ func NewCredentials(username string, realm string) Credentials {
 	}
 }
 
+func NewCredentialsFromPrincipal(cname types.PrincipalName, realm string) Credentials {
+	return Credentials{
+		Username: cname.GetPrincipalNameString(),
+		Realm:    realm,
+		CName:    cname,
+		Keytab:   keytab.NewKeytab(),
+	}
+}
+
 // Set the Keytab in the Credentials struct.
 func (c *Credentials) WithKeytab(kt keytab.Keytab) *Credentials {
 	c.Keytab = kt

+ 28 - 0
iana/flags/constants.go

@@ -0,0 +1,28 @@
+package flags
+
+const (
+	Reserved               = 0
+	Forwardable            = 1
+	Forwarded              = 2
+	Proxiable              = 3
+	Proxy                  = 4
+	AllowPostDate          = 5
+	MayPostDate            = 5
+	PostDated              = 6
+	Invalid                = 7
+	Renewable              = 8
+	Initial                = 9
+	PreAuthent             = 10
+	HWAuthent              = 11
+	OptHardwareAuth        = 11
+	RequestAnonymous       = 12
+	TransitedPolicyChecked = 12
+	OKAsDelegate           = 13
+	EncPARep               = 15
+	Canonicalize           = 15
+	DisableTransitedCheck  = 26
+	RenewableOK            = 27
+	EncTktInSkey           = 28
+	Renew                  = 30
+	Validate               = 31
+)

+ 2 - 1
messages/KDCRep.go

@@ -12,6 +12,7 @@ import (
 	"github.com/jcmturner/gokrb5/crypto"
 	"github.com/jcmturner/gokrb5/crypto/engine"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
+	"github.com/jcmturner/gokrb5/iana/flags"
 	"github.com/jcmturner/gokrb5/iana/keyusage"
 	"github.com/jcmturner/gokrb5/iana/msgtype"
 	"github.com/jcmturner/gokrb5/iana/patype"
@@ -220,7 +221,7 @@ func (k *ASRep) IsValid(cfg *config.Config, creds *credentials.Credentials, asRe
 		return false, fmt.Errorf("Clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds())
 	}
 	// RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
-	if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, types.EncPARep) {
+	if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) {
 		if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
 			return false, errors.New("KDC did not respond appropriately to FAST negotiation")
 		}

+ 11 - 10
messages/KDCReq.go

@@ -12,6 +12,7 @@ import (
 	"github.com/jcmturner/gokrb5/crypto/engine"
 	"github.com/jcmturner/gokrb5/iana"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
+	"github.com/jcmturner/gokrb5/iana/flags"
 	"github.com/jcmturner/gokrb5/iana/keyusage"
 	"github.com/jcmturner/gokrb5/iana/msgtype"
 	"github.com/jcmturner/gokrb5/iana/nametype"
@@ -105,16 +106,16 @@ func NewASReq(c *config.Config, cname types.PrincipalName) ASReq {
 		},
 	}
 	if c.LibDefaults.Forwardable {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Forwardable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
 	}
 	if c.LibDefaults.Canonicalize {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Canonicalize)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
 	}
 	if c.LibDefaults.Proxiable {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Proxiable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
 	}
 	if c.LibDefaults.Renew_lifetime != 0 {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Renewable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
 		a.ReqBody.RTime = t.Add(c.LibDefaults.Renew_lifetime)
 		a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
 
@@ -143,21 +144,21 @@ func NewTGSReq(cname types.PrincipalName, c *config.Config, tkt Ticket, sessionK
 		},
 	}
 	if c.LibDefaults.Forwardable {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Forwardable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
 	}
 	if c.LibDefaults.Canonicalize {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Canonicalize)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
 	}
 	if c.LibDefaults.Proxiable {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Proxiable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
 	}
 	if c.LibDefaults.Renew_lifetime > time.Duration(0) {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Renewable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
 		a.ReqBody.RTime = t.Add(c.LibDefaults.Renew_lifetime)
 	}
 	if renewal {
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Renew)
-		types.SetFlag(&a.ReqBody.KDCOptions, types.Renewable)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renew)
+		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
 	}
 	auth := types.NewAuthenticator(c.LibDefaults.Default_realm, cname)
 	// Add the CName to make validation of the reply easier

+ 85 - 0
service/APExchange.go

@@ -0,0 +1,85 @@
+package service
+
+import (
+	"fmt"
+	"github.com/jcmturner/gokrb5/credentials"
+	"github.com/jcmturner/gokrb5/crypto"
+	"github.com/jcmturner/gokrb5/iana/errorcode"
+	"github.com/jcmturner/gokrb5/iana/flags"
+	"github.com/jcmturner/gokrb5/iana/keyusage"
+	"github.com/jcmturner/gokrb5/keytab"
+	"github.com/jcmturner/gokrb5/messages"
+	"github.com/jcmturner/gokrb5/types"
+	"time"
+)
+
+// Validates an AP_REQ sent to the service. Returns a boolean for if the AP_REQ is valid and the client's principal name and realm.
+func ValidateAPREQ(APReq messages.APReq, kt keytab.Keytab, cAddr string) (bool, credentials.Credentials, error) {
+	var creds credentials.Credentials
+	err := APReq.Ticket.DecryptEncPart(kt)
+	if err != nil {
+		return false, creds, fmt.Errorf("Error decrypting encpart of service ticket provided: %v", err)
+	}
+	ab, err := crypto.DecryptEncPart(APReq.Authenticator, APReq.Ticket.DecryptedEncPart.Key, keyusage.AP_REQ_AUTHENTICATOR)
+	if err != nil {
+		return false, creds, fmt.Errorf("Error decrypting authenticator: %v", err)
+	}
+	var a types.Authenticator
+	err = a.Unmarshal(ab)
+	if err != nil {
+		return false, creds, fmt.Errorf("Error unmarshaling authenticator: %v", err)
+	}
+
+	// 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, creds, err
+	}
+	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.
+		h, err := types.GetHostAddress(cAddr)
+		if err != nil {
+			err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, err.Error())
+			return false, creds, err
+		}
+		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, creds, err
+		}
+	}
+
+	// Check the clock skew between the client and the service server
+	ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
+	t := time.Now().UTC()
+	// Hardcode 5 min max skew. May want to make this configurable
+	d := time.Duration(5) * time.Minute
+	if t.Sub(ct) > d || ct.Sub(t) > d {
+		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("Clock skew with client too large. Greater than %v seconds", d))
+		return false, creds, err
+	}
+
+	// Check for replay
+	rc := GetReplayCache(d)
+	if rc.IsReplay(d, APReq.Ticket.SName, a) {
+		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "Replay detected")
+		return false, creds, err
+	}
+
+	// Check for future tickets or invalid tickets
+	if APReq.Ticket.DecryptedEncPart.StartTime.Sub(t) > d || types.IsFlagSet(&APReq.Ticket.DecryptedEncPart.Flags, flags.Invalid) {
+		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "Service ticket provided is not yet valid")
+		return false, creds, err
+	}
+
+	// Check for expired ticket
+	if t.Sub(APReq.Ticket.DecryptedEncPart.EndTime) > d {
+		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "Service ticket provided has expired")
+		return false, creds, err
+	}
+	creds = credentials.NewCredentialsFromPrincipal(a.CName, a.CRealm)
+	return true, creds, nil
+}

+ 7 - 7
service/cache.go

@@ -30,17 +30,17 @@ them following an event that caused the server to lose track of
 recently seen authenticators.*/
 
 // Cache for tickets received from clients keyed by fully qualified client name. Used to track replay of tickets.
-type ServiceCache map[string]ClientEntries
+type ServiceCache map[string]clientEntries
 
 // Entries for client details sent to the service.
-type ClientEntries struct {
-	ReplayMap map[time.Time]ReplayCacheEntry
+type clientEntries struct {
+	ReplayMap map[time.Time]replayCacheEntry
 	SeqNumber int
 	SubKey    types.EncryptionKey
 }
 
 // Cache entry tracking client time values of tickets sent to the service.
-type ReplayCacheEntry struct {
+type replayCacheEntry struct {
 	PresentedTime time.Time
 	SName         types.PrincipalName
 	CTime         time.Time // This combines the ticket's CTime and Cusec
@@ -70,7 +70,7 @@ func GetReplayCache(d time.Duration) *ServiceCache {
 func (c *ServiceCache) AddEntry(sname types.PrincipalName, a types.Authenticator) {
 	ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
 	if ce, ok := (*c)[a.CName.GetPrincipalNameString()]; ok {
-		ce.ReplayMap[ct] = ReplayCacheEntry{
+		ce.ReplayMap[ct] = replayCacheEntry{
 			PresentedTime: time.Now().UTC(),
 			SName:         sname,
 			CTime:         ct,
@@ -78,8 +78,8 @@ func (c *ServiceCache) AddEntry(sname types.PrincipalName, a types.Authenticator
 		ce.SeqNumber = a.SeqNumber
 		ce.SubKey = a.SubKey
 	} else {
-		(*c)[a.CName.GetPrincipalNameString()] = ClientEntries{
-			ReplayMap: map[time.Time]ReplayCacheEntry{
+		(*c)[a.CName.GetPrincipalNameString()] = clientEntries{
+			ReplayMap: map[time.Time]replayCacheEntry{
 				ct: {
 					PresentedTime: time.Now().UTC(),
 					SName:         sname,

+ 7 - 84
service/http.go

@@ -5,27 +5,21 @@ import (
 	"encoding/base64"
 	"fmt"
 	"github.com/jcmturner/gokrb5/GSSAPI"
-	"github.com/jcmturner/gokrb5/crypto"
-	"github.com/jcmturner/gokrb5/iana/errorcode"
-	"github.com/jcmturner/gokrb5/iana/keyusage"
 	"github.com/jcmturner/gokrb5/keytab"
-	"github.com/jcmturner/gokrb5/messages"
-	"github.com/jcmturner/gokrb5/types"
 	"log"
 	"net/http"
 	"strings"
-	"time"
 )
 
 const (
 	// The response on successful authentication always has this header. Capturing as const so we don't have marshaling and encoding overhead.
 	SPNEGO_NegTokenResp_Krb_Accept_Completed = "Negotiate oRQwEqADCgEAoQsGCSqGSIb3EgECAg=="
 	// The response on a failed authentication always has this rejection header. Capturing as const so we don't have marshaling and encoding overhead.
-	SPNEGO_NegTokenResp_Reject               = "Negotiate oQcwBaADCgEC"
+	SPNEGO_NegTokenResp_Reject = "Negotiate oQcwBaADCgEC"
 )
 
 // Kerberos SPNEGO authentication HTTP handler wrapper.
-func SPNEGOKRB5Authenticate(f http.Handler, ktab keytab.Keytab, l *log.Logger) http.Handler {
+func SPNEGOKRB5Authenticate(f http.Handler, kt keytab.Keytab, l *log.Logger) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
 		if len(s) != 2 || s[0] != "Negotiate" {
@@ -59,30 +53,14 @@ func SPNEGOKRB5Authenticate(f http.Handler, ktab keytab.Keytab, l *log.Logger) h
 			rejectSPNEGO(w, l, fmt.Sprintf("%v - MechToken does not contain an AP_REQ - KRB_AP_ERR_MSG_TYPE", r.RemoteAddr))
 			return
 		}
-		err = mt.APReq.Ticket.DecryptEncPart(ktab)
-		if err != nil {
-			rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error decrypting the service ticket provided: %v", r.RemoteAddr, err))
-			return
-		}
-		ab, err := crypto.DecryptEncPart(mt.APReq.Authenticator, mt.APReq.Ticket.DecryptedEncPart.Key, keyusage.AP_REQ_AUTHENTICATOR)
-		if err != nil {
-			rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error decrypting the authenticator provided: %v", r.RemoteAddr, err))
-			return
-		}
-		var a types.Authenticator
-		err = a.Unmarshal(ab)
-		if err != nil {
-			rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error unmarshalling the authenticator: %v", r.RemoteAddr, err))
-			return
-		}
-		if ok, err := validateAPREQ(a, mt.APReq, r); ok {
-			cnameStr := a.CName.GetPrincipalNameString()
+
+		if ok, creds, err := ValidateAPREQ(mt.APReq, kt, r.RemoteAddr); ok {
 			ctx := r.Context()
-			ctx = context.WithValue(ctx, "cname", cnameStr)
-			ctx = context.WithValue(ctx, "crealm", a.CRealm)
+			ctx = context.WithValue(ctx, "cname", creds.Username)
+			ctx = context.WithValue(ctx, "crealm", creds.Realm)
 			ctx = context.WithValue(ctx, "authenticated", true)
 			if l != nil {
-				l.Printf("%v %s@%s - SPNEGO authentication succeeded", r.RemoteAddr, cnameStr, a.CRealm)
+				l.Printf("%v %s@%s - SPNEGO authentication succeeded", r.RemoteAddr, creds.Username, creds.Realm)
 			}
 			w.Header().Set("WWW-Authenticate", SPNEGO_NegTokenResp_Krb_Accept_Completed)
 			f.ServeHTTP(w, r.WithContext(ctx))
@@ -93,61 +71,6 @@ 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, 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
-	}
-	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.
-		h, err := types.GetHostAddress(r.RemoteAddr)
-		if err != nil {
-			err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, err.Error())
-			return false, err
-		}
-		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)
-	t := time.Now().UTC()
-	// Hardcode 5 min max skew. May want to make this configurable
-	d := time.Duration(5) * time.Minute
-	if t.Sub(ct) > d || ct.Sub(t) > d {
-		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("Clock skew with client too large. Greater than %v seconds", d))
-		return false, err
-	}
-
-	// Check for replay
-	rc := GetReplayCache(d)
-	if rc.IsReplay(d, APReq.Ticket.SName, a) {
-		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "Replay detected")
-		return false, err
-	}
-
-	// Check for future tickets or invalid tickets
-	if APReq.Ticket.DecryptedEncPart.StartTime.Sub(t) > d || types.IsFlagSet(&APReq.Ticket.DecryptedEncPart.Flags, types.Invalid) {
-		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "Service ticket provided is not yet valid")
-		return false, err
-	}
-
-	// Check for expired ticket
-	if t.Sub(APReq.Ticket.DecryptedEncPart.EndTime) > d {
-		err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "Service ticket provided has expired")
-		return false, err
-	}
-	return true, nil
-}
-
 // Set the headers for a rejected SPNEGO negotiation and return an unauthorized status code.
 func rejectSPNEGO(w http.ResponseWriter, l *log.Logger, logMsg string) {
 	if l != nil {

+ 19 - 6
types/Authenticator.go

@@ -7,6 +7,7 @@ import (
 	"github.com/jcmturner/gokrb5/asn1tools"
 	"github.com/jcmturner/gokrb5/iana"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
+	"math/rand"
 	"time"
 )
 
@@ -45,12 +46,24 @@ type Authenticator struct {
 func NewAuthenticator(realm string, cname PrincipalName) Authenticator {
 	t := time.Now().UTC()
 	return Authenticator{
-		AVNO:   iana.PVNO,
-		CRealm: realm,
-		CName:  cname,
-		Cksum:  Checksum{},
-		Cusec:  int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)),
-		CTime:  t,
+		AVNO:      iana.PVNO,
+		CRealm:    realm,
+		CName:     cname,
+		Cksum:     Checksum{},
+		Cusec:     int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)),
+		CTime:     t,
+		SeqNumber: int(rand.Int31()),
+	}
+}
+
+func (a *Authenticator) GenerateSeqNumberAndSubKey(keyType, keySize int) {
+	a.SeqNumber = int(rand.Int31())
+	//Generate subkey value
+	sk := make([]byte, keySize)
+	rand.Read(sk)
+	a.SubKey = EncryptionKey{
+		KeyType:  keyType,
+		KeyValue: sk,
 	}
 }
 

+ 0 - 27
types/KerberosFlags.go

@@ -63,33 +63,6 @@ encoding of a bit string that is declared with the "NamedBit"
 notation.
 */
 
-const (
-	Reserved               = 0
-	Forwardable            = 1
-	Forwarded              = 2
-	Proxiable              = 3
-	Proxy                  = 4
-	AllowPostDate          = 5
-	MayPostDate            = 5
-	PostDated              = 6
-	Invalid                = 7
-	Renewable              = 8
-	Initial                = 9
-	PreAuthent             = 10
-	HWAuthent              = 11
-	OptHardwareAuth        = 11
-	RequestAnonymous       = 12
-	TransitedPolicyChecked = 12
-	OKAsDelegate           = 13
-	EncPARep               = 15
-	Canonicalize           = 15
-	DisableTransitedCheck  = 26
-	RenewableOK            = 27
-	EncTktInSkey           = 28
-	Renew                  = 30
-	Validate               = 31
-)
-
 func NewKrbFlags() asn1.BitString {
 	f := asn1.BitString{}
 	f.Bytes = make([]byte, 4)

+ 12 - 11
types/KerberosFlags_test.go

@@ -2,6 +2,7 @@ package types
 
 import (
 	"github.com/jcmturner/asn1"
+	"github.com/jcmturner/gokrb5/iana/flags"
 	"github.com/stretchr/testify/assert"
 	"testing"
 )
@@ -9,26 +10,26 @@ import (
 func TestKerberosFlags_SetFlag(t *testing.T) {
 	b := []byte{byte(64), byte(0), byte(0), byte(16)}
 	var f asn1.BitString
-	SetFlag(&f, Forwardable)
-	SetFlag(&f, RenewableOK)
+	SetFlag(&f, flags.Forwardable)
+	SetFlag(&f, flags.RenewableOK)
 	assert.Equal(t, b, f.Bytes, "Flag bytes not as expected")
 }
 
 func TestKerberosFlags_UnsetFlag(t *testing.T) {
 	b := []byte{byte(64), byte(0), byte(0), byte(0)}
 	var f asn1.BitString
-	SetFlag(&f, Forwardable)
-	SetFlag(&f, RenewableOK)
-	UnsetFlag(&f, RenewableOK)
+	SetFlag(&f, flags.Forwardable)
+	SetFlag(&f, flags.RenewableOK)
+	UnsetFlag(&f, flags.RenewableOK)
 	assert.Equal(t, b, f.Bytes, "Flag bytes not as expected")
 }
 
 func TestKerberosFlags_IsFlagSet(t *testing.T) {
 	var f asn1.BitString
-	SetFlag(&f, Forwardable)
-	SetFlag(&f, RenewableOK)
-	UnsetFlag(&f, Proxiable)
-	assert.True(t, IsFlagSet(&f, Forwardable))
-	assert.True(t, IsFlagSet(&f, RenewableOK))
-	assert.False(t, IsFlagSet(&f, Proxiable))
+	SetFlag(&f, flags.Forwardable)
+	SetFlag(&f, flags.RenewableOK)
+	UnsetFlag(&f, flags.Proxiable)
+	assert.True(t, IsFlagSet(&f, flags.Forwardable))
+	assert.True(t, IsFlagSet(&f, flags.RenewableOK))
+	assert.False(t, IsFlagSet(&f, flags.Proxiable))
 }