title: Is SetGID/SetUID on a Go[lang] binary safe?
body: <p>I've written a simple <a href="/questions/tagged/go" class="post-tag" title="show questions tagged &#39;go&#39;" rel="tag">go</a> 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.</p>
  
  <p>(I'm well aware that I could also write this in Python or some other scripting language and manage the permissions delegations using <a href="/questions/tagged/sudo" class="post-tag" title="show questions tagged &#39;sudo&#39;" rel="tag">sudo</a> but I'd like to try a different approach here, for my own edification).</p>
  
  <p>After building the program I've used <code>chgrp sys dbcreds.yaml &amp;&amp; chmod 0640 dbcreds.yaml</code> and <code>chgrp sys ./myprog &amp;&amp; chmod g+s ./myprog</code> (as root) ... and everything seems to work.  (I also tested that access was denied, as it should be, prior to the setGID step).</p>
  
  <p>I also tested <code>strace</code> and that results in permission denied (as it should be). (For fun I also ran <code>ltrace -S</code> 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 <em>pthread_....()</em> and one <em>malloc()</em> calls in that listing.  I guess the GO runtime does link to some system library functions after all).</p>
  
  <p>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?  Is there a way to proactive prevent core dumps or ensure that sensitive data (credentials) would not be in core dumps?</p>
  
  <p>One other note: I find the <strong>gopkg.in/yaml.v2</strong> semantics to be a bit disconcerting.  In my YAML file I have something like:</p>
  
  <pre><code>---
  user me
  pw mypassword
  </code></pre>
  
  <p>But in my code I have to use <strong><em>User</em></strong> and <strong><em>Pw</em></strong> (capitalized) rather than using lower case as I would have expected.  I presume this is an implementation decision by the authors of <a href="http://gopkg.in/yaml.v2" rel="nofollow noreferrer">Goyaml</a>.  Is that so?</p>
  
  <pre><code>#!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, &amp;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 &gt;= ?")
      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(&amp;quant)
          if err != nil {
              panic(err.Error())
          }
          fmt.Println(quant)
      }
  }
  </code></pre>