Procházet zdrojové kódy

Support for cross-realm auth with direct trust

Daniel Potapov před 8 roky
rodič
revize
0b1e8dbf33
4 změnil soubory, kde provedl 51 přidání a 21 odebrání
  1. 5 4
      client/TGSExchange.go
  2. 8 1
      client/client.go
  3. 33 12
      client/session.go
  4. 5 4
      messages/KDCReq.go

+ 5 - 4
client/TGSExchange.go

@@ -1,12 +1,13 @@
 package client
 
 import (
+	"strings"
+	"time"
+
 	"gopkg.in/jcmturner/gokrb5.v2/iana/nametype"
 	"gopkg.in/jcmturner/gokrb5.v2/krberror"
 	"gopkg.in/jcmturner/gokrb5.v2/messages"
 	"gopkg.in/jcmturner/gokrb5.v2/types"
-	"strings"
-	"time"
 )
 
 // TGSExchange performs a TGS exchange to retrieve a ticket to the specified SPN.
@@ -38,7 +39,7 @@ func (cl *Client) TGSExchange(spn types.PrincipalName, kdcRealm string, tkt mess
 	if err != nil {
 		return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP")
 	}
-	if tgsRep.Ticket.SName.NameType == nametype.KRB_NT_SRV_INST {
+	if tgsRep.Ticket.SName.NameType == nametype.KRB_NT_SRV_INST && !tgsRep.Ticket.SName.Equal(spn) {
 		if referral > 5 {
 			return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of referrals exceeded")
 		}
@@ -84,7 +85,7 @@ func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.Encryptio
 			return tkt, skey, err
 		}
 	}
-	_, tgsRep, err := cl.TGSExchange(princ, sess.TGT.Realm, sess.TGT, sess.SessionKey, false, 0)
+	_, tgsRep, err := cl.TGSExchange(princ, sess.Realm, sess.TGT, sess.SessionKey, false, 0)
 	if err != nil {
 		return tkt, skey, err
 	}

+ 8 - 1
client/client.go

@@ -4,6 +4,7 @@ package client
 import (
 	"errors"
 	"fmt"
+
 	"gopkg.in/jcmturner/gokrb5.v2/config"
 	"gopkg.in/jcmturner/gokrb5.v2/credentials"
 	"gopkg.in/jcmturner/gokrb5.v2/crypto"
@@ -33,6 +34,7 @@ type Config struct {
 }
 
 // NewClientWithPassword creates a new client from a password credential.
+// Set the realm to empty string to use the default realm from config.
 func NewClientWithPassword(username, realm, password string) Client {
 	creds := credentials.NewCredentials(username, realm)
 	return Client{
@@ -115,6 +117,11 @@ func NewClientFromCCache(c credentials.CCache) (Client, error) {
 // WithConfig sets the Kerberos configuration for the client.
 func (cl *Client) WithConfig(cfg *config.Config) *Client {
 	cl.Config = cfg
+
+	// Use the default Realm if user did not specified it when creating a client
+	if cl.Credentials.Realm == "" {
+		cl.Credentials.Realm = cl.Config.LibDefaults.DefaultRealm
+	}
 	return cl
 }
 
@@ -192,5 +199,5 @@ func (cl *Client) IsConfigured() (bool, error) {
 
 // Login the client with the KDC via an AS exchange.
 func (cl *Client) Login() error {
-	return cl.ASExchange(cl.Config.LibDefaults.DefaultRealm, 0)
+	return cl.ASExchange(cl.Credentials.Realm, 0)
 }

+ 33 - 12
client/session.go

@@ -2,12 +2,13 @@ package client
 
 import (
 	"fmt"
+	"sync"
+	"time"
+
 	"gopkg.in/jcmturner/gokrb5.v2/iana/nametype"
 	"gopkg.in/jcmturner/gokrb5.v2/krberror"
 	"gopkg.in/jcmturner/gokrb5.v2/messages"
 	"gopkg.in/jcmturner/gokrb5.v2/types"
-	"sync"
-	"time"
 )
 
 // Sessions keyed on the realm name
@@ -96,20 +97,40 @@ func (cl *Client) updateSession(s *session) error {
 	return nil
 }
 
-// GetSessionFromRealm returns the session for the realm provided.
-func (cl *Client) GetSessionFromRealm(realm string) (sess *session, err error) {
-	var ok bool
+func (cl *Client) getSessionFromRemoteRealm(realm string) (*session, error) {
+	cl.sessions.mux.RLock()
+	sess, ok := cl.sessions.Entries[cl.Credentials.Realm]
+	cl.sessions.mux.RUnlock()
+	if !ok {
+		return nil, fmt.Errorf("client does not have a session for realm %s, login first", cl.Credentials.Realm)
+	}
+
+	spn := types.PrincipalName{
+		NameType:   nametype.KRB_NT_SRV_INST,
+		NameString: []string{"krbtgt", realm},
+	}
+
+	_, tgsRep, err := cl.TGSExchange(spn, cl.Credentials.Realm, sess.TGT, sess.SessionKey, false, 0)
+	if err != nil {
+		return nil, err
+	}
+	cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
+
 	cl.sessions.mux.RLock()
 	defer cl.sessions.mux.RUnlock()
-	sess, ok = cl.sessions.Entries[realm]
+	return cl.sessions.Entries[realm], nil
+}
+
+// GetSessionFromRealm returns the session for the realm provided.
+func (cl *Client) GetSessionFromRealm(realm string) (*session, error) {
+	cl.sessions.mux.RLock()
+	sess, ok := cl.sessions.Entries[realm]
+	cl.sessions.mux.RUnlock()
 	if !ok {
-		sess, ok = cl.sessions.Entries[cl.Config.LibDefaults.DefaultRealm]
-		if !ok {
-			err = fmt.Errorf("client does not have a session for realm %s or for the default realm %s, login first", realm, cl.Config.LibDefaults.DefaultRealm)
-			return
-		}
+		// Try to request TGT from trusted remote Realm
+		return cl.getSessionFromRemoteRealm(realm)
 	}
-	return
+	return sess, nil
 }
 
 // GetSessionFromPrincipalName returns the session for the realm of the principal provided.

+ 5 - 4
messages/KDCReq.go

@@ -6,6 +6,10 @@ package messages
 import (
 	"crypto/rand"
 	"fmt"
+	"math"
+	"math/big"
+	"time"
+
 	"github.com/jcmturner/asn1"
 	"gopkg.in/jcmturner/gokrb5.v2/asn1tools"
 	"gopkg.in/jcmturner/gokrb5.v2/config"
@@ -19,9 +23,6 @@ import (
 	"gopkg.in/jcmturner/gokrb5.v2/iana/patype"
 	"gopkg.in/jcmturner/gokrb5.v2/krberror"
 	"gopkg.in/jcmturner/gokrb5.v2/types"
-	"math"
-	"math/big"
-	"time"
 )
 
 type marshalKDCReq struct {
@@ -169,7 +170,7 @@ func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tkt
 		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renew)
 		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
 	}
-	auth, err := types.NewAuthenticator(c.LibDefaults.DefaultRealm, cname)
+	auth, err := types.NewAuthenticator(tkt.Realm, cname)
 	if err != nil {
 		return a, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
 	}