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 }