diff --git a/src/internal/syscall/windows/security_windows.go b/src/internal/syscall/windows/security_windows.go index e528744caa..aed04c61c4 100644 --- a/src/internal/syscall/windows/security_windows.go +++ b/src/internal/syscall/windows/security_windows.go @@ -175,3 +175,38 @@ func GetUserName(format uint32) (string, error) { } } } + +// getTokenInfo retrieves a specified type of information about an access token. +func getTokenInfo(t syscall.Token, class uint32, initSize int) (unsafe.Pointer, error) { + n := uint32(initSize) + for { + b := make([]byte, n) + e := syscall.GetTokenInformation(t, class, &b[0], uint32(len(b)), &n) + if e == nil { + return unsafe.Pointer(&b[0]), nil + } + if e != syscall.ERROR_INSUFFICIENT_BUFFER { + return nil, e + } + if n <= uint32(len(b)) { + return nil, e + } + } +} + +type TOKEN_GROUPS struct { + GroupCount uint32 + Groups [1]SID_AND_ATTRIBUTES +} + +func (g *TOKEN_GROUPS) AllGroups() []SID_AND_ATTRIBUTES { + return (*[(1 << 28) - 1]SID_AND_ATTRIBUTES)(unsafe.Pointer(&g.Groups[0]))[:g.GroupCount:g.GroupCount] +} + +func GetTokenGroups(t syscall.Token) (*TOKEN_GROUPS, error) { + i, e := getTokenInfo(t, syscall.TokenGroups, 50) + if e != nil { + return nil, e + } + return (*TOKEN_GROUPS)(i), nil +} diff --git a/src/os/user/lookup_windows.go b/src/os/user/lookup_windows.go index 804fc64cc1..5d99060065 100644 --- a/src/os/user/lookup_windows.go +++ b/src/os/user/lookup_windows.go @@ -434,17 +434,45 @@ func lookupGroupId(gid string) (*Group, error) { } func listGroups(user *User) ([]string, error) { - sid, err := syscall.StringToSid(user.Uid) - if err != nil { - return nil, err - } - username, domain, err := lookupUsernameAndDomain(sid) - if err != nil { - return nil, err - } - sids, err := listGroupsForUsernameAndDomain(username, domain) - if err != nil { - return nil, err + var sids []string + if u, err := Current(); err == nil && u.Uid == user.Uid { + // It is faster and more reliable to get the groups + // of the current user from the current process token. + err := runAsProcessOwner(func() error { + t, err := syscall.OpenCurrentProcessToken() + if err != nil { + return err + } + defer t.Close() + groups, err := windows.GetTokenGroups(t) + if err != nil { + return err + } + for _, g := range groups.AllGroups() { + sid, err := g.Sid.String() + if err != nil { + return err + } + sids = append(sids, sid) + } + return nil + }) + if err != nil { + return nil, err + } + } else { + sid, err := syscall.StringToSid(user.Uid) + if err != nil { + return nil, err + } + username, domain, err := lookupUsernameAndDomain(sid) + if err != nil { + return nil, err + } + sids, err = listGroupsForUsernameAndDomain(username, domain) + if err != nil { + return nil, err + } } // Add the primary group of the user to the list if it is not already there. // This is done only to comply with the POSIX concept of a primary group.