@@ -26,10 +26,6 @@
26using namespace std;
26using namespace std;
28#define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); }
28#define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); }
29−static int child_proc(const char *rootdir, const char *nixdir, uint8_t clear_env, list<struct DirMapping> dirMappings, list<struct SetEnv> envMappings, const char *executable, char * const new_argv[]);
31−volatile uint8_t child_died = 0;
34static void usage(const char *pname) {
30static void usage(const char *pname) {
35 fprintf(stderr, "Usage: %s -n <nixpath> -- <command>\n", pname);
31 fprintf(stderr, "Usage: %s -n <nixpath> -- <command>\n", pname);
@@ -37,7 +33,6 @@ static void usage(const char *pname) {
37 fprintf(stderr, "\t-m <src>:<src>\tmap src on the host to dest in the sandbox\n");
33 fprintf(stderr, "\t-m <src>:<src>\tmap src on the host to dest in the sandbox\n");
38 fprintf(stderr, "\t-d\tdelete all default dir mappings, may break things\n");
34 fprintf(stderr, "\t-d\tdelete all default dir mappings, may break things\n");
39 fprintf(stderr, "\t-p <var>\tpreserve the value of a variable across the -c clear\n");
35 fprintf(stderr, "\t-p <var>\tpreserve the value of a variable across the -c clear\n");
40− fprintf(stderr, "\t-e\tadd an /escape-hatch to the sandbox, and run (outside the sandbox) any strings written to it\n");
42 exit(EXIT_FAILURE);
37 exit(EXIT_FAILURE);
@@ -96,15 +91,8 @@ struct DirMapping parseMapping(string input) {
96 return (struct DirMapping){ src, dest };
91 return (struct DirMapping){ src, dest };
99−static void handle_child_death(int signo, siginfo_t *info, void *context) {
100− if ( (child_pid == 0) || (info->si_pid == child_pid) ) {
105int main(int argc, char *argv[]) {
94int main(int argc, char *argv[]) {
106 uint8_t clear_env = 0;
95 uint8_t clear_env = 0;
107− uint8_t enable_escape_hatch = 0;
108 char *nixdir = NULL;
96 char *nixdir = NULL;
109 list<struct DirMapping> dirMappings;
97 list<struct DirMapping> dirMappings;
110 list<struct SetEnv> envMappings;
98 list<struct SetEnv> envMappings;
@@ -117,21 +105,14 @@ int main(int argc, char *argv[]) {
117 x("run");
105 x("run");
118 x("tmp");
106 x("tmp");
119 x("var");
107 x("var");
127− while ((opt = getopt(argc, argv, "cen:m:dp:")) != -1) {
111+ while ((opt = getopt(argc, argv, "cn:m:dp:")) != -1) {
128 switch (opt) {
112 switch (opt) {
129 case 'c':
113 case 'c':
130 clear_env = 1;
114 clear_env = 1;
133− enable_escape_hatch = 1;
135 case 'n':
116 case 'n':
136 // determine absolute directory for nix dir
117 // determine absolute directory for nix dir
137 nixdir = realpath(optarg, NULL);
118 nixdir = realpath(optarg, NULL);
@@ -179,62 +160,6 @@ int main(int argc, char *argv[]) {
179 err_exit("mkdtemp(%s)", template_);
160 err_exit("mkdtemp(%s)", template_);
188− struct sigaction handle_child;
189− handle_child.sa_sigaction = handle_child_death;
190− handle_child.sa_flags = SA_SIGINFO;
192− struct sigaction old_handler;
193− if (sigaction(SIGCHLD, &handle_child, &old_handler)) {
194− err_exit("sigaction()");
198− child = child_pid = fork();
201− } else if (child == 0) {
202− sigaction(SIGCHLD, &old_handler, NULL);
205− read(unrace[0], buf, 10);
207− return child_proc(rootdir, nixdir, clear_env, dirMappings, envMappings, argv[optind], argv + optind);
210− char fifopath[PATH_MAX];
211− if (enable_escape_hatch) {
212− snprintf(fifopath, PATH_MAX, "%s/escape-hatch", rootdir);
213− mkfifo(fifopath, 0600);
216− if (enable_escape_hatch) {
218− while (!child_died) {
219− int fd = open(fifopath, O_RDONLY);
221− if (errno == EINTR) continue;
222− fprintf(stderr, "error opening escape-hatch: %s\n", strerror(errno));
225− int size = read(fd, buffer, 1024);
232− int ret = waitpid(child, &status, 0);
233− return WEXITSTATUS(status);
237−static int child_proc(const char *rootdir, const char *nixdir, uint8_t clear_env, list<struct DirMapping> dirMappings, list<struct SetEnv> envMappings, const char *executable, char * const new_argv[]) {
238 // get uid, gid before going to new namespace
163 // get uid, gid before going to new namespace
239 uid_t uid = getuid();
164 uid_t uid = getuid();
240 gid_t gid = getgid();
165 gid_t gid = getgid();
@@ -242,7 +167,7 @@ static int child_proc(const char *rootdir, const char *nixdir, uint8_t clear_env
242 // "unshare" into new namespace
167 // "unshare" into new namespace
243 if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) {
168 if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) {
244 if (errno == EPERM) {
169 if (errno == EPERM) {
245− fputs("Run the following to enable unprivileged namespace use:\nsudo bash -c \"sysctl -w kernel.unprivileged_userns_clone=1 ; echo kernel.unprivileged_userns_clone=1 > /etc/sysctl.d/nix-user-chroot.conf\"\n\n", stderr);
170+ fputs("Run the following as root to enable unprivileged namespace use:\nsysctl -w kernel.unprivileged_userns_clone=1 ; echo kernel.unprivileged_userns_clone=1 > /etc/sysctl.d/nix-user-chroot.conf\n\n", stderr);
246 exit(EXIT_FAILURE);
171 exit(EXIT_FAILURE);
248 err_exit("unshare()");
173 err_exit("unshare()");
@@ -256,52 +181,57 @@ static int child_proc(const char *rootdir, const char *nixdir, uint8_t clear_env
256 add_path(m.src, m.dest, rootdir);
181 add_path(m.src, m.dest, rootdir);
259− // make sure nixdir exists
184+ char path_buf[PATH_MAX];
260− struct stat statbuf2;
185+ snprintf(path_buf, sizeof(path_buf), "%s/tmp", rootdir);
261− if (stat(nixdir, &statbuf2) < 0) {
186+ mkdir(path_buf, ~0);
262− err_exit("stat(%s)", nixdir);
187+ snprintf(path_buf, sizeof(path_buf), "%s/var", rootdir);
265− char path_buf[PATH_MAX];
189+ // make sure nixdir exists
266− // mount /nix to new namespace
190+ struct stat statbuf2;
267− snprintf(path_buf, sizeof(path_buf), "%s/nix", rootdir);
191+ if (stat(nixdir, &statbuf2) < 0) {
268− mkdir(path_buf, statbuf2.st_mode & ~S_IFMT);
269− if (mount(nixdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) {
270− err_exit("mount(%s, %s)", nixdir, path_buf);
192+ err_exit("stat(%s)", nixdir);
195+ // mount /nix to new namespace
196+ snprintf(path_buf, sizeof(path_buf), "%s/nix", rootdir);
197+ mkdir(path_buf, statbuf2.st_mode & ~S_IFMT);
198+ if (mount(nixdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) {
199+ err_exit("mount(%s, %s)", nixdir, path_buf);
273− // fixes issue #1 where writing to /proc/self/gid_map fails
202+ // fixes issue #1 where writing to /proc/self/gid_map fails
274− // see user_namespaces(7) for more documentation
203+ // see user_namespaces(7) for more documentation
275− int fd_setgroups = open("/proc/self/setgroups", O_WRONLY);
204+ int fd_setgroups = open("/proc/self/setgroups", O_WRONLY);
276− if (fd_setgroups > 0) {
205+ if (fd_setgroups > 0) {
277− write(fd_setgroups, "deny", 4);
206+ write(fd_setgroups, "deny", 4);
278− close(fd_setgroups);
281− // map the original uid/gid in the new ns
209+ // map the original uid/gid in the new ns
282− char map_buf[1024];
210+ char map_buf[1024];
283− snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid);
211+ snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid);
284− update_map(map_buf, "/proc/self/uid_map");
212+ update_map(map_buf, "/proc/self/uid_map");
285− snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid);
213+ snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid);
286− update_map(map_buf, "/proc/self/gid_map");
214+ update_map(map_buf, "/proc/self/gid_map");
288− // chroot to rootdir
216+ // chroot to rootdir
289− if (chroot(rootdir) < 0) {
217+ if (chroot(rootdir) < 0) {
290− err_exit("chroot(%s)", rootdir);
218+ err_exit("chroot(%s)", rootdir);
293− chdir("/");
221+ chdir("/");
295 if (clear_env) clearenv();
223 if (clear_env) clearenv();
224+ setenv("NIX_PATH", NIX_PATH, 1);
225+ setenv("NIX_SSL_CERT_FILE", NIX_SSL_CERT_FILE, 1);
296 setenv("PATH", ENV_PATH, 1);
226 setenv("PATH", ENV_PATH, 1);
298 for (list<struct SetEnv>::iterator it = envMappings.begin();
228 for (list<struct SetEnv>::iterator it = envMappings.begin();
299− it != envMappings.end(); ++it) {
229+ it != envMappings.end(); ++it) {
300 struct SetEnv e = *it;
230 struct SetEnv e = *it;
301 setenv(e.key.c_str(), e.value.c_str(), 1);
231 setenv(e.key.c_str(), e.value.c_str(), 1);
304 // execute the command
234 // execute the command
305− execvp(executable, new_argv);
235+ execvp(argv[optind], argv + optind);
306− err_exit("execvp(%s)", executable);
236+ err_exit("execvp(%s)", argv[optind]);