From 1379851360349d6617ad32944a25ee5e2bb74fc2 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sat, 8 Aug 2015 19:12:30 -0400 Subject: Baseline firejail 0.9.28 --- src/firemon/Makefile.in | 24 +++ src/firemon/arp.c | 99 +++++++++++++ src/firemon/caps.c | 69 +++++++++ src/firemon/cgroup.c | 64 ++++++++ src/firemon/cpu.c | 68 +++++++++ src/firemon/firemon.c | 222 ++++++++++++++++++++++++++++ src/firemon/firemon.h | 84 +++++++++++ src/firemon/interface.c | 176 ++++++++++++++++++++++ src/firemon/list.c | 35 +++++ src/firemon/netstats.c | 214 +++++++++++++++++++++++++++ src/firemon/procevent.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++ src/firemon/route.c | 213 +++++++++++++++++++++++++++ src/firemon/seccomp.c | 69 +++++++++ src/firemon/top.c | 297 ++++++++++++++++++++++++++++++++++++++ src/firemon/tree.c | 36 +++++ src/firemon/usage.c | 77 ++++++++++ 16 files changed, 2124 insertions(+) create mode 100644 src/firemon/Makefile.in create mode 100644 src/firemon/arp.c create mode 100644 src/firemon/caps.c create mode 100644 src/firemon/cgroup.c create mode 100644 src/firemon/cpu.c create mode 100644 src/firemon/firemon.c create mode 100644 src/firemon/firemon.h create mode 100644 src/firemon/interface.c create mode 100644 src/firemon/list.c create mode 100644 src/firemon/netstats.c create mode 100644 src/firemon/procevent.c create mode 100644 src/firemon/route.c create mode 100644 src/firemon/seccomp.c create mode 100644 src/firemon/top.c create mode 100644 src/firemon/tree.c create mode 100644 src/firemon/usage.c (limited to 'src/firemon') diff --git a/src/firemon/Makefile.in b/src/firemon/Makefile.in new file mode 100644 index 000000000..425289695 --- /dev/null +++ b/src/firemon/Makefile.in @@ -0,0 +1,24 @@ +all: firemon + +PREFIX=@prefix@ +VERSION=@PACKAGE_VERSION@ +NAME=@PACKAGE_NAME@ + +H_FILE_LIST = $(wildcard *.[h]) +C_FILE_LIST = $(wildcard *.c) +OBJS = $(C_FILE_LIST:.c=.o) +BINOBJS = $(foreach file, $(OBJS), $file) +CFLAGS += -ggdb -O2 -DVERSION='"$(VERSION)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security +LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now + +%.o : %.c $(H_FILE_LIST) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +firemon: $(OBJS) ../lib/common.o ../lib/pid.o + $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) + +clean:; rm -f *.o firemon + +distclean: clean + rm -fr Makefile + diff --git a/src/firemon/arp.c b/src/firemon/arp.c new file mode 100644 index 000000000..71beb0630 --- /dev/null +++ b/src/firemon/arp.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" +#define MAXBUF 4096 + +static void print_arp(const char *fname) { + FILE *fp = fopen(fname, "r"); + if (!fp) + return; + + printf(" ARP Table:\n"); + char buf[MAXBUF]; + while (fgets(buf, MAXBUF, fp)) { + // remove blanks, \n + char *ptr = buf; + while (*ptr == ' ' || *ptr == '\t') + ptr++; + char *start = ptr; + if (*start == '\0') + continue; + ptr = strchr(ptr, '\n'); + if (ptr) + *ptr = '\0'; + + // remove table header + //IP address HW type Flags HW address Mask Device + if (strncmp(start, "IP address", 10) == 0) + continue; + + // extract data + char ip[64]; + char type[64]; + char flags[64]; + char mac[64]; + char mask[64]; + char device[64]; + int rv = sscanf(start, "%s %s %s %s %s %s\n", ip, type, flags, mac, mask, device); + if (rv != 6) + continue; + + // destination ip + unsigned a, b, c, d; + if (sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d) != 4 || a > 255 || b > 255 || c > 255 || d > 255) + continue; + uint32_t destip = a * 0x1000000 + b * 0x10000 + c * 0x100 + d; + if (strcmp(flags, "0x0") == 0) + printf(" %d.%d.%d.%d dev %s FAILED\n", + PRINT_IP(destip), device); + else + printf(" %d.%d.%d.%d dev %s lladdr %s REACHABLE\n", + PRINT_IP(destip), device, mac); + } + + fclose(fp); + +} + +void arp(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) { + char *fname; + if (asprintf(&fname, "/proc/%d/net/arp", child) == -1) + errExit("asprintf"); + print_arp(fname); + free(fname); + printf("\n"); + } + } + } +} + + diff --git a/src/firemon/caps.c b/src/firemon/caps.c new file mode 100644 index 000000000..4ae9ab28d --- /dev/null +++ b/src/firemon/caps.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" +#define MAXBUF 4098 + +static void print_caps(int pid) { + char *file; + if (asprintf(&file, "/proc/%d/status", pid) == -1) { + errExit("asprintf"); + exit(1); + } + + FILE *fp = fopen(file, "r"); + if (!fp) { + printf(" Error: cannot open %s\n", file); + free(file); + return; + } + + char buf[MAXBUF]; + while (fgets(buf, MAXBUF, fp)) { + if (strncmp(buf, "CapBnd:", 7) == 0) { + printf(" %s", buf); + fflush(0); + free(file); + fclose(fp); + return; + } + } + fclose(fp); + free(file); +} + +void caps(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); // include all processes + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) + print_caps(child); + } + } + printf("\n"); +} + diff --git a/src/firemon/cgroup.c b/src/firemon/cgroup.c new file mode 100644 index 000000000..214aefaf9 --- /dev/null +++ b/src/firemon/cgroup.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" +#define MAXBUF 4098 + +static void print_cgroup(int pid) { + char *file; + if (asprintf(&file, "/proc/%d/cgroup", pid) == -1) { + errExit("asprintf"); + exit(1); + } + + FILE *fp = fopen(file, "r"); + if (!fp) { + printf(" Error: cannot open %s\n", file); + free(file); + return; + } + + char buf[MAXBUF]; + if (fgets(buf, MAXBUF, fp)) { + printf(" %s", buf); + fflush(0); + } + + fclose(fp); + free(file); +} + +void cgroup(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) + print_cgroup(child); + } + } +} + diff --git a/src/firemon/cpu.c b/src/firemon/cpu.c new file mode 100644 index 000000000..d5d20d1b8 --- /dev/null +++ b/src/firemon/cpu.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" +#define MAXBUF 4098 + +static void print_cpu(int pid) { + char *file; + if (asprintf(&file, "/proc/%d/status", pid) == -1) { + errExit("asprintf"); + exit(1); + } + + FILE *fp = fopen(file, "r"); + if (!fp) { + printf(" Error: cannot open %s\n", file); + free(file); + return; + } + + char buf[MAXBUF]; + while (fgets(buf, MAXBUF, fp)) { + if (strncmp(buf, "Cpus_allowed_list:", 18) == 0) { + printf(" %s", buf); + fflush(0); + free(file); + fclose(fp); + return; + } + } + fclose(fp); + free(file); +} + +void cpu(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) + print_cpu(child); + } + } +} + diff --git a/src/firemon/firemon.c b/src/firemon/firemon.c new file mode 100644 index 000000000..d77d11a7a --- /dev/null +++ b/src/firemon/firemon.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 + + +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; +int arg_nowrap = 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){ + if (terminal_set) + tcsetattr(0, TCSANOW, &tlocal); + exit(0); +} + +// find the first child process for the specified pid +// return -1 if not found +int find_child(int id) { + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 2 && pids[i].parent == id) + return i; + } + + return -1; +} + +// drop privileges +void firemon_drop_privs(void) { + // drop privileges + if (setgroups(0, NULL) < 0) + errExit("setgroups"); + if (setgid(getgid()) < 0) + errExit("setgid/getgid"); + if (setuid(getuid()) < 0) + errExit("setuid/getuid"); +} + +// sleep and wait for a key to be pressed +void firemon_sleep(int st) { + if (terminal_set == 0) { + tcgetattr(0, &twait); // get current terminal attirbutes; 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; + } + + // options without a pid argument + else if (strcmp(argv[i], "--top") == 0) { + top(); // never to return + } + else if (strcmp(argv[i], "--list") == 0) { + list(); + return 0; + } + else if (strcmp(argv[i], "--netstats") == 0) { + netstats(); + return 0; + } + + + // cumulative options with or without a pid argument + 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], "--tree") == 0) { + arg_tree = 1; + } + else if (strcmp(argv[i], "--interface") == 0) { + arg_interface = 1; + } + else if (strcmp(argv[i], "--route") == 0) { + arg_route = 1; + } + else if (strcmp(argv[i], "--arp") == 0) { + arg_arp = 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], "--nowrap") == 0) + arg_nowrap = 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 (arg_tree) + tree((pid_t) pid); + if (arg_interface) + interface((pid_t) pid); + if (arg_route) + route((pid_t) pid); + if (arg_arp) + arp((pid_t) pid); + if (arg_seccomp) + seccomp((pid_t) pid); + if (arg_caps) + caps((pid_t) pid); + if (arg_cpu) + cpu((pid_t) pid); + if (arg_cgroup) + cgroup((pid_t) pid); + + if (!arg_route && !arg_arp && !arg_interface && !arg_tree && !arg_caps && !arg_seccomp) + procevent((pid_t) pid); // never to return + + return 0; +} diff --git a/src/firemon/firemon.h b/src/firemon/firemon.h new file mode 100644 index 000000000..59b1f352c --- /dev/null +++ b/src/firemon/firemon.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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. +*/ +#ifndef FIREMON_H +#define FIREMON_H +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include "../include/pid.h" +#include "../include/common.h" + +// clear screen +static inline void firemon_clrscr(void) { + printf("\033[2J\033[1;1H"); + fflush(0); +} + +// firemon.c +extern int arg_nowrap; +int find_child(int id); +void firemon_drop_privs(void); +void firemon_sleep(int st); + + +// procevent.c +void procevent(pid_t pid); + +// usage.c +void usage(void); + +// top.c +void top(void); + +// list.c +void list(void); + +// interface.c +void interface(pid_t pid); + +// arp.c +void arp(pid_t pid); + +// route.c +void route(pid_t pid); + +// caps.c +void caps(pid_t pid); + +// seccomp.c +void seccomp(pid_t pid); + +// cpu.c +void cpu(pid_t pid); + +// cgroup.c +void cgroup(pid_t pid); + +// tree.c +void tree(pid_t pid); + +// netstats.c +void netstats(void); + +#endif diff --git a/src/firemon/interface.c b/src/firemon/interface.c new file mode 100644 index 000000000..52a9c33cd --- /dev/null +++ b/src/firemon/interface.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 +#include +#include +#include +#include +#include + +//#include +//#include + +// print IP addresses for all interfaces +static void net_ifprint(void) { + uint32_t ip; + uint32_t mask; + struct ifaddrs *ifaddr, *ifa; + + int fd; + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + fprintf(stderr, "Error: cannot open AF_INET socket\n"); + exit(1); + } + + if (getifaddrs(&ifaddr) == -1) + errExit("getifaddrs"); + + // walk through the linked list + printf(" Link status:\n"); + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family == AF_PACKET) { + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) { + if (ifa->ifa_data != NULL) { + struct rtnl_link_stats *stats = ifa->ifa_data; + + // extract mac address + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); + int rv = ioctl (fd, SIOCGIFHWADDR, &ifr); + + if (rv == 0) + printf(" %s UP, %02x:%02x:%02x:%02x:%02x:%02x\n", + ifa->ifa_name, PRINT_MAC((unsigned char *) &ifr.ifr_hwaddr.sa_data)); + else + printf(" %s UP\n", ifa->ifa_name); + + printf(" tx/rx: %u/%u packets, %u/%u bytes\n", + stats->tx_packets, stats->rx_packets, + stats->tx_bytes, stats->rx_bytes); + } + } + else + printf(" %s DOWN\n", ifa->ifa_name); + } + } + + + // walk through the linked list + printf(" IPv4 status:\n"); + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *si = (struct sockaddr_in *) ifa->ifa_netmask; + mask = ntohl(si->sin_addr.s_addr); + si = (struct sockaddr_in *) ifa->ifa_addr; + ip = ntohl(si->sin_addr.s_addr); + + char *status; + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) + status = "UP"; + else + status = "DOWN"; + + printf(" %s %s, %d.%d.%d.%d/%u\n", + ifa->ifa_name, status, PRINT_IP(ip), mask2bits(mask)); + } + } + + + // walk through the linked list + printf(" IPv6 status:\n"); + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET6) { + char host[NI_MAXHOST]; + int s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (s == 0) { + char *ptr; + if ((ptr = strchr(host, '%')) != NULL) + *ptr = '\0'; + char *status; + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) + status = "UP"; + else + status = "DOWN"; + + printf(" %s %s, %s\n", ifa->ifa_name, status, host); + } + } + } + + freeifaddrs(ifaddr); + close(fd); +} + +static void print_sandbox(pid_t pid) { + pid_t child = fork(); + if (child == -1) + return; + + if (child == 0) { + int rv = join_namespace(pid, "net"); + if (rv) + return; + net_ifprint(); + printf("\n"); + exit(0); + } + + // wait for the child to finish + waitpid(child, NULL, 0); +} + +void interface(pid_t pid) { + if (getuid() != 0) { + fprintf(stderr, "Error: you need to be root to run this command\n"); + exit(1); + } + + pid_read(pid); // a pid of 0 will include all processes + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) { + print_sandbox(child); + } + } + } +} + diff --git a/src/firemon/list.c b/src/firemon/list.c new file mode 100644 index 000000000..6a997bde1 --- /dev/null +++ b/src/firemon/list.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" + +void list(void) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(0); // include all processes + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) + pid_print_list(i, 0); + } +} + diff --git a/src/firemon/netstats.c b/src/firemon/netstats.c new file mode 100644 index 000000000..6c4a767f1 --- /dev/null +++ b/src/firemon/netstats.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 + +#define MAXBUF 4096 + +static char *get_header(void) { + char *rv; + if (asprintf(&rv, "%-5.5s %-9.9s %-10.10s %-10.10s %s", + "PID", "User", "RX(KB/s)", "TX(KB/s)", "Command") == -1) + errExit("asprintf"); + + return rv; +} + +void get_stats(int parent) { + // find the first child + int child = -1; + for (child = parent + 1; child < max_pids; child++) { + if (pids[child].parent == parent) + break; + } + + if (child == -1) + goto errexit; + + // open /proc/child/net/dev file and read rx and tx + char *fname; + if (asprintf(&fname, "/proc/%d/net/dev", child) == -1) + errExit("asprintf"); + FILE *fp = fopen(fname, "r"); + if (!fp) { + free(fname); + goto errexit; + } + + char buf[MAXBUF]; + long long unsigned rx = 0; + long long unsigned tx = 0; + while (fgets(buf, MAXBUF, fp)) { + if (strncmp(buf, "Inter", 5) == 0) + continue; + if (strncmp(buf, " face", 5) == 0) + continue; + + char *ptr = buf; + while (*ptr != '\0' && *ptr != ':') { + ptr++; + } + + if (*ptr == '\0') { + fclose(fp); + free(fname); + goto errexit; + } + ptr++; + + long long unsigned rxval; + long long unsigned txval; + unsigned a, b, c, d, e, f, g; + sscanf(ptr, "%llu %u %u %u %u %u %u %u %llu", + &rxval, &a, &b, &c, &d, &e, &f, &g, &txval); + rx += rxval; + tx += txval; + } + + // store data + pids[parent].rx_delta = rx - pids[parent].rx; + pids[parent].rx = rx; + pids[parent].tx_delta = tx - pids[parent].tx; + pids[parent].tx = tx; + + + free(fname); + fclose(fp); + return; + +errexit: + pids[parent].rx = 0; + pids[parent].tx = 0; + pids[parent].rx_delta = 0; + pids[parent].tx_delta = 0; +} + + +static void print_proc(int index, int itv, int col) { + // command + char *cmd = pid_proc_cmdline(index); + char *ptrcmd; + if (cmd == NULL) { + if (pids[index].zombie) + ptrcmd = "(zombie)"; + else + ptrcmd = ""; + } + else + ptrcmd = cmd; + // if the command doesn't have a --net= option, don't print + if (strstr(ptrcmd, "--net=") == NULL) { + if (cmd) + free(cmd); + return; + } + + // pid + char pidstr[10]; + snprintf(pidstr, 10, "%u", index); + + // user + char *user = pid_get_user_name(pids[index].uid); + char *ptruser; + if (user) + ptruser = user; + else + ptruser = ""; + + + float rx_kbps = ((float) pids[index].rx_delta / 1000) / itv; + char ptrrx[15]; + sprintf(ptrrx, "%.03f", rx_kbps); + + float tx_kbps = ((float) pids[index].tx_delta / 1000) / itv; + char ptrtx[15]; + sprintf(ptrtx, "%.03f", tx_kbps); + + char buf[1024 + 1]; + snprintf(buf, 1024, "%-5.5s %-9.9s %-10.10s %-10.10s %s", + pidstr, ptruser, ptrrx, ptrtx, ptrcmd); + if (col < 1024) + buf[col] = '\0'; + printf("%s\n", buf); + + if (cmd) + free(cmd); + if (user) + free(user); + +} + +void netstats(void) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(0); // include all processes + + printf("Displaying network statistics only for sandboxes using a new network namespace.\n"); + + // print processes + while (1) { + // set pid table + int i; + int itv = 5; // 5 second interval + pid_read(0); // todo: preserve the last calculation if any, so we don't have to do get_stats() + + // start rx/tx measurements + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) + get_stats(i); + } + + // wait 5 seconds + firemon_sleep(itv); + + // grab screen size + struct winsize sz; + int row = 24; + int col = 80; + if (!ioctl(0, TIOCGWINSZ, &sz)) { + col = sz.ws_col; + row = sz.ws_row; + } + + // start printing + firemon_clrscr(); + char *header = get_header(); + if (strlen(header) > col) + header[col] = '\0'; + printf("%s\n", header); + if (row > 0) + row--; + free(header); + + // start rx/tx measurements + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + get_stats(i); + print_proc(i, itv, col); + } + } + } +} + diff --git a/src/firemon/procevent.c b/src/firemon/procevent.c new file mode 100644 index 000000000..d2b5f7bbf --- /dev/null +++ b/src/firemon/procevent.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 +#include +#include +#include +#define PIDS_BUFLEN 4096 +#define SERVER_PORT 889 // 889-899 is left unassigned by IANA + +static int pid_is_firejail(pid_t pid) { + uid_t rv = 0; + + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/status", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return 0; + } + + // look for firejail executable name + char buf[PIDS_BUFLEN]; + while (fgets(buf, PIDS_BUFLEN - 1, fp)) { + if (strncmp(buf, "Name:", 5) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') + goto doexit; + if (strncmp(ptr, "firejail", 8) == 0) + rv = 1; +// if (strncmp(ptr, "lxc-execute", 11) == 0) +// rv = 1; + break; + } + } +doexit: + fclose(fp); + free(file); + return rv; +} + + +static int procevent_netlink_setup(void) { + // open socket for process event connector + int sock; + if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)) < 0) { + fprintf(stderr, "Error: cannot open netlink socket\n"); + exit(1); + } + + // bind socket + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_pid = getpid(); + addr.nl_family = AF_NETLINK; + addr.nl_groups = CN_IDX_PROC; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + fprintf(stderr, "Error: cannot bind to netlink socket\n"); + exit(1); + } + + // send monitoring message + struct nlmsghdr nlmsghdr; + memset(&nlmsghdr, 0, sizeof(nlmsghdr)); + nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op)); + nlmsghdr.nlmsg_pid = getpid(); + nlmsghdr.nlmsg_type = NLMSG_DONE; + + struct cn_msg cn_msg; + memset(&cn_msg, 0, sizeof(cn_msg)); + cn_msg.id.idx = CN_IDX_PROC; + cn_msg.id.val = CN_VAL_PROC; + cn_msg.len = sizeof(enum proc_cn_mcast_op); + + struct iovec iov[3]; + iov[0].iov_base = &nlmsghdr; + iov[0].iov_len = sizeof(nlmsghdr); + iov[1].iov_base = &cn_msg; + iov[1].iov_len = sizeof(cn_msg); + + enum proc_cn_mcast_op op = PROC_CN_MCAST_LISTEN; + iov[2].iov_base = &op; + iov[2].iov_len = sizeof(op); + + if (writev(sock, iov, 3) == -1) { + fprintf(stderr, "Error: cannot write to netlink socket\n"); + exit(1); + } + + return sock; +} + + +static int procevent_monitor(const int sock, pid_t mypid) { + ssize_t len; + struct nlmsghdr *nlmsghdr; + + // timeout in order to re-enable firejail module trace + struct timeval tv; + tv.tv_sec = 30; + tv.tv_usec = 0; + + while (1) { +#define BUFFSIZE 4096 + char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE]; + + fd_set readfds; + int max; + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + max = sock; + max++; + + int rv = select(max, &readfds, NULL, NULL, &tv); + if (rv == -1) { + fprintf(stderr, "recv: %s\n", strerror(errno)); + return -1; + } + + // timeout + if (rv == 0) { + tv.tv_sec = 30; + tv.tv_usec = 0; + continue; + } + + + if ((len = recv(sock, buf, sizeof(buf), 0)) == 0) { + return 0; + } + if (len == -1) { + if (errno == EINTR) { + return 0; + } else { + fprintf(stderr,"recv: %s\n", strerror(errno)); + return -1; + } + } + + for (nlmsghdr = (struct nlmsghdr *)buf; + NLMSG_OK (nlmsghdr, len); + nlmsghdr = NLMSG_NEXT (nlmsghdr, len)) { + + struct cn_msg *cn_msg; + struct proc_event *proc_ev; + struct tm tm; + time_t now; + + if ((nlmsghdr->nlmsg_type == NLMSG_ERROR) || + (nlmsghdr->nlmsg_type == NLMSG_NOOP)) + continue; + + cn_msg = NLMSG_DATA(nlmsghdr); + if ((cn_msg->id.idx != CN_IDX_PROC) || + (cn_msg->id.val != CN_VAL_PROC)) + continue; + + (void)time(&now); + (void)localtime_r(&now, &tm); + char line[PIDS_BUFLEN]; + char *lineptr = line; + sprintf(lineptr, "%2.2d:%2.2d:%2.2d", tm.tm_hour, tm.tm_min, tm.tm_sec); + lineptr += strlen(lineptr); + + proc_ev = (struct proc_event *)cn_msg->data; + pid_t pid = 0; + pid_t child = 0; + int remove_pid = 0; + switch (proc_ev->what) { + case PROC_EVENT_FORK: + if (proc_ev->event_data.fork.child_pid != + proc_ev->event_data.fork.child_tgid) + continue; // this is a thread, not a process + pid = proc_ev->event_data.fork.parent_tgid; + if (pids[pid].level > 0) { + child = proc_ev->event_data.fork.child_tgid; + child %= max_pids; + pids[child].level = pids[pid].level + 1; + pids[child].uid = pid_get_uid(child); + } + sprintf(lineptr, " fork"); + break; + case PROC_EVENT_EXEC: + pid = proc_ev->event_data.exec.process_tgid; + sprintf(lineptr, " exec"); + break; + + case PROC_EVENT_EXIT: + if (proc_ev->event_data.exit.process_pid != + proc_ev->event_data.exit.process_tgid) + continue; // this is a thread, not a process + + pid = proc_ev->event_data.exit.process_tgid; + remove_pid = 1; + sprintf(lineptr, " exit"); + break; + + case PROC_EVENT_UID: + pid = proc_ev->event_data.id.process_tgid; + sprintf(lineptr, " uid "); + break; + + case PROC_EVENT_GID: + pid = proc_ev->event_data.id.process_tgid; + sprintf(lineptr, " gid "); + break; + + case PROC_EVENT_SID: + pid = proc_ev->event_data.sid.process_tgid; + sprintf(lineptr, " sid "); + break; + + default: + sprintf(lineptr, "\n"); + continue; + } + + int add_new = 0; + if (pids[pid].level < 0) // not a firejail process + continue; + else if (pids[pid].level == 0) { // new porcess, do we track it? + if (pid_is_firejail(pid) && mypid == 0) { + pids[pid].level = 1; + add_new = 1; + } + else { + pids[pid].level = -1; + continue; + } + } + + lineptr += strlen(lineptr); + sprintf(lineptr, " %u", pid); + lineptr += strlen(lineptr); + + char *user = pids[pid].user; + if (!user) + user = pid_get_user_name(pids[pid].uid); + if (user) { + pids[pid].user = user; + sprintf(lineptr, " (%s)", user); + lineptr += strlen(lineptr); + } + + + int sandbox_closed = 0; // exit sandbox flag + char *cmd = pids[pid].cmd; + if (!cmd) { + cmd = pid_proc_cmdline(pid); + } + if (add_new) { + if (!cmd) + sprintf(lineptr, " NEW SANDBOX\n"); + else + sprintf(lineptr, " NEW SANDBOX: %s\n", cmd); + lineptr += strlen(lineptr); + } + else if (proc_ev->what == PROC_EVENT_EXIT && pids[pid].level == 1) { + sprintf(lineptr, " EXIT SANDBOX\n"); + lineptr += strlen(lineptr); + if (mypid == pid) + sandbox_closed = 1; + } + else { + if (!cmd) { + cmd = pid_proc_cmdline(pid); + } + if (cmd == NULL) + sprintf(lineptr, "\n"); + else { + sprintf(lineptr, " %s\n", cmd); + free(cmd); + } + lineptr += strlen(lineptr); + } + (void) lineptr; + + // print the event + printf("%s", line); + fflush(0); + + // unflag pid for exit events + if (remove_pid) { + if (pids[pid].user) + free(pids[pid].user); + if (pids[pid].cmd) + free(pids[pid].cmd); + memset(&pids[pid], 0, sizeof(Process)); + } + + // print forked child + if (child) { + cmd = pid_proc_cmdline(child); + if (cmd) { + printf("\tchild %u %s\n", child, cmd); + free(cmd); + } + else + printf("\tchild %u\n", child); + } + + // on uid events the uid is changing + if (proc_ev->what == PROC_EVENT_UID) { + if (pids[pid].user) + free(pids[pid].user); + pids[pid].user = 0; + pids[pid].uid = pid_get_uid(pid); + } + + if (sandbox_closed) + exit(0); + } + } + return 0; +} + +static void procevent_print_pids(void) { + // print files + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) + pid_print_tree(i, 0, 1); + } + printf("\n"); +} + +void procevent(pid_t pid) { + // need to be root for this + if (getuid() != 0) { + fprintf(stderr, "Error: you need to be root to get process events\n"); + exit(1); + } + + // read and print sandboxed processes + pid_read(pid); + procevent_print_pids(); + + // monitor using netlink + int sock = procevent_netlink_setup(); + if (sock < 0) { + fprintf(stderr, "Error: cannot open netlink socket\n"); + exit(1); + } + + procevent_monitor(sock, pid); // it will never return from here + assert(0); + close(sock); // quiet static analyzers +} diff --git a/src/firemon/route.c b/src/firemon/route.c new file mode 100644 index 000000000..7f559c7b5 --- /dev/null +++ b/src/firemon/route.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 +#define MAXBUF 4096 + +typedef struct iflist_t { + struct iflist_t *next; + uint32_t ip; +} IfList; +static IfList *ifs = NULL; +static char last_start[MAXBUF + 1]; + +static IfList *list_find(uint32_t ip, uint32_t mask) { + IfList *ptr = ifs; + while (ptr) { + if ((ptr->ip & mask) == (ip & mask)) + return ptr; + ptr = ptr->next; + } + + return NULL; +} + +static void extract_if(const char *fname) { + // clear interface list + while (ifs) { + IfList *tmp = ifs->next; + free(ifs); + ifs = tmp; + } + assert(ifs == NULL); + + FILE *fp = fopen(fname, "r"); + if (!fp) + return; + + char buf[MAXBUF]; + int state = 0; // 0 -wait for Local + // + while (fgets(buf, MAXBUF, fp)) { + // remove blanks, \n + char *ptr = buf; + while (*ptr == ' ' || *ptr == '\t') + ptr++; + char *start = ptr; + if (*start == '\0') + continue; + ptr = strchr(ptr, '\n'); + if (ptr) + *ptr = '\0'; + + if (state == 0) { + if (strncmp(buf, "Local:", 6) == 0) { + state = 1; + continue; + } + } + else if (state == 1) { + // remove broadcast addresses + if (strstr(start,"BROADCAST")) + continue; + else if (*start == '+') + continue; + else if (*start == '|') { + memset(last_start, 0, MAXBUF + 1); + strncpy(last_start, start, MAXBUF); + continue; + } + else if (strstr(buf, "LOCAL")) { +// printf("%s %s\n", last_start, start); + unsigned mbits; + sscanf(start, "/%u", &mbits); + if (mbits != 32) + continue; + + unsigned a, b, c, d; + if (sscanf(last_start, "|-- %u.%u.%u.%u", &a, &b, &c, &d) != 4 || a > 255 || b > 255 || c > 255 || d > 255) + continue; + + IfList *newif = malloc(sizeof(IfList)); + if (!newif) + errExit("malloc"); + newif->ip = a * 0x1000000 + b * 0x10000 + c * 0x100 + d; + newif->next = ifs; + ifs = newif; + } + } + } + + fclose(fp); + + +} + +static void print_route(const char *fname) { + FILE *fp = fopen(fname, "r"); + if (!fp) + return; + + printf(" Route table:\n"); + char buf[MAXBUF]; + while (fgets(buf, MAXBUF, fp)) { + // remove blanks, \n + char *ptr = buf; + while (*ptr == ' ' || *ptr == '\t') + ptr++; + char *start = ptr; + if (*start == '\0') + continue; + ptr = strchr(ptr, '\n'); + if (ptr) + *ptr = '\0'; + + // remove table header + //Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT + if (strncmp(start, "Iface", 5) == 0) + continue; + + // extract data + char ifname[64]; + char destination[64]; + char gateway[64]; + char flags[64]; + char refcnt[64]; + char use[64]; + char metric[64]; + char mask[64]; + int rv = sscanf(start, "%s %s %s %s %s %s %s %s\n", ifname, destination, gateway, flags, refcnt, use, metric, mask); + if (rv != 8) + continue; + + // destination ip + uint32_t destip; + sscanf(destination, "%x", &destip); + destip = ntohl(destip); + uint32_t destmask; + sscanf(mask, "%x", &destmask); + destmask = ntohl(destmask); + uint32_t gw; + sscanf(gateway, "%x", &gw); + gw = ntohl(gw); + +// printf("#%s# #%s# #%s# #%s# #%s# #%s# #%s# #%s#\n", ifname, destination, gateway, flags, refcnt, use, metric, mask); + if (gw != 0) + printf(" %u.%u.%u.%u/%u via %u.%u.%u.%u, dev %s, metric %s\n", + PRINT_IP(destip), mask2bits(destmask), + PRINT_IP(gw), + ifname, + metric); + else { // this is an interface + IfList *ifentry = list_find(destip, destmask); + if (ifentry) { + printf(" %u.%u.%u.%u/%u, dev %s, scope link src %d.%d.%d.%d\n", + PRINT_IP(destip), mask2bits(destmask), + ifname, + PRINT_IP(ifentry->ip)); + } + } + } + + fclose(fp); + +} + +void route(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) { + char *fname; + if (asprintf(&fname, "/proc/%d/net/fib_trie", child) == -1) + errExit("asprintf"); + extract_if(fname); + free(fname); + + if (asprintf(&fname, "/proc/%d/net/route", child) == -1) + errExit("asprintf"); + print_route(fname); + free(fname); + printf("\n"); + } + } + } +} + + diff --git a/src/firemon/seccomp.c b/src/firemon/seccomp.c new file mode 100644 index 000000000..4ffc93f2e --- /dev/null +++ b/src/firemon/seccomp.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" + +#define MAXBUF 4098 +static void print_seccomp(int pid) { + char *file; + if (asprintf(&file, "/proc/%d/status", pid) == -1) { + errExit("asprintf"); + exit(1); + } + + FILE *fp = fopen(file, "r"); + if (!fp) { + printf(" Error: cannot open %s\n", file); + free(file); + return; + } + + char buf[MAXBUF]; + while (fgets(buf, MAXBUF, fp)) { + if (strncmp(buf, "Seccomp:", 8) == 0) { + printf(" %s", buf); + fflush(0); + fclose(fp); + free(file); + return; + } + } + fclose(fp); + free(file); +} + +void seccomp(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); // include all processes + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + pid_print_list(i, 0); + int child = find_child(i); + if (child != -1) + print_seccomp(child); + } + } + printf("\n"); +} + diff --git a/src/firemon/top.c b/src/firemon/top.c new file mode 100644 index 000000000..1eb753694 --- /dev/null +++ b/src/firemon/top.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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 + +static unsigned pgs_rss = 0; +static unsigned pgs_shared = 0; +static unsigned clocktick = 0; +static unsigned long long sysuptime = 0; + +static char *get_header(void) { + char *rv; + if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s", + "PID", "User", "RES(KiB)", "SHR(KiB)", "CPU%", "Prcs", "Uptime", "Command") == -1) + errExit("asprintf"); + + return rv; +} + + +// recursivity!!! +static char *print_top(unsigned index, unsigned parent, unsigned *utime, unsigned *stime, unsigned itv, float *cpu, int *cnt) { + char *rv = NULL; + + char procdir[20]; + snprintf(procdir, 20, "/proc/%u", index); + struct stat s; + if (stat(procdir, &s) == -1) + return NULL; + + if (pids[index].level == 1) { + pgs_rss = 0; + pgs_shared = 0; + *utime = 0; + *stime = 0; + *cnt = 0; + } + + (*cnt)++; + pid_getmem(index, &pgs_rss, &pgs_shared); + unsigned utmp; + unsigned stmp; + pid_get_cpu_time(index, &utmp, &stmp); + *utime += utmp; + *stime += stmp; + + + int i; + for (i = index + 1; i < max_pids; i++) { + if (pids[i].parent == index) + print_top(i, index, utime, stime, itv, cpu, cnt); + } + + if (pids[index].level == 1) { + // pid + char pidstr[10]; + snprintf(pidstr, 10, "%u", index); + + // command + char *cmd = pid_proc_cmdline(index); + char *ptrcmd; + if (cmd == NULL) { + if (pids[index].zombie) + ptrcmd = "(zombie)"; + else + ptrcmd = ""; + } + else + ptrcmd = cmd; + + // user + char *user = pid_get_user_name(pids[index].uid); + char *ptruser; + if (user) + ptruser = user; + else + ptruser = ""; + + // memory + int pgsz = getpagesize(); + char rss[10]; + snprintf(rss, 10, "%u", pgs_rss * pgsz / 1024); + char shared[10]; + snprintf(shared, 10, "%u", pgs_shared * pgsz / 1024); + + // uptime + unsigned long long uptime = pid_get_start_time(index); + if (clocktick == 0) + clocktick = sysconf(_SC_CLK_TCK); + uptime /= clocktick; + uptime = sysuptime - uptime; + unsigned sec = uptime % 60; + uptime -= sec; + uptime /= 60; + unsigned min = uptime % 60; + uptime -= min; + uptime /= 60; + unsigned hour = uptime; + char uptime_str[50]; + snprintf(uptime_str, 50, "%02u:%02u:%02u", hour, min, sec); + + // cpu + itv *= clocktick; + float ud = (float) (*utime - pids[index].utime) / itv * 100; + float sd = (float) (*stime - pids[index].stime) / itv * 100; + float cd = ud + sd; + *cpu = cd; + char cpu_str[10]; + snprintf(cpu_str, 10, "%2.1f", cd); + + // process count + char prcs_str[10]; + snprintf(prcs_str, 10, "%d", *cnt); + + if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s", + pidstr, ptruser, rss, shared, cpu_str, prcs_str, uptime_str, ptrcmd) == -1) + errExit("asprintf"); + + if (cmd) + free(cmd); + if (user) + free(user); + + } + + return rv; +} + + +typedef struct node_t { + struct node_t *next; + char *line; + float cpu; +} Node; + +static Node *head = NULL; + +static void head_clear(void) { + Node *ptr = head; + while (ptr) { + if (ptr->line) + free(ptr->line); + Node *next = ptr->next; + free(ptr); + ptr = next; + } + + head = NULL; +} + +static void head_add(float cpu, char *line) { + // allocate a new node structure + Node *node = malloc(sizeof(Node)); + if (!node) + errExit("malloc"); + node->line = line; + node->cpu = cpu; + node->next = NULL; + + // insert in first list position + if (head == NULL || head->cpu < cpu) { + node->next = head; + head = node; + return; + } + + // insert in the right place + Node *ptr = head; + while (1) { + // last position + Node *current = ptr->next; + if (current == NULL) { + ptr->next = node; + return; + } + + // current position + if (current->cpu < cpu) { + ptr->next = node; + node->next = current; + return; + } + + ptr = current; + } +} + +void head_print(int col, int row) { + Node *ptr = head; + int current = 0; + while (ptr) { + if (current >= row) + break; + + if (strlen(ptr->line) > col) + ptr->line[col] = '\0'; + + if (ptr->next == NULL || current == (row - 1)) { + printf("%s", ptr->line); + fflush(0); + } + else + printf("%s\n", ptr->line); + + ptr = ptr->next; + current++; + } +} + +void top(void) { + if (getuid() == 0) + firemon_drop_privs(); + + while (1) { + // clear linked list + head_clear(); + + // set pid table + int i; + int itv = 5; // 5 second interval + pid_read(0); + + // start cpu measurements + unsigned utime = 0; + unsigned stime = 0; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) + pid_store_cpu(i, 0, &utime, &stime); + } + + // wait 5 seconds + firemon_sleep(itv); + + // grab screen size + struct winsize sz; + int row = 24; + int col = 80; + if (!ioctl(0, TIOCGWINSZ, &sz)) { + col = sz.ws_col; + row = sz.ws_row; + } + + // start printing + firemon_clrscr(); + char *header = get_header(); + if (strlen(header) > col) + header[col] = '\0'; + printf("%s\n", header); + if (row > 0) + row--; + free(header); + + // find system uptime + FILE *fp = fopen("/proc/uptime", "r"); + if (fp) { + float f; + int rv = fscanf(fp, "%f", &f); + (void) rv; + sysuptime = (unsigned long long) f; + fclose(fp); + } + + // print processes + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) { + float cpu = 0; + int cnt = 0; // process count + char *line = print_top(i, 0, &utime, &stime, itv, &cpu, &cnt); + if (line) + head_add(cpu, line); + } + } + head_print(col, row); + } +} + diff --git a/src/firemon/tree.c b/src/firemon/tree.c new file mode 100644 index 000000000..97e0e1f13 --- /dev/null +++ b/src/firemon/tree.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" + +void tree(pid_t pid) { + if (getuid() == 0) + firemon_drop_privs(); + + pid_read(pid); // include all processes + + // print processes + int i; + for (i = 0; i < max_pids; i++) { + if (pids[i].level == 1) + pid_print_tree(i, 0, arg_nowrap); + } + printf("\n"); +} + diff --git a/src/firemon/usage.c b/src/firemon/usage.c new file mode 100644 index 000000000..52788807a --- /dev/null +++ b/src/firemon/usage.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com) + * + * 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" + +void usage(void) { + printf("firemon - version %s\n", VERSION); + printf("Usage: firemon [OPTIONS] [PID]\n\n"); + printf("Monitor processes started in a Firejail sandbox. Without any PID specified,\n"); + printf("all processes started by Firejail are monitored. Descendants of these processes\n"); + printf("are also being monitored.\n\n"); + printf("Options:\n"); + printf("\t--arp - print ARP table for each sandbox.\n\n"); + printf("\t--caps - print capabilities configuration for each sandbox.\n\n"); + printf("\t--cgroup - print control group information for each sandbox.\n\n"); + printf("\t--cpu - print CPU affinity for each sandbox.\n\n"); + printf("\t--help, -? - this help screen.\n\n"); + printf("\t--interface - print network interface information for each sandbox.\n\n"); + printf("\t--list - list all sandboxes.\n\n"); + printf("\t--name=name - print information only about named sandbox.\n\n"); + printf("\t--netstats - monitor network statistics for sandboxes creating a new\n"); + printf("\t\tnetwork namespace.\n\n"); + printf("\t--route - print route table for each sandbox.\n\n"); + printf("\t--seccomp - print seccomp configuration for each sandbox.\n\n"); + printf("\t--tree - print a tree of all sandboxed processes.\n\n"); + printf("\t--top - monitor the most CPU-intensive sandboxes.\n\n"); + printf("\t--version - print program version and exit.\n\n"); + + printf("Without any options, firemon monitors all fork, exec, id change, and exit events\n"); + printf("in the sandbox. Monitoring a specific PID is also supported.\n\n"); + + printf("Option --list prints a list of all sandboxes. The format for each entry is as\n"); + printf("follows:\n\n"); + printf("\tPID:USER:Command\n\n"); + + printf("Option --tree prints the tree of processes running in the sandbox. The format\n"); + printf("for each process entry is as follows:\n\n"); + printf("\tPID:USER:Command\n\n"); + + printf("Option --top is similar to the UNIX top command, however it applies only to\n"); + printf("sandboxes. Listed below are the available fields (columns) in alphabetical\n"); + printf("order:\n\n"); + printf("\tCommand - command used to start the sandbox.\n"); + printf("\tCPU%% - CPU usage, the sandbox share of the elapsed CPU time since the\n"); + printf("\t last screen update\n"); + printf("\tPID - Unique process ID for the task controlling the sandbox.\n"); + printf("\tPrcs - number of processes running in sandbox, including the controlling\n"); + printf("\t process.\n"); + printf("\tRES - Resident Memory Size (KiB), sandbox non-swapped physical memory.\n"); + printf("\t It is a sum of the RES values for all processes running in the\n"); + printf("\t sandbox.\n"); + printf("\tSHR - Shared Memory Size (KiB), it reflects memory shared with other\n"); + printf("\t processes. It is a sum of the SHR values for all processes running\n"); + printf("\t in the sandbox, including the controlling process.\n"); + printf("\tUptime - sandbox running time in hours:minutes:seconds format.\n"); + printf("\tUser - The owner of the sandbox.\n"); + printf("\n"); + printf("License GPL version 2 or later\n"); + printf("Homepage: http://firejail.sourceforge.net\n"); + printf("\n"); +} -- cgit v1.2.3-54-g00ecf