Browse Source

domain trust pac

Jonathan Turner 8 years ago
parent
commit
0d8563326d

+ 3 - 13
client/ASExchange.go

@@ -31,7 +31,7 @@ func (cl *Client) ASExchange(realm string) error {
 
 	var ASRep messages.ASRep
 
-	rb, err := cl.SendToKDC(b, ASReq.ReqBody.SName)
+	rb, err := cl.SendToKDC(b, realm)
 	if err != nil {
 		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
@@ -44,7 +44,7 @@ func (cl *Client) ASExchange(realm string) error {
 			if err != nil {
 				return krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData")
 			}
-			rb, err = cl.SendToKDC(b, ASReq.ReqBody.SName)
+			rb, err = cl.SendToKDC(b, realm)
 			if err != nil {
 				return krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
 			}
@@ -59,17 +59,7 @@ func (cl *Client) ASExchange(realm string) error {
 	if ok, err := ASRep.IsValid(cl.Config, cl.Credentials, ASReq); !ok {
 		return krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid")
 	}
-	s := &session{
-		Realm:                realm,
-		AuthTime:             ASRep.DecryptedEncPart.AuthTime,
-		EndTime:              ASRep.DecryptedEncPart.EndTime,
-		RenewTill:            ASRep.DecryptedEncPart.RenewTill,
-		TGT:                  ASRep.Ticket,
-		SessionKey:           ASRep.DecryptedEncPart.Key,
-		SessionKeyExpiration: ASRep.DecryptedEncPart.KeyExpiration,
-	}
-	cl.sessions[realm] = s
-	cl.EnableAutoSessionRenewal(s)
+	cl.AddSession(ASRep.Ticket, ASRep.DecryptedEncPart)
 	return nil
 }
 

+ 19 - 4
client/TGSExchange.go

@@ -11,8 +11,14 @@ import (
 
 // TGSExchange performs a TGS exchange to retrieve a ticket to the specified SPN.
 // The ticket retrieved is added to the client's cache.
-func (cl *Client) TGSExchange(spn types.PrincipalName, tkt messages.Ticket, sessionKey types.EncryptionKey, renewal bool) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
-	tgsReq, err = messages.NewTGSReq(cl.Credentials.CName, cl.Config, tkt, sessionKey, spn, renewal)
+func (cl *Client) TGSExchange(spn types.PrincipalName, kdcRealm string, tkt messages.Ticket, sessionKey types.EncryptionKey, renewal bool) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
+	//// Check what sessions we have for this SPN.
+	//// Will get the session to the default realm if one does not exist for requested SPN
+	//sess, err := cl.GetSessionFromPrincipalName(spn)
+	//if err != nil {
+	//	return tgsReq, tgsRep,  err
+	//}
+	tgsReq, err = messages.NewTGSReq(cl.Credentials.CName, kdcRealm, cl.Config, tkt, sessionKey, spn, renewal)
 	if err != nil {
 		return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "TGS Exchange Error: failed to generate a new TGS_REQ")
 	}
@@ -20,7 +26,7 @@ func (cl *Client) TGSExchange(spn types.PrincipalName, tkt messages.Ticket, sess
 	if err != nil {
 		return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to generate a new TGS_REQ")
 	}
-	r, err := cl.SendToKDC(b, tgsReq.ReqBody.SName)
+	r, err := cl.SendToKDC(b, kdcRealm)
 	if err != nil {
 		return tgsReq, tgsRep, krberror.Errorf(err, krberror.NetworkingError, "TGS Exchange Error: issue sending TGS_REQ to KDC")
 	}
@@ -32,6 +38,15 @@ func (cl *Client) TGSExchange(spn types.PrincipalName, tkt messages.Ticket, sess
 	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 ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok {
+			return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid")
+		}
+		// The TGS Rep contains a TGT for another domain as the service resides in that domain.
+		cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
+		realm := tgsRep.Ticket.SName.NameString[1]
+		return cl.TGSExchange(spn, realm, tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, false)
+	}
 	if ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok {
 		return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid")
 	}
@@ -64,7 +79,7 @@ func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.Encryptio
 			return tkt, skey, err
 		}
 	}
-	_, tgsRep, err := cl.TGSExchange(princ, sess.TGT, sess.SessionKey, false)
+	_, tgsRep, err := cl.TGSExchange(princ, sess.TGT.Realm, sess.TGT, sess.SessionKey, false)
 	if err != nil {
 		return tkt, skey, err
 	}

+ 1 - 1
client/cache.go

@@ -77,7 +77,7 @@ func (cl *Client) GetCachedTicket(spn string) (messages.Ticket, types.Encryption
 // RenewTicket renews a cache entry ticket
 func (cl *Client) RenewTicket(e CacheEntry) (CacheEntry, error) {
 	spn := e.Ticket.SName
-	_, tgsRep, err := cl.TGSExchange(spn, e.Ticket, e.SessionKey, true)
+	_, tgsRep, err := cl.TGSExchange(spn, e.Ticket.Realm, e.Ticket, e.SessionKey, true)
 	if err != nil {
 		return e, err
 	}

+ 25 - 3
client/client_ad_integration_test.go

@@ -7,6 +7,7 @@ import (
 	"encoding/hex"
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/jcmturner/gokrb5.v1/config"
+	"gopkg.in/jcmturner/gokrb5.v1/iana/etypeID"
 	"gopkg.in/jcmturner/gokrb5.v1/keytab"
 	"gopkg.in/jcmturner/gokrb5.v1/testdata"
 	"testing"
@@ -64,16 +65,21 @@ func TestClient_SuccessfulLogin_AD_TRUST_USER_DOMAIN(t *testing.T) {
 }
 
 func TestClient_GetServiceTicket_AD_TRUST_USER_DOMAIN(t *testing.T) {
-	b, err := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB)
+	b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB)
 	kt, _ := keytab.Parse(b)
 	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
 	c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN}
 	c.LibDefaults.DefaultRealm = "USER.GOKRB5"
 	cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt)
+	c.LibDefaults.DefaultTktEnctypes = []string{"rc4-hmac"}
+	c.LibDefaults.DefaultTktEnctypeIDs = []int{etypeID.ETypesByName["rc4-hmac"]}
+	c.LibDefaults.DefaultTGSEnctypes = []string{"rc4-hmac"}
+	c.LibDefaults.DefaultTGSEnctypeIDs = []int{etypeID.ETypesByName["rc4-hmac"]}
 	cl.WithConfig(c)
 	cl.GoKrb5Conf.DisablePAFXFast = true
 
-	err = cl.Login()
+	err := cl.Login()
+
 	if err != nil {
 		t.Fatalf("Error on login: %v\n", err)
 	}
@@ -83,5 +89,21 @@ func TestClient_GetServiceTicket_AD_TRUST_USER_DOMAIN(t *testing.T) {
 		t.Fatalf("Error getting service ticket: %v\n", err)
 	}
 	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
-	assert.Equal(t, 18, key.KeyType)
+	assert.Equal(t, etypeID.ETypesByName["rc4-hmac"], key.KeyType)
+
+	b, _ = hex.DecodeString(testdata.SYSHTTP_RESGOKRB5_AD_KEYTAB)
+	skt, _ := keytab.Parse(b)
+	err = tkt.DecryptEncPart(skt, "sysHTTP")
+	if err != nil {
+		t.Errorf("Error decrypting ticket with service keytab: %v", err)
+	}
+	isPAC, pac, err := tkt.GetPACType(skt, "sysHTTP")
+	if err != nil {
+		t.Errorf("Error getting PAC: %v", err)
+	}
+	assert.True(t, isPAC, "Did not find PAC in service ticket")
+
+	t.Logf("Res Group prefix: %+v\n", pac.KerbValidationInfo.ResourceGroupDomainSID.ToString())
+
+	t.Logf("PAC: %+v\n", pac.KerbValidationInfo.GetGroupMembershipSIDs())
 }

+ 1 - 10
client/network.go

@@ -5,9 +5,7 @@ import (
 	"encoding/binary"
 	"fmt"
 	"gopkg.in/jcmturner/gokrb5.v1/iana/errorcode"
-	"gopkg.in/jcmturner/gokrb5.v1/iana/nametype"
 	"gopkg.in/jcmturner/gokrb5.v1/messages"
-	"gopkg.in/jcmturner/gokrb5.v1/types"
 	"io"
 	"math/rand"
 	"net"
@@ -15,16 +13,9 @@ import (
 )
 
 // SendToKDC performs network actions to send data to the KDC.
-func (cl *Client) SendToKDC(b []byte, spn types.PrincipalName) ([]byte, error) {
+func (cl *Client) SendToKDC(b []byte, realm string) ([]byte, error) {
 	var rb []byte
 	var kdcs []string
-	var realm string
-	switch spn.NameType {
-	case nametype.KRB_NT_PRINCIPAL:
-		realm = cl.Config.ResolveRealm(spn.NameString[1])
-	case nametype.KRB_NT_SRV_INST:
-		realm = cl.Config.LibDefaults.DefaultRealm
-	}
 	for _, r := range cl.Config.Realms {
 		if r.Realm == realm {
 			kdcs = r.KDC

+ 18 - 3
client/session.go

@@ -1,7 +1,7 @@
 package client
 
 import (
-	"errors"
+	"fmt"
 	"gopkg.in/jcmturner/gokrb5.v1/iana/nametype"
 	"gopkg.in/jcmturner/gokrb5.v1/krberror"
 	"gopkg.in/jcmturner/gokrb5.v1/messages"
@@ -23,6 +23,21 @@ type session struct {
 	SessionKeyExpiration time.Time
 }
 
+//
+func (cl *Client) AddSession(tkt messages.Ticket, dep messages.EncKDCRepPart) {
+	s := &session{
+		Realm:                tkt.SName.NameString[1],
+		AuthTime:             dep.AuthTime,
+		EndTime:              dep.EndTime,
+		RenewTill:            dep.RenewTill,
+		TGT:                  tkt,
+		SessionKey:           dep.Key,
+		SessionKeyExpiration: dep.KeyExpiration,
+	}
+	cl.sessions[tkt.SName.NameString[1]] = s
+	cl.EnableAutoSessionRenewal(s)
+}
+
 // EnableAutoSessionRenewal turns on the automatic renewal for the client's TGT session.
 func (cl *Client) EnableAutoSessionRenewal(s *session) {
 	// TODO look into using a context here
@@ -45,7 +60,7 @@ func (cl *Client) RenewTGT(s *session) error {
 		NameType:   nametype.KRB_NT_SRV_INST,
 		NameString: []string{"krbtgt", s.Realm},
 	}
-	_, tgsRep, err := cl.TGSExchange(spn, s.TGT, s.SessionKey, true)
+	_, tgsRep, err := cl.TGSExchange(spn, s.TGT.Realm, s.TGT, s.SessionKey, true)
 	if err != nil {
 		return krberror.Errorf(err, krberror.KRBMsgError, "Error renewing TGT")
 	}
@@ -80,7 +95,7 @@ func (cl *Client) GetSessionFromRealm(realm string) (sess *session, err error) {
 	if !ok {
 		sess, ok = cl.sessions[cl.Config.LibDefaults.DefaultRealm]
 		if !ok {
-			err = errors.New("client does not have a session, login first")
+			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
 		}
 	}

+ 3 - 0
crypto/crypto.go

@@ -59,6 +59,9 @@ func GetChksumEtype(id int) (etype.EType, error) {
 	case chksumtype.KERB_CHECKSUM_HMAC_MD5:
 		var et RC4HMAC
 		return et, nil
+	case chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED:
+		var et RC4HMAC
+		return et, nil
 	default:
 		return nil, fmt.Errorf("Unknown or unsupported checksum type: %d", id)
 	}

+ 3 - 3
examples/example-AD.go

@@ -73,8 +73,8 @@ func httpServer() *httptest.Server {
 func testAppHandler(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
 	fmt.Fprint(w, "<html>\n<p><h1>TEST.GOKRB5 Handler</h1></p>\n")
-	if validuser, ok := ctx.Value(service.CTXKey_Authenticated).(bool); ok && validuser {
-		if creds, ok := ctx.Value(service.CTXKey_Credentials).(credentials.Credentials); ok {
+	if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser {
+		if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok {
 			fmt.Fprintf(w, "<ul><li>Authenticed user: %s</li>\n", creds.UserName())
 			fmt.Fprintf(w, "<li>User's realm: %s</li>\n", creds.Domain())
 			fmt.Fprint(w, "<li>Authz Attributes (Group Memberships):</li><ul>\n")
@@ -82,7 +82,7 @@ func testAppHandler(w http.ResponseWriter, r *http.Request) {
 				fmt.Fprintf(w, "<li>%v</li>\n", s)
 			}
 			fmt.Fprint(w, "</ul>\n")
-			if ADCreds, ok := creds.Attributes[credentials.AttributeKey_ADCredentials].(credentials.ADCredentials); ok {
+			if ADCreds, ok := creds.Attributes[credentials.AttributeKeyADCredentials].(credentials.ADCredentials); ok {
 				// Now access the fields of the ADCredentials struct. For example:
 				fmt.Fprintf(w, "<li>EffectiveName: %v</li>\n", ADCreds.EffectiveName)
 				fmt.Fprintf(w, "<li>FullName: %v</li>\n", ADCreds.FullName)

+ 19 - 19
messages/KDCRep.go

@@ -265,35 +265,35 @@ func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error {
 // IsValid checks the validity of the TGS_REP message.
 func (k *TGSRep) IsValid(cfg *config.Config, tgsReq TGSReq) (bool, error) {
 	if k.CName.NameType != tgsReq.ReqBody.CName.NameType || k.CName.NameString == nil {
-		return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
+		return false, krberror.NewErrorf(krberror.KRBMsgError, "CName type in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
 	}
 	for i := range k.CName.NameString {
 		if k.CName.NameString[i] != tgsReq.ReqBody.CName.NameString[i] {
 			return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
 		}
 	}
-	if k.CRealm != tgsReq.ReqBody.Realm {
-		return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.CRealm)
+	if k.Ticket.Realm != tgsReq.ReqBody.Realm {
+		return false, krberror.NewErrorf(krberror.KRBMsgError, "Realm in response ticket does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.Ticket.Realm)
 	}
 	if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce {
 		return false, krberror.NewErrorf(krberror.KRBMsgError, "Possible replay attack, nonce in response does not match that in request")
 	}
-	if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
-		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName)
-	}
-	for i := range k.Ticket.SName.NameString {
-		if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
-			return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName)
-		}
-	}
-	if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
-		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
-	}
-	for i := range k.CName.NameString {
-		if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
-			return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
-		}
-	}
+	//if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
+	//	return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName)
+	//}
+	//for i := range k.Ticket.SName.NameString {
+	//	if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
+	//		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName)
+	//	}
+	//}
+	//if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
+	//	return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
+	//}
+	//for i := range k.DecryptedEncPart.SName.NameString {
+	//	if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
+	//		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
+	//	}
+	//}
 	if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm {
 		return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
 	}

+ 4 - 5
messages/KDCReq.go

@@ -128,7 +128,7 @@ func NewASReq(realm string, c *config.Config, cname types.PrincipalName) (ASReq,
 }
 
 // NewTGSReq generates a new KRB_TGS_REQ struct.
-func NewTGSReq(cname types.PrincipalName, c *config.Config, tkt Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
+func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tkt Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
 	nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
 	if err != nil {
 		return TGSReq{}, err
@@ -140,12 +140,11 @@ func NewTGSReq(cname types.PrincipalName, c *config.Config, tkt Ticket, sessionK
 			MsgType: msgtype.KRB_TGS_REQ,
 			ReqBody: KDCReqBody{
 				KDCOptions: types.NewKrbFlags(),
-				Realm:      c.ResolveRealm(spn.NameString[len(spn.NameString)-1]),
+				Realm:      kdcRealm,
 				SName:      spn,
 				Till:       t.Add(c.LibDefaults.TicketLifetime),
-				//Till:  t.Add(time.Duration(2) * time.Minute),
-				Nonce: int(nonce.Int64()),
-				EType: c.LibDefaults.DefaultTGSEnctypeIDs,
+				Nonce:      int(nonce.Int64()),
+				EType:      c.LibDefaults.DefaultTGSEnctypeIDs,
 			},
 			Renewal: renewal,
 		},

+ 3 - 4
pac/kerb_validation_info.go

@@ -225,11 +225,10 @@ func (k *KerbValidationInfo) Unmarshal(b []byte) (err error) {
 
 // GetGroupMembershipSIDs returns a slice of strings containing the group membership SIDs found in the PAC.
 func (k *KerbValidationInfo) GetGroupMembershipSIDs() []string {
-	gSize := len(k.GroupIDs) + len(k.ExtraSIDs)
-	g := make([]string, gSize, gSize)
+	var g []string
 	lSID := k.LogonDomainID.ToString()
 	for i := range k.GroupIDs {
-		g[i] = fmt.Sprintf("%s-%d", lSID, k.GroupIDs[i].RelativeID)
+		g = append(g, fmt.Sprintf("%s-%d", lSID, k.GroupIDs[i].RelativeID))
 	}
 	for _, s := range k.ExtraSIDs {
 		var exists = false
@@ -245,7 +244,7 @@ func (k *KerbValidationInfo) GetGroupMembershipSIDs() []string {
 	}
 	for _, r := range k.ResourceGroupIDs {
 		var exists = false
-		s := fmt.Sprintf("%s-%d", lSID, r)
+		s := fmt.Sprintf("%s-%d", k.ResourceGroupDomainSID.ToString(), r.RelativeID)
 		for _, es := range g {
 			if es == s {
 				exists = true

+ 7 - 0
pac/kerb_validation_info_test.go

@@ -283,4 +283,11 @@ func TestKerbValidationInfo_Unmarshal_DomainTrust(t *testing.T) {
 		{RelativeID: 1108, Attributes: 536870919},
 	}
 	assert.Equal(t, rgids, k.ResourceGroupIDs, "ResourceGroupIDs not as expected")
+	groupSids := []string{"S-1-5-21-2284869408-3503417140-1141177250-1110",
+		"S-1-5-21-2284869408-3503417140-1141177250-513",
+		"S-1-5-21-2284869408-3503417140-1141177250-1109",
+		"S-1-18-1",
+		"S-1-5-21-3062750306-1230139592-1973306805-1107",
+		"S-1-5-21-3062750306-1230139592-1973306805-1108"}
+	assert.Equal(t, groupSids, k.GetGroupMembershipSIDs(), "GroupMembershipSIDs not as expected")
 }