Browse Source

service exported review

Jonathan Turner 8 years ago
parent
commit
f232078697
3 changed files with 62 additions and 73 deletions
  1. 32 12
      service/authenticator.go
  2. 30 30
      service/cache.go
  3. 0 31
      service/config.go

+ 32 - 12
service/authenticator.go

@@ -19,10 +19,31 @@ import (
 type SPNEGOAuthenticator struct {
 	SPNEGOHeaderValue string
 	ClientAddr        string
-	Config            *SPNEGOConfig
+	Config            *Config
 }
 
-type SPNEGOConfig struct {
+// Config for service side implementation
+//
+// Keytab (mandatory) - keytab for the service user
+//
+// KeytabPrincipal (optional) - keytab principal override for the service.
+// The service looks for this principal in the keytab to use to decrypt tickets.
+// If "" is passed as KeytabPrincipal then the principal will be automatically derived
+// from the service name (SName) and realm in the ticket the service is trying to decrypt.
+// This is often sufficient if you create the SPN in MIT KDC with: /usr/sbin/kadmin.local -q "add_principal HTTP/<fqdn>"
+// When Active Directory is used for the KDC this may need to be the account name you have set the SPN against
+// (setspn.exe -a "HTTP/<fqdn>" <account name>)
+// If you are unsure run:
+//
+// klist -k <service's keytab file>
+//
+// and use the value from the Principal column for the keytab entry the service should use.
+//
+// RequireHostAddr - require that the kerberos ticket must include client host IP addresses and one must match the client making the request.
+// This is controled in the client config with the noaddresses option (http://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html).
+//
+// DisablePACDecoding - if set to true decoding of the Microsoft PAC will be disabled.
+type Config struct {
 	Keytab             keytab.Keytab
 	ServicePrincipal   string
 	RequireHostAddr    bool
@@ -34,11 +55,11 @@ func NewSPNEGOAuthenticator(kt keytab.Keytab) (a SPNEGOAuthenticator) {
 	return
 }
 
-func NewSPNEGOConfig(kt keytab.Keytab) *SPNEGOConfig {
-	return &SPNEGOConfig{Keytab: kt}
+func NewSPNEGOConfig(kt keytab.Keytab) *Config {
+	return &Config{Keytab: kt}
 }
 
-func (c *SPNEGOConfig) Authenticate(neg, addr string) (i goidentity.Identity, ok bool, err error) {
+func (c *Config) Authenticate(neg, addr string) (i goidentity.Identity, ok bool, err error) {
 	a := SPNEGOAuthenticator{
 		SPNEGOHeaderValue: neg,
 		ClientAddr:        addr,
@@ -92,14 +113,13 @@ func (a SPNEGOAuthenticator) Mechanism() string {
 // KRB5BasicAuthenticator implements gopkg.in/jcmturner/goidentity.v3.Authenticator interface.
 // It takes username and password so can be used for basic authentication.
 type KRB5BasicAuthenticator struct {
+	SPN              string
 	BasicHeaderValue string
+	ServiceConfig    Config
+	ClientConfig     *config.Config
 	realm            string
 	username         string
 	password         string
-	ServiceKeytab    *keytab.Keytab
-	ServiceAccount   string
-	Config           *config.Config
-	SPN              string
 }
 
 // Authenticate and return the identity. The boolean indicates if the authentication was successful.
@@ -110,7 +130,7 @@ func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool,
 		return
 	}
 	cl := client.NewClientWithPassword(a.username, a.realm, a.password)
-	cl.WithConfig(a.Config)
+	cl.WithConfig(a.ClientConfig)
 	err = cl.Login()
 	if err != nil {
 		// Username and/or password could be wrong
@@ -122,14 +142,14 @@ func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool,
 		err = fmt.Errorf("could not get service ticket: %v", err)
 		return
 	}
-	err = tkt.DecryptEncPart(*a.ServiceKeytab, a.ServiceAccount)
+	err = tkt.DecryptEncPart(a.ServiceConfig.Keytab, a.ServiceConfig.ServicePrincipal)
 	if err != nil {
 		err = fmt.Errorf("could not decrypt service ticket: %v", err)
 		return
 	}
 	cl.Credentials.SetAuthTime(time.Now().UTC())
 	cl.Credentials.SetAuthenticated(true)
-	isPAC, pac, err := tkt.GetPACType(*a.ServiceKeytab, a.ServiceAccount)
+	isPAC, pac, err := tkt.GetPACType(a.ServiceConfig.Keytab, a.ServiceConfig.ServicePrincipal)
 	if isPAC && err != nil {
 		err = fmt.Errorf("error processing PAC: %v", err)
 		return

+ 30 - 30
service/cache.go

@@ -31,28 +31,28 @@ recently seen authenticators.*/
 
 // Cache for tickets received from clients keyed by fully qualified client name. Used to track replay of tickets.
 type Cache struct {
-	Entries map[string]clientEntries
+	entries map[string]clientEntries
 	mux     sync.RWMutex
 }
 
 // clientEntries holds entries of client details sent to the service.
 type clientEntries struct {
-	ReplayMap map[time.Time]replayCacheEntry
-	SeqNumber int64
-	SubKey    types.EncryptionKey
+	replayMap map[time.Time]replayCacheEntry
+	seqNumber int64
+	subKey    types.EncryptionKey
 }
 
 // Cache entry tracking client time values of tickets sent to the service.
 type replayCacheEntry struct {
-	PresentedTime time.Time
-	SName         types.PrincipalName
-	CTime         time.Time // This combines the ticket's CTime and Cusec
+	presentedTime time.Time
+	sName         types.PrincipalName
+	cTime         time.Time // This combines the ticket's CTime and Cusec
 }
 
 func (c *Cache) getClientEntries(cname types.PrincipalName) (clientEntries, bool) {
 	c.mux.RLock()
 	defer c.mux.RUnlock()
-	ce, ok := c.Entries[cname.GetPrincipalNameString()]
+	ce, ok := c.entries[cname.GetPrincipalNameString()]
 	return ce, ok
 }
 
@@ -60,7 +60,7 @@ func (c *Cache) getClientEntry(cname types.PrincipalName, t time.Time) (replayCa
 	if ce, ok := c.getClientEntries(cname); ok {
 		c.mux.RLock()
 		defer c.mux.RUnlock()
-		if e, ok := ce.ReplayMap[t]; ok {
+		if e, ok := ce.replayMap[t]; ok {
 			return e, true
 		}
 	}
@@ -76,7 +76,7 @@ func GetReplayCache(d time.Duration) *Cache {
 	// Create a singleton of the ReplayCache and start a background thread to regularly clean out old entries
 	once.Do(func() {
 		replayCache = Cache{
-			Entries: make(map[string]clientEntries),
+			entries: make(map[string]clientEntries),
 		}
 		go func() {
 			for {
@@ -95,26 +95,26 @@ func (c *Cache) AddEntry(sname types.PrincipalName, a types.Authenticator) {
 	if ce, ok := c.getClientEntries(a.CName); ok {
 		c.mux.Lock()
 		defer c.mux.Unlock()
-		ce.ReplayMap[ct] = replayCacheEntry{
-			PresentedTime: time.Now().UTC(),
-			SName:         sname,
-			CTime:         ct,
+		ce.replayMap[ct] = replayCacheEntry{
+			presentedTime: time.Now().UTC(),
+			sName:         sname,
+			cTime:         ct,
 		}
-		ce.SeqNumber = a.SeqNumber
-		ce.SubKey = a.SubKey
+		ce.seqNumber = a.SeqNumber
+		ce.subKey = a.SubKey
 	} else {
 		c.mux.Lock()
 		defer c.mux.Unlock()
-		c.Entries[a.CName.GetPrincipalNameString()] = clientEntries{
-			ReplayMap: map[time.Time]replayCacheEntry{
+		c.entries[a.CName.GetPrincipalNameString()] = clientEntries{
+			replayMap: map[time.Time]replayCacheEntry{
 				ct: {
-					PresentedTime: time.Now().UTC(),
-					SName:         sname,
-					CTime:         ct,
+					presentedTime: time.Now().UTC(),
+					sName:         sname,
+					cTime:         ct,
 				},
 			},
-			SeqNumber: a.SeqNumber,
-			SubKey:    a.SubKey,
+			seqNumber: a.SeqNumber,
+			subKey:    a.SubKey,
 		}
 	}
 }
@@ -123,14 +123,14 @@ func (c *Cache) AddEntry(sname types.PrincipalName, a types.Authenticator) {
 func (c *Cache) ClearOldEntries(d time.Duration) {
 	c.mux.Lock()
 	defer c.mux.Unlock()
-	for ke, ce := range c.Entries {
-		for k, e := range ce.ReplayMap {
-			if time.Now().UTC().Sub(e.PresentedTime) > d {
-				delete(ce.ReplayMap, k)
+	for ke, ce := range c.entries {
+		for k, e := range ce.replayMap {
+			if time.Now().UTC().Sub(e.presentedTime) > d {
+				delete(ce.replayMap, k)
 			}
 		}
-		if len(ce.ReplayMap) == 0 {
-			delete(c.Entries, ke)
+		if len(ce.replayMap) == 0 {
+			delete(c.entries, ke)
 		}
 	}
 }
@@ -139,7 +139,7 @@ func (c *Cache) ClearOldEntries(d time.Duration) {
 func (c *Cache) IsReplay(sname types.PrincipalName, a types.Authenticator) bool {
 	ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
 	if e, ok := c.getClientEntry(a.CName, ct); ok {
-		if e.SName.Equal(sname) {
+		if e.sName.Equal(sname) {
 			return true
 		}
 	}

+ 0 - 31
service/config.go

@@ -1,31 +0,0 @@
-package service
-
-import "gopkg.in/jcmturner/gokrb5.v5/keytab"
-
-// Config for service side implementation
-//
-// Keytab (mandatory) - keytab for the service user
-//
-// KeytabPrincipal (optional) - keytab principal override for the service.
-// The service looks for this principal in the keytab to use to decrypt tickets.
-// If "" is passed as KeytabPrincipal then the principal will be automatically derived
-// from the service name (SName) and realm in the ticket the service is trying to decrypt.
-// This is often sufficient if you create the SPN in MIT KDC with: /usr/sbin/kadmin.local -q "add_principal HTTP/<fqdn>"
-// When Active Directory is used for the KDC this may need to be the account name you have set the SPN against
-// (setspn.exe -a "HTTP/<fqdn>" <account name>)
-// If you are unsure run:
-//
-// klist -k <service's keytab file>
-//
-// and use the value from the Principal column for the keytab entry the service should use.
-//
-// RequireHostAddr - require that the kerberos ticket must include client host IP addresses and one must match the client making the request.
-// This is controled in the client config with the noaddresses option (http://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html).
-//
-// DisablePACDecoding - if set to true decoding of the Microsoft PAC will be disabled.
-type Config struct {
-	Keytab             keytab.Keytab
-	KeytabPrincipal    string
-	RequireHostAddr    bool
-	DisablePACDecoding bool
-}