// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.sandbox; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.shell.Command; import com.google.devtools.build.lib.shell.CommandException; import com.google.devtools.build.lib.vfs.Path; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; /** * Helper class for running the namespace sandbox. This runner prepares environment inside the * sandbox, handles sandbox output, performs cleanup and changes invocation if necessary. */ final class DarwinSandboxRunner extends SandboxRunner { private static final String SANDBOX_EXEC = "/usr/bin/sandbox-exec"; private final Path sandboxExecRoot; private final Path sandboxConfigPath; private final Set<Path> writableDirs; private final Set<Path> inaccessiblePaths; DarwinSandboxRunner( Path sandboxPath, Path sandboxExecRoot, Set<Path> writableDirs, Set<Path> inaccessiblePaths, boolean verboseFailures) { super(verboseFailures); this.sandboxExecRoot = sandboxExecRoot; this.sandboxConfigPath = sandboxPath.getRelative("sandbox.sb"); this.writableDirs = writableDirs; this.inaccessiblePaths = inaccessiblePaths; } static boolean isSupported(CommandEnvironment cmdEnv) { if (!ProcessWrapperRunner.isSupported(cmdEnv)) { return false; } List<String> args = new ArrayList<>(); args.add(SANDBOX_EXEC); args.add("-p"); args.add("(version 1) (allow default)"); args.add("/usr/bin/true"); ImmutableMap<String, String> env = ImmutableMap.of(); File cwd = new File("/usr/bin"); Command cmd = new Command(args.toArray(new String[0]), env, cwd); try { cmd.execute( /* stdin */ new byte[] {}, Command.NO_OBSERVER, ByteStreams.nullOutputStream(), ByteStreams.nullOutputStream(), /* killSubprocessOnInterrupt */ true); } catch (CommandException e) { return false; } return true; } @Override protected Command getCommand( CommandEnvironment cmdEnv, List<String> arguments, Map<String, String> env, int timeout, boolean allowNetwork, boolean useFakeHostname, boolean useFakeUsername) throws IOException { writeConfig(allowNetwork); List<String> commandLineArgs = new ArrayList<>(); commandLineArgs.add(SANDBOX_EXEC); commandLineArgs.add("-f"); commandLineArgs.add(sandboxConfigPath.getPathString()); commandLineArgs.addAll(ProcessWrapperRunner.getCommandLine(cmdEnv, arguments, timeout)); return new Command(commandLineArgs.toArray(new String[0]), env, sandboxExecRoot.getPathFile()); } private void writeConfig(boolean allowNetwork) throws IOException { try (PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(sandboxConfigPath.getOutputStream(), UTF_8)))) { // Note: In Apple's sandbox configuration language, the *last* matching rule wins. out.println("(version 1)"); out.println("(debug deny)"); out.println("(allow default)"); if (!allowNetwork) { out.println("(deny network*)"); out.println("(allow network* (local ip \"localhost:*\"))"); out.println("(allow network* (remote ip \"localhost:*\"))"); } // By default, everything is read-only. out.println("(deny file-write*)"); out.println("(allow file-write*"); for (Path path : writableDirs) { out.println(" (subpath \"" + path.getPathString() + "\")"); } out.println(")"); if (!inaccessiblePaths.isEmpty()) { out.println("(deny file-read*"); // The sandbox configuration file is not part of a cache key and sandbox-exec doesn't care // about ordering of paths in expressions, so it's fine if the iteration order is random. for (Path inaccessiblePath : inaccessiblePaths) { out.println(" (subpath \"" + inaccessiblePath + "\")"); } out.println(")"); } } } }