6

I am debugging a program and not quite sure why I can not drop privileges.

I have root permissions via sudo and I can call setgid/setuid, but the operation [is] is not supported.

Basic code to reproduce (golang):

package main

import (
    "fmt"
    "os"
    "strconv"
    "syscall"
)

func main() {
    if os.Getuid() != 0 {
        fmt.Println("run as root")
        os.Exit(1)
    }

    uid, err := strconv.Atoi(os.Getenv("SUDO_UID"))
    check("", err)

    gid, err := strconv.Atoi(os.Getenv("SUDO_GID"))
    check("", err)

    fmt.Printf("uid: %d, gid: %d\n", uid, gid)

    check("gid", syscall.Setgid(gid))
    check("uid", syscall.Setuid(uid))
}

func check(message string, err error) {
    if err != nil {
        fmt.Printf("%s: %s\n", message, err)
        os.Exit(1)
    }
}

Example output:

$ sudo ./drop-sudo 
uid: 1000, gid: 1000
gid: operation not supported

System info:

$ uname -a
Linux user2460234 4.15.0-34-generic #37-Ubuntu SMP Mon Aug 27 15:21:48 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
4
  • 1
    I don't know if this ever got fixed, but Setuid/Setgid do not work properly with Go on Linux - github.com/golang/go/issues/1435 Commented Sep 16, 2018 at 22:49
  • comment out the Setuid line and see if Setgid works. Commented Sep 16, 2018 at 22:52
  • @RuiFRibeiro that doesn't work either; they are in the right order (setgid before setuid). Commented Sep 17, 2018 at 0:32
  • Yeah, they are, it was just a test. Commented Sep 17, 2018 at 0:45

3 Answers 3

8

Your programming language simply does not support such things.

It's complex to do this stuff on Linux, because of the architecture of Linux. The C libraries (e.g. GNU and musl) hide this complexity. It continues to be one of the known problems with threads on Linux.

The Go language does not replicate the mechanism of the C libraries. The current implementation of those functions is not a system call, and has not been since 2014.

Further reading

2
  • "The known problems with threads on Linux" still stands with current kernel(s)? Commented Jun 22, 2019 at 19:42
  • 1
    It should be supported as of go v1.16.6. I tested this today github.com/golang/go/issues/1435 Commented Jul 31, 2021 at 19:25
1

You can check with libcap, it has an official go library with examples. The example uses psx.Syscall3(syscall.SYS_SETUID,...) to set uid (as @JdeBP said, go removes implementation for syscall.Setuid()). The psx library is part of libcap.

Package psx provides a CGo backed API for invoking system calls in a way that each system call is mirrored on all pthreads of the combined Go/CGo runtime. Since the Go runtime treats all pthreads as interchangeable, a feature like this is needed to meaningfully change process privilege (including dropping privilege) in a Go program running on Linux

1

As of go 1.16, syscall.Setuid() and friends are fully supported under Linux.

That is, the reproducer program provided in this question, compiled using the go1.16 toolchain under Linux, will build a program that works as intended. All of the observations that it doesn't work are only true of earlier go toolchain releases under Linux.

The psx package can be used to make something like it work in earlier go toolchains too (as noted by ivzhh).

2
  • 1
    you need to give more relevant explanation and how your answer would help Commented Feb 21, 2021 at 16:28
  • 1
    @FargolK I have reworded it and added some links to further information. Commented Feb 24, 2021 at 15:49

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.