on master help sign in
Merge pull request #41 from input-output-hk/escape-hatch

add an escape-hatch option to allow things like xdg-open to work inside the sandbox

commit113d8c6b426b0932a64c58c21cd065baad4c2314 authorMatthew Bauer <mjbauer95@gmail.com> dateSun 21 Oct 2018, 19:41 (8 years ago) parent014d3a67f12322
1 file changed+75−4
nix-user-chroot/main.cpp+75−4
nix-user-chroot/main.cpp+75−4
@@ -26,6 +26,10 @@
2626using namespace std;
2727
2828#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[]);
30+
31+volatile uint8_t child_died = 0;
32+int child_pid = 0;
2933
3034static void usage(const char *pname) {
3135 fprintf(stderr, "Usage: %s -n <nixpath> -- <command>\n", pname);
@@ -33,6 +37,7 @@ static void usage(const char *pname) {
3337 fprintf(stderr, "\t-m <src>:<src>\tmap src on the host to dest in the sandbox\n");
3438 fprintf(stderr, "\t-d\tdelete all default dir mappings, may break things\n");
3539 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");
3641
3742 exit(EXIT_FAILURE);
3843}
@@ -91,8 +96,15 @@ struct DirMapping parseMapping(string input) {
9196 return (struct DirMapping){ src, dest };
9297}
9398
99+static void handle_child_death(int signo, siginfo_t *info, void *context) {
100+ if ( (child_pid == 0) || (info->si_pid == child_pid) ) {
101+ child_died = 1;
102+ }
103+}
104+
94105int main(int argc, char *argv[]) {
95106 uint8_t clear_env = 0;
107+ uint8_t enable_escape_hatch = 0;
96108 char *nixdir = NULL;
97109 list<struct DirMapping> dirMappings;
98110 list<struct SetEnv> envMappings;
@@ -111,11 +123,14 @@ int main(int argc, char *argv[]) {
111123#undef x
112124
113125 int opt;
114− while ((opt = getopt(argc, argv, "cn:m:dp:")) != -1) {
126+ while ((opt = getopt(argc, argv, "cen:m:dp:")) != -1) {
115127 switch (opt) {
116128 case 'c':
117129 clear_env = 1;
118130 break;
131+ case 'e':
132+ enable_escape_hatch = 1;
133+ break;
119134 case 'n':
120135 // determine absolute directory for nix dir
121136 nixdir = realpath(optarg, NULL);
@@ -163,6 +178,62 @@ int main(int argc, char *argv[]) {
163178 err_exit("mkdtemp(%s)", template_);
164179 }
165180
181+ int unrace[2];
182+
183+ if (pipe(unrace)) {
184+ err_exit("pipe()");
185+ }
186+
187+ struct sigaction handle_child;
188+ handle_child.sa_sigaction = handle_child_death;
189+ handle_child.sa_flags = SA_SIGINFO;
190+
191+ struct sigaction old_handler;
192+ if (sigaction(SIGCHLD, &handle_child, &old_handler)) {
193+ err_exit("sigaction()");
194+ }
195+
196+ int child;
197+ child = child_pid = fork();
198+ if (child < 0) {
199+ err_exit("fork()");
200+ } else if (child == 0) {
201+ sigaction(SIGCHLD, &old_handler, NULL);
202+ close(unrace[1]);
203+ char buf[10];
204+ read(unrace[0], buf, 10);
205+ close(unrace[0]);
206+ return child_proc(rootdir, nixdir, clear_env, dirMappings, envMappings, argv[optind], argv + optind);
207+ } else {
208+ close(unrace[0]);
209+ char fifopath[PATH_MAX];
210+ if (enable_escape_hatch) {
211+ snprintf(fifopath, PATH_MAX, "%s/escape-hatch", rootdir);
212+ mkfifo(fifopath, 0600);
213+ }
214+ close(unrace[1]);
215+ if (enable_escape_hatch) {
216+ char buffer[1024];
217+ while (!child_died) {
218+ int fd = open(fifopath, O_RDONLY);
219+ if (fd < 0) {
220+ if (errno == EINTR) continue;
221+ fprintf(stderr, "error opening escape-hatch: %s\n", strerror(errno));
222+ continue;
223+ }
224+ int size = read(fd, buffer, 1024);
225+ buffer[size] = 0;
226+ system(buffer);
227+ close(fd);
228+ }
229+ }
230+ int status;
231+ int ret = waitpid(child, &status, 0);
232+ return WEXITSTATUS(status);
233+ }
234+}
235+
236+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[]) {
166237 // get uid, gid before going to new namespace
167238 uid_t uid = getuid();
168239 gid_t gid = getgid();
@@ -170,7 +241,7 @@ int main(int argc, char *argv[]) {
170241 // "unshare" into new namespace
171242 if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) {
172243 if (errno == EPERM) {
173− 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);
244+ 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);
174245 exit(EXIT_FAILURE);
175246 } else {
176247 err_exit("unshare()");
@@ -230,6 +301,6 @@ int main(int argc, char *argv[]) {
230301 }
231302
232303 // execute the command
233− execvp(argv[optind], argv + optind);
234− err_exit("execvp(%s)", argv[optind]);
304+ execvp(executable, new_argv);
305+ err_exit("execvp(%s)", executable);
235306}