sandbox: No longer change the user to 'nobody' by default.

This can be reactivated by passing the --sandbox_fake_username flag
to Bazel.

Reasoning: 'nobody' has a non-existent home directory on many Linux
distros, leading to issues when tools try to stat / read / write to the
home directory.

Related to #2688.

RELNOTES: The Linux sandbox no longer changes the user to 'nobody' by
default, instead the current user is used as is. The old behavior can be
restored via the --sandbox_fake_username flag.

--
PiperOrigin-RevId: 151115218
MOS_MIGRATED_REVID=151115218
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java
index a297ebe..471158a 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxRunner.java
@@ -108,7 +108,8 @@
       Map<String, String> environment,
       int timeout,
       boolean allowNetwork,
-      boolean useFakeHostname)
+      boolean useFakeHostname,
+      boolean useFakeUsername)
       throws IOException {
     writeConfig(allowNetwork);
 
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java
index 29d9084..68d3f69 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java
@@ -105,9 +105,10 @@
       Map<String, String> env,
       int timeout,
       boolean allowNetwork,
-      boolean useFakeHostname)
+      boolean useFakeHostname,
+      boolean useFakeUsername)
       throws IOException {
-    writeConfig(timeout, allowNetwork, useFakeHostname);
+    writeConfig(timeout, allowNetwork, useFakeHostname, useFakeUsername);
 
     List<String> commandLineArgs = new ArrayList<>(3 + spawnArguments.size());
     commandLineArgs.add(execRoot.getRelative("_bin/linux-sandbox").getPathString());
@@ -117,7 +118,8 @@
     return new Command(commandLineArgs.toArray(new String[0]), env, sandboxExecRoot.getPathFile());
   }
 
-  private void writeConfig(int timeout, boolean allowNetwork, boolean useFakeHostname)
+  private void writeConfig(
+      int timeout, boolean allowNetwork, boolean useFakeHostname, boolean useFakeUsername)
       throws IOException {
     List<String> fileArgs = new ArrayList<>();
 
@@ -163,10 +165,15 @@
     }
 
     if (useFakeHostname) {
-      // Use a fake hostname ("localhost") inside the sandbox when blocking network access.
+      // Use a fake hostname ("localhost") inside the sandbox.
       fileArgs.add("-H");
     }
 
+    if (useFakeUsername) {
+      // Use a fake username ("nobody") inside the sandbox.
+      fileArgs.add("-U");
+    }
+
     FileSystemUtils.writeLinesAs(argumentsFilePath, StandardCharsets.ISO_8859_1, fileArgs);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java
index b4102a7..f3ee9d1 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperRunner.java
@@ -58,7 +58,8 @@
       Map<String, String> env,
       int timeout,
       boolean allowNetwork,
-      boolean useFakeHostname) {
+      boolean useFakeHostname,
+      boolean useFakeUsername) {
     List<String> commandLineArgs = new ArrayList<>(5 + spawnArguments.size());
     commandLineArgs.add(execRoot.getRelative("_bin/process-wrapper").getPathString());
     commandLineArgs.add(Integer.toString(timeout));
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
index 303ec0f..a7185f4 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
@@ -94,6 +94,14 @@
   public boolean sandboxFakeHostname;
 
   @Option(
+    name = "sandbox_fake_username",
+    defaultValue = "false",
+    category = "strategy",
+    help = "Change the current username to 'nobody' for sandboxed actions."
+  )
+  public boolean sandboxFakeUsername;
+
+  @Option(
       name = "sandbox_tmpfs_path",
       allowMultiple = true,
       defaultValue = "",
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java
index 49d661d..efa423e 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxRunner.java
@@ -50,6 +50,7 @@
    * @param allowNetwork - whether networking should be allowed for the process.
    * @param sandboxDebug - whether debugging message should be printed.
    * @param useFakeHostname - whether the hostname should be set to 'localhost' inside the sandbox.
+   * @param useFakeUsername - whether the username should be set to 'nobody' inside the sandbox.
    */
   void run(
       List<String> arguments,
@@ -58,11 +59,14 @@
       int timeout,
       boolean allowNetwork,
       boolean sandboxDebug,
-      boolean useFakeHostname)
+      boolean useFakeHostname,
+      boolean useFakeUsername)
       throws ExecException {
     Command cmd;
     try {
-      cmd = getCommand(arguments, environment, timeout, allowNetwork, useFakeHostname);
+      cmd =
+          getCommand(
+              arguments, environment, timeout, allowNetwork, useFakeHostname, useFakeUsername);
     } catch (IOException e) {
       throw new UserExecException("I/O error during sandboxed execution", e);
     }
@@ -111,13 +115,15 @@
    * @param timeout - after how many seconds should the process be killed.
    * @param allowNetwork - whether networking should be allowed for the process.
    * @param useFakeHostname - whether the hostname should be set to 'localhost' inside the sandbox.
+   * @param useFakeUsername - whether the username should be set to 'nobody' inside the sandbox.
    */
   protected abstract Command getCommand(
       List<String> arguments,
       Map<String, String> environment,
       int timeout,
       boolean allowNetwork,
-      boolean useFakeHostname)
+      boolean useFakeHostname,
+      boolean useFakeUsername)
       throws IOException;
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java
index ffa0ff4..ba88c8f 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java
@@ -84,7 +84,8 @@
           Spawns.getTimeoutSeconds(spawn),
           SandboxHelpers.shouldAllowNetwork(buildRequest, spawn),
           sandboxOptions.sandboxDebug,
-          sandboxOptions.sandboxFakeHostname);
+          sandboxOptions.sandboxFakeHostname,
+          sandboxOptions.sandboxFakeUsername);
     } catch (ExecException e) {
       execException = e;
     }
diff --git a/src/main/tools/linux-sandbox-options.cc b/src/main/tools/linux-sandbox-options.cc
index 96354ec..78831e6 100644
--- a/src/main/tools/linux-sandbox-options.cc
+++ b/src/main/tools/linux-sandbox-options.cc
@@ -73,7 +73,8 @@
           "    The -M option specifies which directory to mount, the -m option "
           "specifies where to\n"
           "  -N  if set, a new network namespace will be created\n"
-          "  -R  if set, make the uid/gid be root, otherwise use nobody\n"
+          "  -R  if set, make the uid/gid be root\n"
+          "  -U  if set, make the uid/gid be nobody\n"
           "  -D  if set, debug info will be printed\n"
           "  @FILE  read newline-separated arguments from FILE\n"
           "  --  command to run inside sandbox, followed by arguments\n");
@@ -123,8 +124,8 @@
   int c;
   bool source_specified;
 
-  while ((c = getopt(args->size(), args->data(), ":CW:T:t:l:L:w:e:M:m:HNRD")) !=
-         -1) {
+  while ((c = getopt(args->size(), args->data(),
+                     ":CW:T:t:l:L:w:e:M:m:HNRUD")) != -1) {
     if (c != 'M' && c != 'm') source_specified = false;
     switch (c) {
       case 'C':
@@ -200,8 +201,21 @@
         opt.create_netns = true;
         break;
       case 'R':
+        if (opt.fake_username) {
+          Usage(args->front(),
+                "The -R option cannot be used at the same time us the -U "
+                "option.");
+        }
         opt.fake_root = true;
         break;
+      case 'U':
+        if (opt.fake_root) {
+          Usage(args->front(),
+                "The -U option cannot be used at the same time us the -R "
+                "option.");
+        }
+        opt.fake_username = true;
+        break;
       case 'D':
         opt.debug = true;
         break;
diff --git a/src/main/tools/linux-sandbox-options.h b/src/main/tools/linux-sandbox-options.h
index 5c78e46..daf1fd6 100644
--- a/src/main/tools/linux-sandbox-options.h
+++ b/src/main/tools/linux-sandbox-options.h
@@ -46,6 +46,8 @@
   bool create_netns;
   // Pretend to be root inside the namespace (-R)
   bool fake_root;
+  // Set the username inside the sandbox to 'nobody' (-U)
+  bool fake_username;
   // Print debugging messages (-D)
   bool debug;
   // Command to run (--)
diff --git a/src/main/tools/linux-sandbox-pid1.cc b/src/main/tools/linux-sandbox-pid1.cc
index 76dccba..17a7143 100644
--- a/src/main/tools/linux-sandbox-pid1.cc
+++ b/src/main/tools/linux-sandbox-pid1.cc
@@ -120,8 +120,13 @@
     }
   }
 
-  int inner_uid = 0, inner_gid = 0;
-  if (!opt.fake_root) {
+  int inner_uid, inner_gid;
+  if (opt.fake_root) {
+    // Change our username to 'root'.
+    inner_uid = 0;
+    inner_gid = 0;
+  } else if (opt.fake_username) {
+    // Change our username to 'nobody'.
     struct passwd *pwd = getpwnam("nobody");
     if (pwd == NULL) {
       DIE("unable to find passwd entry for user nobody")
@@ -129,6 +134,10 @@
 
     inner_uid = pwd->pw_uid;
     inner_gid = pwd->pw_gid;
+  } else {
+    // Do not change the username inside the sandbox.
+    inner_uid = global_outer_uid;
+    inner_gid = global_outer_gid;
   }
 
   WriteFile("/proc/self/uid_map", "%d %d 1\n", inner_uid, global_outer_uid);
diff --git a/src/main/tools/linux-sandbox.cc b/src/main/tools/linux-sandbox.cc
index 0d73174..9d2fd01 100644
--- a/src/main/tools/linux-sandbox.cc
+++ b/src/main/tools/linux-sandbox.cc
@@ -25,12 +25,14 @@
  *  - If the process takes longer than the timeout (-T), it will be killed with
  *    SIGTERM. If it does not exit within the grace period (-t), it all of its
  *    children will be killed with SIGKILL.
+ *  - If option -R is passed, the process will run as user 'root'.
+ *  - If option -U is passed, the process will run as user 'nobody'.
+ *  - Otherwise, the process runs using the current uid / gid.
  *  - If linux-sandbox itself gets killed, the process and all of its children
  *    will be killed.
  *  - If linux-sandbox's parent dies, it will kill itself, the process and all
  *    the children.
  *  - Network access is allowed, but can be disabled via -N.
- *  - The process runs as user "nobody", unless fakeroot is enabled (-R).
  *  - The hostname and domainname will be set to "sandbox".
  *  - The process runs in its own PID namespace, so other processes on the
  *    system are invisible.
diff --git a/src/test/shell/bazel/linux-sandbox_test.sh b/src/test/shell/bazel/linux-sandbox_test.sh
index 87310c3..6529ae2 100755
--- a/src/test/shell/bazel/linux-sandbox_test.sh
+++ b/src/test/shell/bazel/linux-sandbox_test.sh
@@ -47,14 +47,19 @@
   expect_log "\"execvp(/does/not/exist, 0x[[:alnum:]]*)\": No such file or directory"
 }
 
-function test_default_user_is_nobody() {
+function test_default_user_is_current_user() {
   $linux_sandbox $SANDBOX_DEFAULT_OPTS -- /usr/bin/id &> $TEST_log || fail
-  expect_log "uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)"
+  expect_log "$(id)"
 }
 
 function test_user_switched_to_root() {
   $linux_sandbox $SANDBOX_DEFAULT_OPTS -R -- /usr/bin/id &> $TEST_log || fail
-  expect_log "uid=0(root) gid=0(root)"
+  expect_log "uid=0(root) gid=0(root) groups=0(root)"
+}
+
+function test_user_switched_to_nobody() {
+  $linux_sandbox $SANDBOX_DEFAULT_OPTS -U -- /usr/bin/id &> $TEST_log || fail
+  expect_log "uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)"
 }
 
 function test_network_namespace() {