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