diff --git a/util/groupmember/groupmember.go b/util/groupmember/groupmember.go index 2a9b5d24e..3597bfa76 100644 --- a/util/groupmember/groupmember.go +++ b/util/groupmember/groupmember.go @@ -7,14 +7,28 @@ package groupmember import ( - "errors" - "runtime" + "os/user" ) -var ErrNotImplemented = errors.New("not implemented for GOOS=" + runtime.GOOS) - // IsMemberOfGroup reports whether the provided user is a member of // the provided system group. func IsMemberOfGroup(group, userName string) (bool, error) { - return isMemberOfGroup(group, userName) + u, err := user.Lookup(userName) + if err != nil { + return false, err + } + ugids, err := u.GroupIds() + if err != nil { + return false, err + } + g, err := user.LookupGroup(group) + if err != nil { + return false, err + } + for _, ugid := range ugids { + if g.Gid == ugid { + return true, nil + } + } + return false, nil } diff --git a/util/groupmember/groupmember_cgo.go b/util/groupmember/groupmember_cgo.go deleted file mode 100644 index 008a62987..000000000 --- a/util/groupmember/groupmember_cgo.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build cgo && !osusergo -// +build cgo,!osusergo - -package groupmember - -import ( - "os/user" - "sync" -) - -func isMemberOfGroup(group, name string) (bool, error) { - u, err := user.Lookup(name) - if err != nil { - return false, err - } - ugids, err := u.GroupIds() - if err != nil { - return false, err - } - gid, err := getGroupID(group) - if err != nil { - return false, err - } - for _, ugid := range ugids { - if gid == ugid { - return true, nil - } - } - return false, nil -} - -var groupIDCache sync.Map // of string - -func getGroupID(groupName string) (string, error) { - s, ok := groupIDCache.Load(groupName) - if ok { - return s.(string), nil - } - g, err := user.LookupGroup(groupName) - if err != nil { - return "", err - } - groupIDCache.Store(groupName, g.Gid) - return g.Gid, nil -} diff --git a/util/groupmember/groupmember_noimpl.go b/util/groupmember/groupmember_noimpl.go deleted file mode 100644 index 5a0d9a061..000000000 --- a/util/groupmember/groupmember_noimpl.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !cgo && !linux && !darwin -// +build !cgo,!linux,!darwin - -package groupmember - -func isMemberOfGroup(group, name string) (bool, error) { return false, ErrNotImplemented } diff --git a/util/groupmember/groupmember_notcgo.go b/util/groupmember/groupmember_notcgo.go deleted file mode 100644 index a2102701c..000000000 --- a/util/groupmember/groupmember_notcgo.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build (!cgo || osusergo) && (linux || darwin) -// +build !cgo osusergo -// +build linux darwin - -package groupmember - -import ( - "bufio" - "fmt" - "os" - "os/exec" - "strings" - - "go4.org/mem" - "tailscale.com/version/distro" -) - -func isMemberOfGroup(group, name string) (bool, error) { - if distro.Get() == distro.Synology { - return isMemberOfGroupEtcGroup(group, name) - } - cmd := exec.Command("/usr/bin/env", "groups", name) - out, err := cmd.CombinedOutput() - if err != nil { - return false, err - } - groups := strings.Split(strings.TrimSpace(string(out)), " ") - for _, g := range groups { - if g == group { - return true, nil - } - } - return false, nil -} - -func isMemberOfGroupEtcGroup(group, name string) (bool, error) { - f, err := os.Open("/etc/group") - if err != nil { - return false, err - } - defer f.Close() - s := bufio.NewScanner(f) - var agLine string - for s.Scan() { - if !mem.HasPrefix(mem.B(s.Bytes()), mem.S(fmt.Sprintf("%s:", group))) { - continue - } - agLine = s.Text() - break - } - if err := s.Err(); err != nil { - return false, err - } - if agLine == "" { - return false, fmt.Errorf("admin group not defined") - } - agEntry := strings.Split(agLine, ":") - if len(agEntry) < 4 { - return false, fmt.Errorf("malformed admin group entry") - } - agMembers := agEntry[3] - for _, m := range strings.Split(agMembers, ",") { - if m == name { - return true, nil - } - } - return false, nil -}