// ShouldDropPrivs returns true if the application runs with sufficient // privileges so that it should drop them func ShouldDropPrivs() bool { if groups, err := syscall.Getgroups(); err == nil { for _, gid := range groups { if gid == 0 { return true } } } return syscall.Getuid() == 0 || syscall.Getgid() == 0 }
func (k *PosixKernel) Getgroups(count int, buf co.Buf) uint64 { groups, err := syscall.Getgroups() if err != nil { return Errno(err) } length := uint64(len(groups)) if count > 0 { if count < len(groups) { groups = groups[:count] } tmp := make([]uint32, len(groups)) for i, v := range groups { tmp[i] = uint32(v) } if err := buf.Pack(tmp); err != nil { return UINT64_MAX // FIXME } } return length }
// Getgroups returns a list of the numeric ids of groups that the caller belongs to. func Getgroups() ([]int, error) { gids, e := syscall.Getgroups() return gids, NewSyscallError("getgroups", e) }
// Activates the mitigation measurements. func Activate(uid int, gid int, path string) { if !CanActivate() { panic("Cannot activate mitigation measurements!") } // chroot directory err := syscall.Chroot(path) if err != nil { panic(err) } // change directory to new / err = syscall.Chdir("/") if err != nil { panic(err) } // drop all other groups err = syscall.Setgroups([]int{gid}) if err != nil { panic(err) } // verify the empty group list gids, err := syscall.Getgroups() if err != nil { panic("Could not read groups!") } if len(gids) > 1 { panic("Could not drop groups!") } else if len(gids) == 1 { if gids[0] != gid { panic("Could not drop foreign groups!") } } // change group err = syscall.Setgid(gid) if err != nil { panic(err) } // verify the group change ngid := syscall.Getgid() if ngid != gid { panic("Could not change group id!") } // change user err = syscall.Setuid(uid) if err != nil { panic(err) } // verify the user change nuid := syscall.Getuid() if nuid != uid { panic("Could not change user id!") } // now drop all environment variables os.Clearenv() }
// The test will only work when running as root. func TestActivate(t *testing.T) { // create temporary directory to test chrooting tmp, err := ioutil.TempDir("", "mitigationtest") if err != nil { t.Fatal(err) } defer os.Remove(tmp) err = syscall.Chmod(tmp, 0755) if err != nil { t.Fatal("Could not change temporary directory permissions!") } // improve cpu usage to test for broken os implementations of setuid() runtime.GOMAXPROCS(2) // create some go routines as root to later test them var sync chan bool = make(chan bool) for i := 0; i < TEST_ROUTINES_COUNT; i++ { go func() { // no op sync <- true }() } for i := 0; i < TEST_ROUTINES_COUNT; i++ { <-sync } // modify environment err = os.Setenv("malicous_env", "bad string") if err != nil { t.Fatal("Cannot setup environment variables!") } if len(os.Environ()) == 0 { t.Fatal("Environ() or Setenv() are broken!") } // do it! Activate(TEST_UID, TEST_GID, tmp) // verify uids uid := syscall.Getuid() if uid != TEST_UID { t.Error("Failed to change UID") } euid := syscall.Geteuid() if euid != TEST_UID { t.Error("Failed to change EUID") } // verify gid gid := syscall.Getgid() if gid != TEST_GID { t.Error("Failed to change GID") } // verify groups gids, err := syscall.Getgroups() if err != nil { t.Fatal("Could not get group list") } if len(gids) > 1 { t.Error("Not all groups are dropped!") } else if len(gids) == 1 { if gids[0] != TEST_GID { t.Error("Not all foreign groups are dropped!") } } // verify directory dh, err := os.Open("/") if err != nil { t.Fatal("Cannot open my root directory", err) } files, err := dh.Readdir(-1) if err != nil { t.Fatal("Cannot read my root directory") } if len(files) > 0 { t.Error("Root not changed to empty temporary directory!") } // verify environment if len(os.Environ()) > 0 { t.Error("Environment variables found!") } // test setuid() behaviour var results chan int = make(chan int) // start multiple goroutines, in a good OS, all all routines // should be switched to the new user for i := 0; i < TEST_ROUTINES_COUNT; i++ { go func() { results <- syscall.Getuid() }() } // check the results for i := 0; i < TEST_ROUTINES_COUNT; i++ { uid := <-results if uid != TEST_UID { t.Error("false uid: ", uid, " (you are using an unsafe os, read the package documentation!)") break } } }