13

I'm creating a small program that requires privileged access to a network port below 1024, so it runs with sudo.

If part of what the utility will need to do requires knowing who the user is that invoked the application, I would need a way to query who the actual user is; using the OS/user method of getting the user points to "System Administrator" or other root user because it's running in a sudo context.

Is there a way in GoLang to pull the user who is running the application under sudo? (Go 1.4.2, running on OS X 10.10.3).

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
Bart Silverstrim
  • 3,445
  • 6
  • 34
  • 44
  • Why do you need this functionality? – fuz Apr 19 '15 at 18:34
  • That's unconventional. If the user proved he is worthy of being root any way, why do you need to write down who that user was? It's not that `sudo` doesn't keep logs on its own. – fuz Apr 19 '15 at 18:47
  • For troubleshooting the utility's use with it's own logging. – Bart Silverstrim Apr 19 '15 at 18:48
  • Sorry, but what's the problem with looking in the sudo log (part of `/var/log/auth.log`) like with any other application? If you want to know who invoked the application, you could consider making it suid root. In this case, one of the uids (forgot which one) is the uid of the original user. – fuz Apr 19 '15 at 18:49
  • It seemed lot simpler for a small utility that someone is going to run on their system for troubleshooting to have a single log to zip up and send than for me to query their logfiles or explain to them how to do the queries through their logfiles. It helped keep the entire thing more self-contained. – Bart Silverstrim Apr 19 '15 at 18:51
  • And why does it matter who executed the utility? `sudo` is designed to alter the user a program runs under, so it's always root who executes your program. You could try working with capabilities instead. – fuz Apr 19 '15 at 18:53
  • 1
    Always being root who runs the program when sudo is invoked is probably related to why I asked the question in the first place. – Bart Silverstrim Apr 19 '15 at 18:57

3 Answers3

16

sudo creates the SUDO_UID/SUDO_GID and the SUDO_USER environment variables for this, which contains the user id, group id and username of the account invoking sudo. See e.g. here

So in Go you can read those environment variables with os.Getenv().

You might want to trust those variables only if running as root, i.e. if os.Geteuid() returns 0

Community
  • 1
  • 1
nos
  • 223,662
  • 58
  • 417
  • 506
7

Here's how I check if the current user is root in the latest Golang version go1.16.1:

package main

import (
    "fmt"
    "log"
    "os/user"
)

func isRoot() bool {
    currentUser, err := user.Current()
    if err != nil {
        log.Fatalf("[isRoot] Unable to get current user: %s", err)
    }
    return currentUser.Username == "root"
}

func main() {   
    fmt.Printf("Am I root? %v", isRoot())

}

https://play.golang.org/p/tRMR5IW6GAc

Alfonso
  • 1,125
  • 1
  • 13
  • 23
5

Since anyone can set SUDO_UID, SUDO_GID and SUDO_USER, an attacker can just export those themselves and bypass the security you're trying to implement.

The way that I've found, is to find the pid of the Go program you're currently running, get the name of the user that owns that pid, then check if its root.

import (
    "fmt"
    "os"
    "os/exec"
    "strconv"
)

func main() {
    if getProcessOwner() == "root" {
        fmt.Println("You're sudo!")
    }
}

func getProcessOwner() string {
    stdout, err := exec.Command("ps", "-o", "user=", "-p", strconv.Itoa(os.Getpid())).Output()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    return string(stdout)
}
Nick Saccente
  • 150
  • 1
  • 9
  • Is the comparison to root any better than uid == 0? – Daniel Aug 12 '20 at 19:36
  • I want to say that it doesn't matter, but I'm not sure. For me, checking for the string literal root feels a lot easier to read by someone picking up my code later. – Nick Saccente Aug 16 '20 at 16:33
  • if getProcessOwner() == "root" this evaluates to false because of newline character. if getProcessOwner() == "root\n" evaluates to true – Ali Tahir Sep 14 '20 at 05:33
  • The \n wasn't needed for me, I run Fedora 31. I wonder if it varies based on distribution? I'd say a partial match would be a better overall solution, but then an adversary could create a user named "groot" or "rooty" and it would technically match. – Nick Saccente Sep 18 '20 at 01:55