/* * Copyright (C) 2014-2021 Firejail Authors * * This file is part of firejail project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "firemon.h" #include #include #include #include #include #include pid_t skip_process = 0; int arg_debug = 0; static int arg_route = 0; static int arg_arp = 0; static int arg_tree = 0; static int arg_interface = 0; static int arg_seccomp = 0; static int arg_caps = 0; static int arg_cpu = 0; static int arg_cgroup = 0; static int arg_x11 = 0; static int arg_top = 0; static int arg_list = 0; static int arg_netstats = 0; static int arg_apparmor = 0; int arg_wrap = 0; static struct termios tlocal; // startup terminal setting static struct termios twait; // no wait on key press static int terminal_set = 0; static void my_handler(int s){ // Remove unused parameter warning (void)s; if (terminal_set) tcsetattr(0, TCSANOW, &tlocal); _exit(0); } // find the second child process for the specified pid // return -1 if not found // // Example: //14776:netblue:/usr/bin/firejail /usr/bin/transmission-qt // 14777:netblue:/usr/bin/firejail /usr/bin/transmission-qt // 14792:netblue:/usr/bin/transmission-qt // We need 14792, the first real sandboxed process int find_child(int id) { int i; int first_child = -1; // find the first child for (i = 0; i < max_pids; i++) { if (pids[i].level == 2 && pids[i].parent == id) { // skip /usr/bin/xdg-dbus-proxy (started by firejail for dbus filtering) char *cmdline = pid_proc_cmdline(i); if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) == 0) { free(cmdline); continue; } free(cmdline); first_child = i; break; } } if (first_child == -1) return -1; // find the second-level child for (i = 0; i < max_pids; i++) { if (pids[i].level == 3 && pids[i].parent == first_child) return i; } // if a second child is not found, return the first child pid // this happens for processes sandboxed with --join return first_child; } // sleep and wait for a key to be pressed void firemon_sleep(int st) { if (terminal_set == 0) { tcgetattr(0, &twait); // get current terminal attributes; 0 is the file descriptor for stdin memcpy(&tlocal, &twait, sizeof(tlocal)); twait.c_lflag &= ~ICANON; // disable canonical mode twait.c_lflag &= ~ECHO; // no echo twait.c_cc[VMIN] = 1; // wait until at least one keystroke available twait.c_cc[VTIME] = 0; // no timeout terminal_set = 1; } tcsetattr(0, TCSANOW, &twait); fd_set fds; FD_ZERO(&fds); FD_SET(0,&fds); int maxfd = 1; struct timeval ts; ts.tv_sec = st; ts.tv_usec = 0; int ready = select(maxfd, &fds, (fd_set *) 0, (fd_set *) 0, &ts); (void) ready; if( FD_ISSET(0, &fds)) { getchar(); tcsetattr(0, TCSANOW, &tlocal); printf("\n"); exit(0); } tcsetattr(0, TCSANOW, &tlocal); } int main(int argc, char **argv) { unsigned pid = 0; int i; // handle CTRL-C signal (SIGINT, my_handler); signal (SIGTERM, my_handler); for (i = 1; i < argc; i++) { // default options if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) { usage(); return 0; } else if (strcmp(argv[i], "--version") == 0) { printf("firemon version %s\n\n", VERSION); return 0; } else if (strcmp(argv[i], "--debug") == 0) arg_debug = 1; // options without a pid argument else if (strcmp(argv[i], "--top") == 0) arg_top = 1; else if (strcmp(argv[i], "--list") == 0) arg_list = 1; else if (strcmp(argv[i], "--tree") == 0) arg_tree = 1; #ifdef HAVE_NETWORK else if (strcmp(argv[i], "--netstats") == 0) { struct stat s; if (getuid() != 0 && stat("/proc/sys/kernel/grsecurity", &s) == 0) { fprintf(stderr, "Error: this feature is not available on Grsecurity systems\n"); exit(1); } arg_netstats = 1; } #endif // cumulative options with or without a pid argument else if (strcmp(argv[i], "--x11") == 0) arg_x11 = 1; else if (strcmp(argv[i], "--cgroup") == 0) arg_cgroup = 1; else if (strcmp(argv[i], "--cpu") == 0) arg_cpu = 1; else if (strcmp(argv[i], "--seccomp") == 0) arg_seccomp = 1; else if (strcmp(argv[i], "--caps") == 0) arg_caps = 1; else if (strcmp(argv[i], "--interface") == 0) { if (getuid() != 0) { fprintf(stderr, "Error: you need to be root to run this command\n"); exit(1); } arg_interface = 1; } #ifdef HAVE_NETWORK else if (strcmp(argv[i], "--route") == 0) arg_route = 1; else if (strcmp(argv[i], "--arp") == 0) arg_arp = 1; #endif else if (strcmp(argv[i], "--apparmor") == 0) arg_apparmor = 1; else if (strncmp(argv[i], "--name=", 7) == 0) { char *name = argv[i] + 7; if (name2pid(name, (pid_t *) &pid)) { fprintf(stderr, "Error: cannot find sandbox %s\n", name); return 1; } } // etc else if (strcmp(argv[i], "--wrap") == 0) arg_wrap = 1; // invalid option else if (*argv[i] == '-') { fprintf(stderr, "Error: invalid option\n"); return 1; } // PID argument else { // this should be a pid number char *ptr = argv[i]; while (*ptr != '\0') { if (!isdigit(*ptr)) { fprintf(stderr, "Error: not a valid PID number\n"); exit(1); } ptr++; } sscanf(argv[i], "%u", &pid); break; } } // if the parent is firejail, skip the process pid_t ppid = getppid(); char *pcomm = pid_proc_comm(ppid); if (pcomm && strcmp(pcomm, "firejail") == 0) skip_process = ppid; // allow only root user if /proc is mounted hidepid if (pid_hidepid() && getuid() != 0) { fprintf(stderr, "Error: /proc is mounted hidepid, you would need to be root to run this command\n"); exit(1); } if (arg_top) { top(); // print all sandboxes, --name disregarded return 0; } if (arg_list) { list(); // print all sandboxes, --name disregarded return 0; } if (arg_netstats) { netstats(); // print all sandboxes, --name disregarded return 0; } if (arg_tree) { tree(pid); return 0; } // if --name requested without other options, print all data if (pid && !arg_cpu && !arg_seccomp && !arg_caps && !arg_apparmor && !arg_cgroup && !arg_x11 && !arg_interface && !arg_route && !arg_arp) { arg_tree = 1; arg_cpu = 1; arg_seccomp = 1; arg_caps = 1; arg_cgroup = 1; arg_x11 = 1; arg_interface = 1; arg_route = 1; arg_arp = 1; arg_apparmor = 1; } // cumulative options int print_procs = 1; if (arg_cpu) { cpu((pid_t) pid, print_procs); print_procs = 0; } if (arg_seccomp) { seccomp((pid_t) pid, print_procs); print_procs = 0; } if (arg_caps) { caps((pid_t) pid, print_procs); print_procs = 0; } if (arg_apparmor) { apparmor((pid_t) pid, print_procs); print_procs = 0; } if (arg_cgroup) { cgroup((pid_t) pid, print_procs); print_procs = 0; } if (arg_x11) { x11((pid_t) pid, print_procs); print_procs = 0; } if (arg_interface && getuid() == 0) { interface((pid_t) pid, print_procs); print_procs = 0; } if (arg_route) { route((pid_t) pid, print_procs); print_procs = 0; } if (arg_arp) { arp((pid_t) pid, print_procs); print_procs = 0; } (void) print_procs; if (getuid() == 0) { tree((pid_t) pid); // pid initialized as zero, will print the tree for all processes if a specific pid was not requested procevent((pid_t) pid); } return 0; }