main.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Copyright 2018 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // This tool tries to prohibit access to tools on the system on which the build
  15. // is run.
  16. //
  17. // The rationale is that if the build uses a binary that is not shipped in the
  18. // source tree, it is unknowable which version of that binary will be installed
  19. // and therefore the output of the build will be unpredictable. Therefore, we
  20. // should make every effort to use only tools under our control.
  21. //
  22. // This is currently implemented by a "sandbox" that sets $PATH to a specific,
  23. // single directory and creates a symlink for every binary in $PATH in it. That
  24. // symlink will point to path_interposer, which then uses an embedded
  25. // configuration to determine whether to allow access to the binary (in which
  26. // case it calls the original executable) or not (in which case it fails). It
  27. // can also optionally log invocations.
  28. //
  29. // This, of course, does not help if one invokes the tool in question with its
  30. // full path.
  31. package main
  32. import (
  33. "bytes"
  34. "fmt"
  35. "io"
  36. "io/ioutil"
  37. "os"
  38. "os/exec"
  39. "path/filepath"
  40. "strconv"
  41. "syscall"
  42. "android/soong/ui/build/paths"
  43. )
  44. func main() {
  45. interposer, err := os.Executable()
  46. if err != nil {
  47. fmt.Fprintln(os.Stderr, "Unable to locate interposer executable:", err)
  48. os.Exit(1)
  49. }
  50. if fi, err := os.Lstat(interposer); err == nil {
  51. if fi.Mode()&os.ModeSymlink != 0 {
  52. link, err := os.Readlink(interposer)
  53. if err != nil {
  54. fmt.Fprintln(os.Stderr, "Unable to read link to interposer executable:", err)
  55. os.Exit(1)
  56. }
  57. if filepath.IsAbs(link) {
  58. interposer = link
  59. } else {
  60. interposer = filepath.Join(filepath.Dir(interposer), link)
  61. }
  62. }
  63. } else {
  64. fmt.Fprintln(os.Stderr, "Unable to stat interposer executable:", err)
  65. os.Exit(1)
  66. }
  67. exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
  68. sendLog: paths.SendLog,
  69. config: paths.GetConfig,
  70. lookupParents: lookupParents,
  71. })
  72. if err != nil {
  73. fmt.Fprintln(os.Stderr, err.Error())
  74. }
  75. os.Exit(exitCode)
  76. }
  77. var usage = fmt.Errorf(`To use the PATH interposer:
  78. * Write the original PATH variable to <interposer>_origpath
  79. * Set up a directory of symlinks to the PATH interposer, and use that in PATH
  80. If a tool isn't in the allowed list, a log will be posted to the unix domain
  81. socket at <interposer>_log.`)
  82. type mainOpts struct {
  83. sendLog func(logSocket string, entry *paths.LogEntry, done chan interface{})
  84. config func(name string) paths.PathConfig
  85. lookupParents func() []paths.LogProcess
  86. }
  87. func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) {
  88. base := filepath.Base(args[0])
  89. origPathFile := interposer + "_origpath"
  90. if base == filepath.Base(interposer) {
  91. return 1, usage
  92. }
  93. origPath, err := ioutil.ReadFile(origPathFile)
  94. if err != nil {
  95. if os.IsNotExist(err) {
  96. return 1, usage
  97. } else {
  98. return 1, fmt.Errorf("Failed to read original PATH: %v", err)
  99. }
  100. }
  101. cmd := &exec.Cmd{
  102. Args: args,
  103. Env: os.Environ(),
  104. Stdin: os.Stdin,
  105. Stdout: stdout,
  106. Stderr: stderr,
  107. }
  108. if err := os.Setenv("PATH", string(origPath)); err != nil {
  109. return 1, fmt.Errorf("Failed to set PATH env: %v", err)
  110. }
  111. if config := opts.config(base); config.Log || config.Error {
  112. var procs []paths.LogProcess
  113. if opts.lookupParents != nil {
  114. procs = opts.lookupParents()
  115. }
  116. if opts.sendLog != nil {
  117. waitForLog := make(chan interface{})
  118. opts.sendLog(interposer+"_log", &paths.LogEntry{
  119. Basename: base,
  120. Args: args,
  121. Parents: procs,
  122. }, waitForLog)
  123. defer func() { <-waitForLog }()
  124. }
  125. if config.Error {
  126. return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base)
  127. }
  128. }
  129. cmd.Path, err = exec.LookPath(base)
  130. if err != nil {
  131. return 1, err
  132. }
  133. if err = cmd.Run(); err != nil {
  134. if exitErr, ok := err.(*exec.ExitError); ok {
  135. if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
  136. if status.Exited() {
  137. return status.ExitStatus(), nil
  138. } else if status.Signaled() {
  139. exitCode := 128 + int(status.Signal())
  140. return exitCode, nil
  141. } else {
  142. return 1, exitErr
  143. }
  144. } else {
  145. return 1, nil
  146. }
  147. }
  148. }
  149. return 0, nil
  150. }
  151. type procEntry struct {
  152. Pid int
  153. Ppid int
  154. Command string
  155. }
  156. func readProcs() map[int]procEntry {
  157. cmd := exec.Command("ps", "-o", "pid,ppid,command")
  158. data, err := cmd.Output()
  159. if err != nil {
  160. return nil
  161. }
  162. return parseProcs(data)
  163. }
  164. func parseProcs(data []byte) map[int]procEntry {
  165. lines := bytes.Split(data, []byte("\n"))
  166. if len(lines) < 2 {
  167. return nil
  168. }
  169. // Remove the header
  170. lines = lines[1:]
  171. ret := make(map[int]procEntry, len(lines))
  172. for _, line := range lines {
  173. fields := bytes.SplitN(line, []byte(" "), 2)
  174. if len(fields) != 2 {
  175. continue
  176. }
  177. pid, err := strconv.Atoi(string(fields[0]))
  178. if err != nil {
  179. continue
  180. }
  181. line = bytes.TrimLeft(fields[1], " ")
  182. fields = bytes.SplitN(line, []byte(" "), 2)
  183. if len(fields) != 2 {
  184. continue
  185. }
  186. ppid, err := strconv.Atoi(string(fields[0]))
  187. if err != nil {
  188. continue
  189. }
  190. ret[pid] = procEntry{
  191. Pid: pid,
  192. Ppid: ppid,
  193. Command: string(bytes.TrimLeft(fields[1], " ")),
  194. }
  195. }
  196. return ret
  197. }
  198. func lookupParents() []paths.LogProcess {
  199. procs := readProcs()
  200. if procs == nil {
  201. return nil
  202. }
  203. list := []paths.LogProcess{}
  204. pid := os.Getpid()
  205. for {
  206. entry, ok := procs[pid]
  207. if !ok {
  208. break
  209. }
  210. list = append([]paths.LogProcess{
  211. {
  212. Pid: pid,
  213. Command: entry.Command,
  214. },
  215. }, list...)
  216. pid = entry.Ppid
  217. }
  218. return list
  219. }