Is SetGID/SetUID on a Go[lang] binary safe?
Question: I've written a simple
[![](//i.stack.imgur.com/sawHl.png)go](/questions/tagged/go "show questions
tagged 'go'") program using YAML and the MySQL drivers with the intention of
providing a simple utility to update a database without exposing the username
and password credentials to the user executing the program.

(I'm well aware that I could also write this in Python or some other scripting
language and manage the permissions delegations using
[sudo](/questions/tagged/sudo "show questions tagged 'sudo'") but I'd like to
try a different approach here, for my own edification).

After building the program I've used `chgrp sys dbcreds.yaml && chmod 0640
dbcreds.yaml` and `chgrp sys ./myprog && chmod g+s ./myprog` (as root) ... and
everything seems to work. (I also tested that access was denied, as it should
be, prior to the setGID step).

I also tested `strace` results in permission denied (as it should be). (For
fun I also ran `ltrace -S` on it; this is under Linux. As expected I did not
see many normal libc function calls ... through I am surprised to have seen a
few _pthread_....()_ and one _malloc()_ calls in that listing. I guess the GO
runtime does link to some system library functions after all).

My question: is this safe? Is there any known way to cause a Go program, such
as this (below) to core dump or expose its memory after it has read these
private credentials? Is there a way to drop my SGID privs after I've read my
credentials? Are there any examples of SUID/SGID exploits on Go binaries? Is
there a better way to do this?

One other note: I find the **gopkg.in/yaml.v2** semantics to be a bit
disconcerting. In my YAML file I have something like:

    
    
    ---
    user me
    pw mypassword
    

But in my code I have to use **_User_** and **_Pw_** (capitalized) rather than
using lower case as I would have expected. I presume this is an implementation
decision by the authored of [Goyaml](http://gopkg.in/yaml.v2). Is that so?

    
    
    #!go
    package main
    
    import (
        "fmt"
        "database/sql"
        _ "github.com/go-sql-driver/mysql"
        "gopkg.in/yaml.v2"
        "io/ioutil"
        "os"
        "strconv"
    )
    
    type Creds struct {
        User string
        Pw   string
    }
    
    func main() {
    
        filename := "./dbcreds.yaml"
        var creds Creds
        conf, err := ioutil.ReadFile(filename)
        if err != nil {
            panic(err)
        }
        err = yaml.Unmarshal(conf, &creds)
        if err != nil {
            panic(err)
        }
    
        var arg1 int
        arg1, err = strconv.Atoi(os.Args[1])
        if err != nil {
            panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
        }
    
        fmt.Println("arg1: ", arg1, "\n")
        dsn := fmt.Sprintf("%s:%s@/mydatabase", creds.User, creds.Pw)
        db, err := sql.Open("mysql", dsn)
        if err != nil {
            panic(err.Error())
        }
        defer db.Close()
    
        err = db.Ping()
        if err != nil {
            panic(err.Error())
        }
    
        stmtOut, err := db.Prepare("SELECT quant FROM c WHERE id >= ?")
        if err != nil {
            panic(err.Error())
        }
        defer stmtOut.Close()
    
        rows, err := stmtOut.Query(arg1)
        if err != nil {
            panic(err.Error())
        }
        defer rows.Close()
    
        for rows.Next() {
            var quant int
            err = rows.Scan(&quant)
            if err != nil {
                panic(err.Error())
            }
            fmt.Println(quant)
        }
    }
    


Answer: A setuid/setgid Go program is reasonably safe, with one major caveat. Go
setuid/setgid programs are in general no more, and no less, secure than C/C++
setuid/setgid programs.

It's true that you can force a Go program to dump core by running it with the
environment variable GOTRACEBACK=crash and then sending it a signal. However,
this is OK for your purposes because the Go program will (try to) create the
core dump by sending itself the SIGABRT signal. The kernel will not generate a
core dump for a setuid/setgid program killed by a signal.

The major caveat for Go is that on GNU/Linux systems you can not drop back to
the original user ID. This is because of how setuid (and setgid, setgroups,
setreuid, setregid, setresuid, and setresgid) are implemented for multi-
threaded programs on GNU/Linux. The details are at
<http://golang.org/issue/1435> .

On your final note Uw and Pw need to be capitalized because the standard
reflect package does not permit writing to unexported fields.

