void join(pid_t pid, int argc, char **argv, int index) { EUID_ASSERT(); char *homedir = cfg.homedir; extract_command(argc, argv, index); signal (SIGTERM, signal_handler); // if the pid is that of a firejail process, use the pid of the first child process EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (comm) { if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid); } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n"); exit(1); } } EUID_ROOT(); // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_caps_seccomp(pid); extract_cpu(pid); extract_cgroup(pid); extract_nogroups(pid); extract_user_namespace(pid); } // set cgroup if (cfg.cgroup) // not available for uid 0 set_cgroup(cfg.cgroup); // join namespaces if (arg_join_network) { if (join_namespace(pid, "net")) exit(1); } else if (arg_join_filesystem) { if (join_namespace(pid, "mnt")) exit(1); } else { if (join_namespace(pid, "ipc")) exit(1); if (join_namespace(pid, "net")) exit(1); if (join_namespace(pid, "pid")) exit(1); if (join_namespace(pid, "uts")) exit(1); if (join_namespace(pid, "mnt")) exit(1); } pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // chroot into /proc/PID/root directory char *rootdir; if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); int rv; if (!arg_join_network) { rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option if (rv == 0) printf("changing root to %s\n", rootdir); } prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died if (chdir("/") < 0) errExit("chdir"); if (homedir) { struct stat s; if (stat(homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(homedir) < 0) errExit("chdir"); } } // set cpu affinity if (cfg.cpus) // not available for uid 0 set_cpu_affinity(); // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP // set protocol filter if (getuid() != 0) protocol_filter_load(RUN_PROTOCOL_CFG); if (cfg.protocol) { // not available for uid 0 protocol_filter(); } // set seccomp filter if (apply_seccomp == 1) // not available for uid 0 seccomp_set(); #endif // fix qt 4.8 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) errExit("setenv"); if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, errExit("setenv"); // mount user namespace or drop privileges if (arg_noroot) { // not available for uid 0 if (arg_debug) printf("Joining user namespace\n"); if (join_namespace(1, "user")) exit(1); // user namespace resets capabilities // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); } else drop_privs(arg_nogroups); // nogroups not available for uid 0 // set prompt color to green char *prompt = getenv("FIREJAIL_PROMPT"); if (prompt && strcmp(prompt, "yes") == 0) { //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) errExit("setenv"); } // set nice if (arg_nice) { errno = 0; int rv = nice(cfg.nice); (void) rv; if (errno) { fprintf(stderr, "Warning: cannot set nice value\n"); errno = 0; } } // run cmdline trough shell if (cfg.command_line == NULL) { // if the sandbox was started with --shell=none, it is possible we don't have a shell // inside the sandbox if (cfg.shell == NULL) { cfg.shell = guess_shell(); if (!cfg.shell) { fprintf(stderr, "Error: no POSIX shell found, please use --shell command line option\n"); exit(1); } } struct stat s; if (stat(cfg.shell, &s) == -1) { fprintf(stderr, "Error: %s shell not found inside the sandbox\n", cfg.shell); exit(1); } cfg.command_line = cfg.shell; cfg.window_title = cfg.shell; } int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } start_application(); // it will never get here!!! } // wait for the child to finish waitpid(child, NULL, 0); flush_stdin(); exit(0); }
void join(pid_t pid, int argc, char **argv, int index) { EUID_ASSERT(); char *homedir = cfg.homedir; extract_command(argc, argv, index); // if the pid is that of a firejail process, use the pid of the first child process EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (comm) { if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid); } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n"); exit(1); } } EUID_ROOT(); // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_caps_seccomp(pid); extract_cpu(pid); extract_cgroup(pid); extract_nogroups(pid); extract_user_namespace(pid); } // set cgroup if (cfg.cgroup) // not available for uid 0 set_cgroup(cfg.cgroup); // join namespaces if (arg_join_network) { if (join_namespace(pid, "net")) exit(1); } else if (arg_join_filesystem) { if (join_namespace(pid, "mnt")) exit(1); } else { if (join_namespace(pid, "ipc")) exit(1); if (join_namespace(pid, "net")) exit(1); if (join_namespace(pid, "pid")) exit(1); if (join_namespace(pid, "uts")) exit(1); if (join_namespace(pid, "mnt")) exit(1); } pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // chroot into /proc/PID/root directory char *rootdir; if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); int rv; if (!arg_join_network) { rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option if (rv == 0) printf("changing root to %s\n", rootdir); } prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died if (chdir("/") < 0) errExit("chdir"); if (homedir) { struct stat s; if (stat(homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(homedir) < 0) errExit("chdir"); } } // set cpu affinity if (cfg.cpus) // not available for uid 0 set_cpu_affinity(); // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP // set protocol filter if (getuid() != 0) protocol_filter_load(RUN_PROTOCOL_CFG); if (cfg.protocol) { // not available for uid 0 protocol_filter(); } // set seccomp filter if (apply_seccomp == 1) // not available for uid 0 seccomp_set(); #endif // fix qt 4.8 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) errExit("setenv"); if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, errExit("setenv"); // mount user namespace or drop privileges if (arg_noroot) { // not available for uid 0 if (arg_debug) printf("Joining user namespace\n"); if (join_namespace(1, "user")) exit(1); } else drop_privs(arg_nogroups); // nogroups not available for uid 0 // set prompt color to green //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) errExit("setenv"); // run cmdline trough /bin/bash if (cfg.command_line == NULL) { struct stat s; // replace the process with a shell if (stat("/bin/bash", &s) == 0) execlp("/bin/bash", "/bin/bash", NULL); else if (stat("/usr/bin/zsh", &s) == 0) execlp("/usr/bin/zsh", "/usr/bin/zsh", NULL); else if (stat("/bin/csh", &s) == 0) execlp("/bin/csh", "/bin/csh", NULL); else if (stat("/bin/sh", &s) == 0) execlp("/bin/sh", "/bin/sh", NULL); // no shell found, print an error and exit fprintf(stderr, "Error: no POSIX shell found\n"); sleep(5); exit(1); } else { // run the command supplied by the user int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } char *arg[5]; arg[0] = "/bin/bash"; arg[1] = "-c"; if (arg_debug) printf("Starting %s\n", cfg.command_line); if (!arg_doubledash) { arg[2] = cfg.command_line; arg[3] = NULL; } else { arg[2] = "--"; arg[3] = cfg.command_line; arg[4] = NULL; } execvp("/bin/bash", arg); } // it will never get here!!! } // wait for the child to finish waitpid(child, NULL, 0); exit(0); }
void join(pid_t pid, int argc, char **argv, int index) { EUID_ASSERT(); pid_t parent = pid; // in case the pid is that of a firejail process, use the pid of the first child process pid = switch_to_child(pid); // now check if the pid belongs to a firejail sandbox if (invalid_sandbox(pid)) { fprintf(stderr, "Error: no valid sandbox\n"); exit(1); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n"); exit(1); } } extract_x11_display(parent); EUID_ROOT(); // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_nonewprivs(pid); // redundant on Linux >= 4.10; duplicated in function extract_caps extract_caps(pid); extract_cpu(pid); extract_cgroup(pid); extract_nogroups(pid); extract_user_namespace(pid); } // set cgroup if (cfg.cgroup) // not available for uid 0 set_cgroup(cfg.cgroup); // set umask, also uid 0 extract_umask(pid); // join namespaces if (arg_join_network) { if (join_namespace(pid, "net")) exit(1); } else if (arg_join_filesystem) { if (join_namespace(pid, "mnt")) exit(1); } else { if (join_namespace(pid, "ipc") || join_namespace(pid, "net") || join_namespace(pid, "pid") || join_namespace(pid, "uts") || join_namespace(pid, "mnt")) exit(1); } pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // drop discretionary access control capabilities for root sandboxes caps_drop_dac_override(); // chroot into /proc/PID/root directory char *rootdir; if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); int rv; if (!arg_join_network) { rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option if (rv == 0) printf("changing root to %s\n", rootdir); } EUID_USER(); if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } // set caps filter EUID_ROOT(); if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP if (getuid() != 0) seccomp_load_file_list(); #endif // mount user namespace or drop privileges if (arg_noroot) { // not available for uid 0 if (arg_debug) printf("Joining user namespace\n"); if (join_namespace(1, "user")) exit(1); // user namespace resets capabilities // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); } // set nonewprivs if (arg_nonewprivs == 1) { // not available for uid 0 int rv = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); if (arg_debug && rv == 0) printf("NO_NEW_PRIVS set\n"); } EUID_USER(); int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } // drop privileges drop_privs(arg_nogroups); // kill the child in case the parent died prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); extract_command(argc, argv, index); if (cfg.command_line == NULL) { assert(cfg.shell); cfg.command_line = cfg.shell; cfg.window_title = cfg.shell; } if (arg_debug) printf("Extracted command #%s#\n", cfg.command_line); // set cpu affinity if (cfg.cpus) // not available for uid 0 set_cpu_affinity(); // set nice value if (arg_nice) set_nice(cfg.nice); // add x11 display if (display) { char *display_str; if (asprintf(&display_str, ":%d", display) == -1) errExit("asprintf"); setenv("DISPLAY", display_str, 1); free(display_str); } start_application(0, NULL); // it will never get here!!! } int status = 0; //***************************** // following code is signal-safe install_handler(); // wait for the child to finish waitpid(child, &status, 0); // restore default signal action signal(SIGTERM, SIG_DFL); // end of signal-safe code //***************************** flush_stdin(); if (WIFEXITED(status)) { status = WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { status = WTERMSIG(status); } else { status = 0; } exit(status); }
void join(pid_t pid, int argc, char **argv, int index) { EUID_ASSERT(); char *homedir = cfg.homedir; extract_command(argc, argv, index); signal (SIGTERM, signal_handler); // if the pid is that of a firejail process, use the pid of the first child process EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (comm) { if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid); } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n"); exit(1); } } EUID_ROOT(); // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_caps_seccomp(pid); extract_cpu(pid); extract_cgroup(pid); extract_nogroups(pid); extract_user_namespace(pid); } // set cgroup if (cfg.cgroup) // not available for uid 0 set_cgroup(cfg.cgroup); // join namespaces if (arg_join_network) { if (join_namespace(pid, "net")) exit(1); } else if (arg_join_filesystem) { if (join_namespace(pid, "mnt")) exit(1); } else { if (join_namespace(pid, "ipc") || join_namespace(pid, "net") || join_namespace(pid, "pid") || join_namespace(pid, "uts") || join_namespace(pid, "mnt")) exit(1); } pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // chroot into /proc/PID/root directory char *rootdir; if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); int rv; if (!arg_join_network) { rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option if (rv == 0) printf("changing root to %s\n", rootdir); } prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died if (chdir("/") < 0) errExit("chdir"); if (homedir) { struct stat s; if (stat(homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(homedir) < 0) errExit("chdir"); } } // set cpu affinity if (cfg.cpus) // not available for uid 0 set_cpu_affinity(); // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP // read cfg.protocol from file if (getuid() != 0) protocol_filter_load(RUN_PROTOCOL_CFG); if (cfg.protocol) { // not available for uid 0 seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter } // set seccomp filter if (apply_seccomp == 1) // not available for uid 0 seccomp_load(RUN_SECCOMP_CFG); #endif // mount user namespace or drop privileges if (arg_noroot) { // not available for uid 0 if (arg_debug) printf("Joining user namespace\n"); if (join_namespace(1, "user")) exit(1); // user namespace resets capabilities // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); } else drop_privs(arg_nogroups); // nogroups not available for uid 0 // set nice if (arg_nice) { errno = 0; int rv = nice(cfg.nice); (void) rv; if (errno) { fprintf(stderr, "Warning: cannot set nice value\n"); errno = 0; } } env_defaults(); if (cfg.command_line == NULL) { assert(cfg.shell); cfg.command_line = cfg.shell; cfg.window_title = cfg.shell; } int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } start_application(); // it will never get here!!! } // wait for the child to finish waitpid(child, NULL, 0); flush_stdin(); exit(0); }
void join(pid_t pid, const char *homedir, int argc, char **argv, int index) { extract_command(argc, argv, index); // if the pid is that of a firejail process, use the pid of the first child process char *comm = pid_proc_comm(pid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid); } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { exechelp_logerrv("firejail", FIREJAIL_ERROR, "Error: permission is denied to join a sandbox created by a different user.\n"); exit(1); } } // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_caps_seccomp(pid); extract_cpu(pid); extract_cgroup(pid); extract_nogroups(pid); extract_user_namespace(pid); } // set cgroup if (cfg.cgroup) set_cgroup(cfg.cgroup); // join namespaces if (join_namespace(pid, "ipc")) exit(1); if (join_namespace(pid, "net")) exit(1); if (join_namespace(pid, "pid")) exit(1); if (join_namespace(pid, "uts")) exit(1); if (join_namespace(pid, "mnt")) exit(1); pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // chroot into /proc/PID/root directory char *rootdir; if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); int rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option if (rv == 0) printf("changing root to %s\n", rootdir); prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died if (chdir("/") < 0) errExit("chdir"); if (homedir) { struct stat s; if (stat(homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(homedir) < 0) errExit("chdir"); } } // set cpu affinity if (cfg.cpus) set_cpu_affinity(); // set caps filter if (apply_caps == 1) caps_set(caps); #ifdef HAVE_SECCOMP // set seccomp filter if (apply_seccomp == 1) seccomp_set(); #endif /* We then load the variables that the original Firejail instance applied * to the original Firejail client whose namespace we're joining. */ load_domain_env_from_chroot_proc(); // mount user namespace or drop privileges if (arg_noroot) { if (arg_debug) printf("Joining user namespace\n"); if (join_namespace(1, "user")) exit(1); } else drop_privs(arg_nogroups); // set prompt color to green //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) errExit("setenv"); // run icmdline trough /bin/bash if (cfg.command_line == NULL) // replace the process with a regular bash session execlp("/bin/bash", "/bin/bash", NULL); else { // run the command supplied by the user int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } char *arg[5]; arg[0] = "/bin/bash"; arg[1] = "-c"; if (arg_debug) printf("Starting %s\n", cfg.command_line); exechelp_logv("firejail", "Starting %s in sandbox %d\n", cfg.command_line, pid); if (!arg_doubledash) { arg[2] = cfg.command_line; arg[3] = NULL; } else { arg[2] = "--"; arg[3] = cfg.command_line; arg[4] = NULL; } execvp("/bin/bash", arg); } // it will never get here!!! } // wait for the child to finish waitpid(child, NULL, 0); exit(0); }