Explorar o código

enable client to use password for pre-auth
fix krberror output

Jonathan Turner %!s(int64=8) %!d(string=hai) anos
pai
achega
6aa472fbcf
Modificáronse 5 ficheiros con 51 adicións e 25 borrados
  1. 5 5
      client/ASExchange.go
  2. 29 1
      client/client.go
  3. 3 17
      client/client_integration_test.go
  4. 2 0
      crypto/crypto.go
  5. 12 2
      krberror/error.go

+ 5 - 5
client/ASExchange.go

@@ -18,7 +18,7 @@ func (cl *Client) ASExchange() error {
 		return errors.New("Client is not configured correctly")
 		return errors.New("Client is not configured correctly")
 	}
 	}
 	ASReq := messages.NewASReq(cl.Config, cl.Credentials.CName)
 	ASReq := messages.NewASReq(cl.Config, cl.Credentials.CName)
-	err := setPAData(cl, &ASReq)
+	err := setPAData(cl, messages.KRBError{}, &ASReq)
 	if err != nil {
 	if err != nil {
 		return krberror.Errorf(err, krberror.KRBMSG_ERROR, "AS Exchange Error: failed setting AS_REQ PAData")
 		return krberror.Errorf(err, krberror.KRBMSG_ERROR, "AS Exchange Error: failed setting AS_REQ PAData")
 	}
 	}
@@ -34,7 +34,7 @@ func (cl *Client) ASExchange() error {
 		if e, ok := err.(messages.KRBError); ok && e.ErrorCode == errorcode.KDC_ERR_PREAUTH_REQUIRED {
 		if e, ok := err.(messages.KRBError); ok && e.ErrorCode == errorcode.KDC_ERR_PREAUTH_REQUIRED {
 			// 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.Assume_PA_ENC_TIMESTAMP_Required = true
 			cl.GoKrb5Conf.Assume_PA_ENC_TIMESTAMP_Required = true
-			err = setPAData(cl, &ASReq)
+			err = setPAData(cl, e, &ASReq)
 			if err != nil {
 			if err != nil {
 				return krberror.Errorf(err, krberror.KRBMSG_ERROR, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
 				return krberror.Errorf(err, krberror.KRBMSG_ERROR, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
 			}
 			}
@@ -68,7 +68,7 @@ func (cl *Client) ASExchange() error {
 	return nil
 	return nil
 }
 }
 
 
-func setPAData(cl *Client, ASReq *messages.ASReq) error {
+func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) error {
 	if !cl.GoKrb5Conf.Disable_PA_FX_FAST {
 	if !cl.GoKrb5Conf.Disable_PA_FX_FAST {
 		pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
 		pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
 		ASReq.PAData = append(ASReq.PAData, pa)
 		ASReq.PAData = append(ASReq.PAData, pa)
@@ -83,9 +83,9 @@ func setPAData(cl *Client, ASReq *messages.ASReq) error {
 		if err != nil {
 		if err != nil {
 			return krberror.Errorf(err, krberror.ENCRYPTING_ERROR, "Error creating etype")
 			return krberror.Errorf(err, krberror.ENCRYPTING_ERROR, "Error creating etype")
 		}
 		}
-		key, err := cl.Credentials.Keytab.GetEncryptionKey(cl.Credentials.CName.NameString, cl.Config.LibDefaults.Default_realm, 1, etype.GetETypeID())
+		key, err := cl.Key(etype, krberr)
 		if err != nil {
 		if err != nil {
-			return krberror.Errorf(err, krberror.ENCRYPTING_ERROR, "Error getting key from keytab in credentials")
+			return krberror.Errorf(err, krberror.ENCRYPTING_ERROR, "Error getting key from credentials")
 		}
 		}
 		paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1)
 		paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1)
 		if err != nil {
 		if err != nil {

+ 29 - 1
client/client.go

@@ -6,10 +6,14 @@ import (
 	"fmt"
 	"fmt"
 	"github.com/jcmturner/gokrb5/config"
 	"github.com/jcmturner/gokrb5/config"
 	"github.com/jcmturner/gokrb5/credentials"
 	"github.com/jcmturner/gokrb5/credentials"
+	"github.com/jcmturner/gokrb5/crypto"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+	"github.com/jcmturner/gokrb5/iana/errorcode"
 	"github.com/jcmturner/gokrb5/iana/nametype"
 	"github.com/jcmturner/gokrb5/iana/nametype"
 	"github.com/jcmturner/gokrb5/keytab"
 	"github.com/jcmturner/gokrb5/keytab"
 	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/types"
 	"github.com/jcmturner/gokrb5/types"
+	"os"
 )
 )
 
 
 // Client side configuration and state.
 // Client side configuration and state.
@@ -119,6 +123,28 @@ func (cl *Client) WithPassword(password string) *Client {
 	return cl
 	return cl
 }
 }
 
 
+// Key returns a key for the client. Preferably from a keytab and then generated from the password.
+// The KRBError would have been returned from the KDC and must be of type KDC_ERR_PREAUTH_REQUIRED.
+// If a KRBError is not available pass nil and a key will be returned from the credentials keytab.
+func (c *Client) Key(etype etype.EType, krberr messages.KRBError) (types.EncryptionKey, error) {
+	if c.Credentials.HasKeytab() && etype != nil {
+		return c.Credentials.Keytab.GetEncryptionKey(c.Credentials.CName.NameString, c.Credentials.Realm, 0, etype.GetETypeID())
+	} else if c.Credentials.HasPassword() {
+		if krberr.ErrorCode == errorcode.KDC_ERR_PREAUTH_REQUIRED {
+			var pas types.PADataSequence
+			err := pas.Unmarshal(krberr.EData)
+			if err != nil {
+				return types.EncryptionKey{}, fmt.Errorf("Could not get PAData from KRBError to generate key from password: %v", err)
+			}
+			key, _, err := crypto.GetKeyFromPassword(c.Credentials.Password, krberr.CName, krberr.CRealm, etype.GetETypeID(), pas)
+			return key, err
+		}
+		key, _, err := crypto.GetKeyFromPassword(c.Credentials.Password, c.Credentials.CName, c.Credentials.Realm, etype.GetETypeID(), types.PADataSequence{})
+		return key, err
+	}
+	return types.EncryptionKey{}, errors.New("Credential has neither keytab or password to generate key.")
+}
+
 // LoadConfig loads the Kerberos configuration for the client from file path specified.
 // LoadConfig loads the Kerberos configuration for the client from file path specified.
 func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
 func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
 	cfg, err := config.Load(cfgPath)
 	cfg, err := config.Load(cfgPath)
@@ -131,13 +157,15 @@ func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
 
 
 // IsConfigured indicates if the client has the values required set.
 // IsConfigured indicates if the client has the values required set.
 func (cl *Client) IsConfigured() bool {
 func (cl *Client) IsConfigured() bool {
-	if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
+	if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() && cl.session.AuthTime.IsZero() {
 		return false
 		return false
 	}
 	}
 	if cl.Credentials.Username == "" {
 	if cl.Credentials.Username == "" {
 		return false
 		return false
 	}
 	}
 	if cl.Config.LibDefaults.Default_realm == "" {
 	if cl.Config.LibDefaults.Default_realm == "" {
+		fmt.Fprintf(os.Stderr, "hello: %v\n", cl)
+
 		return false
 		return false
 	}
 	}
 	for _, r := range cl.Config.Realms {
 	for _, r := range cl.Config.Realms {

+ 3 - 17
client/client_integration_test.go

@@ -281,21 +281,7 @@ func TestNewClientFromCCache(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Error creating client from CCache: %v", err)
 		t.Fatalf("Error creating client from CCache: %v", err)
 	}
 	}
-	//c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
-	//cl.WithConfig(c)
-	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")
+	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
+	cl.WithConfig(c)
+	assert.True(t, cl.IsConfigured(), "Client was not configured from CCache")
 }
 }

+ 2 - 0
crypto/crypto.go

@@ -9,6 +9,7 @@ import (
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"github.com/jcmturner/gokrb5/iana/patype"
 	"github.com/jcmturner/gokrb5/iana/patype"
 	"github.com/jcmturner/gokrb5/types"
 	"github.com/jcmturner/gokrb5/types"
+	"os"
 )
 )
 
 
 // GetEtype returns an instances of the required etype struct for the etype ID.
 // GetEtype returns an instances of the required etype struct for the etype ID.
@@ -74,6 +75,7 @@ func GetKeyFromPassword(passwd string, cname types.PrincipalName, realm string,
 				continue
 				continue
 			}
 			}
 			salt = string(pa.PADataValue)
 			salt = string(pa.PADataValue)
+			fmt.Fprintf(os.Stderr, "1Salt: %v\n", salt)
 		case patype.PA_ETYPE_INFO:
 		case patype.PA_ETYPE_INFO:
 			if paID > pa.PADataType {
 			if paID > pa.PADataType {
 				continue
 				continue

+ 12 - 2
krberror/error.go

@@ -43,16 +43,26 @@ func NewKrberror(et, s string) Krberror {
 // Errorf appends to or creates a new Krberror.
 // Errorf appends to or creates a new Krberror.
 func Errorf(err error, et, format string, a ...interface{}) Krberror {
 func Errorf(err error, et, format string, a ...interface{}) Krberror {
 	if e, ok := err.(Krberror); ok {
 	if e, ok := err.(Krberror); ok {
+		if a == nil {
+			e.EText = append([]string{fmt.Sprintf("%s: "+format, et)}, e.EText...)
+			return e
+		}
 		e.EText = append([]string{fmt.Sprintf("%s: "+format, et, a)}, e.EText...)
 		e.EText = append([]string{fmt.Sprintf("%s: "+format, et, a)}, e.EText...)
 		return e
 		return e
 	}
 	}
-	return NewErrorf(et, format+": %v", a, err)
+	return NewErrorf(et, format+": %s", a, err)
 }
 }
 
 
 // NewErrorf creates a new Krberror from a formatted string.
 // NewErrorf creates a new Krberror from a formatted string.
 func NewErrorf(et, format string, a ...interface{}) Krberror {
 func NewErrorf(et, format string, a ...interface{}) Krberror {
+	var s string
+	if a == nil {
+		s = fmt.Sprintf("%s: %s", et, format)
+	} else {
+		s = fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a))
+	}
 	return Krberror{
 	return Krberror{
 		RootCause: et,
 		RootCause: et,
-		EText:     []string{fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a))},
+		EText:     []string{s},
 	}
 	}
 }
 }