void cpu_print_filter(pid_t pid) { EUID_ASSERT(); // if the pid is that of a firejail process, use the pid of the first child process EUID_ROOT(); // grsecurity char *comm = pid_proc_comm(pid); EUID_USER(); // grsecurity if (comm) { if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; } } 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 denied.\n"); exit(1); } } print_cpu(pid); exit(0); }
pid_t switch_to_child(pid_t pid) { EUID_ROOT(); errno = 0; char *comm = pid_proc_comm(pid); if (!comm) { if (errno == ENOENT) { fprintf(stderr, "Error: cannot find process with pid %d\n", pid); exit(1); } else { fprintf(stderr, "Error: cannot read /proc file\n"); exit(1); } } EUID_USER(); if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 1) { fprintf(stderr, "Error: no valid sandbox\n"); exit(1); } fmessage("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) child); pid = child; } free(comm); return pid; }
// return 1 if firejail --x11 on command line int pid_proc_cmdline_x11(const pid_t pid) { // if comm is not firejail return 0 char *comm = pid_proc_comm(pid); if (strcmp(comm, "firejail") != 0) { free(comm); return 0; } free(comm); // open /proc/pid/cmdline file char *fname; int fd; if (asprintf(&fname, "/proc/%d/cmdline", pid) == -1) return 0; if ((fd = open(fname, O_RDONLY)) < 0) { free(fname); return 0; } free(fname); // read file unsigned char buffer[BUFLEN]; ssize_t len; if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { close(fd); return 0; } buffer[len] = '\0'; close(fd); // skip the first argument int i; for (i = 0; buffer[i] != '\0'; i++); // parse remaining command line options while (1) { // extract argument i++; if (i >= len) break; char *arg = (char *)buffer + i; // detect the last command line option if (strcmp(arg, "--") == 0) break; if (strncmp(arg, "--", 2) != 0) break; // check x11 if (strncmp(arg, "--x11", 5) == 0) return 1; i += strlen(arg); } return 0; }
// --protocol.print void protocol_print_filter(pid_t pid) { EUID_ASSERT(); (void) pid; #ifdef SYS_socket // 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; } } 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 denied.\n"); exit(1); } } // find the seccomp filter EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_PROTOCOL_CFG) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == -1) { printf("Cannot access seccomp filter.\n"); exit(1); } // read and print the filter protocol_filter_load(fname); free(fname); if (cfg.protocol) printf("%s\n", cfg.protocol); exit(0); #else fprintf(stderr, "Warning: --protocol not supported on this platform\n"); return; #endif }
void seccomp_print_filter(pid_t pid) { // 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; } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { struct stat s; char *dir; if (asprintf(&dir, "/proc/%u/ns", pid) == -1) errExit("asprintf"); if (stat(dir, &s) < 0) errExit("stat"); if (s.st_uid != uid) { printf("Error: permission denied.\n"); exit(1); } } // find the seccomp filter char *fname; if (asprintf(&fname, "/proc/%d/root/tmp/firejail/mnt/seccomp", pid) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == -1) { printf("Cannot access seccomp filter.\n"); exit(1); } // read and print the filter read_seccomp_file(fname); drop_privs(1); filter_debug(); exit(0); }
void seccomp_print_filter(pid_t pid) { EUID_ASSERT(); // 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; } } 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 denied.\n"); exit(1); } } // find the seccomp filter EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_CFG) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == -1) { printf("Cannot access seccomp filter.\n"); exit(1); } // read and print the filter read_seccomp_file(fname); drop_privs(1); filter_debug(); free(fname); exit(0); }
void seccomp_print_filter(pid_t pid) { // 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; } } 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 denied to print seccomp filters (sandbox belongs to a different user).\n"); exit(1); } } // find the seccomp filter char *fname; if (asprintf(&fname, "/proc/%d/root/tmp/firejail/mnt/seccomp", pid) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == -1) { exechelp_logerrv("firejail", FIREJAIL_ERROR, "Error: Cannot access seccomp filter.\n"); exit(1); } // read and print the filter read_seccomp_file(fname); drop_privs(1); filter_debug(); exit(0); }
void seccomp_print_filter(pid_t pid) { EUID_ASSERT(); // 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; } } 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 denied.\n"); exit(1); } } // find the seccomp filter EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_CFG) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == -1) { printf("Cannot access seccomp filter.\n"); exit(1); } // read and print the filter - run this as root, the user doesn't have access sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FSECCOMP, "print", fname); free(fname); exit(0); }
void caps_print_filter(pid_t pid) { // 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; } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { struct stat s; char *dir; if (asprintf(&dir, "/proc/%u/ns", pid) == -1) errExit("asprintf"); if (stat(dir, &s) < 0) errExit("stat"); if (s.st_uid != uid) { printf("Error: permission denied.\n"); exit(1); } } uint64_t caps = extract_caps(pid); drop_privs(1); int i; uint64_t mask; int elems = sizeof(capslist) / sizeof(capslist[0]); for (i = 0, mask = 1; i < elems; i++, mask <<= 1) { printf("%-18.18s - %s\n", capslist[i].name, (mask & caps)? "enabled": "disabled"); } exit(0); }
void caps_print_filter(pid_t pid) { EUID_ASSERT(); // 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; } } 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 denied.\n"); exit(1); } } uint64_t caps = extract_caps(pid); drop_privs(1); int i; uint64_t mask; int elems = sizeof(capslist) / sizeof(capslist[0]); for (i = 0, mask = 1; i < elems; i++, mask <<= 1) { printf("%-18.18s - %s\n", capslist[i].name, (mask & caps)? "enabled": "disabled"); } exit(0); }
void net_dns_print(pid_t pid) { // drop privileges - will not be able to read /etc/resolv.conf for --noroot option // drop_privs(1); // 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; } } free(comm); } char *fname; if (asprintf(&fname, "/proc/%d/root/etc/resolv.conf", pid) == -1) errExit("asprintf"); // access /etc/resolv.conf FILE *fp = fopen(fname, "r"); if (!fp) { fprintf(stderr, "Error: cannot access /etc/resolv.conf\n"); exit(1); } char buf[MAXBUF]; while (fgets(buf, MAXBUF, fp)) printf("%s", buf); printf("\n"); fclose(fp); free(fname); exit(0); }
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, 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); }
// return 1 if error int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } char *cmd = pid_proc_cmdline(newpid); if (cmd) { // mark the end of the name char *ptr = strstr(cmd, "--name="); char *start = ptr; if (!ptr) { free(cmd); // extract name for /run/mnt/firejail/fslogger file char *fname; if (asprintf(&fname, "/proc/%d/root/run/firejail/mnt/fslogger", newpid) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == 0) { FILE *fp = fopen(fname, "r"); if (fp) { char buf[BUFLEN]; if (fgets(buf, BUFLEN, fp)) { if (strncmp(buf, "sandbox name: ", 14) == 0) { char *ptr2 = buf + 14; if (strncmp(name, ptr2, strlen(name)) == 0) { fclose(fp); *pid = newpid; closedir(dir); return 0; } } } fclose(fp); } } continue; } while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; *ptr = '\0'; int rv = strcmp(start + 7, name); if (rv == 0) { free(cmd); *pid = newpid; closedir(dir); return 0; } free(cmd); } } closedir(dir); return 1; }
// return 1 if error // this function requires root access - todo: fix it! int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } // look for the sandbox name in /run/firejail/name/<PID> // todo: use RUN_FIREJAIL_NAME_DIR define from src/firejail/firejail.h char *fname; if (asprintf(&fname, "/run/firejail/name/%d", newpid) == -1) errExit("asprintf"); FILE *fp = fopen(fname, "r"); if (fp) { char buf[BUFLEN]; if (fgets(buf, BUFLEN, fp)) { // remove \n char *ptr = strchr(buf, '\n'); if (ptr) { *ptr = '\0'; if (strcmp(buf, name) == 0) { // we found it! fclose(fp); free(fname); *pid = newpid; closedir(dir); return 0; } } else fprintf(stderr, "Error: invalid %s\n", fname); } fclose(fp); } free(fname); } closedir(dir); return 1; }
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); }
void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) { //************************ // verify sandbox //************************ char *comm = pid_proc_comm(pid); if (!comm) { fprintf(stderr, "Error: cannot find sandbox\n"); exit(1); } // remove \n and check for firejail sandbox char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail") != 0) { fprintf(stderr, "Error: cannot find sandbox\n"); exit(1); } free(comm); // check network namespace char *cmd = pid_proc_cmdline(pid); if (!cmd || strstr(cmd, "--net") == NULL) { fprintf(stderr, "Error: the sandbox doesn't use a new network namespace\n"); exit(1); } free(cmd); //************************ // join the network namespace //************************ pid_t child; if (find_child(pid, &child) == -1) { fprintf(stderr, "Error: cannot join the network namespace\n"); exit(1); } if (join_namespace(child, "net")) { fprintf(stderr, "Error: cannot join the network namespace\n"); exit(1); } // set shm file if (strcmp(command, "set") == 0) bandwidth_shm_set(pid, dev, down, up); else if (strcmp(command, "clear") == 0) bandwidth_shm_remove(pid, dev); //************************ // build command //************************ char *devname = NULL; if (dev) { // read shm network map file char *fname; if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1) errExit("asprintf"); FILE *fp = fopen(fname, "r"); if (!fp) { fprintf(stderr, "Error: cannot read netowk map filel %s\n", fname); exit(1); } char buf[1024]; int len = strlen(dev); while (fgets(buf, 1024, fp)) { // remove '\n' char *ptr = strchr(buf, '\n'); if (ptr) *ptr = '\0'; if (*buf == '\0') break; if (strncmp(buf, dev, len) == 0 && buf[len] == ':') { devname = strdup(buf + len + 1); if (!devname) errExit("strdup"); // check device in namespace if (if_nametoindex(devname) == 0) { fprintf(stderr, "Error: cannot find network device %s\n", devname); exit(1); } break; } } free(fname); fclose(fp); } // build fshaper.sh command cmd = NULL; if (devname) { if (strcmp(command, "set") == 0) { if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s %d %d", PREFIX, command, devname, down, up) == -1) errExit("asprintf"); } else { if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s", PREFIX, command, devname) == -1) errExit("asprintf"); } } else { if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s", PREFIX, command) == -1) errExit("asprintf"); } assert(cmd); // wipe out environment variables environ = NULL; //************************ // build command //************************ // elevate privileges if (setreuid(0, 0)) errExit("setreuid"); if (setregid(0, 0)) errExit("setregid"); char *arg[4]; arg[0] = "/bin/bash"; arg[1] = "-c"; arg[2] = cmd; arg[3] = NULL; execvp("/bin/bash", arg); // it will never get here exit(0); }
// return 1 if error int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { exechelp_logerrv("firejail", FIREJAIL_ERROR, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } char *cmd = pid_proc_cmdline(newpid); if (cmd) { // mark the end of the name char *ptr = strstr(cmd, "--name="); char *start = ptr; if (!ptr) { free(cmd); continue; } // set a NULL pointer on the next identified argument char *next_for_sure = strstr(ptr, " --"); if (next_for_sure) *next_for_sure = '\0'; // else we can't allow spaces in the --name argument else { while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; *ptr = '\0'; } int rv = strcmp(start + 7, name); if (rv == 0) { free(cmd); *pid = newpid; closedir(dir); return 0; } free(cmd); } } closedir(dir); return 1; }
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); }
//*********************************** // command execution //*********************************** void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) { EUID_ASSERT(); //************************ // verify sandbox //************************ EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (!comm) { fprintf(stderr, "Error: cannot find sandbox\n"); exit(1); } // check for firejail sandbox if (strcmp(comm, "firejail") != 0) { fprintf(stderr, "Error: cannot find sandbox\n"); exit(1); } free(comm); // check network namespace char *name; if (asprintf(&name, "/run/firejail/network/%d-netmap", pid) == -1) errExit("asprintf"); struct stat s; if (stat(name, &s) == -1) { fprintf(stderr, "Error: the sandbox doesn't use a new network namespace\n"); exit(1); } //************************ // join the network namespace //************************ pid_t child; if (find_child(pid, &child) == -1) { fprintf(stderr, "Error: cannot join the network namespace\n"); exit(1); } EUID_ROOT(); if (join_namespace(child, "net")) { fprintf(stderr, "Error: cannot join the network namespace\n"); exit(1); } // set run file if (strcmp(command, "set") == 0) bandwidth_set(pid, dev, down, up); else if (strcmp(command, "clear") == 0) bandwidth_remove(pid, dev); //************************ // build command //************************ char *devname = NULL; if (dev) { // read network map file char *fname; if (asprintf(&fname, "%s/%d-netmap", RUN_FIREJAIL_NETWORK_DIR, (int) pid) == -1) errExit("asprintf"); FILE *fp = fopen(fname, "r"); if (!fp) { fprintf(stderr, "Error: cannot read network map file %s\n", fname); exit(1); } char buf[1024]; int len = strlen(dev); while (fgets(buf, 1024, fp)) { // remove '\n' char *ptr = strchr(buf, '\n'); if (ptr) *ptr = '\0'; if (*buf == '\0') break; if (strncmp(buf, dev, len) == 0 && buf[len] == ':') { devname = strdup(buf + len + 1); if (!devname) errExit("strdup"); // check device in namespace if (if_nametoindex(devname) == 0) { fprintf(stderr, "Error: cannot find network device %s\n", devname); exit(1); } break; } } free(fname); fclose(fp); } // build fshaper.sh command char *cmd = NULL; if (devname) { if (strcmp(command, "set") == 0) { if (asprintf(&cmd, "%s/firejail/fshaper.sh --%s %s %d %d", LIBDIR, command, devname, down, up) == -1) errExit("asprintf"); } else { if (asprintf(&cmd, "%s/firejail/fshaper.sh --%s %s", LIBDIR, command, devname) == -1) errExit("asprintf"); } } else { if (asprintf(&cmd, "%s/firejail/fshaper.sh --%s", LIBDIR, command) == -1) errExit("asprintf"); } assert(cmd); // wipe out environment variables environ = NULL; //************************ // build command //************************ // elevate privileges if (setreuid(0, 0)) errExit("setreuid"); if (setregid(0, 0)) errExit("setregid"); char *arg[4]; arg[0] = "/bin/sh"; arg[1] = "-c"; arg[2] = cmd; arg[3] = NULL; clearenv(); execvp(arg[0], arg); // it will never get here errExit("execvp"); }
void shut(pid_t pid) { EUID_ASSERT(); EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (comm) { if (strcmp(comm, "firejail") != 0) { fprintf(stderr, "Error: this is not a firejail sandbox\n"); exit(1); } free(comm); } else errExit("/proc/PID/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 shutdown a sandbox created by a different user.\n"); exit(1); } } printf("Sending SIGTERM to %u\n", pid); kill(pid, SIGTERM); // wait for not more than 11 seconds int monsec = 11; char *monfile; if (asprintf(&monfile, "/proc/%d/cmdline", pid) == -1) errExit("asprintf"); int killdone = 0; while (monsec) { sleep(1); monsec--; FILE *fp = fopen(monfile, "r"); if (!fp) { killdone = 1; break; } char c; size_t count = fread(&c, 1, 1, fp); fclose(fp); if (count == 0) { // all done killdone = 1; break; } } free(monfile); // force SIGKILL if (!killdone) { // kill the process and its child pid_t child; if (find_child(pid, &child) == 0) { printf("Sending SIGKILL to %u\n", child); kill(child, SIGKILL); } printf("Sending SIGKILL to %u\n", pid); kill(pid, SIGKILL); } EUID_ROOT(); delete_run_files(pid); }
// return 1 if error int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } char *cmd = pid_proc_cmdline(newpid); if (cmd) { // mark the end of the name char *ptr = strstr(cmd, "--name="); char *start = ptr; if (!ptr) { free(cmd); continue; } while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; *ptr = '\0'; int rv = strcmp(start + 7, name); if (rv == 0) { free(cmd); *pid = newpid; closedir(dir); return 0; } free(cmd); } } closedir(dir); return 1; }
static int monitor_application(pid_t app_pid) { monitored_pid = app_pid; signal (SIGTERM, sandbox_handler); EUID_USER(); int status = 0; while (monitored_pid) { usleep(20000); char *msg; if (asprintf(&msg, "monitoring pid %d\n", monitored_pid) == -1) errExit("asprintf"); logmsg(msg); if (arg_debug) printf("%s\n", msg); free(msg); pid_t rv; do { rv = waitpid(-1, &status, 0); if (rv == -1) break; } while(rv != monitored_pid); if (arg_debug) printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; monitored_pid = 0; while ((entry = readdir(dir)) != NULL) { unsigned pid; if (sscanf(entry->d_name, "%u", &pid) != 1) continue; if (pid == 1) continue; // todo: make this generic // Dillo browser leaves a dpid process running, we need to shut it down if (strcmp(cfg.command_name, "dillo") == 0) { char *pidname = pid_proc_comm(pid); if (pidname && strcmp(pidname, "dpid") == 0) break; free(pidname); } monitored_pid = pid; break; } closedir(dir); if (monitored_pid != 0 && arg_debug) printf("Sandbox monitor: monitoring %u\n", monitored_pid); } // return the latest exit status. return status; #if 0 // todo: find a way to shut down interfaces before closing the namespace // the problem is we don't have enough privileges to shutdown interfaces in this moment // shut down bridge/macvlan interfaces if (any_bridge_configured()) { if (cfg.bridge0.configured) { printf("Shutting down %s\n", cfg.bridge0.devsandbox); net_if_down( cfg.bridge0.devsandbox); } if (cfg.bridge1.configured) { printf("Shutting down %s\n", cfg.bridge1.devsandbox); net_if_down( cfg.bridge1.devsandbox); } if (cfg.bridge2.configured) { printf("Shutting down %s\n", cfg.bridge2.devsandbox); net_if_down( cfg.bridge2.devsandbox); } if (cfg.bridge3.configured) { printf("Shutting down %s\n", cfg.bridge3.devsandbox); net_if_down( cfg.bridge3.devsandbox); } usleep(20000); // 20 ms sleep } #endif }