github.phpd.cn/thought-machine/please@v12.2.0+incompatible/tools/sandbox/main.c (about)

     1  // please_sandbox is a very small binary to implement sandboxing
     2  // of tests (and possibly other build actions) via cgroups.
     3  // Essentially this is a very lightweight replacement for Docker
     4  // where we would use it for tests to avoid port clashes etc.
     5  //
     6  // Note that this is a no-op on non-Linux OSs because they will not
     7  // support namespaces / cgroups. We still behave similarly otherwise
     8  // in order for it to be transparent to the rest of the system.
     9  
    10  #define _GNU_SOURCE
    11  #include <stdio.h>
    12  #include <stdlib.h>
    13  #include <unistd.h>
    14  
    15  #ifdef __linux__
    16  #include <sched.h>
    17  #include <string.h>
    18  #include <net/if.h>
    19  #include <sys/ioctl.h>
    20  #include <sys/mount.h>
    21  #include <sys/prctl.h>
    22  #include <sys/types.h>
    23  
    24  // TODO(peterebden): Remove the following once our build machine gets updated...
    25  #ifndef MS_LAZYTIME
    26  #define MS_LAZYTIME	(1<<25)
    27  #endif
    28  
    29  // drop_root is ported more or less directly from Chrome's chrome-sandbox helper.
    30  // It simply drops us back to whatever user invoked us originally (i.e. before suid
    31  // got involved).
    32  int drop_root() {
    33      if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
    34          perror("prctl(PR_SET_DUMPABLE)");
    35          return 1;
    36      }
    37      if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
    38          perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
    39          return 1;
    40      }
    41      gid_t rgid, egid, sgid;
    42      if (getresgid(&rgid, &egid, &sgid)) {
    43          perror("getresgid");
    44          return 1;
    45      }
    46      if (setresgid(rgid, rgid, rgid)) {
    47          perror("setresgid");
    48          return 1;
    49      }
    50      uid_t ruid, euid, suid;
    51      if (getresuid(&ruid, &euid, &suid)) {
    52          perror("getresuid");
    53          return 1;
    54      }
    55      if (setresuid(ruid, ruid, ruid)) {
    56          perror("setresuid");
    57          return 1;
    58      }
    59      return 0;
    60  }
    61  
    62  // lo_up brings up the loopback interface in the new network namespace.
    63  // By default the namespace is created with lo but it is down.
    64  // Note that this can't be done with system() because it loses the
    65  // required capabilities.
    66  int lo_up() {
    67      const int sock = socket(AF_INET, SOCK_DGRAM, 0);
    68      if (sock < 0) {
    69          perror("socket");
    70          return 1;
    71      }
    72  
    73      struct ifreq req;
    74      memset(&req, 0, sizeof(req));
    75      strncpy(req.ifr_name, "lo", IFNAMSIZ);
    76      if (ioctl(sock, SIOCGIFFLAGS, &req) < 0) {
    77          perror("SIOCGIFFLAGS");
    78          return 1;
    79      }
    80  
    81      req.ifr_flags |= IFF_UP;
    82      if (ioctl(sock, SIOCSIFFLAGS, &req) < 0) {
    83          perror("SIOCSIFFLAGS");
    84          return 1;
    85      }
    86      close(sock);
    87      return 0;
    88  }
    89  
    90  // mount_tmp mounts a tmpfs on /tmp for the tests to muck about in.
    91  int mount_tmp() {
    92      // Remounting / as private is necessary so that the tmpfs mount isn't visible to anyone else.
    93      if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) {
    94          perror("remount");
    95          return 1;
    96      }
    97      const int flags = MS_LAZYTIME | MS_NOATIME | MS_NODEV | MS_NOSUID;
    98      if (mount("tmpfs", "/tmp", "tmpfs", flags, NULL) != 0) {
    99          perror("mount");
   100          return 1;
   101      }
   102      return setenv("TMPDIR", "/tmp", 1);
   103  }
   104  
   105  // contain separates the process into new namespaces to sandbox it.
   106  int contain(char* argv[]) {
   107      if (unshare(CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWNS) != 0) {
   108          return 1;
   109      }
   110      if (mount_tmp() != 0) {
   111        return 1;
   112      }
   113      if (lo_up() != 0) {
   114          return 1;
   115      }
   116      if (drop_root() != 0) {
   117          return 1;
   118      }
   119      return execvp(argv[0], argv);
   120  }
   121  
   122  #else
   123  
   124  // On non-Linux systems contain simply execs a subprocess.
   125  // It's not really expected to be used there, this is simply to make it compile.
   126  int contain(char* argv[]) {
   127      return execvp(argv[0], argv);
   128  }
   129  
   130  #endif
   131  
   132  
   133  int main(int argc, char* argv[]) {
   134      if (argc < 2) {
   135          fputs("please_sandbox implements sandboxing for Please.\n", stderr);
   136          fputs("It takes no flags, it simply executes the command given as arguments.\n", stderr);
   137          fputs("Usage: plz_sandbox command args...\n", stderr);
   138          exit(1);
   139      }
   140      return contain(&argv[1]);
   141  }