tailscale/util/groupmember/groupmember_notcgo.go

73 lines
1.5 KiB
Go
Raw Permalink Normal View History

// 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
}