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/firejail/Makefile.in | 28 + src/firejail/arg-checking.txt | 85 + src/firejail/arp.c | 474 ++++ src/firejail/bandwidth.c | 483 ++++ src/firejail/caps.c | 453 ++++ src/firejail/cgroup.c | 118 + src/firejail/cpu.c | 141 ++ src/firejail/firejail.h | 354 +++ src/firejail/fs.c | 825 +++++++ src/firejail/fs_dev.c | 163 ++ src/firejail/fs_home.c | 494 ++++ src/firejail/fs_hostname.c | 157 ++ src/firejail/fs_trace.c | 76 + src/firejail/fs_var.c | 388 +++ src/firejail/join.c | 364 +++ src/firejail/list.c | 65 + src/firejail/main.c | 1168 +++++++++ src/firejail/netfilter.c | 164 ++ src/firejail/network.c | 362 +++ src/firejail/network.txt | 95 + src/firejail/network_main.c | 268 +++ src/firejail/output.c | 84 + src/firejail/profile.c | 444 ++++ src/firejail/restricted_shell.c | 96 + src/firejail/rlimit.c | 62 + src/firejail/sandbox.c | 490 ++++ src/firejail/seccomp.c | 658 ++++++ src/firejail/shutdown.c | 98 + src/firejail/syscall.c | 4942 +++++++++++++++++++++++++++++++++++++++ src/firejail/usage.c | 312 +++ src/firejail/util.c | 480 ++++ src/firejail/veth.c | 191 ++ 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 + src/fshaper/fshaper.sh | 69 + src/ftee/Makefile.in | 24 + src/ftee/ftee.h | 24 + src/ftee/main.c | 228 ++ src/include/common.h | 115 + src/include/libnetlink.h | 163 ++ src/include/pid.h | 58 + src/lib/Makefile.in | 20 + src/lib/common.c | 192 ++ src/lib/libnetlink.c | 803 +++++++ src/lib/pid.c | 392 ++++ src/libtrace/Makefile.in | 25 + src/libtrace/libtrace.c | 609 +++++ src/man/firejail-login.txt | 36 + src/man/firejail-profile.txt | 181 ++ src/man/firejail.txt | 1196 ++++++++++ src/man/firemon.txt | 107 + src/tools/check-caps.sh | 46 + src/tools/extract_caps.c | 83 + src/tools/extract_syscalls.c | 91 + src/tools/mkcoverit.sh | 45 + src/tools/rvtest.c | 144 ++ src/tools/ttytest.c | 36 + 71 files changed, 21393 insertions(+) create mode 100644 src/firejail/Makefile.in create mode 100644 src/firejail/arg-checking.txt create mode 100644 src/firejail/arp.c create mode 100644 src/firejail/bandwidth.c create mode 100644 src/firejail/caps.c create mode 100644 src/firejail/cgroup.c create mode 100644 src/firejail/cpu.c create mode 100644 src/firejail/firejail.h create mode 100644 src/firejail/fs.c create mode 100644 src/firejail/fs_dev.c create mode 100644 src/firejail/fs_home.c create mode 100644 src/firejail/fs_hostname.c create mode 100644 src/firejail/fs_trace.c create mode 100644 src/firejail/fs_var.c create mode 100644 src/firejail/join.c create mode 100644 src/firejail/list.c create mode 100644 src/firejail/main.c create mode 100644 src/firejail/netfilter.c create mode 100644 src/firejail/network.c create mode 100644 src/firejail/network.txt create mode 100644 src/firejail/network_main.c create mode 100644 src/firejail/output.c create mode 100644 src/firejail/profile.c create mode 100644 src/firejail/restricted_shell.c create mode 100644 src/firejail/rlimit.c create mode 100644 src/firejail/sandbox.c create mode 100644 src/firejail/seccomp.c create mode 100644 src/firejail/shutdown.c create mode 100644 src/firejail/syscall.c create mode 100644 src/firejail/usage.c create mode 100644 src/firejail/util.c create mode 100644 src/firejail/veth.c 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 create mode 100755 src/fshaper/fshaper.sh create mode 100644 src/ftee/Makefile.in create mode 100644 src/ftee/ftee.h create mode 100644 src/ftee/main.c create mode 100644 src/include/common.h create mode 100644 src/include/libnetlink.h create mode 100644 src/include/pid.h create mode 100644 src/lib/Makefile.in create mode 100644 src/lib/common.c create mode 100644 src/lib/libnetlink.c create mode 100644 src/lib/pid.c create mode 100644 src/libtrace/Makefile.in create mode 100644 src/libtrace/libtrace.c create mode 100644 src/man/firejail-login.txt create mode 100644 src/man/firejail-profile.txt create mode 100644 src/man/firejail.txt create mode 100644 src/man/firemon.txt create mode 100755 src/tools/check-caps.sh create mode 100644 src/tools/extract_caps.c create mode 100644 src/tools/extract_syscalls.c create mode 100755 src/tools/mkcoverit.sh create mode 100644 src/tools/rvtest.c create mode 100644 src/tools/ttytest.c (limited to 'src') diff --git a/src/firejail/Makefile.in b/src/firejail/Makefile.in new file mode 100644 index 000000000..1f7b563c4 --- /dev/null +++ b/src/firejail/Makefile.in @@ -0,0 +1,28 @@ +all: firejail + +PREFIX=@prefix@ +VERSION=@PACKAGE_VERSION@ +NAME=@PACKAGE_NAME@ +HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ +HAVE_SECCOMP=@HAVE_SECCOMP@ +HAVE_CHROOT=@HAVE_CHROOT@ +HAVE_BIND=@HAVE_BIND@ + +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)"' -DPREFIX='"$(PREFIX)"' $(HAVE_SECCOMP) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_BIND) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security +LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread + +%.o : %.c $(H_FILE_LIST) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o + $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) + +clean:; rm -f *.o firejail firejail.1 firejail.1.gz + +distclean: clean + rm -fr Makefile + diff --git a/src/firejail/arg-checking.txt b/src/firejail/arg-checking.txt new file mode 100644 index 000000000..c1ab2cb21 --- /dev/null +++ b/src/firejail/arg-checking.txt @@ -0,0 +1,85 @@ +arg checking: + +1. --output=filename + - not supported in profiles + - checking no "..", + - checking no link, + - checking no dir, + - checking same permissions, + - checking no hard links + - unit test + +2. --chroot=dirname + - not supported in profiles + - expand "~" + - checking no "..", + - checking is dir, + - checking no link + - checking directory structure + - unit test + +3. --bind=dirname1,dirname2, --bind=filename1,filenam2 + - supported in profiles + - accepted only when running as root + - checking string chars + - checking no ".." + - unit test non root + +4. --tmpfs=dirname + - supported in profiles + - checking string chars + - checking no ".." + - unit test + +5. --blacklist=filename, --blacklist=dirname + - supported in profiles + - checking string chars + - checking no ".." + - unit test + +6. --read-only=filename, --read-only=dirname + - supported in profiles + - checking string chars + - checking no ".." + - unit test + +7. --profile=filename + - check access as real GID/UID + - checking no dir + - checking no link + - checking no ".." + - unit test + +8. --private=dirname + - supported in profiles + - expand "~" + - check is dir + - check no link + - checking no ".." + - check same owner + - unit test + +9. --private.keep=filelist + - supported in profiles + - checking no ".." + - checking file found + - checking same owner + - checking no link + - unit test + +10. --netfilter=filename + - supported in profiles + - check access as real GID/UID + - checking no dir + - checking no link + - checking no ".." + - unit test + +11. --shell=filename + - not supported in profiles + - check access as real GID/UID + - checking no dir + - checking no link + - checking no ".." + - unit test + diff --git a/src/firejail/arp.c b/src/firejail/arp.c new file mode 100644 index 000000000..37f8716e7 --- /dev/null +++ b/src/firejail/arp.c @@ -0,0 +1,474 @@ +/* + * 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 "firejail.h" +#include +#include +#include //TCP/IP Protocol Suite for Linux +#include +#include +#include +#include +#include +#include + +typedef struct arp_hdr_t { + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t opcode; + uint8_t sender_mac[6]; + uint8_t sender_ip[4]; + uint8_t target_mac[6]; + uint8_t target_ip[4]; +} ArpHdr; + +// returns 0 if the address is not in use, -1 otherwise +int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr) { + if (strlen(dev) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", dev); + exit(1); + } + + if (arg_debug) + printf("Trying %d.%d.%d.%d ...\n", PRINT_IP(destaddr)); + + // find interface address + int sock; + if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + errExit("socket"); + + srcaddr = htonl(srcaddr); + destaddr = htonl(destaddr); + + // Find interface MAC address + struct ifreq ifr; + memset(&ifr, 0, sizeof (ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) + errExit("ioctl"); + close(sock); + + // configure layer2 socket address information + struct sockaddr_ll addr; + memset(&addr, 0, sizeof(addr)); + if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) + errExit("if_nametoindex"); + addr.sll_family = AF_PACKET; + memcpy (addr.sll_addr, ifr.ifr_hwaddr.sa_data, 6); + addr.sll_halen = htons(6); + + // build the arp packet header + ArpHdr hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.htype = htons(1); + hdr.ptype = htons(ETH_P_IP); + hdr.hlen = 6; + hdr.plen = 4; + hdr.opcode = htons(1); //ARPOP_REQUEST + memcpy(hdr.sender_mac, ifr.ifr_hwaddr.sa_data, 6); + memcpy(hdr.sender_ip, (uint8_t *)&srcaddr, 4); + memcpy(hdr.target_ip, (uint8_t *)&destaddr, 4); + + // buiild ethernet frame + uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc + memset(frame, 0, sizeof(frame)); + frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; + memcpy(frame + 6, ifr.ifr_hwaddr.sa_data, 6); + frame[12] = ETH_P_ARP / 256; + frame[13] = ETH_P_ARP % 256; + memcpy (frame + 14, &hdr, sizeof(hdr)); + + // open layer2 socket + if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) + errExit("socket"); + + int len; + if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) + errExit("send"); + fflush(0); + + // wait not more than one second for an answer + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + int maxfd = sock; + struct timeval ts; + ts.tv_sec = 1; // 1 second wait time + ts.tv_usec = 0; + while (1) { + int nready = select(maxfd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts); + if (nready < 0) + errExit("select"); + else if (nready == 0) { // timeout + close(sock); + return 0; + } + else { + // read the incoming packet + int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); + if (len < 0) { + perror("recvfrom"); + close(sock); + return -1; + } + + // parse the incomming packet + if (len < 14 + sizeof(ArpHdr)) + continue; + if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) + continue; + memcpy(&hdr, frame + 14, sizeof(ArpHdr)); + if (hdr.opcode == htons(1)) + continue; + if (hdr.opcode == htons(2)) { + // check my mac and my address + if (memcmp(ifr.ifr_hwaddr.sa_data, hdr.target_mac, 6) != 0) + continue; + uint32_t ip; + memcpy(&ip, hdr.target_ip, 4); + if (ip != srcaddr) { + continue; + } + close(sock); + return -1; + } + } + } + + // it will never get here! + close(sock); + return -1; +} + +// assign a random IP address and check it +// the address needs to be in the range if it --iprange was specified +static uint32_t arp_random(const char *dev, Bridge *br) { + assert(dev); + assert(br); + uint32_t ifip = br->ip; + uint32_t ifmask = br->mask; + assert(ifip); + assert(ifmask); + + if (arg_debug) + printf("ARP-scan %s, %d.%d.%d.%d/%d\n", + dev, PRINT_IP(ifip), mask2bits(ifmask)); + + // determine the range based on network address + uint32_t range = ~ifmask + 1; // the number of potential addresses + // this software is not supported for /31 networks + if (range < 4) + return 0; // the user will have to set the IP address manually + range -= 2; // subtract the network address and the broadcast address + uint32_t start = (ifip & ifmask) + 1; + + // adjust range based on --iprange params + if (br->iprange_start && br->iprange_end) { + start = br->iprange_start; + range = br->iprange_end - br->iprange_start; + } + + if (arg_debug) + printf("IP address range from %d.%d.%d.%d to %d.%d.%d.%d\n", + PRINT_IP(start), PRINT_IP(start + range)); + + // generate a random address - 10 tries + uint32_t dest = 0; + int i = 0; + for (i = 0; i < 10; i++) { + dest = start + ((uint32_t) rand()) % range; + if (dest == ifip) // do not allow the interface address + continue; // try again + + // if we've made it up to here, we have a valid address + break; + } + if (i == 10) // we failed 10 times + return 0; + + // check address + uint32_t rv = arp_check(dev, dest, ifip); + if (!rv) + return dest; + return 0; +} + +// go sequentially trough all IP addresses and assign the first one not in use +static uint32_t arp_sequential(const char *dev, Bridge *br) { + assert(dev); + assert(br); + uint32_t ifip = br->ip; + uint32_t ifmask = br->mask; + assert(ifip); + assert(ifmask); + + // range based on network address + uint32_t range = ~ifmask + 1; // the number of potential addresses + // this software is not supported for /31 networks + if (range < 4) + return 0; // the user will have to set the IP address manually + range -= 2; // subtract the network address and the broadcast address + + // try all possible ip addresses in ascending order + // start address + uint32_t dest = (ifip & ifmask) + 1; + if (br->iprange_start) + dest = br->iprange_start; + // end address + uint32_t last = dest + range - 1; + if (br->iprange_end) + last = br->iprange_end; + + if (arg_debug) + printf("Trying IP address range from %d.%d.%d.%d to %d.%d.%d.%d\n", + PRINT_IP(dest), PRINT_IP(last)); + + // loop through addresses and stop as soon as you find an unused one + while (dest <= last) { + if (dest == ifip) { + dest++; + continue; + } + uint32_t rv = arp_check(dev, dest, ifip); + if (!rv) + return dest; + dest++; + } + + return 0; +} + +// assign an IP address first trying some random addresses, and if this fails +// by doing an arp scan. +// +// dev is the name of the device to use in scanning, +// br is bridge structure holding the ip address and mask to use in +// arp packets. It also holds values for for the range of addresses +// if --iprange was set by the user +uint32_t arp_assign(const char *dev, Bridge *br) { + assert(br); + uint32_t ip = 0; + + // try two random IP addresses + ip = arp_random(dev, br); + if (!ip) + ip = arp_random(dev, br); + + // try all possible IP addresses one by one + if (!ip) + ip = arp_sequential(dev, br); + + // print result + if (!ip) { + fprintf(stderr, "Error: cannot assign an IP address; it looks like all of them are in use.\n"); + logerr("Cannot assign an IP address; it looks like all of them are in use."); + exit(1); + } + + return ip; +} + +// scan interface (--scan option) +void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) { + assert(dev); + assert(ifip); + +// printf("Scanning interface %s (%d.%d.%d.%d/%d)\n", +// dev, PRINT_IP(ifip & ifmask), mask2bits(ifmask)); + + if (strlen(dev) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", dev); + exit(1); + } + + // find interface mac address + int sock; + if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + errExit("socket"); + struct ifreq ifr; + memset(&ifr, 0, sizeof (ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) + errExit("ioctl"); + close(sock); + uint8_t mac[6]; + memcpy (mac, ifr.ifr_hwaddr.sa_data, 6); + + // open layer2 socket + if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) + errExit("socket"); + + // try all possible ip addresses in ascending order + uint32_t range = ~ifmask + 1; // the number of potential addresses + // this software is not supported for /31 networks + if (range < 4) { + fprintf(stderr, "Warning: this option is not supported for /31 networks\n"); + close(sock); + return; + } + + uint32_t dest = (ifip & ifmask) + 1; + uint32_t last = dest + range - 1; + uint32_t src = htonl(ifip); + + // wait not more than one second for an answer + int header_printed = 0; + int last_ip = 0; + struct timeval ts; + ts.tv_sec = 2; // 2 seconds receive timeout + ts.tv_usec = 0; + + while (1) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(sock, &wfds); + int maxfd = sock; + + uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc + memset(frame, 0, ETH_FRAME_LEN); + + int nready; + if (dest < last) + nready = select(maxfd + 1, &rfds, &wfds, (fd_set *) 0, NULL); + else + nready = select(maxfd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &ts); + + if (nready < 0) + errExit("select"); + + if (nready == 0) { // timeout + break; + } + + if (FD_ISSET(sock, &wfds) && dest < last) { + // configure layer2 socket address information + struct sockaddr_ll addr; + memset(&addr, 0, sizeof(addr)); + if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) + errExit("if_nametoindex"); + addr.sll_family = AF_PACKET; + memcpy (addr.sll_addr, mac, 6); + addr.sll_halen = htons(6); + + // build the arp packet header + ArpHdr hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.htype = htons(1); + hdr.ptype = htons(ETH_P_IP); + hdr.hlen = 6; + hdr.plen = 4; + hdr.opcode = htons(1); //ARPOP_REQUEST + memcpy(hdr.sender_mac, mac, 6); + memcpy(hdr.sender_ip, (uint8_t *)&src, 4); + uint32_t dst = htonl(dest); + memcpy(hdr.target_ip, (uint8_t *)&dst, 4); + + // buiild ethernet frame + uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc + memset(frame, 0, sizeof(frame)); + frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; + memcpy(frame + 6, mac, 6); + frame[12] = ETH_P_ARP / 256; + frame[13] = ETH_P_ARP % 256; + memcpy (frame + 14, &hdr, sizeof(hdr)); + + // send packet + int len; + if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) + errExit("send"); +//printf("send %d bytes to %d.%d.%d.%d\n", len, PRINT_IP(dest)); + fflush(0); + dest++; + } + + if (FD_ISSET(sock, &rfds)) { + // read the incoming packet + int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); + if (len < 0) { + perror("recvfrom"); + } + + // parse the incomming packet + if (len < 14 + sizeof(ArpHdr)) + continue; + + // look only at ARP packets + if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) + continue; + + ArpHdr hdr; + memcpy(&hdr, frame + 14, sizeof(ArpHdr)); + + if (hdr.opcode == htons(2)) { + // check my mac and my address + if (memcmp(mac, hdr.target_mac, 6) != 0) + continue; + uint32_t ip; + memcpy(&ip, hdr.target_ip, 4); + if (ip != src) + continue; + memcpy(&ip, hdr.sender_ip, 4); + ip = ntohl(ip); + + if (ip == last_ip) // filter duplicates + continue; + last_ip = ip; + + // printing + if (header_printed == 0) { + printf(" Network scan:\n"); + + // print parent interface + if (cfg.bridge0.configured && cfg.bridge0.ip && cfg.bridge0.macvlan && + (cfg.bridge0.ip & cfg.bridge0.mask) == (ifip & cfg.bridge0.mask)) + printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", + PRINT_MAC(cfg.bridge0.mac), PRINT_IP(cfg.bridge0.ip)); + + if (cfg.bridge1.configured && cfg.bridge1.ip && cfg.bridge1.macvlan && + (cfg.bridge1.ip & cfg.bridge1.mask) == (ifip & cfg.bridge1.mask)) + printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", + PRINT_MAC(cfg.bridge1.mac), PRINT_IP(cfg.bridge1.ip)); + + if (cfg.bridge2.configured && cfg.bridge2.ip && cfg.bridge2.macvlan && + (cfg.bridge2.ip & cfg.bridge2.mask) == (ifip & cfg.bridge2.mask)) + printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", + PRINT_MAC(cfg.bridge2.mac), PRINT_IP(cfg.bridge2.ip)); + + if (cfg.bridge3.configured && cfg.bridge3.ip && cfg.bridge3.macvlan && + (cfg.bridge3.ip & cfg.bridge3.mask) == (ifip & cfg.bridge3.mask)) + printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", + PRINT_MAC(cfg.bridge3.mac), PRINT_IP(cfg.bridge3.ip)); + + header_printed = 1; + } + printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", + PRINT_MAC(hdr.sender_mac), PRINT_IP(ip)); + } + } + } + + close(sock); +} + + diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c new file mode 100644 index 000000000..25dd7a0fb --- /dev/null +++ b/src/firejail/bandwidth.c @@ -0,0 +1,483 @@ +/* + * 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. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "firejail.h" + +//*********************************** +// interface bandwidth linked list +//*********************************** +typedef struct ifbw_t { + struct ifbw_t *next; + char *txt; +} IFBW; +IFBW *ifbw = NULL; + + +#if 0 +static void ifbw_print(void) { + IFBW *ptr = ifbw; + while (ptr) { + printf("#%s#\n", ptr->txt); + ptr = ptr->next; + } +} +#endif + +static void ifbw_add(IFBW *ptr) { + assert(ptr); + + if (ifbw != NULL) + ptr->next = ifbw; + ifbw = ptr; +} + + +IFBW *ifbw_find(const char *dev) { + assert(dev); + int len = strlen(dev); + assert(len); + + if (ifbw == NULL) + return NULL; + + IFBW *ptr = ifbw; + while (ptr) { + if (strncmp(ptr->txt, dev, len) == 0 && ptr->txt[len] == ':') + return ptr; + ptr = ptr->next; + } + + return NULL; +} + +void ifbw_remove(IFBW *r) { + if (ifbw == NULL) + return; + + // remove the first element + if (ifbw == r) { + ifbw = ifbw->next; + return; + } + + // walk the list + IFBW *ptr = ifbw->next; + IFBW *prev = ifbw; + while (ptr) { + if (ptr == r) { + prev->next = ptr->next; + return; + } + + prev = ptr; + ptr = ptr->next; + } + + return; +} + +int fibw_count(viod) { + int rv = 0; + IFBW *ptr = ifbw; + + while (ptr) { + rv++; + ptr = ptr->next; + } + + return rv; +} + + +//*********************************** +// shm file handling +//*********************************** +void shm_create_firejail_dir(void) { + struct stat s; + if (stat("/dev/shm/firejail", &s) == -1) { + /* coverity[toctou] */ + if (mkdir("/dev/shm/firejail", 0777) == -1) + errExit("mkdir"); + if (chown("/dev/shm/firejail", 0, 0) == -1) + errExit("chown"); + } + else { // check /dev/shm/firejail directory belongs to root end exit if doesn't! + if (s.st_uid != 0 || s.st_gid != 0) { + fprintf(stderr, "Error: non-root %s directory, exiting...\n", "/dev/shm/firejail"); + exit(1); + } + } +} + +static void shm_create_bandwidth_file(pid_t pid) { + char *fname; + if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1) + errExit("asprintf"); + + // if the file already exists, do nothing + struct stat s; + if (stat(fname, &s) == 0) { + free(fname); + return; + } + + // create an empty file and set mod and ownership + /* coverity[toctou] */ + FILE *fp = fopen(fname, "w"); + if (fp) { + fclose(fp); + + /* coverity[toctou] */ + if (chmod(fname, 0644) == -1) + errExit("chmod"); + /* coverity[toctou] */ + if (chown(fname, 0, 0) == -1) + errExit("chown"); + } + else { + fprintf(stderr, "Error: cannot create bandwidth file in /dev/shm/firejail directory\n"); + exit(1); + } + + free(fname); +} + +// delete shm bandwidth file +void bandwidth_shm_del_file(pid_t pid) { + char *fname; + if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1) + errExit("asprintf"); + unlink(fname); + free(fname); +} + +void network_shm_del_file(pid_t pid) { + char *fname; + if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1) + errExit("asprintf"); + unlink(fname); + free(fname); +} + +void network_shm_set_file(pid_t pid) { + char *fname; + if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1) + errExit("asprintf"); + + // create an empty file and set mod and ownership + FILE *fp = fopen(fname, "w"); + if (fp) { + if (cfg.bridge0.configured) + fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox); + if (cfg.bridge1.configured) + fprintf(fp, "%s:%s\n", cfg.bridge1.dev, cfg.bridge1.devsandbox); + if (cfg.bridge2.configured) + fprintf(fp, "%s:%s\n", cfg.bridge2.dev, cfg.bridge2.devsandbox); + if (cfg.bridge3.configured) + fprintf(fp, "%s:%s\n", cfg.bridge3.dev, cfg.bridge3.devsandbox); + fclose(fp); + + if (chmod(fname, 0644) == -1) + errExit("chmod"); + if (chown(fname, 0, 0) == -1) + errExit("chown"); + } + else { + fprintf(stderr, "Error: cannot create network map file in /dev/shm/firejail directory\n"); + exit(1); + } + + free(fname); +} + + +void shm_read_bandwidth_file(pid_t pid) { + assert(ifbw == NULL); + + char *fname; + if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1) + errExit("asprintf"); + + FILE *fp = fopen(fname, "r"); + if (fp) { + char buf[1024]; + while (fgets(buf, 1024,fp)) { + // remove '\n' + char *ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + if (strlen(buf) == 0) + continue; + + // create a new IFBW entry + IFBW *ifbw_new = malloc(sizeof(IFBW)); + if (!ifbw_new) + errExit("malloc"); + memset(ifbw_new, 0, sizeof(IFBW)); + ifbw_new->txt = strdup(buf); + if (!ifbw_new->txt) + errExit("strdup"); + + // add it to the linked list + ifbw_add(ifbw_new); + } + + fclose(fp); + } +} + +void shm_write_bandwidth_file(pid_t pid) { + if (ifbw == NULL) + return; // nothing to do + + char *fname; + if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1) + errExit("asprintf"); + + FILE *fp = fopen(fname, "w"); + if (fp) { + IFBW *ptr = ifbw; + while (ptr) { + fprintf(fp, "%s\n", ptr->txt); + ptr = ptr->next; + } + fclose(fp); + } + else { + fprintf(stderr, "Error: cannot write bandwidht file %s\n", fname); + exit(1); + } +} + +//*********************************** +// add or remove interfaces +//*********************************** + +// remove interface from shm file +void bandwidth_shm_remove(pid_t pid, const char *dev) { + // create bandwidth directory & file in case they are not in the filesystem yet + shm_create_firejail_dir(); + shm_create_bandwidth_file(pid); + + // read bandwidth file + shm_read_bandwidth_file(pid); + + // find the element and remove it + IFBW *elem = ifbw_find(dev); + if (elem) { + ifbw_remove(elem); + shm_write_bandwidth_file(pid) ; + } + + // remove the file if there are no entries in the list + if (ifbw == NULL) { + bandwidth_shm_del_file(pid); + } +} + +// add interface to shm file +void bandwidth_shm_set(pid_t pid, const char *dev, int down, int up) { + // create bandwidth directory & file in case they are not in the filesystem yet + shm_create_firejail_dir(); + shm_create_bandwidth_file(pid); + + // create the new text entry + char *txt; + if (asprintf(&txt, "%s: RX %dKB/s, TX %dKB/s", dev, down, up) == -1) + errExit("asprintf"); + + // read bandwidth file + shm_read_bandwidth_file(pid); + + // look for an existing entry and replace the text + IFBW *ptr = ifbw_find(dev); + if (ptr) { + assert(ptr->txt); + free(ptr->txt); + ptr->txt = txt; + } + // ... or add a new entry + else { + IFBW *ifbw_new = malloc(sizeof(IFBW)); + if (!ifbw_new) + errExit("malloc"); + memset(ifbw_new, 0, sizeof(IFBW)); + ifbw_new->txt = txt; + + // add it to the linked list + ifbw_add(ifbw_new); + } + shm_write_bandwidth_file(pid) ; +} + + +//*********************************** +// command execution +//*********************************** +void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up) { + if (!name || strlen(name) == 0) { + fprintf(stderr, "Error: invalid sandbox name\n"); + exit(1); + } + pid_t pid; + if (name2pid(name, &pid)) { + fprintf(stderr, "Error: cannot find sandbox %s\n", name); + exit(1); + } + + bandwidth_pid(pid, command, dev, down, up); +} + +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); +} diff --git a/src/firejail/caps.c b/src/firejail/caps.c new file mode 100644 index 000000000..d8777c0db --- /dev/null +++ b/src/firejail/caps.c @@ -0,0 +1,453 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int capget(cap_user_header_t hdrp, cap_user_data_t datap); +extern int capset(cap_user_header_t hdrp, const cap_user_data_t datap); + + +typedef struct { + char *name; + int nr; +} CapsEntry; + +static CapsEntry capslist[] = { +// +// code generated using tools/extract-caps +// updated manually based on kernel 3.18/include/linux/capability.h (added block suspend and audit_read +// +#ifdef CAP_CHOWN + {"chown", CAP_CHOWN }, +#endif +#ifdef CAP_DAC_OVERRIDE + {"dac_override", CAP_DAC_OVERRIDE }, +#endif +#ifdef CAP_DAC_READ_SEARCH + {"dac_read_search", CAP_DAC_READ_SEARCH }, +#endif +#ifdef CAP_FOWNER + {"fowner", CAP_FOWNER }, +#endif +#ifdef CAP_FSETID + {"fsetid", CAP_FSETID }, +#endif +#ifdef CAP_KILL + {"kill", CAP_KILL }, +#endif +#ifdef CAP_SETGID + {"setgid", CAP_SETGID }, +#endif +#ifdef CAP_SETUID + {"setuid", CAP_SETUID }, +#endif +#ifdef CAP_SETPCAP + {"setpcap", CAP_SETPCAP }, +#endif +#ifdef CAP_LINUX_IMMUTABLE + {"linux_immutable", CAP_LINUX_IMMUTABLE }, +#endif +#ifdef CAP_NET_BIND_SERVICE + {"net_bind_service", CAP_NET_BIND_SERVICE }, +#endif +#ifdef CAP_NET_BROADCAST + {"net_broadcast", CAP_NET_BROADCAST }, +#endif +#ifdef CAP_NET_ADMIN + {"net_admin", CAP_NET_ADMIN }, +#endif +#ifdef CAP_NET_RAW + {"net_raw", CAP_NET_RAW }, +#endif +#ifdef CAP_IPC_LOCK + {"ipc_lock", CAP_IPC_LOCK }, +#endif +#ifdef CAP_IPC_OWNER + {"ipc_owner", CAP_IPC_OWNER }, +#endif +#ifdef CAP_SYS_MODULE + {"sys_module", CAP_SYS_MODULE }, +#endif +#ifdef CAP_SYS_RAWIO + {"sys_rawio", CAP_SYS_RAWIO }, +#endif +#ifdef CAP_SYS_CHROOT + {"sys_chroot", CAP_SYS_CHROOT }, +#endif +#ifdef CAP_SYS_PTRACE + {"sys_ptrace", CAP_SYS_PTRACE }, +#endif +#ifdef CAP_SYS_PACCT + {"sys_pacct", CAP_SYS_PACCT }, +#endif +#ifdef CAP_SYS_ADMIN + {"sys_admin", CAP_SYS_ADMIN }, +#endif +#ifdef CAP_SYS_BOOT + {"sys_boot", CAP_SYS_BOOT }, +#endif +#ifdef CAP_SYS_NICE + {"sys_nice", CAP_SYS_NICE }, +#endif +#ifdef CAP_SYS_RESOURCE + {"sys_resource", CAP_SYS_RESOURCE }, +#endif +#ifdef CAP_SYS_TIME + {"sys_time", CAP_SYS_TIME }, +#endif +#ifdef CAP_SYS_TTY_CONFIG + {"sys_tty_config", CAP_SYS_TTY_CONFIG }, +#endif +#ifdef CAP_MKNOD + {"mknod", CAP_MKNOD }, +#endif +#ifdef CAP_LEASE + {"lease", CAP_LEASE }, +#endif +#ifdef CAP_AUDIT_WRITE + {"audit_write", CAP_AUDIT_WRITE }, +#endif +#ifdef CAP_AUDIT_CONTROL + {"audit_control", CAP_AUDIT_CONTROL }, +#endif +#ifdef CAP_SETFCAP + {"setfcap", CAP_SETFCAP }, +#endif +#ifdef CAP_MAC_OVERRIDE + {"mac_override", CAP_MAC_OVERRIDE }, +#endif +#ifdef CAP_MAC_ADMIN + {"mac_admin", CAP_MAC_ADMIN }, +#endif +#ifdef CAP_SYSLOG + {"syslog", CAP_SYSLOG }, +#endif +#ifdef CAP_WAKE_ALARM + {"wake_alarm", CAP_WAKE_ALARM }, +#endif +// not in Debian 7 +#ifdef CAP_BLOCK_SUSPEND + {"block_suspend", CAP_BLOCK_SUSPEND }, +#else + {"block_suspend", 36 }, +#endif +#ifdef CAP_AUDIT_READ + {"audit_read", CAP_AUDIT_READ }, +#else + {"audit_read", 37 }, +#endif + +// +// end of generated code +// +}; // end of capslist + +const char *caps_find_nr(int nr) { + int i; + int elems = sizeof(capslist) / sizeof(capslist[0]); + for (i = 0; i < elems; i++) { + if (nr == capslist[i].nr) + return capslist[i].name; + } + + return "unknown"; +} + +// return -1 if error, or syscall number +static int caps_find_name(const char *name) { + int i; + int elems = sizeof(capslist) / sizeof(capslist[0]); + for (i = 0; i < elems; i++) { + if (strcmp(name, capslist[i].name) == 0) + return capslist[i].nr; + } + + return -1; +} + +// return 1 if error, 0 if OK +int caps_check_list(const char *clist, void (*callback)(int)) { + // don't allow empty lists + if (clist == NULL || *clist == '\0') { + fprintf(stderr, "Error: empty capabilities lists are not allowed\n"); + return -1; + } + + // work on a copy of the string + char *str = strdup(clist); + if (!str) + errExit("strdup"); + + char *ptr = str; + char *start = str; + while (*ptr != '\0') { + if (islower(*ptr) || isdigit(*ptr) || *ptr == '_') + ; + else if (*ptr == ',') { + *ptr = '\0'; + int nr = caps_find_name(start); + if (nr == -1) { + fprintf(stderr, "Error: capability %s not found\n", start); + free(str); + return -1; + } + else if (callback != NULL) + callback(nr); + + start = ptr + 1; + } + ptr++; + } + if (*start != '\0') { + int nr = caps_find_name(start); + if (nr == -1) { + fprintf(stderr, "Error: capability %s not found\n", start); + free(str); + return -1; + } + else if (callback != NULL) + callback(nr); + } + + free(str); + return 0; +} + +void caps_print(void) { + int i; + int elems = sizeof(capslist) / sizeof(capslist[0]); + + // check current caps supported by the kernel + int cnt = 0; + unsigned long cap; + for (cap=0; cap <= 63; cap++) { + int code = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0); + if (code == 0) + cnt++; + } + printf("Your kernel supports %d capabilities.\n", cnt); + + for (i = 0; i < elems; i++) { + printf("%d\t- %s\n", capslist[i].nr, capslist[i].name); + } +} + + + + +// enabled by default +int caps_default_filter(void) { + // drop capabilities + if (prctl(PR_CAPBSET_DROP, CAP_SYS_MODULE, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYS_MODULE"); + else if (arg_debug) + printf("Drop CAP_SYS_MODULE\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_SYS_RAWIO, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYS_RAWIO"); + else if (arg_debug) + printf("Drop CAP_SYS_RAWIO\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYS_BOOT"); + else if (arg_debug) + printf("Drop CAP_SYS_BOOT\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_SYS_NICE, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYS_NICE"); + else if (arg_debug) + printf("Drop CAP_SYS_NICE\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_SYS_TTY_CONFIG, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYS_TTY_CONFIG"); + else if (arg_debug) + printf("Drop CAP_SYS_TTY_CONFIG\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_SYSLOG, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYSLOG"); + else if (arg_debug) + printf("Drop CAP_SYSLOG\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_MKNOD, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_MKNOD"); + else if (arg_debug) + printf("Drop CAP_MKNOD\n"); + + if (prctl(PR_CAPBSET_DROP, CAP_SYS_ADMIN, 0, 0, 0) && arg_debug) + fprintf(stderr, "Warning: cannot drop CAP_SYS_ADMIN"); + else if (arg_debug) + printf("Drop CAP_SYS_ADMIN\n"); + + return 0; +} + +void caps_drop_all(void) { + if (arg_debug) + printf("Droping all capabilities\n"); + + unsigned long cap; + for (cap=0; cap <= 63; cap++) { + int code = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0); + if (code == -1 && errno != EINVAL) + errExit("PR_CAPBSET_DROP"); + } +} + + +void caps_set(uint64_t caps) { + if (arg_debug) + printf("Set caps filter %llx\n", (unsigned long long) caps); + + unsigned long i; + uint64_t mask = 1LLU; + for (i = 0; i < 64; i++, mask <<= 1) { + if ((mask & caps) == 0) { + int code = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); + if (code == -1 && errno != EINVAL) + errExit("PR_CAPBSET_DROP"); + } + } +} + + +static uint64_t filter; + +static void caps_set_bit(int nr) { + uint64_t mask = 1LLU << nr; + filter |= mask; +} +static void caps_reset_bit(int nr) { + uint64_t mask = 1LLU << nr; + filter &= ~mask; +} + +void caps_drop_list(const char *clist) { + filter = 0; + filter--; + caps_check_list(clist, caps_reset_bit); + caps_set(filter); +} + +void caps_keep_list(const char *clist) { + filter = 0; + caps_check_list(clist, caps_set_bit); + caps_set(filter); +} + +#define MAXBUF 4098 +static uint64_t extract_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); + exit(1); + } + + char buf[MAXBUF]; + while (fgets(buf, MAXBUF, fp)) { + if (strncmp(buf, "CapBnd:", 7) == 0) { + char *ptr = buf + 8; + unsigned long long val; + sscanf(ptr, "%llx", &val); + free(file); + fclose(fp); + return val; + } + } + fclose(fp); + free(file); + printf("Error: cannot read caps configuration\n"); + exit(1); +} + + +void caps_print_filter_name(const char *name) { + if (!name || strlen(name) == 0) { + fprintf(stderr, "Error: invalid sandbox name\n"); + exit(1); + } + pid_t pid; + if (name2pid(name, &pid)) { + fprintf(stderr, "Error: cannot find sandbox %s\n", name); + exit(1); + } + + caps_print_filter(pid); +} + +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); +} diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c new file mode 100644 index 000000000..7366a6699 --- /dev/null +++ b/src/firejail/cgroup.c @@ -0,0 +1,118 @@ +/* + * 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 "firejail.h" +#include + +#define MAXBUF 4096 + +void save_cgroup(void) { + if (cfg.cgroup == NULL) + return; + + char *fname; + if (asprintf(&fname, "%s/cgroup", MNT_DIR) == -1) + errExit(fname); + + FILE *fp = fopen(fname, "w"); + if (fp) { + fprintf(fp, "%s", cfg.cgroup); + fflush(0); + fclose(fp); + if (chown(fname, 0, 0) < 0) + errExit("chown"); + } + else { + fprintf(stderr, "Error: cannot save cgroup\n"); + free(fname); + exit(1); + } + + free(fname); +} + +void load_cgroup(const char *fname) { + if (!fname) + return; + + FILE *fp = fopen(fname, "r"); + if (fp) { + char buf[MAXBUF]; + if (fgets(buf, MAXBUF, fp)) { + cfg.cgroup = strdup(buf); + if (!cfg.cgroup) + errExit("strdup"); + } + else + goto errout; + + fclose(fp); + return; + } +errout: + fprintf(stderr, "Warrning: cannot load control group\n"); + if (fp) + fclose(fp); +} + + +void set_cgroup(const char *path) { + // path starts with /sys/fs/cgroup + if (strncmp(path, "/sys/fs/cgroup", 14) != 0) + goto errout; + + // path ends in tasks + char *ptr = strstr(path, "tasks"); + if (!ptr) + goto errout; + if (*(ptr + 5) != '\0') + goto errout; + + // no .. traversal + ptr = strstr(path, ".."); + if (ptr) + goto errout; + + // tasks file exists + struct stat s; + if (stat(path, &s) == -1) + goto errout; + + // task file belongs to the user running the sandbox + if (s.st_uid != getuid() && s.st_gid != getgid()) + goto errout2; + + // add the task to cgroup + /* coverity[toctou] */ + FILE *fp = fopen(path, "a"); + if (!fp) + goto errout; + pid_t pid = getpid(); + int rv = fprintf(fp, "%d\n", pid); + (void) rv; + fclose(fp); + return; + +errout: + fprintf(stderr, "Error: invalid cgroup\n"); + exit(1); +errout2: + fprintf(stderr, "Error: you don't have permissions to use this control group\n"); + exit(1); +} diff --git a/src/firejail/cpu.c b/src/firejail/cpu.c new file mode 100644 index 000000000..633081a3f --- /dev/null +++ b/src/firejail/cpu.c @@ -0,0 +1,141 @@ +/* + * 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 "firejail.h" +#include + +// converts a numeric cpu value in the corresponding bit mask +static void set_cpu(const char *str) { + if (strlen(str) == 0) + return; + + int val = atoi(str); + if (val < 0 || val >= 32) { + fprintf(stderr, "Error: invalid cpu number. Accepted values are between 0 and 31.\n"); + exit(1); + } + + uint32_t mask = 1; + int i; + for (i = 0; i < val; i++, mask <<= 1); + cfg.cpus |= mask; +} + +void read_cpu_list(const char *str) { + char *tmp = strdup(str); + if (tmp == NULL) + errExit("strdup"); + + char *ptr = tmp; + while (*ptr != '\0') { + if (*ptr == ',' || isdigit(*ptr)) + ; + else { + fprintf(stderr, "Error: invalid cpu list\n"); + exit(1); + } + ptr++; + } + + char *start = tmp; + ptr = tmp; + while (*ptr != '\0') { + if (*ptr == ',') { + *ptr = '\0'; + set_cpu(start); + start = ptr + 1; + } + ptr++; + } + set_cpu(start); + free(tmp); +} + +void save_cpu(void) { + if (cfg.cpus == 0) + return; + + char *fname; + if (asprintf(&fname, "%s/cpu", MNT_DIR) == -1) + errExit("asprintf"); + FILE *fp = fopen(fname, "w"); + if (fp) { + fprintf(fp, "%x\n", cfg.cpus); + fclose(fp); + if (chown(fname, 0, 0) < 0) + errExit("chown"); + } + else { + fprintf(stderr, "Error: cannot save cpu affinity mask\n"); + free(fname); + exit(1); + } + + free(fname); +} + +void load_cpu(const char *fname) { + if (!fname) + return; + + FILE *fp = fopen(fname, "r"); + if (fp) { + unsigned tmp; + int rv = fscanf(fp, "%x", &tmp); + if (rv) + cfg.cpus = (uint32_t) tmp; + fclose(fp); + } + else + fprintf(stderr, "Warning: cannot load cpu affinity mask\n"); +} + +void set_cpu_affinity(void) { + // set cpu affinity + cpu_set_t mask; + CPU_ZERO(&mask); + + int i; + uint32_t m = 1; + for (i = 0; i < 32; i++, m <<= 1) { + if (cfg.cpus & m) + CPU_SET(i, &mask); + } + + if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { + fprintf(stderr, "Warning: cannot set cpu affinity\n"); + fprintf(stderr, " "); + perror("sched_setaffinity"); + } + + // verify cpu affinity + cpu_set_t mask2; + CPU_ZERO(&mask2); + if (sched_getaffinity(0, sizeof(mask2), &mask2) == -1) { + fprintf(stderr, "Warning: cannot verify cpu affinity\n"); + fprintf(stderr, " "); + perror("sched_getaffinity"); + } + else { + if (CPU_EQUAL(&mask, &mask2)) + printf("CPU affinity set\n"); + else + printf("CPU affinity not set\n"); + } +} diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h new file mode 100644 index 000000000..2ec6e54c9 --- /dev/null +++ b/src/firejail/firejail.h @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2014, 2015 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. +*/ +#ifndef FIREJAIL_H +#define FIREJAIL_H +#include "../include/common.h" + +#define USELOCK +#define FIREJAIL_DIR "/tmp/firejail" +#define RO_DIR "/tmp/firejail/firejail.ro.dir" +#define RO_FILE "/tmp/firejail/firejail.ro.file" +#define MNT_DIR "/tmp/firejail/mnt" +#define OVERLAY_DIR "/tmp/firejail/overlay" +#define HOME_DIR "/tmp/firejail/mnt/home" +#define MAX_INCLUDE_LEVEL 6 + +// main.c +typedef struct bridge_t { + // on the host + char *dev; // interface device name: bridge or regular ethernet + uint32_t ip; // interface device IP address + uint32_t mask; // interface device mask + uint8_t mac[6]; // interface mac address + + // inside the sandbox + char *devsandbox; // name of the device inside the sandbox + uint32_t ipsandbox; // ip address inside the sandbox + uint8_t macsandbox[6]; // mac address inside the sandbox + uint32_t iprange_start;// iprange arp scan start range + uint32_t iprange_end; // iprange arp scan end range + + // flags + uint8_t arg_ip_none; // --ip=none + uint8_t macvlan; // set by --net=eth0 (or eth1, ...); reset by --net=br0 (or br1, ...) + uint8_t configured; + uint8_t scan; // set by --scan +} Bridge; + +typedef struct profile_entry_t { + struct profile_entry_t *next; + char *data; +}ProfileEntry; + +typedef struct config_t { + // user data + char *username; + char *homedir; + + // filesystem + ProfileEntry *profile; + char *chrootdir; // chroot directory + char *home_private; // private home directory + char *home_private_keep; // keep list for private home directory + char *cwd; // current working directory + + // networking + char *hostname; + uint32_t defaultgw; // default gateway + Bridge bridge0; + Bridge bridge1; + Bridge bridge2; + Bridge bridge3; + uint32_t dns1; // up to 3 IP addresses for dns servers + uint32_t dns2; + uint32_t dns3; + + // rlimits + unsigned rlimit_nofile; + unsigned rlimit_nproc; + unsigned rlimit_fsize; + unsigned rlimit_sigpending; + + // cpu affinity and control groups + uint32_t cpus; + char *cgroup; + + + // command line + char *command_line; + char *command_name; + char *shell; + char **original_argv; + int original_argc; + int original_program_index; +} Config; +extern Config cfg; + +static inline int any_bridge_configured(void) { + if (cfg.bridge3.configured || cfg.bridge2.configured || cfg.bridge1.configured || cfg.bridge0.configured) + return 1; + else + return 0; +} +extern int arg_private; // mount private /home and /tmp directory +extern int arg_debug; // print debug messages +extern int arg_nonetwork; // --net=none +extern int arg_command; // -c +extern int arg_overlay; // --overlay +extern int arg_zsh; // use zsh as default shell +extern int arg_csh; // use csh as default shell + +extern int arg_seccomp; // enable default seccomp filter +extern char *arg_seccomp_list;// optional seccomp list on top of default filter +extern char *arg_seccomp_list_drop; // seccomp drop list +extern char *arg_seccomp_list_keep; // seccomp keep list + +extern int arg_caps_default_filter; // enable default capabilities filter +extern int arg_caps_drop; // drop list +extern int arg_caps_drop_all; // drop all capabilities +extern int arg_caps_keep; // keep list +extern char *arg_caps_list; // optional caps list + +extern int arg_trace; // syscall tracing support +extern int arg_rlimit_nofile; // rlimit nofile +extern int arg_rlimit_nproc; // rlimit nproc +extern int arg_rlimit_fsize; // rlimit fsize +extern int arg_rlimit_sigpending;// rlimit sigpending +extern int arg_nox11; // kill the program if x11 unix domain socket is accessed +extern int arg_nodbus; // kill the program if D-Bus is accessed +extern int arg_nogroups; // disable supplementary groups +extern int arg_noroot; // create a new user namespace and disable root user +extern int arg_netfilter; // enable netfilter +extern char *arg_netfilter_file; // netfilter file +extern int arg_doubledash; // double dash +extern int arg_shell_none; // run the program directly without a shell +extern int arg_private_dev; // private dev directory +extern int arg_scan; // arp-scan all interfaces + +extern int parent_to_child_fds[2]; +extern int child_to_parent_fds[2]; +extern pid_t sandbox_pid; + + + +#define MAX_ARGS 128 // maximum number of command arguments (argc) +extern char *fullargv[MAX_ARGS]; +extern int fullargc; + +// main.c +void check_user_namespace(void); + +// sandbox.c +int sandbox(void* sandbox_arg); + +// network_main.c +void net_configure_bridge(Bridge *br, char *dev_name); +void net_configure_sandbox_ip(Bridge *br); +void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child); +void net_check_cfg(void); +void net_dns_print_name(const char *name); +void net_dns_print(pid_t pid); + +// network.c +void net_if_up(const char *ifname); +void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask); +int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6]); +int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); +void net_ifprint(void); +void net_bridge_add_interface(const char *bridge, const char *dev); +uint32_t network_get_defaultgw(void); +int net_config_mac(const char *ifname, const unsigned char mac[6]); +int net_get_mac(const char *ifname, unsigned char mac[6]); + +// fs.c +// build /tmp/firejail directory +void fs_build_firejail_dir(void); +// build /tmp/firejail/mnt directory +void fs_build_mnt_dir(void); +// blacklist files or directoies by mounting empty files on top of them +void fs_blacklist(const char *homedir); +//void fs_blacklist(char **blacklist, const char *homedir); +// remount a directory read-only +void fs_rdonly(const char *dir); +// mount /proc and /sys directories +void fs_proc_sys_dev_boot(void); +// build a basic read-only filesystem +void fs_basic_fs(void); +// mount overlayfs on top of / directory +void fs_overlayfs(void); +// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf +void fs_chroot(const char *rootdir); +int fs_check_chroot_dir(const char *rootdir); + +// profile.c +// find and read the profile specified by name from dir directory +int profile_find(const char *name, const char *dir); +// read a profile file +void profile_read(const char *fname, const char *skip1, const char *skip2); +// check profile line; if line == 0, this was generated from a command line option +// return 1 if the command is to be added to the linked list of profile commands +// return 0 if the command was already executed inside the function +int profile_check_line(char *ptr, int lineno); +// add a profile entry in cfg.profile list; use str to populate the list +void profile_add(char *str); + +// list.c +void list(void); +void tree(void); +void top(void); +void netstats(void); + +// usage.c +void usage(void); + +// join.c +void join(pid_t pid, const char *homedir, int argc, char **argv, int index); +void join_name(const char *name, const char *homedir, int argc, char **argv, int index); +void shut(pid_t pid); +void shut_name(const char *name); + +// restricted_shell.c +extern char *restricted_user; +int restricted_shell(const char *user); + +// arp.c +// returns 0 if the address is not in use, -1 otherwise +int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr); +// assign an IP address using arp scanning +uint32_t arp_assign(const char *dev, Bridge *br); +// scan interface (--scan option) +void arp_scan(const char *dev, uint32_t srcaddr, uint32_t srcmask); + +// veth.c +int net_create_veth(const char *dev, const char *nsdev, unsigned pid); +int net_create_macvlan(const char *dev, const char *parent, unsigned pid); + +// util.c +void drop_privs(int nogroups); +void extract_command_name(const char *str); +void logsignal(int s); +void logmsg(const char *msg); +void logargs(int argc, char **argv) ; +void logerr(const char *msg); +int copy_file(const char *srcname, const char *destname); +char *get_link(const char *fname); +int is_dir(const char *fname); +int is_link(const char *fname); +char *line_remove_spaces(const char *buf); +char *split_comma(char *str); +int not_unsigned(const char *str); +int find_child(pid_t parent, pid_t *child); +void check_private_dir(void); +void update_map(char *mapping, char *map_file); +void wait_for_other(int fd); +void notify_other(int fd); + +// fs_var.c +void fs_var_log(void); // mounting /var/log +void fs_var_lib(void); // various other fixes for software in /var directory +void fs_var_cache(void); // various other fixes for software in /var/cache directory +void fs_var_run(void); +void fs_var_lock(void); +void fs_var_tmp(void); +void fs_var_utmp(void); +void dbg_test_dir(const char *dir); + +// fs_dev.c +void fs_dev_shm(void); +void fs_private_dev(void); + +// fs_home.c +// private mode (--private) +void fs_private(void); +// private mode (--private=homedir) +void fs_private_homedir(void); +// private mode (--private.keep=list) +void fs_private_home_list(void); +// check directory linst specified by user (--private.keep option) - exit if it fails +void fs_check_home_list(void); +// check new private home directory (--private= option) - exit if it fails +void fs_check_private_dir(void); + + +// seccomp.c +int seccomp_filter_drop(void); +int seccomp_filter_keep(void); +void seccomp_set(void); +void seccomp_print_filter_name(const char *name); +void seccomp_print_filter(pid_t pid); + +// caps.c +int caps_default_filter(void); +void caps_print(void); +void caps_drop_all(void); +void caps_set(uint64_t caps); +int caps_check_list(const char *clist, void (*callback)(int)); +void caps_drop_list(const char *clist); +void caps_keep_list(const char *clist); +void caps_print_filter(pid_t pid); +void caps_print_filter_name(const char *name); + +// syscall.c +const char *syscall_find_nr(int nr); +// return -1 if error, 0 if no error +int syscall_check_list(const char *slist, void (*callback)(int)); +// print all available syscalls +void syscall_print(void); + +// fs_trace.c +void fs_trace_preload(void); +void fs_trace(void); + +// fs_hostname.c +void fs_hostname(const char *hostname); +void fs_resolvconf(void); + +// rlimit.c +void set_rlimits(void); + +// cpu.c +void read_cpu_list(const char *str); +void set_cpu_affinity(void); +void load_cpu(const char *fname); +void save_cpu(void); + +// cgroup.c +void save_cgroup(void); +void load_cgroup(const char *fname); +void set_cgroup(const char *path); + +// output.c +void check_output(int argc, char **argv); + +// netfilter.c +void check_netfilter_file(const char *fname); +void netfilter(const char *fname); + +// bandwidth.c +void shm_create_firejail_dir(void); +void bandwidth_shm_del_file(pid_t pid); +void bandwidth_shm_set(pid_t pid, const char *dev, int down, int up); +void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up); +void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up); +void network_shm_del_file(pid_t pid); +void network_shm_set_file(pid_t pid); + + +#endif \ No newline at end of file diff --git a/src/firejail/fs.c b/src/firejail/fs.c new file mode 100644 index 000000000..1fc1c0942 --- /dev/null +++ b/src/firejail/fs.c @@ -0,0 +1,825 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include + +// build /tmp/firejail directory +void fs_build_firejail_dir(void) { + struct stat s; + + if (stat(FIREJAIL_DIR, &s)) { + if (arg_debug) + printf("Creating %s directory\n", FIREJAIL_DIR); + /* coverity[toctou] */ + int rv = mkdir(FIREJAIL_DIR, S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown(FIREJAIL_DIR, 0, 0) < 0) + errExit("chown"); + if (chmod(FIREJAIL_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + errExit("chmod"); + } + else { // check /tmp/firejail directory belongs to root end exit if doesn't! + if (s.st_uid != 0 || s.st_gid != 0) { + fprintf(stderr, "Error: non-root %s directory, exiting...\n", FIREJAIL_DIR); + exit(1); + } + } +} + + +// build /tmp/firejail/mnt directory +static int tmpfs_mounted = 0; +void fs_build_mnt_dir(void) { + struct stat s; + fs_build_firejail_dir(); + + // create /tmp/firejail directory + if (stat(MNT_DIR, &s)) { + if (arg_debug) + printf("Creating %s directory\n", MNT_DIR); + /* coverity[toctou] */ + int rv = mkdir(MNT_DIR, S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown(MNT_DIR, 0, 0) < 0) + errExit("chown"); + if (chmod(MNT_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + errExit("chmod"); + } + + // ... and mount tmpfs on top of it + if (!tmpfs_mounted) { + // mount tmpfs on top of /tmp/firejail/mnt + if (arg_debug) + printf("Mounting tmpfs on %s directory\n", MNT_DIR); + if (mount("tmpfs", MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /tmp/firejail/mnt"); + tmpfs_mounted = 1; + } +} + +// build /tmp/firejail/overlay directory +void fs_build_overlay_dir(void) { + struct stat s; + fs_build_firejail_dir(); + + // create /tmp/firejail directory + if (stat(OVERLAY_DIR, &s)) { + if (arg_debug) + printf("Creating %s directory\n", MNT_DIR); + /* coverity[toctou] */ + int rv = mkdir(OVERLAY_DIR, S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown(OVERLAY_DIR, 0, 0) < 0) + errExit("chown"); + if (chmod(OVERLAY_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + errExit("chmod"); + } +} + + + + + +//*********************************************** +// process profile file +//*********************************************** +typedef enum { + BLACKLIST_FILE, + MOUNT_READONLY, + MOUNT_TMPFS, + OPERATION_MAX +} OPERATION; + + +static char *create_empty_dir(void) { + struct stat s; + fs_build_firejail_dir(); + + if (stat(RO_DIR, &s)) { + /* coverity[toctou] */ + int rv = mkdir(RO_DIR, S_IRUSR | S_IXUSR); + if (rv == -1) + errExit("mkdir"); + if (chown(RO_DIR, 0, 0) < 0) + errExit("chown"); + } + + return RO_DIR; +} + +static char *create_empty_file(void) { + struct stat s; + fs_build_firejail_dir(); + + if (stat(RO_FILE, &s)) { + /* coverity[toctou] */ + FILE *fp = fopen(RO_FILE, "w"); + if (!fp) + errExit("fopen"); + fclose(fp); + if (chown(RO_FILE, 0, 0) < 0) + errExit("chown"); + if (chmod(RO_FILE, S_IRUSR) < 0) + errExit("chown"); + } + + return RO_FILE; +} + +static void disable_file(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) { + assert(fname); + assert(emptydir); + assert(emptyfile); + assert(op data, "bind", 4) == 0) { + char *dname1 = entry->data + 5; + char *dname2 = split_comma(dname1); + if (dname2 == NULL) { + fprintf(stderr, "Error: second directory missing in bind command\n"); + entry = entry->next; + continue; + } + struct stat s; + if (stat(dname1, &s) == -1) { + fprintf(stderr, "Error: cannot find directories for bind command\n"); + entry = entry->next; + continue; + } + if (stat(dname2, &s) == -1) { + fprintf(stderr, "Error: cannot find directories for bind command\n"); + entry = entry->next; + continue; + } + + // mount --bind olddir newdir + if (arg_debug) + printf("Mount-bind %s on top of %s\n", dname1, dname2); + // preserve dname2 mode and ownership + if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); + /* coverity[toctou] */ + if (chown(dname2, s.st_uid, s.st_gid) == -1) + errExit("mount-bind chown"); + /* coverity[toctou] */ + if (chmod(dname2, s.st_mode) == -1) + errExit("mount-bind chmod"); + + entry = entry->next; + continue; + } + + // process blacklist command + if (strncmp(entry->data, "blacklist", 9) == 0) { + ptr = entry->data + 10; + op = BLACKLIST_FILE; + } + else if (strncmp(entry->data, "read-only", 9) == 0) { + ptr = entry->data + 10; + op = MOUNT_READONLY; + } + else if (strncmp(entry->data, "tmpfs", 5) == 0) { + ptr = entry->data + 6; + op = MOUNT_TMPFS; + } + else { + fprintf(stderr, "Error: invalid profile line %s\n", entry->data); + entry = entry->next; + continue; + } + + // replace home macro in blacklist array + char *new_name = NULL; + if (strncmp(ptr, "${HOME}", 7) == 0) { + if (asprintf(&new_name, "%s%s", homedir, ptr + 7) == -1) + errExit("asprintf"); + ptr = new_name; + } + + // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories + if (strncmp(ptr, "${PATH}", 7) == 0) { + expand_path(op, "/bin", ptr + 7, emptydir, emptyfile); + expand_path(op, "/sbin", ptr + 7, emptydir, emptyfile); + expand_path(op, "/usr/bin", ptr + 7, emptydir, emptyfile); + expand_path(op, "/usr/sbin", ptr + 7, emptydir, emptyfile); + } + else + globbing(op, ptr, emptydir, emptyfile); + + if (new_name) + free(new_name); + entry = entry->next; + } +} + +//*********************************************** +// mount namespace +//*********************************************** + +// remount a directory read-only +void fs_rdonly(const char *dir) { + assert(dir); + // check directory exists + struct stat s; + int rv = stat(dir, &s); + if (rv == 0) { + // mount --bind /bin /bin + if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount read-only"); + // mount --bind -o remount,ro /bin + if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0) + errExit("mount read-only"); + } +} +void fs_rdonly_noexit(const char *dir) { + assert(dir); + // check directory exists + struct stat s; + int rv = stat(dir, &s); + if (rv == 0) { + int merr = 0; + // mount --bind /bin /bin + if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0) + merr = 1; + // mount --bind -o remount,ro /bin + if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0) + merr = 1; + if (merr) + fprintf(stderr, "Warning: cannot mount %s read-only\n", dir); + } +} + +// mount /proc and /sys directories +void fs_proc_sys_dev_boot(void) { + struct stat s; + + if (arg_debug) + printf("Remounting /proc and /proc/sys filesystems\n"); + if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) + errExit("mounting /proc"); + + // remount /proc/sys readonly + if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0) + errExit("mounting /proc/sys"); + + if (mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0) + errExit("mounting /proc/sys"); + + + /* Mount a version of /sys that describes the network namespace */ + if (arg_debug) + printf("Remounting /sys directory\n"); + if (umount2("/sys", MNT_DETACH) < 0) + fprintf(stderr, "Warning: failed to unmount /sys\n"); + if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) + fprintf(stderr, "Warning: failed to mount /sys\n"); + +// if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) +// errExit("mounting /sys"); + + + // mounting firejail kernel module files + if (stat("/proc/firejail-uptime", &s) == 0) { + errno = 0; + FILE *fp = fopen("/proc/firejail", "w"); + int cnt = 0; + while (errno == EBUSY && cnt < 10) { + if (!fp) { + int s = random(); + s /= 200000; + usleep(s); + fp = fopen("/proc/firejail", "w"); + } + else + break; + } + if (!fp) { + fprintf(stderr, "Error: cannot register sandbox with firejail-lkm\n"); + exit(1); + } + if (fp) { + // registration + fprintf(fp, "register\n"); + fflush(0); + // filtering x11 connect calls + if (arg_nox11) { + fprintf(fp, "no connect unix /tmp/.X11\n"); + fflush(0); + printf("X11 access disabled\n"); + } + if (arg_nodbus) { + fprintf(fp, "no connect unix /var/run/dbus/system_bus_socket\n"); + fflush(0); + fprintf(fp, "no connect unix /tmp/dbus\n"); + fflush(0); + printf("D-Bus access disabled\n"); + } + fclose(fp); + if (mount("/proc/firejail-uptime", "/proc/uptime", NULL, MS_BIND|MS_REC, NULL) < 0) + fprintf(stderr, "Warning: cannot mount /proc/firejail-uptime\n"); + } + } + + // Disable SysRq + // a linux box can be shut down easily using the following commands (as root): + // # echo 1 > /proc/sys/kernel/sysrq + // #echo b > /proc/sysrq-trigger + // for more information see https://www.kernel.org/doc/Documentation/sysrq.txt + if (arg_debug) + printf("Disable /proc/sysrq-trigger\n"); + fs_rdonly_noexit("/proc/sysrq-trigger"); + + // disable hotplug and uevent_helper + if (arg_debug) + printf("Disable /proc/sys/kernel/hotplug\n"); + fs_rdonly_noexit("/proc/sys/kernel/hotplug"); + if (arg_debug) + printf("Disable /sys/kernel/uevent_helper\n"); + fs_rdonly_noexit("/sys/kernel/uevent_helper"); + + // read-only /proc/irq and /proc/bus + if (arg_debug) + printf("Disable /proc/irq\n"); + fs_rdonly_noexit("/proc/irq"); + if (arg_debug) + printf("Disable /proc/bus\n"); + fs_rdonly_noexit("/proc/bus"); + + // disable /proc/kcore + disable_file(BLACKLIST_FILE, "/proc/kcore", "not used", "/dev/null"); + + // disable /proc/kallsyms + disable_file(BLACKLIST_FILE, "/proc/kallsyms", "not used", "/dev/null"); + + // disable /boot + if (stat("/boot", &s) == 0) { + if (arg_debug) + printf("Mounting a new /boot directory\n"); + if (mount("tmpfs", "/boot", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /boot directory"); + } + + // disable /dev/port + if (stat("/dev/port", &s) == 0) { + disable_file(BLACKLIST_FILE, "/dev/port", "not used", "/dev/null"); + } +} + +static void sanitize_home(void) { + // extract current /home directory data + struct dirent *dir; + DIR *d = opendir("/home"); + if (d == NULL) + return; + + char *emptydir = create_empty_dir(); + while ((dir = readdir(d))) { + if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0) + continue; + + if (dir->d_type == DT_DIR ) { + // get properties + struct stat s; + char *name; + if (asprintf(&name, "/home/%s", dir->d_name) == -1) + continue; + if (stat(name, &s) == -1) + continue; + if (S_ISLNK(s.st_mode)) { + free(name); + continue; + } + + if (strcmp(name, cfg.homedir) == 0) + continue; + +// printf("directory %u %u:%u #%s#\n", +// s.st_mode, +// s.st_uid, +// s.st_gid, +// name); + + // disable directory + disable_file(BLACKLIST_FILE, name, emptydir, "not used"); + free(name); + } + } + closedir(d); +} + + + + + + +// build a basic read-only filesystem +void fs_basic_fs(void) { + if (arg_debug) + printf("Mounting read-only /bin, /sbin, /lib, /lib64, /usr, /etc, /var\n"); + fs_rdonly("/bin"); + fs_rdonly("/sbin"); + fs_rdonly("/lib"); + fs_rdonly("/lib64"); + fs_rdonly("/usr"); + fs_rdonly("/etc"); + fs_rdonly("/var"); + + // update /var directory in order to support multiple sandboxes running on the same root directory + if (!arg_private_dev) + fs_dev_shm(); + fs_var_lock(); + fs_var_tmp(); + fs_var_log(); + fs_var_lib(); + fs_var_cache(); + fs_var_utmp(); + + // only in user mode + if (getuid()) + sanitize_home(); +} + + +// mount overlayfs on top of / directory +// mounting an overlay and chrooting into it: +// +// Old Ubuntu kernel +// # cd ~ +// # mkdir -p overlay/root +// # mkdir -p overlay/diff +// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root +// # chroot /root/overlay/root +// to shutdown, first exit the chroot and then unmount the overlay +// # exit +// # umount /root/overlay/root +// +// Kernels 3.18+ +// # cd ~ +// # mkdir -p overlay/root +// # mkdir -p overlay/diff +// # mkdir -p overlay/work +// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root +// # cat /etc/mtab | grep overlay +// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0 +// # chroot /root/overlay/root +// to shutdown, first exit the chroot and then unmount the overlay +// # exit +// # umount /root/overlay/root + + +// to do: fix the code below; also, it might work without /dev; impose seccomp/caps filters when not root +#include +void fs_overlayfs(void) { + // check kernel version + struct utsname u; + int rv = uname(&u); + if (rv != 0) + errExit("uname"); + int major; + int minor; + if (2 != sscanf(u.release, "%d.%d", &major, &minor)) { + fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version); + exit(1); + } + + if (arg_debug) + printf("Linux kernel version %d.%d\n", major, minor); + int oldkernel = 0; + if (major < 3) { + fprintf(stderr, "Error: minimum kernel version required 3.x\n"); + exit(1); + } + if (major == 3 && minor < 18) + oldkernel = 1; + + // build overlay directories + fs_build_mnt_dir(); + + char *oroot; + if(asprintf(&oroot, "%s/oroot", MNT_DIR) == -1) + errExit("asprintf"); + if (mkdir(oroot, S_IRWXU | S_IRWXG | S_IRWXO)) + errExit("mkdir"); + if (chown(oroot, 0, 0) < 0) + errExit("chown"); + if (chmod(oroot, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + errExit("chmod"); + + char *odiff; + if(asprintf(&odiff, "%s/odiff", MNT_DIR) == -1) + errExit("asprintf"); + if (mkdir(odiff, S_IRWXU | S_IRWXG | S_IRWXO)) + errExit("mkdir"); + if (chown(odiff, 0, 0) < 0) + errExit("chown"); + if (chmod(odiff, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + errExit("chmod"); + + char *owork; + if(asprintf(&owork, "%s/owork", MNT_DIR) == -1) + errExit("asprintf"); + if (mkdir(owork, S_IRWXU | S_IRWXG | S_IRWXO)) + errExit("mkdir"); + if (chown(owork, 0, 0) < 0) + errExit("chown"); + if (chmod(owork, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) + errExit("chmod"); + + // mount overlayfs + if (arg_debug) + printf("Mounting OverlayFS\n"); + char *option; + if (oldkernel) { // old Ubuntu/OpenSUSE kernels + if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1) + errExit("asprintf"); + if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0) + errExit("mounting overlayfs"); + } + else { // kernel 3.18 or newer + if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1) + errExit("asprintf"); + if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) + errExit("mounting overlayfs"); + } + + // mount-bind dev directory + if (arg_debug) + printf("Mounting /dev\n"); + char *dev; + if (asprintf(&dev, "%s/dev", oroot) == -1) + errExit("asprintf"); + if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mounting /dev"); + + // chroot in the new filesystem + if (chroot(oroot) == -1) + errExit("chroot"); + // update /var directory in order to support multiple sandboxes running on the same root directory + if (!arg_private_dev) + fs_dev_shm(); + fs_var_lock(); + fs_var_tmp(); + fs_var_log(); + fs_var_lib(); + fs_var_cache(); + fs_var_utmp(); + + // only in user mode + if (getuid()) + sanitize_home(); + + // cleanup and exit + free(option); + free(oroot); + free(odiff); +} + + + +#ifdef HAVE_CHROOT +// return 1 if error +int fs_check_chroot_dir(const char *rootdir) { + assert(rootdir); + struct stat s; + char *name; + + // check /dev + if (asprintf(&name, "%s/dev", rootdir) == -1) + errExit("asprintf"); + if (stat(name, &s) == -1) { + fprintf(stderr, "Error: cannot find /dev in chroot directory\n"); + return 1; + } + free(name); + + // check /var/tmp + if (asprintf(&name, "%s/var/tmp", rootdir) == -1) + errExit("asprintf"); + if (stat(name, &s) == -1) { + fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n"); + return 1; + } + free(name); + + // check /proc + if (asprintf(&name, "%s/proc", rootdir) == -1) + errExit("asprintf"); + if (stat(name, &s) == -1) { + fprintf(stderr, "Error: cannot find /proc in chroot directory\n"); + return 1; + } + free(name); + + // check /proc + if (asprintf(&name, "%s/tmp", rootdir) == -1) + errExit("asprintf"); + if (stat(name, &s) == -1) { + fprintf(stderr, "Error: cannot find /tmp in chroot directory\n"); + return 1; + } + free(name); + + // check /bin/bash + if (asprintf(&name, "%s/bin/bash", rootdir) == -1) + errExit("asprintf"); + if (stat(name, &s) == -1) { + fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n"); + return 1; + } + free(name); + + return 0; +} + +// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf +void fs_chroot(const char *rootdir) { + assert(rootdir); + + //*********************************** + // mount-bind a /dev in rootdir + //*********************************** + // mount /dev + char *newdev; + if (asprintf(&newdev, "%s/dev", rootdir) == -1) + errExit("asprintf"); + if (arg_debug) + printf("Mounting /dev on %s\n", newdev); + if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mounting /dev"); + + // some older distros don't have a /run directory + // create one by default + // no exit on error, let the user deal with any problems + char *rundir; + if (asprintf(&rundir, "%s/run", rootdir) == -1) + errExit("asprintf"); + if (!is_dir(rundir)) { + int rv = mkdir(rundir, S_IRWXU | S_IRWXG | S_IRWXO); + (void) rv; + rv = chown(rundir, 0, 0); + (void) rv; + } + + // copy /etc/resolv.conf in chroot directory + // if resolv.conf in chroot is a symbolic link, this will fail + // no exit on error, let the user deal with the problem + char *fname; + if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1) + errExit("asprintf"); + if (arg_debug) + printf("Updating /etc/resolv.conf in %s\n", fname); + if (copy_file("/etc/resolv.conf", fname) == -1) + fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n"); + + // chroot into the new directory + if (arg_debug) + printf("Chrooting into %s\n", rootdir); + if (chroot(rootdir) < 0) + errExit("chroot"); + + // update /var directory in order to support multiple sandboxes running on the same root directory + if (!arg_private_dev) + fs_dev_shm(); + fs_var_lock(); + fs_var_tmp(); + fs_var_log(); + fs_var_lib(); + fs_var_cache(); + fs_var_utmp(); + + // only in user mode + if (getuid()) + sanitize_home(); + +} +#endif + + diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c new file mode 100644 index 000000000..80bd11582 --- /dev/null +++ b/src/firejail/fs_dev.c @@ -0,0 +1,163 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#include + +static void create_char_dev(const char *path, mode_t mode, int major, int minor) { + dev_t dev = makedev(major, minor); + int rv = mknod(path, S_IFCHR | mode, dev); + if (rv == -1) + goto errexit; + + + if (chmod(path, mode) < 0) + goto errexit; + if (chown(path, 0, 0) < 0) + goto errexit; + + return; + +errexit: + fprintf(stderr, "Error: cannot create %s device\n", path); + exit(1); +} + +static void create_link(const char *oldpath, const char *newpath) { + if (symlink(oldpath, newpath) == -1) + goto errexit; + if (chown(newpath, 0, 0) < 0) + goto errexit; + return; + +errexit: + fprintf(stderr, "Error: cannot create %s device\n", newpath); + exit(1); +} + +void fs_private_dev(void){ + // install a new /dev directory + if (arg_debug) + printf("Mounting tmpfs on /dev\n"); + if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /dev"); + + // create /dev/shm + if (arg_debug) + printf("Create /dev/shm directory\n"); + int rv = mkdir("/dev/shm", S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown("/dev/shm", 0, 0) < 0) + errExit("chown"); + if (chmod("/dev/shm", S_IRWXU | S_IRWXG | S_IRWXO) < 0) + errExit("chmod"); + + // create devices + create_char_dev("/dev/zero", 0666, 1, 5); // mknod -m 666 /dev/zero c 1 5 + create_char_dev("/dev/null", 0666, 1, 3); // mknod -m 666 /dev/null c 1 3 + create_char_dev("/dev/full", 0666, 1, 7); // mknod -m 666 /dev/full c 1 7 + create_char_dev("/dev/random", 0666, 1, 8); // Mknod -m 666 /dev/random c 1 8 + create_char_dev("/dev/urandom", 0666, 1, 9); // mknod -m 666 /dev/urandom c 1 9 + create_char_dev("/dev/tty", 0666, 5, 0); // mknod -m 666 /dev/tty c 5 0 +#if 0 + create_dev("/dev/tty0", "mknod -m 666 /dev/tty0 c 4 0"); + create_dev("/dev/console", "mknod -m 622 /dev/console c 5 1"); +#endif + + // pseudo-terminal + rv = mkdir("/dev/pts", 0755); + if (rv == -1) + errExit("mkdir"); + if (chown("/dev/pts", 0, 0) < 0) + errExit("chown"); + if (chmod("/dev/pts", 0755) < 0) + errExit("chmod"); + create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); + create_link("/dev/pts/ptmx", "/dev/ptmx"); + // mount -vt devpts -o newinstance -o ptmxmode=0666 devpts //dev/pts + if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, "newinstance,ptmxmode=0666") < 0) + errExit("mounting /dev/pts"); + +#if 0 + // stdin, stdout, stderr + create_link("/proc/self/fd", "/dev/fd"); + create_link("/proc/self/fd/0", "/dev/stdin"); + create_link("/proc/self/fd/1", "/dev/stdout"); + create_link("/proc/self/fd/2", "/dev/stderr"); +#endif +} + + +void fs_dev_shm(void) { + uid_t uid = getuid(); // set a new shm only if we started as root + if (uid) + return; + + if (is_dir("/dev/shm")) { + if (arg_debug) + printf("Mounting tmpfs on /dev/shm\n"); + if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /dev/shm"); + } + else { + char *lnk = get_link("/dev/shm"); + if (lnk) { + // convert a link such as "../shm" into "/shm" + char *lnk2 = lnk; + int cnt = 0; + while (strncmp(lnk2, "../", 3) == 0) { + cnt++; + lnk2 = lnk2 + 3; + } + if (cnt != 0) + lnk2 = lnk + (cnt - 1) * 3 + 2; + + if (!is_dir(lnk2)) { + // create directory + if (mkdir(lnk2, S_IRWXU|S_IRWXG|S_IRWXO)) + errExit("mkdir"); + if (chown(lnk2, 0, 0)) + errExit("chown"); + if (chmod(lnk2, S_IRWXU|S_IRWXG|S_IRWXO)) + errExit("chmod"); + } + if (arg_debug) + printf("Mounting tmpfs on %s on behalf of /dev/shm\n", lnk2); + if (mount("tmpfs", lnk2, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /var/tmp"); + free(lnk); + } + else { + fprintf(stderr, "Warning: /dev/shm not mounted\n"); + dbg_test_dir("/dev/shm"); + } + + } +} diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c new file mode 100644 index 000000000..853e3930b --- /dev/null +++ b/src/firejail/fs_home.c @@ -0,0 +1,494 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void skel(const char *homedir, uid_t u, gid_t g) { + char *fname; + // zsh + if (arg_zsh) { + // copy skel files + if (asprintf(&fname, "%s/.zshrc", homedir) == -1) + errExit("asprintf"); + struct stat s; + // don't copy it if we already have the file + if (stat(fname, &s) == 0) + return; + if (stat("/etc/skel/.zshrc", &s) == 0) { + if (copy_file("/etc/skel/.zshrc", fname) == 0) { + if (chown(fname, u, g) == -1) + errExit("chown"); + } + } + else { // + FILE *fp = fopen(fname, "w"); + if (fp) { + fprintf(fp, "\n"); + fclose(fp); + if (chown(fname, u, g) == -1) + errExit("chown"); + if (chmod(fname, S_IRUSR | S_IWUSR) < 0) + errExit("chown"); + } + } + free(fname); + } + // csh + else if (arg_csh) { + // copy skel files + if (asprintf(&fname, "%s/.cshrc", homedir) == -1) + errExit("asprintf"); + struct stat s; + // don't copy it if we already have the file + if (stat(fname, &s) == 0) + return; + if (stat("/etc/skel/.cshrc", &s) == 0) { + if (copy_file("/etc/skel/.cshrc", fname) == 0) { + if (chown(fname, u, g) == -1) + errExit("chown"); + } + } + else { // + /* coverity[toctou] */ + FILE *fp = fopen(fname, "w"); + if (fp) { + fprintf(fp, "\n"); + fclose(fp); + if (chown(fname, u, g) == -1) + errExit("chown"); + if (chmod(fname, S_IRUSR | S_IWUSR) < 0) + errExit("chown"); + } + } + free(fname); + } + // bash etc. + else { + // copy skel files + if (asprintf(&fname, "%s/.bashrc", homedir) == -1) + errExit("asprintf"); + struct stat s; + // don't copy it if we already have the file + if (stat(fname, &s) == 0) + return; + if (stat("/etc/skel/.bashrc", &s) == 0) { + if (copy_file("/etc/skel/.bashrc", fname) == 0) { + /* coverity[toctou] */ + if (chown(fname, u, g) == -1) + errExit("chown"); + } + } + free(fname); + } +} + +static int store_xauthority(void) { + // put a copy of .Xauthority in MNT_DIR + fs_build_mnt_dir(); + + char *src; + char *dest; + if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1) + errExit("asprintf"); + if (asprintf(&dest, "%s/.Xauthority", MNT_DIR) == -1) + errExit("asprintf"); + + struct stat s; + if (stat(src, &s) == 0) { + int rv = copy_file(src, dest); + if (rv) { + fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); + return 0; + } + return 1; // file copied + } + + return 0; +} + +static void copy_xauthority(void) { + // put a copy of .Xauthority in MNT_DIR + fs_build_mnt_dir(); + + char *src; + char *dest; + if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) + errExit("asprintf"); + if (asprintf(&src, "%s/.Xauthority", MNT_DIR) == -1) + errExit("asprintf"); + int rv = copy_file(src, dest); + if (rv) + fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); + + // set permissions and ownership + if (chown(dest, getuid(), getgid()) < 0) + errExit("chown"); + if (chmod(dest, S_IRUSR | S_IWUSR) < 0) + errExit("chmod"); + + // delete the temporary file + unlink(src); +} + +// private mode (--private=homedir): +// mount homedir on top of /home/user, +// tmpfs on top of /root in nonroot mode, +// tmpfs on top of /tmp in root mode, +// set skel files, +// restore .Xauthority +void fs_private_homedir(void) { + char *homedir = cfg.homedir; + char *private_homedir = cfg.home_private; + assert(homedir); + assert(private_homedir); + + int xflag = store_xauthority(); + + uid_t u = getuid(); + gid_t g = getgid(); + struct stat s; + if (stat(homedir, &s) == -1) { + fprintf(stderr, "Error: cannot find user home directory\n"); + exit(1); + } + + + // mount bind private_homedir on top of homedir + if (arg_debug) + printf("Mount-bind %s on top of %s\n", private_homedir, homedir); + if (mount(private_homedir, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); +// preserve mode and ownership +// if (chown(homedir, s.st_uid, s.st_gid) == -1) +// errExit("mount-bind chown"); +// if (chmod(homedir, s.st_mode) == -1) +// errExit("mount-bind chmod"); + + if (u != 0) { + // mask /root + if (arg_debug) + printf("Mounting a new /root directory\n"); + if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) + errExit("mounting home directory"); + } + else { + // mask /home + if (arg_debug) + printf("Mounting a new /home directory\n"); + if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting home directory"); + + // mask /tmp only in root mode; KDE keeps all kind of sockets in /tmp! + if (arg_debug) + printf("Mounting a new /tmp directory\n"); + if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting tmp directory"); + } + + + skel(homedir, u, g); + if (xflag) + copy_xauthority(); +} + +// private mode (--private): +// mount tmpfs over /home/user, +// tmpfs on top of /root in nonroot mode, +// tmpfs on top of /tmp in root mode +// set skel files, +// restore .Xauthority +void fs_private(void) { + char *homedir = cfg.homedir; + assert(homedir); + uid_t u = getuid(); + gid_t g = getgid(); + + int xflag = store_xauthority(); + + // mask /home + if (arg_debug) + printf("Mounting a new /home directory\n"); + if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting home directory"); + + // mask /root + if (arg_debug) + printf("Mounting a new /root directory\n"); + if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) + errExit("mounting home directory"); + + if (u != 0) { + // create /home/user + if (arg_debug) + printf("Create a new user directory\n"); + int rv = mkdir(homedir, S_IRWXU); + if (rv == -1) + errExit("mkdir"); + if (chown(homedir, u, g) < 0) + errExit("chown"); + } + else { + // mask tmp only in root mode; KDE keeps all kind of sockets in /tmp! + if (arg_debug) + printf("Mounting a new /tmp directory\n"); + if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting tmp directory"); + } + + skel(homedir, u, g); + if (xflag) + copy_xauthority(); +} + +static void check_dir_or_file(const char *name) { + assert(name); + struct stat s; + char *fname; + if (asprintf(&fname, "%s/%s", cfg.homedir, name) == -1) + errExit("asprintf"); + if (arg_debug) + printf("***************Checking %s\n", fname); + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: file %s not found.\n", fname); + exit(1); + } + + // check uid + uid_t uid = getuid(); + gid_t gid = getgid(); + if (s.st_uid != uid || s.st_gid != gid) { + fprintf(stderr, "Error: only files or directories created by the current user are allowed.\n"); + exit(1); + } + + // dir or regular file + if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) { + free(fname); + return; + } + + if (!is_link(fname)) { + free(fname); + return; + } + + fprintf(stderr, "Error: invalid file type, %s.\n", fname); + exit(1); +} + +// check directory linst specified by user (--private.keep option) - exit if it fails +void fs_check_home_list(void) { + if (strstr(cfg.home_private_keep, "..")) { + fprintf(stderr, "Error: invalid private.keep list\n"); + exit(1); + } + + char *dlist = strdup(cfg.home_private_keep); + if (!dlist) + errExit("strdup"); + + char *ptr = strtok(dlist, ","); + check_dir_or_file(ptr); + while ((ptr = strtok(NULL, ",")) != NULL) + check_dir_or_file(ptr); + + free(dlist); +} + +// check new private home directory (--private= option) - exit if it fails +void fs_check_private_dir(void) { + // if the directory starts with ~, expand the home directory + if (*cfg.home_private == '~') { + char *tmp; + if (asprintf(&tmp, "%s%s", cfg.homedir, cfg.home_private + 1) == -1) + errExit("asprintf"); + cfg.home_private = tmp; + } + + if (!is_dir(cfg.home_private) || is_link(cfg.home_private) || strstr(cfg.home_private, "..")) { + fprintf(stderr, "Error: invalid private directory\n"); + exit(1); + } + + // check home directory and chroot home directory have the same owner + struct stat s2; + int rv = stat(cfg.home_private, &s2); + if (rv < 0) { + fprintf(stderr, "Error: cannot find %s directory\n", cfg.home_private); + exit(1); + } + + struct stat s1; + rv = stat(cfg.homedir, &s1); + if (rv < 0) { + fprintf(stderr, "Error: cannot find %s directory, full path name required\n", cfg.homedir); + exit(1); + } + if (s1.st_uid != s2.st_uid) { + printf("Error: the two home directories must have the same owner\n"); + exit(1); + } +} + +#if 0 +static int mkpath(char* file_path, mode_t mode) { + assert(file_path && *file_path); + char* p; + for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { + *p='\0'; + if (mkdir(file_path, mode)==-1) { + if (errno!=EEXIST) { *p='/'; return -1; } + } + *p='/'; + } + return 0; +} +#endif + +static void duplicate(char *fname) { + char *cmd; + + // copy the file + if (asprintf(&cmd, "cp -a --parents %s/%s %s", cfg.homedir, fname, HOME_DIR) == -1) + errExit("asprintf"); + if (arg_debug) + printf("%s\n", cmd); + if (system(cmd)) + errExit("system cp -a --parents"); + free(cmd); +} + + +// private mode (--private.keep=list): +// mount homedir on top of /home/user, +// tmpfs on top of /root in nonroot mode, +// tmpfs on top of /tmp in root mode, +// set skel files, +// restore .Xauthority +void fs_private_home_list(void) { + char *homedir = cfg.homedir; + char *private_list = cfg.home_private_keep; + assert(homedir); + assert(private_list); + + int xflag = store_xauthority(); + + uid_t u = getuid(); + gid_t g = getgid(); + struct stat s; + if (stat(homedir, &s) == -1) { + fprintf(stderr, "Error: cannot find user home directory\n"); + exit(1); + } + + // create /tmp/firejail/mnt/home directory + fs_build_mnt_dir(); + int rv = mkdir(HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown(HOME_DIR, u, g) < 0) + errExit("chown"); + if (chmod(HOME_DIR, 0755) < 0) + errExit("chmod"); + + // copy the list of files in the new home directory + // using a new child process without root privileges + pid_t child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + if (arg_debug) + printf("Copying files in the new home:\n"); + + // drop privileges + if (setgroups(0, NULL) < 0) + errExit("setgroups"); + if (setgid(getgid()) < 0) + errExit("setgid/getgid"); + if (setuid(getuid()) < 0) + errExit("setuid/getuid"); + + // copy the list of files in the new home directory + char *dlist = strdup(cfg.home_private_keep); + if (!dlist) + errExit("strdup"); + + char *ptr = strtok(dlist, ","); + duplicate(ptr); + + while ((ptr = strtok(NULL, ",")) != NULL) + duplicate(ptr); + free(dlist); + exit(0); + } + // wait for the child to finish + waitpid(child, NULL, 0); + + // mount bind private_homedir on top of homedir + char *newhome; + if (asprintf(&newhome, "%s%s", HOME_DIR, cfg.homedir) == -1) + errExit("asprintf"); + + if (arg_debug) + printf("Mount-bind %s on top of %s\n", newhome, homedir); + if (mount(newhome, homedir, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind"); +// preserve mode and ownership +// if (chown(homedir, s.st_uid, s.st_gid) == -1) +// errExit("mount-bind chown"); +// if (chmod(homedir, s.st_mode) == -1) +// errExit("mount-bind chmod"); + + if (u != 0) { + // mask /root + if (arg_debug) + printf("Mounting a new /root directory\n"); + if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0) + errExit("mounting home directory"); + } + else { + // mask /home + if (arg_debug) + printf("Mounting a new /home directory\n"); + if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting home directory"); + + // mask /tmp only in root mode; KDE keeps all kind of sockets in /tmp! + if (arg_debug) + printf("Mounting a new /tmp directory\n"); + if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting tmp directory"); + } + + skel(homedir, u, g); + if (xflag) + copy_xauthority(); + +} + diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c new file mode 100644 index 000000000..fb3fc530e --- /dev/null +++ b/src/firejail/fs_hostname.c @@ -0,0 +1,157 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include + +void fs_hostname(const char *hostname) { + struct stat s; + fs_build_mnt_dir(); + + // create a new /etc/hostname + if (stat("/etc/hostname", &s) == 0) { + if (arg_debug) + printf("Creating a new /etc/hostname file\n"); + char *fhost; + if (asprintf(&fhost, "%s/hostname", MNT_DIR) == -1) + errExit("asprintf"); + FILE *fp = fopen(fhost, "w"); + if (!fp) { + fprintf(stderr, "Error: cannot create %s\n", fhost); + free(fhost); + exit(1); + } + fprintf(fp, "%s\n", hostname); + fclose(fp); + + // mode and owner + if (chown(fhost, 0, 0) < 0) + errExit("chown"); + if (chmod(fhost, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) + errExit("chmod"); + + // bind-mount the file on top of /etc/hostname + if (mount(fhost, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind /etc/hostname"); + free(fhost); + } + + // create a new /etc/hosts + if (stat("/etc/hosts", &s) == 0) { + if (arg_debug) + printf("Creating a new /etc/hosts file\n"); + char *fhost; + if (asprintf(&fhost, "%s/hosts", MNT_DIR) == -1) + errExit("asprintf"); + // copy /etc/host into our new file, and modify it on the fly + /* coverity[toctou] */ + FILE *fp1 = fopen("/etc/hosts", "r"); + if (!fp1) { + fprintf(stderr, "Error: cannot open /etc/hosts\n"); + free(fhost); + exit(1); + } + FILE *fp2 = fopen(fhost, "w"); + if (!fp2) { + fprintf(stderr, "Error: cannot create %s\n", fhost); + free(fhost); + exit(1); + } + + char buf[4096]; + while (fgets(buf, sizeof(buf), fp1)) { + // remove '\n' + char *ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + + // copy line + if (strstr(buf, "127.0.0.1")) + fprintf(fp2, "%s %s\n", buf, hostname); + else + fprintf(fp2, "%s\n", buf); + } + fclose(fp1); + fclose(fp2); + + // mode and owner + if (chown(fhost, 0, 0) < 0) + errExit("chown"); + if (chmod(fhost, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) + errExit("chmod"); + + // bind-mount the file on top of /etc/hostname + if (mount(fhost, "/etc/hosts", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind /etc/hosts"); + free(fhost); + } +} + +void fs_resolvconf(void) { + if (cfg.dns1 == 0) + return; + + struct stat s; + fs_build_mnt_dir(); + + // create a new /etc/hostname + if (stat("/etc/resolv.conf", &s) == 0) { + if (arg_debug) + printf("Creating a new /etc/resolv.conf file\n"); + char *fname; + if (asprintf(&fname, "%s/resolv.conf", MNT_DIR) == -1) + errExit("asprintf"); + FILE *fp = fopen(fname, "w"); + if (!fp) { + fprintf(stderr, "Error: cannot create %s\n", fname); + free(fname); + exit(1); + } + + if (cfg.dns1) + fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); + if (cfg.dns2) + fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); + if (cfg.dns3) + fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); + fclose(fp); + + // mode and owner + if (chown(fname, 0, 0) < 0) + errExit("chown"); + if (chmod(fname, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) + errExit("chmod"); + + // bind-mount the file on top of /etc/hostname + if (mount(fname, "/etc/resolv.conf", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind /etc/resolv.conf"); + free(fname); + } + else { + fprintf(stderr, "Error: cannot set DNS servers, /etc/resolv.conf file is missing\n"); + exit(1); + } +} + + diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c new file mode 100644 index 000000000..1c7ef5cbe --- /dev/null +++ b/src/firejail/fs_trace.c @@ -0,0 +1,76 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include + +void fs_trace_preload(void) { + struct stat s; + + // create an empty /etc/ld.so.preload + if (stat("/etc/ld.so.preload", &s)) { + if (arg_debug) + printf("Creating an empty /etc/ld.so.preload file\n"); + /* coverity[toctou] */ + FILE *fp = fopen("/etc/ld.so.preload", "w"); + if (!fp) + errExit("fopen"); + fclose(fp); + if (chown("/etc/ld.so.preload", 0, 0) < 0) + errExit("chown"); + if (chmod("/etc/ld.so.preload", S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) + errExit("chmod"); + } +} + +void fs_trace(void) { + // create /tmp/firejail/mnt directory + fs_build_mnt_dir(); + + // create the new ld.so.preload file and mount-bind it + if (arg_debug) + printf("Create the new ld.so.preload file\n"); + char *preload; + if (asprintf(&preload, "%s/ld.so.preload", MNT_DIR) == -1) + errExit("asprintf"); + FILE *fp = fopen(preload, "w"); + if (!fp) + errExit("fopen"); + fprintf(fp, "%s/lib/firejail/libtrace.so\n", PREFIX); + fclose(fp); + if (chown(preload, 0, 0) < 0) + errExit("chown"); + if (chmod(preload, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0) + errExit("chmod"); + + // mount the new preload file + if (arg_debug) + printf("Mount the new ld.so.preload file\n"); + if (mount(preload, "/etc/ld.so.preload", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind ls.so.preload"); +} + + + \ No newline at end of file diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c new file mode 100644 index 000000000..ee0f81828 --- /dev/null +++ b/src/firejail/fs_var.c @@ -0,0 +1,388 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct dirdata_t{ + struct dirdata_t *next; + char *name; + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; +} DirData; + +static DirData *dirlist = NULL; + +static void release_all(void) { + DirData *ptr = dirlist; + while (ptr) { + DirData *next = ptr->next; + free(ptr->name); + free(ptr); + ptr = next; + } + dirlist = NULL; +} + +static void build_list(const char *srcdir) { + // extract current /var/log directory data + struct dirent *dir; + DIR *d = opendir(srcdir); + if (d == NULL) + return; + + while ((dir = readdir(d))) { + if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0) + continue; + + if (dir->d_type == DT_DIR ) { + // get properties + struct stat s; + char *name; + if (asprintf(&name, "%s/%s", srcdir, dir->d_name) == -1) + continue; + if (stat(name, &s) == -1) + continue; + if (S_ISLNK(s.st_mode)) { + free(name); + continue; + } + +// printf("directory %u %u:%u %s\n", +// s.st_mode, +// s.st_uid, +// s.st_gid, +// dir->d_name); + + DirData *ptr = malloc(sizeof(DirData)); + if (ptr == NULL) + errExit("malloc"); + memset(ptr, 0, sizeof(DirData)); + ptr->name = name; + ptr->st_mode = s.st_mode; + ptr->st_uid = s.st_uid; + ptr->st_gid = s.st_gid; + ptr->next = dirlist; + dirlist = ptr; + } + } + closedir(d); +} + +static void build_dirs(void) { + // create directories under /var/log + DirData *ptr = dirlist; + while (ptr) { + if (mkdir(ptr->name, ptr->st_mode)) + errExit("mkdir"); + if (chown(ptr->name, ptr->st_uid, ptr->st_gid)) + errExit("chown"); + ptr = ptr->next; + } +} + +void fs_var_log(void) { + build_list("/var/log"); + + // create /var/log if it does't exit + if (is_dir("/var/log")) { + // extract group id for /var/log/wtmp + struct stat s; + gid_t wtmp_group = 0; + if (stat("/var/log/wtmp", &s) == 0) + wtmp_group = s.st_gid; + + // mount a tmpfs on top of /var/log + if (arg_debug) + printf("Mounting tmpfs on /var/log\n"); + if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/log"); + + build_dirs(); + release_all(); + + // create an empty /var/log/wtmp file + /* coverity[toctou] */ + FILE *fp = fopen("/var/log/wtmp", "w"); + if (fp) + fclose(fp); + if (chown("/var/log/wtmp", 0, wtmp_group) < 0) + errExit("chown"); + if (chmod("/var/log/wtmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) + errExit("chmod"); + + // create an empty /var/log/btmp file + fp = fopen("/var/log/btmp", "w"); + if (fp) + fclose(fp); + if (chown("/var/log/btmp", 0, wtmp_group) < 0) + errExit("chown"); + if (chmod("/var/log/btmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP) < 0) + errExit("chmod"); + } + else + fprintf(stderr, "Warning: cannot mount tmpfs in top of /var/log\n"); +} + +void fs_var_lib(void) { + struct stat s; + + // ISC DHCP multiserver + if (stat("/var/lib/dhcp", &s) == 0) { + if (arg_debug) + printf("Mounting tmpfs on /var/lib/dhcp\n"); + if (mount("tmpfs", "/var/lib/dhcp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/lib/dhcp"); + + // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file + FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "w"); + + if (fp) { + fprintf(fp, "\n"); + fclose(fp); + if (chown("/var/lib/dhcp/dhcpd.leases", 0, 0) == -1) + errExit("chown"); + if (chmod("/var/lib/dhcp/dhcpd.leases", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) + errExit("chmod"); + } + } + + // nginx multiserver + if (stat("/var/lib/nginx", &s) == 0) { + if (arg_debug) + printf("Mounting tmpfs on /var/lib/nginx\n"); + if (mount("tmpfs", "/var/lib/nginx", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/lib/nginx"); + } + + // net-snmp multiserver + if (stat("/var/lib/snmp", &s) == 0) { + if (arg_debug) + printf("Mounting tmpfs on /var/lib/snmp\n"); + if (mount("tmpfs", "/var/lib/snmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/lib/snmp"); + } + + // this is where sudo remembers its state + if (stat("/var/lib/sudo", &s) == 0) { + if (arg_debug) + printf("Mounting tmpfs on /var/lib/sudo\n"); + if (mount("tmpfs", "/var/lib/sudo", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/lib/sudo"); + } +} + +void fs_var_cache(void) { + struct stat s; + + if (stat("/var/cache/apache2", &s) == 0) { + if (arg_debug) + printf("Mounting tmpfs on /var/cache/apache2\n"); + if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/cahce/apache2"); + } + + if (stat("/var/cache/lighttpd", &s) == 0) { + if (arg_debug) + printf("Mounting tmpfs on /var/cache/lighttpd\n"); + if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /var/cache/lighttpd"); + + struct passwd *p = getpwnam("www-data"); + uid_t uid = 0; + gid_t gid = 0; + if (p) { + uid = p->pw_uid; + gid = p->pw_gid; + } + + int rv = mkdir("/var/cache/lighttpd/compress", S_IRWXU | S_IRWXG | S_IRWXO); + if (rv == -1) + errExit("mkdir"); + if (chown("/var/cache/lighttpd/compress", uid, gid) < 0) + errExit("chown"); + + rv = mkdir("/var/cache/lighttpd/uploads", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (rv == -1) + errExit("mkdir"); + if (chown("/var/cache/lighttpd/uploads", uid, gid) < 0) + errExit("chown"); + } +} + +void dbg_test_dir(const char *dir) { + if (arg_debug) { + if (is_dir(dir)) + printf("%s is a directory\n", dir); + if (is_link(dir)) { + char *lnk = get_link(dir); + if (lnk) { + printf("%s is a symbolic link to %s\n", dir, lnk); + free(lnk); + } + } + } +} + + +void fs_var_lock(void) { + + if (is_dir("/var/lock")) { + if (arg_debug) + printf("Mounting tmpfs on /var/lock\n"); + if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /lock"); + } + else { + char *lnk = get_link("/var/lock"); + if (lnk) { + // convert a link such as "../shm" into "/shm" + char *lnk2 = lnk; + int cnt = 0; + while (strncmp(lnk2, "../", 3) == 0) { + cnt++; + lnk2 = lnk2 + 3; + } + if (cnt != 0) + lnk2 = lnk + (cnt - 1) * 3 + 2; + + if (!is_dir(lnk2)) { + // create directory + if (mkdir(lnk2, S_IRWXU|S_IRWXG|S_IRWXO)) + errExit("mkdir"); + if (chown(lnk2, 0, 0)) + errExit("chown"); + if (chmod(lnk2, S_IRWXU|S_IRWXG|S_IRWXO)) + errExit("chmod"); + } + if (arg_debug) + printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk2); + if (mount("tmpfs", lnk2, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /var/lock"); + free(lnk); + } + else { + fprintf(stderr, "Warning: /var/lock not mounted\n"); + dbg_test_dir("/var/lock"); + } + } +} + +void fs_var_tmp(void) { + + if (!is_link("/var/tmp")) { + if (arg_debug) + printf("Mounting tmpfs on /var/tmp\n"); + if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) + errExit("mounting /var/tmp"); + } + else { + fprintf(stderr, "Warning: /var/tmp not mounted\n"); + dbg_test_dir("/var/tmp"); + } +} + +void fs_var_utmp(void) { + struct stat s; + + // extract utmp group id + gid_t utmp_group = 0; + if (stat("/var/run/utmp", &s) == 0) + utmp_group = s.st_gid; + else { + fprintf(stderr, "Warning: cannot find /var/run/utmp\n"); + return; + } + + // create /tmp/firejail/mnt directory + fs_build_mnt_dir(); + + // create a new utmp file + if (arg_debug) + printf("Create the new utmp file\n"); + char *utmp; + if (asprintf(&utmp, "%s/utmp", MNT_DIR) == -1) + errExit("asprintf"); + FILE *fp = fopen(utmp, "w"); + if (!fp) + errExit("fopen"); + + // read current utmp + struct utmp *u; + struct utmp u_boot; + setutent(); + while ((u = getutent()) != NULL) { + if (u->ut_type == BOOT_TIME) { + memcpy(&u_boot, u, sizeof(u_boot)); + u_boot.ut_tv.tv_sec = (unsigned) time(NULL); + } + } + endutent(); + + // save new utmp file + fwrite(&u_boot, sizeof(u_boot), 1, fp); + fclose(fp); + if (chown(utmp, 0, utmp_group) < 0) + errExit("chown"); + if (chmod(utmp, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0) + errExit("chmod"); + + // mount the new utmp file + if (arg_debug) + printf("Mount the new utmp file\n"); + if (mount(utmp, "/var/run/utmp", NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mount bind utmp"); +} + + +#if 0 +Testing servers: + +brctl addbr br0 +ifconfig br0 10.10.20.1/24 + +apt-get install snmpd +insserv -r snmpd +sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/snmpd start; sleep inf" + +apt-get install apache2 +insserv -r apache2 +sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/apache2 start; sleep inf" + +apt-get install nginx +insserv -r nginx +sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/nginx start; sleep inf" + +apt-get install lighttpd +insserv -r lighttpd +sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/lighttpd start; sleep inf" + +apt-get install isc-dhcp-server +insserv -r isc-dhcp-server +sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/isc-dhcp-server start; sleep inf" +#endif diff --git a/src/firejail/join.c b/src/firejail/join.c new file mode 100644 index 000000000..e2d2ca7fc --- /dev/null +++ b/src/firejail/join.c @@ -0,0 +1,364 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include + +static int apply_caps = 0; +static uint64_t caps = 0; +static int apply_seccomp = 0; +#define BUFLEN 4096 + +static void extract_command(int argc, char **argv, int index) { + if (index >= argc) + return; + + // doubledash followed by positional parameters + if (strcmp(argv[index], "--") == 0) { + arg_doubledash = 1; + index++; + if (index >= argc) + return; + } + + // first argv needs to be a valid command + if (arg_doubledash == 0 && *argv[index] == '-') { + fprintf(stderr, "Error: invalid option %s after --join\n", argv[index]); + exit(1); + } + + + int len = 0; + int i; + // calculate command length + for (i = index; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + assert(len > 0); + + // build command + cfg.command_line = malloc(len + 1); + *cfg.command_line = '\0'; + for (i = index; i < argc; i++) { + strcat(cfg.command_line, argv[i]); + strcat(cfg.command_line, " "); + } + if (arg_debug) + printf("Extracted command #%s#\n", cfg.command_line); +} + +static void extract_nogroups(pid_t pid) { + char *fname; + if (asprintf(&fname, "/proc/%d/root%s/groups", pid, MNT_DIR) == -1) + errExit("asprintf"); + + struct stat s; + if (stat(fname, &s) == -1) + return; + + arg_nogroups = 1; + free(fname); +} + +static void extract_cpu(pid_t pid) { + char *fname; + if (asprintf(&fname, "/proc/%d/root%s/cpu", pid, MNT_DIR) == -1) + errExit("asprintf"); + + struct stat s; + if (stat(fname, &s) == -1) + return; + + // there is a cpu file in MNT_DIR; load the information from the file + load_cpu(fname); + free(fname); +} + +static void extract_cgroup(pid_t pid) { + char *fname; + if (asprintf(&fname, "/proc/%d/root%s/cgroup", pid, MNT_DIR) == -1) + errExit("asprintf"); + + struct stat s; + if (stat(fname, &s) == -1) + return; + + // there is a cgroup file in MNT_DIR; load the information from the file + load_cgroup(fname); + free(fname); +} + +static void extract_caps_seccomp(pid_t pid) { + // 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); + fprintf(stderr, "Error: cannot open stat file for process %u\n", pid); + exit(1); + } + + char buf[BUFLEN]; + while (fgets(buf, BUFLEN - 1, fp)) { + if (strncmp(buf, "Seccomp:", 8) == 0) { + char *ptr = buf + 8; + int val; + sscanf(ptr, "%d", &val); + if (val == 2) + apply_seccomp = 1; + break; + } + else if (strncmp(buf, "CapBnd:", 7) == 0) { + char *ptr = buf + 8; + unsigned long long val; + sscanf(ptr, "%llx", &val); + apply_caps = 1; + caps = val; + } + } + fclose(fp); + free(file); +} + +void extract_user_namespace(pid_t pid) { + // test user namespaces available in the kernel + struct stat s1; + struct stat s2; + struct stat s3; + if (stat("/proc/self/ns/user", &s1) == 0 && + stat("/proc/self/uid_map", &s2) == 0 && + stat("/proc/self/gid_map", &s3) == 0); + else + return; + + // read uid map + char *uidmap; + if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1) + errExit("asprintf"); + FILE *fp = fopen(uidmap, "r"); + if (!fp) { + free(uidmap); + return; + } + + // check uid map + int u1; + int u2; + if (fscanf(fp, "%d %d", &u1, &u2) == 2) { + if (arg_debug) + printf("User namespace detected: %s, %d, %d\n", uidmap, u1, u2); + if (u1 != 0 || u2 != 0) + arg_noroot = 1; + } + fclose(fp); + free(uidmap); +} + +void join_name(const char *name, const char *homedir, int argc, char **argv, int index) { + if (!name || strlen(name) == 0) { + fprintf(stderr, "Error: invalid sandbox name\n"); + exit(1); + } + pid_t pid; + if (name2pid(name, &pid)) { + fprintf(stderr, "Error: cannot find sandbox %s\n", name); + exit(1); + } + + join(pid, homedir, argc, argv, index); +} + +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) { + 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) { + fprintf(stderr, "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 + + // 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) { + 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); + 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); +} + + + diff --git a/src/firejail/list.c b/src/firejail/list.c new file mode 100644 index 000000000..c2c4e801f --- /dev/null +++ b/src/firejail/list.c @@ -0,0 +1,65 @@ +/* + * 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 "firejail.h" + +void top(void) { + drop_privs(1); + + char *arg[4]; + arg[0] = "bash"; + arg[1] = "-c"; + arg[2] = "firemon --top"; + arg[3] = NULL; + execvp("/bin/bash", arg); +} + +void netstats(void) { + drop_privs(1); + + char *arg[4]; + arg[0] = "bash"; + arg[1] = "-c"; + arg[2] = "firemon --netstats"; + arg[3] = NULL; + execvp("/bin/bash", arg); +} + +void list(void) { + drop_privs(1); + + char *arg[4]; + arg[0] = "bash"; + arg[1] = "-c"; + arg[2] = "firemon --list"; + arg[3] = NULL; + execvp("/bin/bash", arg); +} + +void tree(void) { + drop_privs(1); + + char *arg[4]; + arg[0] = "bash"; + arg[1] = "-c"; + arg[2] = "firemon --tree"; + arg[3] = NULL; + execvp("/bin/bash", arg); +} + diff --git a/src/firejail/main.c b/src/firejail/main.c new file mode 100644 index 000000000..78971aa86 --- /dev/null +++ b/src/firejail/main.c @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2014, 2015 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 "firejail.h" +#include "../include/pid.h" +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +{ +struct tms tm; +clock_t systick = times(&tm); +printf("time %s:%d %u\n", __FILE__, __LINE__, (uint32_t) systick); +} +#endif + +#define STACK_SIZE (1024 * 1024) +static char child_stack[STACK_SIZE]; // space for child's stack +Config cfg; // configuration +int arg_private = 0; // mount private /home and /tmp directoryu +int arg_debug = 0; // print debug messages +int arg_nonetwork = 0; // --net=none +int arg_command = 0; // -c +int arg_overlay = 0; // --overlay +int arg_zsh = 0; // use zsh as default shell +int arg_csh = 0; // use csh as default shell + +int arg_seccomp = 0; // enable default seccomp filter +char *arg_seccomp_list = NULL; // optional seccomp list on top of default filter +char *arg_seccomp_list_drop = NULL; // seccomp drop list +char *arg_seccomp_list_keep = NULL; // seccomp keep list + +int arg_caps_default_filter = 0; // enable default capabilities filter +int arg_caps_drop = 0; // drop list +int arg_caps_drop_all = 0; // drop all capabilities +int arg_caps_keep = 0; // keep list +char *arg_caps_list = NULL; // optional caps list + +int arg_trace = 0; // syscall tracing support +int arg_rlimit_nofile = 0; // rlimit nofile +int arg_rlimit_nproc = 0; // rlimit nproc +int arg_rlimit_fsize = 0; // rlimit fsize +int arg_rlimit_sigpending = 0; // rlimit fsize +int arg_nox11 = 0; // kill the program if x11 unix domain socket is accessed +int arg_nodbus = 0; // kill the program if D-Bus is accessed +int arg_nogroups = 0; // disable supplementary groups +int arg_noroot = 0; // create a new user namespace and disable root user +int arg_netfilter; // enable netfilter +char *arg_netfilter_file = NULL; // netfilter file +int arg_doubledash = 0; // double dash +int arg_shell_none = 0; // run the program directly without a shell +int arg_private_dev = 0; // private dev directory +int arg_scan = 0; // arp-scan all interfaces + +int parent_to_child_fds[2]; +int child_to_parent_fds[2]; + +char *fullargv[MAX_ARGS]; // expanded argv for restricted shell +int fullargc = 0; +static pid_t child = 0; +pid_t sandbox_pid; + +static void myexit(int rv) { + logmsg("exiting..."); + if (!arg_command) + printf("\nparent is shutting down, bye...\n"); + + struct stat s; + if (stat("/proc/firejail", &s) == 0) { + /* coverity[toctou] */ + FILE *fp = fopen("/proc/firejail", "w"); + if (fp) { + // deregistration + fprintf(fp, "release\n"); + fflush(0); + fclose(fp); + } + } + + // delete sandbox files in shared memory + bandwidth_shm_del_file(sandbox_pid); // bandwidht file + network_shm_del_file(sandbox_pid); // network map file + + exit(rv); +} + +static void my_handler(int s){ + printf("\nSignal %d caught, shutting down the child process\n", s); + logsignal(s); + kill(child, SIGKILL); + myexit(1); +} + +static void extract_user_data(void) { + // check suid + if (geteuid()) { + fprintf(stderr, "Error: the sandbox is not setuid root\n"); + exit(1); + } + + struct passwd *pw = getpwuid(getuid()); + if (!pw) + errExit("getpwuid"); + cfg.username = strdup(pw->pw_name); + if (!cfg.username) + errExit("strdup"); + + // build home directory name + cfg.homedir = NULL; + if (pw->pw_dir != NULL) { + cfg.homedir = strdup(pw->pw_dir); + if (!cfg.homedir) + errExit("strdup"); + } + else { + fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); + exit(1); + } + + cfg.cwd = getcwd(NULL, 0); +} + + + + +static inline Bridge *last_bridge_configured(void) { + if (cfg.bridge3.configured) + return &cfg.bridge3; + else if (cfg.bridge2.configured) + return &cfg.bridge2; + else if (cfg.bridge1.configured) + return &cfg.bridge1; + else if (cfg.bridge0.configured) + return &cfg.bridge0; + else + return NULL; +} + + + +// return 1 if error, 0 if a valid pid was found +static int read_pid(char *str, pid_t *pid) { + char *endptr; + errno = 0; + pid_t pidtmp = strtol(str, &endptr, 10); + if ((errno == ERANGE && (pidtmp == LONG_MAX || pidtmp == LONG_MIN)) + || (errno != 0 && pidtmp == 0)) { + return 1; + } + if (endptr == str) { + return 1; + } + *pid = pidtmp; + return 0; +} + +static void init_cfg(void) { + memset(&cfg, 0, sizeof(cfg)); + + cfg.bridge0.devsandbox = "eth0"; + cfg.bridge1.devsandbox = "eth1"; + cfg.bridge2.devsandbox = "eth2"; + cfg.bridge3.devsandbox = "eth3"; + + extract_user_data(); +} + +static void check_network(Bridge *br) { + assert(br); + if (br->macvlan == 0) // for bridge devices check network range or arp-scan and assign address + net_configure_sandbox_ip(br); + else if (br->ipsandbox) { // for macvlan check network range + char *rv = in_netrange(br->ipsandbox, br->ip, br->mask); + if (rv) { + fprintf(stderr, "%s", rv); + exit(1); + } + } +} + + +void check_user_namespace(void) { + if (getuid() == 0) { + fprintf(stderr, "Error: --noroot option cannot be used when starting the sandbox as root.\n"); + exit(1); + } + + // test user namespaces available in the kernel + struct stat s1; + struct stat s2; + struct stat s3; + if (stat("/proc/self/ns/user", &s1) == 0 && + stat("/proc/self/uid_map", &s2) == 0 && + stat("/proc/self/gid_map", &s3) == 0) + arg_noroot = 1; + else { + fprintf(stderr, "Warning: user namespaces not available in the current kernel.\n"); + arg_noroot = 0; + } +} + +//******************************************* +// Main program +//******************************************* +int main(int argc, char **argv) { + int i; + int prog_index = -1; // index in argv where the program command starts + int lockfd = -1; + int arg_ipc = 0; + int arg_cgroup = 0; + int custom_profile = 0; // custom profile loaded + + // initialize globals + init_cfg(); + cfg.original_argv = argv; + cfg.original_argc = argc; + + + // initialize random number generator + sandbox_pid = getpid(); + time_t t = time(NULL); + srand(t ^ sandbox_pid); + + // check firejail directories + fs_build_firejail_dir(); + shm_create_firejail_dir(); + bandwidth_shm_del_file(sandbox_pid); + + // is this a login shell? + if (*argv[0] == '-') { + fullargc = restricted_shell(cfg.username); + if (fullargc) { + int j; + for (i = 1, j = fullargc; i < argc && j < MAX_ARGS; i++, j++, fullargc++) + fullargv[j] = argv[i]; + + // replace argc/argv with fullargc/fullargv + argv = fullargv; + argc = j; + } + } + else { + // check --output option and execute it; + check_output(argc, argv); // the function will not return if --output option was found + } + + // parse arguments + for (i = 1; i < argc; i++) { + //************************************* + // basic arguments + //************************************* + if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-?") == 0) { + usage(); + exit(0); + } + else if (strcmp(argv[i], "--version") == 0) { + printf("firejail version %s\n", VERSION); + exit(0); + } + else if (strcmp(argv[i], "--debug") == 0) + arg_debug = 1; + + else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { + logargs(argc, argv); + + // extract the command + if ((i + 1) == argc) { + fprintf(stderr, "Error: command expected after --bandwidth option\n"); + exit(1); + } + char *cmd = argv[i + 1]; + if (strcmp(cmd, "status") && strcmp(cmd, "clear") && strcmp(cmd, "set")) { + fprintf(stderr, "Error: invalid --bandwidth command\n"); + exit(1); + } + + // extract network name + char *dev = NULL; + int down = 0; + int up = 0; + if (strcmp(cmd, "set") == 0 || strcmp(cmd, "clear") == 0) { + // extract device name + if ((i + 2) == argc) { + fprintf(stderr, "Error: network name expected after --bandwidth %s option\n", cmd); + exit(1); + } + dev = argv[i + 2]; + + // check device name + if (if_nametoindex(dev) == 0) { + fprintf(stderr, "Error: network device %s not found\n", dev); + exit(1); + } + + // extract bandwidth + if (strcmp(cmd, "set") == 0) { + if ((i + 4) >= argc) { + fprintf(stderr, "Error: invalid --bandwidth set command\n"); + exit(1); + } + + down = atoi(argv[i + 3]); + if (down < 0) { + fprintf(stderr, "Error: invalid download speed\n"); + exit(1); + } + up = atoi(argv[i + 4]); + if (up < 0) { + fprintf(stderr, "Error: invalid upload speed\n"); + exit(1); + } + } + } + + // extract pid or sandbox name + pid_t pid; + if (read_pid(argv[i] + 12, &pid) == 0) + bandwidth_pid(pid, cmd, dev, down, up); + else + bandwidth_name(argv[i] + 12, cmd, dev, down, up); + + // it will never get here + exit(0); + } + + //************************************* + // independent commands - the program will exit! + //************************************* +#ifdef HAVE_SECCOMP + else if (strcmp(argv[i], "--debug-syscalls") == 0) { + syscall_print(); + exit(0); + } + else if (strncmp(argv[i], "--seccomp.print=", 16) == 0) { + // join sandbox by pid or by name + pid_t pid; + if (read_pid(argv[i] + 16, &pid) == 0) + seccomp_print_filter(pid); + else + seccomp_print_filter_name(argv[i] + 16); + + // it will never get here!!! + exit(0); + } +#endif + else if (strncmp(argv[i], "--caps.print=", 13) == 0) { + // join sandbox by pid or by name + pid_t pid; + if (read_pid(argv[i] + 13, &pid) == 0) + caps_print_filter(pid); + else + caps_print_filter_name(argv[i] + 13); + + // it will never get here!!! + exit(0); + } + + else if (strncmp(argv[i], "--dns.print=", 12) == 0) { + // join sandbox by pid or by name + pid_t pid; + if (read_pid(argv[i] + 12, &pid) == 0) + net_dns_print(pid); + else + net_dns_print_name(argv[i] + 12); + + // it will never get here!!! + exit(0); + } + else if (strcmp(argv[i], "--debug-caps") == 0) { + caps_print(); + exit(0); + } + else if (strcmp(argv[i], "--list") == 0) { + list(); + exit(0); + } + else if (strcmp(argv[i], "--tree") == 0) { + tree(); + exit(0); + } + else if (strcmp(argv[i], "--top") == 0) { + top(); + exit(0); + } + else if (strcmp(argv[i], "--netstats") == 0) { + netstats(); + exit(0); + } + else if (strncmp(argv[i], "--join=", 7) == 0) { + logargs(argc, argv); + + // join sandbox by pid or by name + pid_t pid; + if (read_pid(argv[i] + 7, &pid) == 0) + join(pid, cfg.homedir, argc, argv, i + 1); + else + join_name(argv[i] + 7, cfg.homedir, argc, argv, i + 1); + + // it will never get here!!! + exit(0); + } + else if (strncmp(argv[i], "--shutdown=", 11) == 0) { + logargs(argc, argv); + + // shutdown sandbox by pid or by name + pid_t pid; + if (read_pid(argv[i] + 11, &pid) == 0) + shut(pid); + else + shut_name(argv[i] + 11); + + // it will never get here!!! + exit(0); + } + + //************************************* + // filtering + //************************************* +#ifdef HAVE_SECCOMP + else if (strcmp(argv[i], "--seccomp") == 0) { + if (arg_seccomp) { + fprintf(stderr, "Error: seccomp already enabled\n"); + exit(1); + } + arg_seccomp = 1; + } + else if (strncmp(argv[i], "--seccomp=", 10) == 0) { + if (arg_seccomp) { + fprintf(stderr, "Error: seccomp already enabled\n"); + exit(1); + } + arg_seccomp = 1; + arg_seccomp_list = strdup(argv[i] + 10); + if (!arg_seccomp_list) + errExit("strdup"); + } + else if (strncmp(argv[i], "--seccomp.drop=", 15) == 0) { + if (arg_seccomp) { + fprintf(stderr, "Error: seccomp already enabled\n"); + exit(1); + } + arg_seccomp = 1; + arg_seccomp_list_drop = strdup(argv[i] + 15); + if (!arg_seccomp_list_drop) + errExit("strdup"); + } + else if (strncmp(argv[i], "--seccomp.keep=", 15) == 0) { + if (arg_seccomp) { + fprintf(stderr, "Error: seccomp already enabled\n"); + exit(1); + } + arg_seccomp = 1; + arg_seccomp_list_keep = strdup(argv[i] + 15); + if (!arg_seccomp_list_keep) + errExit("strdup"); + } +#endif + else if (strcmp(argv[i], "--caps") == 0) + arg_caps_default_filter = 1; + else if (strcmp(argv[i], "--caps.drop=all") == 0) + arg_caps_drop_all = 1; + else if (strncmp(argv[i], "--caps.drop=", 12) == 0) { + arg_caps_drop = 1; + arg_caps_list = strdup(argv[i] + 12); + if (!arg_caps_list) + errExit("strdup"); + // verify caps list and exit if problems + if (caps_check_list(arg_caps_list, NULL)) + return 1; + } + else if (strncmp(argv[i], "--caps.keep=", 12) == 0) { + arg_caps_keep = 1; + arg_caps_list = strdup(argv[i] + 12); + if (!arg_caps_list) + errExit("strdup"); + // verify caps list and exit if problems + if (caps_check_list(arg_caps_list, NULL)) + return 1; + } + + + else if (strcmp(argv[i], "--trace") == 0) + arg_trace = 1; + else if (strncmp(argv[i], "--rlimit-nofile=", 16) == 0) { + if (not_unsigned(argv[i] + 16)) { + fprintf(stderr, "Error: invalid rlimt nofile\n"); + exit(1); + } + sscanf(argv[i] + 16, "%u", &cfg.rlimit_nofile); + arg_rlimit_nofile = 1; + } + else if (strncmp(argv[i], "--rlimit-nproc=", 15) == 0) { + if (not_unsigned(argv[i] + 15)) { + fprintf(stderr, "Error: invalid rlimt nproc\n"); + exit(1); + } + sscanf(argv[i] + 15, "%u", &cfg.rlimit_nproc); + arg_rlimit_nproc = 1; + } + else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) { + if (not_unsigned(argv[i] + 15)) { + fprintf(stderr, "Error: invalid rlimt fsize\n"); + exit(1); + } + sscanf(argv[i] + 15, "%u", &cfg.rlimit_fsize); + arg_rlimit_fsize = 1; + } + else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) { + if (not_unsigned(argv[i] + 20)) { + fprintf(stderr, "Error: invalid rlimt sigpending\n"); + exit(1); + } + sscanf(argv[i] + 20, "%u", &cfg.rlimit_sigpending); + arg_rlimit_sigpending = 1; + } + else if (strncmp(argv[i], "--ipc-namespace", 15) == 0) + arg_ipc = 1; + else if (strncmp(argv[i], "--cpu=", 6) == 0) + read_cpu_list(argv[i] + 6); + else if (strcmp(argv[i], "--nox11") == 0) { + // check if firejail lkm is present + struct stat s; + if (stat("/proc/firejail", &s) < 0) { + fprintf(stderr, "Error: firejail Linux kernel module not found. The module" + " is required for --nox11 option to work.\n"); + exit(1); + } + arg_nox11 = 1; + } + else if (strcmp(argv[i], "--nodbus") == 0) { + // check if firejail lkm is present + struct stat s; + if (stat("/proc/firejail", &s) < 0) { + fprintf(stderr, "Error: firejail Linux kernel module not found. The module" + " is required for --nodbus option to work.\n"); + exit(1); + } + arg_nodbus = 1; + } + else if (strncmp(argv[i], "--cgroup=", 9) == 0) { + if (arg_cgroup) { + fprintf(stderr, "Error: only a cgroup can be defined\n"); + exit(1); + } + arg_cgroup = 1; + cfg.cgroup = strdup(argv[i] + 9); + if (!cfg.cgroup) + errExit("strdup"); + set_cgroup(cfg.cgroup); + } + + //************************************* + // filesystem + //************************************* +#ifdef HAVE_BIND + else if (strncmp(argv[i], "--bind=", 7) == 0) { + char *line; + if (asprintf(&line, "bind %s", argv[i] + 7) == -1) + errExit("asprintf"); + + profile_check_line(line, 0); // will exit if something wrong + profile_add(line); + } +#endif + else if (strncmp(argv[i], "--tmpfs=", 8) == 0) { + char *line; + if (asprintf(&line, "tmpfs %s", argv[i] + 8) == -1) + errExit("asprintf"); + + profile_check_line(line, 0); // will exit if something wrong + profile_add(line); + } + else if (strncmp(argv[i], "--blacklist=", 12) == 0) { + char *line; + if (asprintf(&line, "blacklist %s", argv[i] + 12) == -1) + errExit("asprintf"); + + profile_check_line(line, 0); // will exit if something wrong + profile_add(line); + } + else if (strncmp(argv[i], "--read-only=", 12) == 0) { + char *line; + if (asprintf(&line, "read-only %s", argv[i] + 12) == -1) + errExit("asprintf"); + + profile_check_line(line, 0); // will exit if something wrong + profile_add(line); + } + else if (strcmp(argv[i], "--overlay") == 0) { + if (cfg.chrootdir) { + fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); + exit(1); + } + arg_overlay = 1; + } + else if (strncmp(argv[i], "--profile=", 10) == 0) { + // multiple profile files are allowed! + char *ptr = argv[i] + 10; + if (is_dir(ptr) || is_link(ptr) || strstr(ptr, "..")) { + fprintf(stderr, "Error: invalid profile file\n"); + exit(1); + } + + // access call checks as real UID/GID, not as effective UID/GID + if (access(argv[i] + 10, R_OK)) { + fprintf(stderr, "Error: cannot access profile file\n"); + return 1; + } + + profile_read(argv[i] + 10, NULL, NULL); + custom_profile = 1; + } +#ifdef HAVE_CHROOT + else if (strncmp(argv[i], "--chroot=", 9) == 0) { + if (arg_overlay) { + fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); + exit(1); + } + + // extract chroot dirname + cfg.chrootdir = argv[i] + 9; + // if the directory starts with ~, expand the home directory + if (*cfg.chrootdir == '~') { + char *tmp; + if (asprintf(&tmp, "%s%s", cfg.homedir, cfg.chrootdir + 1) == -1) + errExit("asprintf"); + cfg.chrootdir = tmp; + } + + // check chroot dirname exists + if (strstr(cfg.chrootdir, "..") || !is_dir(cfg.chrootdir) || is_link(cfg.chrootdir)) { + fprintf(stderr, "Error: invalid directory %s\n", cfg.chrootdir); + return 1; + } + + // check chroot directory structure + if (fs_check_chroot_dir(cfg.chrootdir)) { + fprintf(stderr, "Error: invalid chroot\n"); + exit(1); + } + } +#endif + else if (strcmp(argv[i], "--private") == 0) + arg_private = 1; + else if (strncmp(argv[i], "--private=", 10) == 0) { + if (cfg.home_private_keep) { + fprintf(stderr, "Error: a private list of files was already defined with --private.keep option.\n"); + exit(1); + } + + // extract private home dirname + cfg.home_private = argv[i] + 10; + fs_check_private_dir(); + arg_private = 1; + } + else if (strncmp(argv[i], "--private.keep=", 15) == 0) { + if (cfg.home_private) { + fprintf(stderr, "Error: a private home directory was already defined with --private option.\n"); + exit(1); + } + + // extract private home dirname + cfg.home_private_keep = argv[i] + 15; + fs_check_home_list(); + arg_private = 1; + } + else if (strcmp(argv[i], "--private-dev") == 0) { + arg_private_dev = 1; + } + + + + //************************************* + // hostname, etc + //************************************* + else if (strncmp(argv[i], "--name=", 7) == 0) { + cfg.hostname = argv[i] + 7; + if (strlen(cfg.hostname) == 0) { + fprintf(stderr, "Error: please provide a name for sandbox\n"); + return 1; + } + } + else if (strcmp(argv[i], "--nogroups") == 0) + arg_nogroups = 1; + else if (strcmp(argv[i], "--noroot") == 0) { + check_user_namespace(); + } + + //************************************* + // network + //************************************* + else if (strncmp(argv[i], "--net=", 6) == 0) { + if (strcmp(argv[i] + 6, "none") == 0) { + arg_nonetwork = 1; + cfg.bridge0.configured = 0; + cfg.bridge1.configured = 0; + cfg.bridge2.configured = 0; + cfg.bridge3.configured = 0; + continue; + } + if (strcmp(argv[i] + 6, "lo") == 0) { + fprintf(stderr, "Error: cannot attach to lo device\n"); + exit(1); + } + + Bridge *br; + if (cfg.bridge0.configured == 0) + br = &cfg.bridge0; + else if (cfg.bridge1.configured == 0) + br = &cfg.bridge1; + else if (cfg.bridge2.configured == 0) + br = &cfg.bridge2; + else if (cfg.bridge3.configured == 0) + br = &cfg.bridge3; + else { + fprintf(stderr, "Error: maximum 4 network devices allowed\n"); + return 1; + } + net_configure_bridge(br, argv[i] + 6); + } + else if (strcmp(argv[i], "--scan") == 0) { + arg_scan = 1; + } + else if (strncmp(argv[i], "--iprange=", 10) == 0) { + Bridge *br = last_bridge_configured(); + if (br == NULL) { + fprintf(stderr, "Error: no network device configured\n"); + return 1; + } + if (br->iprange_start || br->iprange_end) { + fprintf(stderr, "Error: cannot configure the IP range twice for the same interface\n"); + return 1; + } + + // parse option arguments + char *firstip = argv[i] + 10; + char *secondip = firstip; + while (*secondip != '\0') { + if (*secondip == ',') + break; + secondip++; + } + if (*secondip == '\0') { + fprintf(stderr, "Error: invalid IP range\n"); + return 1; + } + *secondip = '\0'; + secondip++; + + // check addresses + if (atoip(firstip, &br->iprange_start) || atoip(secondip, &br->iprange_end) || + br->iprange_start >= br->iprange_end) { + fprintf(stderr, "Error: invalid IP range\n"); + return 1; + } + if (in_netrange(br->iprange_start, br->ip, br->mask) || in_netrange(br->iprange_end, br->ip, br->mask)) { + fprintf(stderr, "Error: IP range addresses not in network range\n"); + return 1; + } + } + else if (strncmp(argv[i], "--mac=", 6) == 0) { + Bridge *br = last_bridge_configured(); + if (br == NULL) { + fprintf(stderr, "Error: no network device configured\n"); + return 1; + } + if (mac_not_zero(br->macsandbox)) { + fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); + return 1; + } + + // read the address + if (atomac(argv[i] + 6, br->macsandbox)) { + fprintf(stderr, "Error: invalid MAC address\n"); + return 1; + } + } + else if (strncmp(argv[i], "--ip=", 5) == 0) { + Bridge *br = last_bridge_configured(); + if (br == NULL) { + fprintf(stderr, "Error: no network device configured\n"); + return 1; + } + if (br->arg_ip_none || br->ipsandbox) { + fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); + return 1; + } + + // configure this IP address for the last bridge defined + if (strcmp(argv[i] + 5, "none") == 0) + br->arg_ip_none = 1; + else { + if (atoip(argv[i] + 5, &br->ipsandbox)) { + fprintf(stderr, "Error: invalid IP address\n"); + return 1; + } + } + } + else if (strncmp(argv[i], "--defaultgw=", 12) == 0) { + if (atoip(argv[i] + 12, &cfg.defaultgw)) { + fprintf(stderr, "Error: invalid IP address\n"); + return 1; + } + } + else if (strncmp(argv[i], "--dns=", 6) == 0) { + uint32_t dns; + if (atoip(argv[i] + 6, &dns)) { + fprintf(stderr, "Error: invalid DNS server IP address\n"); + return 1; + } + + if (cfg.dns1 == 0) + cfg.dns1 = dns; + else if (cfg.dns2 == 0) + cfg.dns2 = dns; + else if (cfg.dns3 == 0) + cfg.dns3 = dns; + else { + fprintf(stderr, "Error: up to 3 DNS servers can be specified\n"); + return 1; + } + } + else if (strcmp(argv[i], "--netfilter") == 0) + arg_netfilter = 1; + else if (strncmp(argv[i], "--netfilter=", 12) == 0) { + arg_netfilter = 1; + arg_netfilter_file = argv[i] + 12; + check_netfilter_file(arg_netfilter_file); + } + + //************************************* + // command + //************************************* + else if (strcmp(argv[i], "--csh") == 0) { + if (arg_shell_none) { + fprintf(stderr, "Error: --shell=none was already specified.\n"); + return 1; + } + if (arg_zsh || cfg.shell ) { + fprintf(stderr, "Error: only one default user shell can be specified\n"); + return 1; + } + arg_csh = 1; + } + else if (strcmp(argv[i], "--zsh") == 0) { + if (arg_shell_none) { + fprintf(stderr, "Error: --shell=none was already specified.\n"); + return 1; + } + if (arg_csh || cfg.shell ) { + fprintf(stderr, "Error: only one default user shell can be specified\n"); + return 1; + } + arg_zsh = 1; + } + else if (strcmp(argv[i], "--shell=none") == 0) { + arg_shell_none = 1; + if (arg_csh || arg_zsh || cfg.shell) { + fprintf(stderr, "Error: a shell was already specified\n"); + return 1; + } + } + else if (strncmp(argv[i], "--shell=", 8) == 0) { + if (arg_shell_none) { + fprintf(stderr, "Error: --shell=none was already specified.\n"); + return 1; + } + if (arg_csh || arg_zsh || cfg.shell) { + fprintf(stderr, "Error: only one user shell can be specified\n"); + return 1; + } + cfg.shell = argv[i] + 8; + + if (is_dir(cfg.shell) || is_link(cfg.shell) || strstr(cfg.shell, "..")) { + fprintf(stderr, "Error: invalid shell\n"); + exit(1); + } + + // access call checks as real UID/GID, not as effective UID/GID + if (access(cfg.shell, R_OK)) { + fprintf(stderr, "Error: cannot access shell file\n"); + exit(1); + } + } + else if (strcmp(argv[i], "-c") == 0) { + arg_command = 1; + if (i == (argc - 1)) { + fprintf(stderr, "Error: option -c requires an argument\n"); + return 1; + } + } + else if (strcmp(argv[i], "--") == 0) { + // double dash - positional params to follow + arg_doubledash = 1; + i++; + if (i >= argc) { + fprintf(stderr, "Error: program name not found\n"); + exit(1); + } + extract_command_name(argv[i]); + prog_index = i; + cfg.original_program_index = i; + break; + } + else { + // is this an invalid option? + if (*argv[i] == '-') { + fprintf(stderr, "Error: invalid %s command line option\n", argv[i]); + return 1; + } + + // we have a program name coming + extract_command_name(argv[i]); + prog_index = i; + cfg.original_program_index = i; + break; + } + } + + // check network configuration options - it will exit if anything went wrong + net_check_cfg(); + + // check user namespace (--noroot) options + if (arg_noroot) { + if (arg_overlay) { + fprintf(stderr, "Error: --overlay and --noroot are mutually exclusive.\n"); + exit(1); + } + else if (cfg.chrootdir) { + fprintf(stderr, "Error: --chroot and --noroot are mutually exclusive.\n"); + exit(1); + } + } + + // log command + logargs(argc, argv); + if (fullargc) { + char *msg; + if (asprintf(&msg, "user %s entering restricted shell", cfg.username) == -1) + errExit("asprintf"); + logmsg(msg); + free(msg); + } + + // build the sandbox command + if (prog_index == -1 && arg_zsh) { + cfg.command_line = "/usr/bin/zsh"; + cfg.command_name = "zsh"; + } + else if (prog_index == -1 && arg_csh) { + cfg.command_line = "/bin/csh"; + cfg.command_name = "csh"; + } + else if (prog_index == -1 && cfg.shell) { + cfg.command_line = cfg.shell; + cfg.command_name = cfg.shell; + } + else if (prog_index == -1) { + cfg.command_line = "/bin/bash"; + cfg.command_name = "bash"; + } + else { + // calculate the length of the command + int i; + int len = 0; + int argcnt = argc - prog_index; + for (i = 0; i < argcnt; i++) + len += strlen(argv[i + prog_index]) + 1; // + ' ' + + // build the string + cfg.command_line = malloc(len + 1); // + '\0' + if (!cfg.command_line) + errExit("malloc"); + char *ptr = cfg.command_line; + for (i = 0; i < argcnt; i++) { + sprintf(ptr, "%s ", argv[i + prog_index]); + ptr += strlen(ptr); + } + } + + // load the profile + { + assert(cfg.command_name); + if (arg_debug) + printf("Command name #%s#\n", cfg.command_name); + if (!custom_profile) { + // look for a profile in ~/.config/firejail directory + char *usercfgdir; + if (asprintf(&usercfgdir, "%s/.config/firejail", cfg.homedir) == -1) + errExit("asprintf"); + int rv = profile_find(cfg.command_name, usercfgdir); + free(usercfgdir); + custom_profile = rv; + } + if (!custom_profile) { + // look for a user profile in /etc/firejail directory + int rv = profile_find(cfg.command_name, "/etc/firejail"); + custom_profile = rv; + } + } + + // check and assign an IP address - for macvlan it will be done again in the sandbox! + if (any_bridge_configured()) { + lockfd = open("/tmp/firejail/firejail.lock", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (lockfd != -1) { + int rv = fchown(lockfd, 0, 0); + (void) rv; + flock(lockfd, LOCK_EX); + } + + check_network(&cfg.bridge0); + check_network(&cfg.bridge1); + check_network(&cfg.bridge2); + check_network(&cfg.bridge3); + + // save network mapping in shared memory + network_shm_set_file(sandbox_pid); + } + + // create the parent-child communication pipe + if (pipe(parent_to_child_fds) < 0) + errExit("pipe"); + if (pipe(child_to_parent_fds) < 0) + errExit("pipe"); + + // clone environment + int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; + + // in root mode also enable CLONE_NEWIPC + // in user mode CLONE_NEWIPC will break MIT Shared Memory Extension (MIT-SHM) + if (getuid() == 0 || arg_ipc) + flags |= CLONE_NEWIPC; + + if (any_bridge_configured() || arg_nonetwork) { + flags |= CLONE_NEWNET; + } + else if (arg_debug) + printf("Using the local network stack\n"); + + child = clone(sandbox, + child_stack + STACK_SIZE, + flags, + NULL); + if (child == -1) + errExit("clone"); + + if (!arg_command) { + printf("Parent pid %u, child pid %u\n", sandbox_pid, child); + // print the path of the new log directory + if (getuid() == 0) // only for root + printf("The new log directory is /proc/%d/root/var/log\n", child); + } + + + + // create veth pair or macvlan device + if (cfg.bridge0.configured && !arg_nonetwork) { + if (cfg.bridge0.macvlan == 0) + net_configure_veth_pair(&cfg.bridge0, "eth0", child); + else + net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); + } + + if (cfg.bridge1.configured && !arg_nonetwork) { + if (cfg.bridge1.macvlan == 0) + net_configure_veth_pair(&cfg.bridge1, "eth1", child); + else + net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); + } + + if (cfg.bridge2.configured && !arg_nonetwork) { + if (cfg.bridge2.macvlan == 0) + net_configure_veth_pair(&cfg.bridge2, "eth2", child); + else + net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); + } + + if (cfg.bridge3.configured && !arg_nonetwork) { + if (cfg.bridge3.macvlan == 0) + net_configure_veth_pair(&cfg.bridge3, "eth3", child); + else + net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); + } + + // close each end of the unused pipes + close(parent_to_child_fds[0]); + close(child_to_parent_fds[1]); + + // notify child that base setup is complete + notify_other(parent_to_child_fds[1]); + + // wait for child to create new user namespace with CLONE_NEWUSER + wait_for_other(child_to_parent_fds[0]); + close(child_to_parent_fds[0]); + + if (arg_noroot) { + // update the UID and GID maps in the new child user namespace + // uid + char *map_path; + if (asprintf(&map_path, "/proc/%d/uid_map", child) == -1) + errExit("asprintf"); + char *map; + uid_t uid = getuid(); + if (asprintf(&map, "%d %d 1", uid, uid) == -1) + errExit("asprintf"); + update_map(map, map_path); + free(map); + free(map_path); + + //gid + if (asprintf(&map_path, "/proc/%d/gid_map", child) == -1) + errExit("asprintf"); + gid_t gid = getgid(); + if (asprintf(&map, "%d %d 1", gid, gid) == -1) + errExit("asprintf"); + update_map(map, map_path); + free(map); + free(map_path); + } + + // notify child that UID/GID mapping is complete + notify_other(parent_to_child_fds[1]); + close(parent_to_child_fds[1]); + + if (lockfd != -1) + flock(lockfd, LOCK_UN); + + // handle CTRL-C in parent + signal (SIGINT, my_handler); + signal (SIGTERM, my_handler); + + // wait for the child to finish + waitpid(child, NULL, 0); + myexit(0); + return 0; +} diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c new file mode 100644 index 000000000..dbed4ac30 --- /dev/null +++ b/src/firejail/netfilter.c @@ -0,0 +1,164 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include + +static char *client_filter = +"*filter\n" +":INPUT DROP [0:0]\n" +":FORWARD DROP [0:0]\n" +":OUTPUT ACCEPT [0:0]\n" +"-A INPUT -i lo -j ACCEPT\n" +"# echo replay is handled by -m state RELEATED/ESTABLISHED below\n" +"#-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT\n" +"-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n" +"-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT\n" +"-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT\n" +"-A INPUT -p icmp --icmp-type echo-request -j ACCEPT \n" +"COMMIT\n"; + +void check_netfilter_file(const char *fname) { + if (is_dir(fname) || is_link(fname) || strstr(fname, "..")) { + fprintf(stderr, "Error: invalid network filter file\n"); + exit(1); + } + + // access call checks as real UID/GID, not as effective UID/GID + if (access(fname, R_OK)) { + fprintf(stderr, "Error: cannot access network filter file\n"); + exit(1); + } +} + + +void netfilter(const char *fname) { + // default filter + char *filter = client_filter; + + // custom filter + int allocated = 0; + if (fname) { + // buffer the filter + struct stat s; + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: cannot find network filter file\n"); + exit(1); + } + + filter = malloc(s.st_size + 1); // + '\0' + memset(filter, 0, s.st_size + 1); + if (!filter) + errExit("malloc"); + + /* coverity[toctou] */ + FILE *fp = fopen(fname, "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open network filter file\n"); + exit(1); + } + + size_t sz = fread(filter, 1, s.st_size, fp); + if (sz != s.st_size) { + fprintf(stderr, "Error: cannot read network filter file\n"); + exit(1); + } + fclose(fp); + allocated = 1; + } + + // mount a tempfs on top of /tmp directory + if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /tmp"); + + // create the filter file + FILE *fp = fopen("/tmp/netfilter", "w"); + if (!fp) { + fprintf(stderr, "Error: cannot open /tmp/netfilter file\n"); + exit(1); + } + fprintf(fp, "%s\n", filter); + fclose(fp); + + // find iptables command + struct stat s; + char *iptables = NULL; + char *iptables_restore = NULL; + if (stat("/sbin/iptables", &s) == 0) { + iptables = "/sbin/iptables"; + iptables_restore = "/sbin/iptables-restore"; + } + else if (stat("/usr/sbin/iptables", &s) == 0) { + iptables = "/usr/sbin/iptables"; + iptables_restore = "/usr/sbin/iptables-restore"; + } + if (iptables == NULL || iptables_restore == NULL) { + fprintf(stderr, "Error: iptables command not found\n"); + goto doexit; + } + + // push filter + pid_t child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + if (arg_debug) + printf("Installing network filter:\n%s\n", filter); + + int fd; + if((fd = open("/tmp/netfilter", O_RDONLY)) == -1) { + fprintf(stderr,"Error: cannot open /tmp/netfilter\n"); + exit(1); + } + dup2(fd,STDIN_FILENO); + close(fd); + + // wipe out environment variables + environ = NULL; + execl(iptables_restore, iptables_restore, NULL); + // it will never get here!!! + } + // wait for the child to finish + waitpid(child, NULL, 0); + + // debug + if (arg_debug) { + child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + environ = NULL; + execl(iptables, iptables, "-vL", NULL); + // it will never get here!!! + } + // wait for the child to finish + waitpid(child, NULL, 0); + } + +doexit: + // unmount /tmp + umount("/tmp"); + + if (allocated) + free(filter); +} diff --git a/src/firejail/network.c b/src/firejail/network.c new file mode 100644 index 000000000..6a1d52744 --- /dev/null +++ b/src/firejail/network.c @@ -0,0 +1,362 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// scan interfaces in current namespace and print IP address/mask for each interface +void net_ifprint(void) { + uint32_t ip; + uint32_t mask; + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) + errExit("getifaddrs"); + + printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", + "Interface", "MAC", "IP", "Mask", "Status"); + // walk through the linked list + 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); + + // interface status + char *status; + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) + status = "UP"; + else + status = "DOWN"; + + // ip address and mask + char ipstr[30]; + sprintf(ipstr, "%d.%d.%d.%d", PRINT_IP(ip)); + char maskstr[30]; + sprintf(maskstr, "%d.%d.%d.%d", PRINT_IP(mask)); + + // mac address + unsigned char mac[6]; + net_get_mac(ifa->ifa_name, mac); + char macstr[30]; + if (strcmp(ifa->ifa_name, "lo") == 0) + macstr[0] = '\0'; + else + sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x", PRINT_MAC(mac)); + + // print + printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", + ifa->ifa_name, macstr, ipstr, maskstr, status); + + // network scanning + if (!arg_scan) // scanning disabled + continue; + if (strcmp(ifa->ifa_name, "lo") == 0) // no loopbabck scanning + continue; + if (mask2bits(mask) < 16) // not scanning large networks + continue; + if (!ip) // if not configured + continue; + // only if the interface is up and running + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) + arp_scan(ifa->ifa_name, ip, mask); + } + } + freeifaddrs(ifaddr); +} + + +// return -1 if the interface was not found; if the interface was found retrn 0 and fill in IP address and mask +int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6]) { + assert(bridge); + assert(ip); + assert(mask); + int rv = -1; + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) + errExit("getifaddrs"); + + // walk through the linked list; if the interface is found, extract IP address and mask + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (strcmp(ifa->ifa_name, bridge) != 0) + 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); + if (strcmp(ifa->ifa_name, "lo") != 0) + net_get_mac(ifa->ifa_name, mac); + + rv = 0; + break; + } + } + + freeifaddrs(ifaddr); + return rv; +} + +// bring interface up +void net_if_up(const char *ifname) { + if (strlen(ifname) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", ifname); + exit(1); + } + + int sock = socket(AF_INET,SOCK_DGRAM,0); + if (sock < 0) + errExit("socket"); + + // get the existing interface flags + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + + // read the existing flags + if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + + ifr.ifr_flags |= IFF_UP; + + // set the new flags + if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + + // checking + // read the existing flags + if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + + // wait not more than 500ms for the interface to come up + int cnt = 0; + while (cnt < 50) { + usleep(10000); // sleep 10ms + + // read the existing flags + if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + if (ifr.ifr_flags & IFF_RUNNING) + break; + cnt++; + } + + close(sock); +} + +// configure interface +void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask) { + if (strlen(ifname) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", ifname); + exit(1); + } + + int sock = socket(AF_INET,SOCK_DGRAM,0); + if (sock < 0) + errExit("socket"); + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip); + if (ioctl( sock, SIOCSIFADDR, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + + if (ip != 0) { + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(mask); + if (ioctl( sock, SIOCSIFNETMASK, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + } + + close(sock); + usleep(10000); // sleep 10ms +} + + +// add an IP route, return -1 if error, 0 if the route was added +int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { + int sock; + struct rtentry route; + struct sockaddr_in *addr; + int err = 0; + + // create the socket + if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + errExit("socket"); + + memset(&route, 0, sizeof(route)); + + addr = (struct sockaddr_in*) &route.rt_gateway; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(gw); + + addr = (struct sockaddr_in*) &route.rt_dst; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(ip); + + addr = (struct sockaddr_in*) &route.rt_genmask; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(mask); + + route.rt_flags = RTF_UP | RTF_GATEWAY; + route.rt_metric = 0; + if ((err = ioctl(sock, SIOCADDRT, &route)) != 0) { + close(sock); + return -1; + } + + close(sock); + return 0; +} + + +// add a veth device to a bridge +void net_bridge_add_interface(const char *bridge, const char *dev) { + if (strlen(bridge) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", bridge); + exit(1); + } + + struct ifreq ifr; + int err; + int ifindex = if_nametoindex(dev); + + if (ifindex <= 0) + errExit("if_nametoindex"); + + int sock; + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); +#ifdef SIOCBRADDIF + ifr.ifr_ifindex = ifindex; + err = ioctl(sock, SIOCBRADDIF, &ifr); + if (err < 0) +#endif + { + unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; + + ifr.ifr_data = (char *) args; + err = ioctl(sock, SIOCDEVPRIVATE, &ifr); + } + (void) err; + close(sock); +} + +#define BUFSIZE 1024 +uint32_t network_get_defaultgw(void) { + FILE *fp = fopen("/proc/self/net/route", "r"); + if (!fp) + errExit("fopen"); + + char buf[BUFSIZE]; + uint32_t retval = 0; + while (fgets(buf, BUFSIZE, fp)) { + if (strncmp(buf, "Iface", 5) == 0) + continue; + + char *ptr = buf; + while (*ptr != ' ' && *ptr != '\t') + ptr++; + while (*ptr == ' ' || *ptr == '\t') + ptr++; + + unsigned dest; + unsigned gw; + int rv = sscanf(ptr, "%x %x", &dest, &gw); + if (rv == 2 && dest == 0) { + retval = ntohl(gw); + break; + } + } + + fclose(fp); + return retval; +} + +int net_config_mac(const char *ifname, const unsigned char mac[6]) { + struct ifreq ifr; + int sock; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); + + if (ioctl(sock, SIOCSIFHWADDR, &ifr) == -1) + errExit("ioctl"); + close(sock); + return 0; +} + +int net_get_mac(const char *ifname, unsigned char mac[6]) { + + struct ifreq ifr; + int sock; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) + errExit("ioctl"); + memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); + + close(sock); + return 0; +} diff --git a/src/firejail/network.txt b/src/firejail/network.txt new file mode 100644 index 000000000..673d5b941 --- /dev/null +++ b/src/firejail/network.txt @@ -0,0 +1,95 @@ +struct Bridge { + char *dev; // bridge device name + uint32_t ip; // bridge device IP address + uint32_t mask; // bridge device mask + uint32_t ipsandbox // sandbox interface IP address +} + +net_configure_bridge(br, device) { + br->dev = devname; + br->ip = extracted from kernel device - using net_get_if_addr() in network.c + br->mask = extracted from kernel device - using net_get_if_addr() in network.c + check available network range; /31 networks are not supported +} + +net_configure_sandbox_ip(br) { + if br->ip_snadbox + check br->ipsandbox inside the bridge network + arp_check(br->ipsandbox) // send an arp req to check if anybody else is using this address + else + br->ipsandbox = arp_assign(); +} + +net_configure_veth_pair { + create a veth pair + place one interface end in the bridge + place the other end in the namespace of the child process +} + +net_bridge_wait_ip { + arp_check br->ipsandbox address to come up + wait for not more than 5 seconds +} + +main() { + + foreach argv[i] { + if --net + br = next bridge available + net_configure_bridge(br, device name from argv[i]); + else if --ip + br = last bridge configured + br->ipsandbox = ip address extracted from argv[i] + else if --defaultgw + cfg.defaultgw = ip address extracted from argv[i] + } + + net_check_cfg(); // check the validity of network configuration so far + + if (any bridge configured) { + lock /var/lock/firejail.lock file + for each bridge + net_configure_sandbox_ip(br) + } + + clone (new network namespace if any bridge configured or --net=none) + + if (any bridge configured) { + for each bridge + net_configure_veth_pair + } + + notify child init is done + + if (any bridge configured) { + for each bridge + net_bridge_wait_ip + unlock /var/lock/firejail.lock file + } + + wait on child + exit +} + + +****************************************************** +* macvlan notes +****************************************************** +Configure a macvlan interface + +# ip link add virtual0 link eth0 type macvlan mode bridge +(you can configure it with # ifconfig virtual0 192.168.1.52/24 up) + +Create a new network namespace and move the interface in the new network namespace + +# ip netns add dummy0 +# ip link set virtual0 netns dummy0 + +Join the namespace and configure the interfaces + +# ip netns exec dummy0 bash +# ifconfig lo up +# ifconfig virtual0 192.168.1.52/24 + +Investigate ipvlan interface - added to linux kernel 3.19 +https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvlan.txt diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c new file mode 100644 index 000000000..c2459b0cd --- /dev/null +++ b/src/firejail/network_main.c @@ -0,0 +1,268 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include + +// configure bridge structure +// - extract ip address and mask from the bridge interface +void net_configure_bridge(Bridge *br, char *dev_name) { + assert(br); + assert(dev_name); + + br->dev = dev_name; + + // check the bridge device exists + char sysbridge[30 + strlen(br->dev)]; + sprintf(sysbridge, "/sys/class/net/%s/bridge", br->dev); + struct stat s; + int rv = stat(sysbridge, &s); + if (rv == 0) { + // this is a bridge device + br->macvlan = 0; + } + else { + // is this a regular Ethernet interface + if (if_nametoindex(br->dev) > 0) { + br->macvlan = 1; + char *newname; + if (asprintf(&newname, "%s-%u", br->devsandbox, getpid()) == -1) + errExit("asprintf"); + br->devsandbox = newname; + } + else { + fprintf(stderr, "Error: cannot find network device %s\n", br->dev); + exit(1); + } + } + + if (net_get_if_addr(br->dev, &br->ip, &br->mask, br->mac)) { + fprintf(stderr, "Error: interface %s is not configured\n", br->dev); + exit(1); + } + if (arg_debug) { + if (br->macvlan == 0) + printf("Bridge device %s at %d.%d.%d.%d/%d\n", + br->dev, PRINT_IP(br->ip), mask2bits(br->mask)); + else + printf("macvlan parent device %s at %d.%d.%d.%d/%d\n", + br->dev, PRINT_IP(br->ip), mask2bits(br->mask)); + } + + uint32_t range = ~br->mask + 1; // the number of potential addresses + // this software is not supported for /31 networks + if (range < 4) { + fprintf(stderr, "Error: the software is not supported for /31 networks\n"); + exit(1); + } + br->configured = 1; +} + + +void net_configure_sandbox_ip(Bridge *br) { + assert(br); + if (br->configured == 0) + return; + + if (br->arg_ip_none) + br->ipsandbox = 0; + else if (br->ipsandbox) { + // check network range + char *rv = in_netrange(br->ipsandbox, br->ip, br->mask); + if (rv) { + fprintf(stderr, "%s", rv); + exit(1); + } + // send an ARP request and check if there is anybody on this IP address + if (arp_check(br->dev, br->ipsandbox, br->ip)) { + fprintf(stderr, "Error: IP address %d.%d.%d.%d is already in use\n", PRINT_IP(br->ipsandbox)); + exit(1); + } + } + else + // ip address assigned by arp-scan for a bridge device + br->ipsandbox = arp_assign(br->dev, br); //br->ip, br->mask); +} + + +// create a veth pair +// - br - bridge device +// - ifname - interface name in sandbox namespace +// - child - child process running the namespace + +void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { + assert(br); + if (br->configured == 0) + return; + + // create a veth pair + char *dev; + if (asprintf(&dev, "veth%u%s", getpid(), ifname) < 0) + errExit("asprintf"); + net_create_veth(dev, ifname, child); + + // bring up the interface + net_if_up(dev); + + // add interface to the bridge + net_bridge_add_interface(br->dev, dev); + + char *msg; + if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) + errExit("asprintf"); + logmsg(msg); + fflush(0); + free(msg); +} + +// the default address should be in the range of at least on of the bridge devices +void check_default_gw(uint32_t defaultgw) { + assert(defaultgw); + + if (cfg.bridge0.configured) { + char *rv = in_netrange(defaultgw, cfg.bridge0.ip, cfg.bridge0.mask); + if (rv == 0) + return; + } + if (cfg.bridge1.configured) { + char *rv = in_netrange(defaultgw, cfg.bridge1.ip, cfg.bridge1.mask); + if (rv == 0) + return; + } + if (cfg.bridge2.configured) { + char *rv = in_netrange(defaultgw, cfg.bridge2.ip, cfg.bridge2.mask); + if (rv == 0) + return; + } + if (cfg.bridge3.configured) { + char *rv = in_netrange(defaultgw, cfg.bridge3.ip, cfg.bridge3.mask); + if (rv == 0) + return; + } + + fprintf(stderr, "Error: default gateway %d.%d.%d.%d is not in the range of any network\n", PRINT_IP(defaultgw)); + exit(1); +} + +void net_check_cfg(void) { + int net_configured = 0; + if (cfg.bridge0.configured) + net_configured++; + if (cfg.bridge1.configured) + net_configured++; + if (cfg.bridge2.configured) + net_configured++; + if (cfg.bridge3.configured) + net_configured++; + + // --defaultgw requires a network + if (cfg.defaultgw && net_configured == 0) { + fprintf(stderr, "Error: option --defaultgw requires at least one network to be configured\n"); + exit(1); + } + + if (net_configured == 0) // nothing to check + return; + + // --net=none + if (arg_nonetwork && net_configured) { + fprintf(stderr, "Error: --net and --net=none are mutually exclusive\n"); + exit(1); + } + + // check default gateway address or assign one + assert(cfg.bridge0.configured); + if (cfg.defaultgw) + check_default_gw(cfg.defaultgw); + else { + // first network is a regular bridge + if (cfg.bridge0.macvlan == 0) + cfg.defaultgw = cfg.bridge0.ip; + // first network is a mac device + else { + // get the host default gw + uint32_t gw = network_get_defaultgw(); + // check the gateway is network range + if (in_netrange(gw, cfg.bridge0.ip, cfg.bridge0.mask)) + gw = 0; + cfg.defaultgw = gw; + } + } +} + + + +void net_dns_print_name(const char *name) { + if (!name || strlen(name) == 0) { + fprintf(stderr, "Error: invalid sandbox name\n"); + exit(1); + } + pid_t pid; + if (name2pid(name, &pid)) { + fprintf(stderr, "Error: cannot find sandbox %s\n", name); + exit(1); + } + + net_dns_print(pid); +} + +#define MAXBUF 4096 +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); +} diff --git a/src/firejail/output.c b/src/firejail/output.c new file mode 100644 index 000000000..32adac108 --- /dev/null +++ b/src/firejail/output.c @@ -0,0 +1,84 @@ +#include "firejail.h" +#include +#include +#include + +void check_output(int argc, char **argv) { + int i; + char *outfile = NULL; +// drop_privs(0); + + int found = 0; + for (i = 1; i < argc; i++) { + if (strncmp(argv[i], "--output=", 9) == 0) { + found = 1; + outfile = argv[i] + 9; + + // do not accept directories, links, and files with ".." + if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) { + fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n"); + exit(1); + } + + struct stat s; + if (stat(outfile, &s) == 0) { + // check permissions + if (s.st_uid != getuid() || s.st_gid != getgid()) { + fprintf(stderr, "Error: the output file needs to be owned by the current user.\n"); + exit(1); + } + + // check hard links + if (s.st_nlink != 1) { + fprintf(stderr, "Error: no hard links allowed.\n"); + exit(1); + } + } + + // drop privileges and try to open the file for writing + drop_privs(0); + /* coverity[toctou] */ + FILE *fp = fopen(outfile, "a"); + if (!fp) { + fprintf(stderr, "Error: cannot open output file %s\n", outfile); + exit(1); + } + fclose(fp); + break; + } + } + if (!found) + return; + + + // build the new command line + int len = 0; + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; // + ' ' + } + len += 50 + strlen(outfile); // tee command + + char *cmd = malloc(len + 1); // + '\0' + if (!cmd) + errExit("malloc"); + + char *ptr = cmd; + for (i = 0; i < argc; i++) { + if (strncmp(argv[i], "--output=", 9) == 0) + continue; + ptr += sprintf(ptr, "%s ", argv[i]); + } + sprintf(ptr, "| %s/lib/firejail/ftee %s", PREFIX, outfile); + + // run command + char *a[4]; + a[0] = "/bin/bash"; + a[1] = "-c"; + a[2] = cmd; + a[3] = NULL; + + execvp(a[0], a); + + perror("execvp"); + exit(1); +} diff --git a/src/firejail/profile.c b/src/firejail/profile.c new file mode 100644 index 000000000..343907584 --- /dev/null +++ b/src/firejail/profile.c @@ -0,0 +1,444 @@ +/* + * 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 "firejail.h" +#include +#include + +#define MAX_READ 8192 // line buffer for profile files + +// find and read the profile specified by name from dir directory +int profile_find(const char *name, const char *dir) { + assert(name); + assert(dir); + + int rv = 0; + DIR *dp; + char *pname; + if (asprintf(&pname, "%s.profile", name) == -1) + errExit("asprintf"); + + dp = opendir (dir); + if (dp != NULL) { + struct dirent *ep; + while ((ep = readdir(dp)) != NULL) { + if (strcmp(ep->d_name, pname) == 0) { + if (arg_debug) + printf("Found %s profile in %s directory\n", name, dir); + char *etcpname; + if (asprintf(&etcpname, "%s/%s", dir, pname) == -1) + errExit("asprintf"); + profile_read(etcpname, NULL, NULL); + free(etcpname); + rv = 1; + break; + } + } + (void) closedir (dp); + } + + free(pname); + return rv; +} + + +//*************************************************** +// run-time profiles +//*************************************************** +static void check_file_name(char *ptr, int lineno) { + if (strncmp(ptr, "${HOME}", 7) == 0) + ptr += 7; + else if (strncmp(ptr, "${PATH}", 7) == 0) + ptr += 7; + + int len = strlen(ptr); + // file globbing ('*') is allowed + if (strcspn(ptr, "\\&!?\"'<>%^(){}[];, ") != len) { + if (lineno == 0) + fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); + else + fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno); + exit(1); + } +} + + +// check profile line; if line == 0, this was generated from a command line option +// return 1 if the command is to be added to the linked list of profile commands +// return 0 if the command was already executed inside the function +int profile_check_line(char *ptr, int lineno) { + // seccomp, caps, private, user namespace + if (strcmp(ptr, "noroot") == 0) { + check_user_namespace(); + return 0; + } + else if (strcmp(ptr, "seccomp") == 0) { + arg_seccomp = 1; + return 0; + } + else if (strcmp(ptr, "caps") == 0) { + arg_caps_default_filter = 1; + return 0; + } + else if (strcmp(ptr, "caps.drop all") == 0) { + arg_caps_drop_all = 1; + return 0; + } + else if (strcmp(ptr, "shell none") == 0) { + arg_shell_none = 1; + return 0; + } + else if (strcmp(ptr, "private") == 0) { + arg_private = 1; + return 0; + } + else if (strcmp(ptr, "private-dev") == 0) { + arg_private_dev = 1; + return 0; + } + else if (strcmp(ptr, "nogroups") == 0) { + arg_nogroups = 1; + return 0; + } + else if (strcmp(ptr, "netfilter") == 0) { + arg_netfilter = 1; + return 0; + } + else if (strncmp(ptr, "netfilter ", 10) == 0) { + arg_netfilter = 1; + arg_netfilter_file = strdup(ptr + 10); + if (!arg_netfilter_file) + errExit("strdup"); + check_netfilter_file(arg_netfilter_file); + return 0; + } + + // seccomp drop list on top of default list + if (strncmp(ptr, "seccomp ", 8) == 0) { + arg_seccomp = 1; +#ifdef HAVE_SECCOMP + arg_seccomp_list = strdup(ptr + 8); + if (!arg_seccomp_list) + errExit("strdup"); +#endif + return 0; + } + + // seccomp drop list without default list + if (strncmp(ptr, "seccomp.drop ", 13) == 0) { + arg_seccomp = 1; +#ifdef HAVE_SECCOMP + arg_seccomp_list_drop = strdup(ptr + 13); + if (!arg_seccomp_list_drop) + errExit("strdup"); +#endif + return 0; + } + + // seccomp keep list + if (strncmp(ptr, "seccomp.keep ", 13) == 0) { + arg_seccomp = 1; +#ifdef HAVE_SECCOMP + arg_seccomp_list_keep= strdup(ptr + 13); + if (!arg_seccomp_list_keep) + errExit("strdup"); +#endif + return 0; + } + + // caps drop list + if (strncmp(ptr, "caps.drop ", 10) == 0) { + arg_caps_drop = 1; + arg_caps_list = strdup(ptr + 10); + if (!arg_caps_list) + errExit("strdup"); + // verify seccomp list and exit if problems + if (caps_check_list(arg_caps_list, NULL)) + exit(1); + return 0; + } + + // caps keep list + if (strncmp(ptr, "caps.keep ", 10) == 0) { + arg_caps_keep = 1; + arg_caps_list = strdup(ptr + 10); + if (!arg_caps_list) + errExit("strdup"); + // verify seccomp list and exit if problems + if (caps_check_list(arg_caps_list, NULL)) + exit(1); + return 0; + } + + // dns + if (strncmp(ptr, "dns ", 4) == 0) { + uint32_t dns; + if (atoip(ptr + 4, &dns)) { + fprintf(stderr, "Error: invalid DNS server IP address\n"); + return 1; + } + + if (cfg.dns1 == 0) + cfg.dns1 = dns; + else if (cfg.dns2 == 0) + cfg.dns2 = dns; + else if (cfg.dns3 == 0) + cfg.dns3 = dns; + else { + fprintf(stderr, "Error: up to 3 DNS servers can be specified\n"); + return 1; + } + return 0; + } + + // cpu affinity + if (strncmp(ptr, "cpu ", 4) == 0) { + read_cpu_list(ptr + 4); + return 0; + } + + // cgroup + if (strncmp(ptr, "cgroup ", 7) == 0) { + set_cgroup(ptr + 7); + return 0; + } + + // private directory + if (strncmp(ptr, "private ", 8) == 0) { + cfg.home_private = ptr + 8; + fs_check_private_dir(); + arg_private = 1; + return 0; + } + + // private list of files and directories + if (strncmp(ptr, "private.keep ", 13) == 0) { + cfg.home_private_keep = ptr + 13; + fs_check_home_list(); + arg_private = 1; + return 0; + } + + // filesystem bind + if (strncmp(ptr, "bind ", 5) == 0) { + if (getuid() != 0) { + fprintf(stderr, "Error: --bind option is available only if running as root\n"); + exit(1); + } + + // extract two directories + char *dname1 = ptr + 5; + char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories + if (dname2 == NULL) { + fprintf(stderr, "Error: mising second directory for bind\n"); + exit(1); + } + + // check directories + check_file_name(dname1, lineno); + check_file_name(dname2, lineno); + if (strstr(dname1, "..") || strstr(dname2, "..")) { + fprintf(stderr, "Error: invalid file name.\n"); + exit(1); + } + + // insert comma back + *(dname2 - 1) = ','; + return 1; + } + + // rlimit + if (strncmp(ptr, "rlimit", 6) == 0) { + if (strncmp(ptr, "rlimit-nofile ", 14) == 0) { + ptr += 14; + if (not_unsigned(ptr)) { + fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); + exit(1); + } + sscanf(ptr, "%u", &cfg.rlimit_nofile); + arg_rlimit_nofile = 1; + } + else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) { + ptr += 13; + if (not_unsigned(ptr)) { + fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); + exit(1); + } + sscanf(ptr, "%u", &cfg.rlimit_nproc); + arg_rlimit_nproc = 1; + } + else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) { + ptr += 13; + if (not_unsigned(ptr)) { + fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); + exit(1); + } + sscanf(ptr, "%u", &cfg.rlimit_fsize); + arg_rlimit_fsize = 1; + } + else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) { + ptr += 18; + if (not_unsigned(ptr)) { + fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); + exit(1); + } + sscanf(ptr, "%u", &cfg.rlimit_sigpending); + arg_rlimit_sigpending = 1; + } + else { + fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); + exit(1); + } + + return 0; + } + + // rest of filesystem + if (strncmp(ptr, "blacklist ", 10) == 0) + ptr += 10; + else if (strncmp(ptr, "read-only ", 10) == 0) + ptr += 10; + else if (strncmp(ptr, "tmpfs ", 6) == 0) + ptr += 6; + else { + if (lineno == 0) + fprintf(stderr, "Error: \"%s\" as a command line option is invalid\n", ptr); + else + fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno); + exit(1); + } + + // some characters just don't belong in filenames + check_file_name(ptr, lineno); + if (strstr(ptr, "..")) { + if (lineno == 0) + fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); + else + fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno); + exit(1); + } + return 1; +} + +// add a profile entry in cfg.profile list; use str to populate the list +void profile_add(char *str) { + ProfileEntry *prf = malloc(sizeof(ProfileEntry)); + if (!prf) + errExit("malloc"); + prf->next = NULL; + prf->data = str; + + // add prf to the list + if (cfg.profile == NULL) { + cfg.profile = prf; + return; + } + ProfileEntry *ptr = cfg.profile; + while (ptr->next != NULL) + ptr = ptr->next; + ptr->next = prf; +} + +// read a profile file +static int include_level = 0; +// skip1, skip2 - if the string is found in the line, the line is not interpreted +void profile_read(const char *fname, const char *skip1, const char *skip2) { + // exit program if maximum include level was reached + if (include_level > MAX_INCLUDE_LEVEL) { + fprintf(stderr, "Error: maximum profile include level was reached\n"); + exit(1); + } + + if (strlen(fname) == 0) { + fprintf(stderr, "Error: invalid profile file\n"); + exit(1); + } + + // open profile file: + FILE *fp = fopen(fname, "r"); + if (fp == NULL) { + fprintf(stderr, "Error: cannot open profile file\n"); + exit(1); + } + + fprintf(stderr, "Reading profile %s\n", fname); + + // read the file line by line + char buf[MAX_READ + 1]; + int lineno = 0; + while (fgets(buf, MAX_READ, fp)) { + ++lineno; + // remove empty space - ptr in allocated memory + char *ptr = line_remove_spaces(buf); + if (ptr == NULL) + continue; + + // comments + if (*ptr == '#' || *ptr == '\0') { + free(ptr); + continue; + } + + // process include + if (strncmp(ptr, "include ", 8) == 0) { + include_level++; + + // extract profile filename and new skip params + char *newprofile = ptr + 8; // profile name + char *newskip1 = NULL; // new skip1 + char *newskip2 = NULL; // new skip2 + char *p = newprofile; + while (*p != '\0') { + if (*p == ' ') { + *p = '\0'; + if (newskip1 == NULL) + newskip1 = p + 1; + else if (newskip2 == NULL) + newskip2 = p + 1; + } + p++; + } + + // recursivity + profile_read(newprofile, newskip1, newskip2); + include_level--; + free(ptr); + continue; + } + + // skip + if (skip1) { + if (strstr(ptr, skip1)) { + free(ptr); + continue; + } + } + if (skip2) { + if (strstr(ptr, skip2)) { + free(ptr); + continue; + } + } + + // verify syntax, exit in case of error + if (profile_check_line(ptr, lineno)) + profile_add(ptr); + } + fclose(fp); +} diff --git a/src/firejail/restricted_shell.c b/src/firejail/restricted_shell.c new file mode 100644 index 000000000..ba3aae759 --- /dev/null +++ b/src/firejail/restricted_shell.c @@ -0,0 +1,96 @@ +/* + * 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 "firejail.h" + +#define MAX_READ 4096 // maximum line length +char *restricted_user = NULL; + + +int restricted_shell(const char *user) { + assert(user); + + // open profile file: + FILE *fp = fopen("/etc/firejail/login.users", "r"); + if (fp == NULL) + return 0; + + int lineno = 0; + char buf[MAX_READ]; + while (fgets(buf, MAX_READ, fp)) { + lineno++; + + // remove empty spaces at the beginning of the line + char *ptr = buf; + while (*ptr == ' ' || *ptr == '\t') { + ptr++; + } + if (*ptr == '\n' || *ptr == '#') + continue; + + // parse line + char *usr = ptr; + char *args = strchr(usr, ':'); + if (args == NULL) { + fprintf(stderr, "Error: users.conf line %d\n", lineno); + exit(1); + } + *args = '\0'; + args++; + ptr = strchr(args, '\n'); + if (ptr) + *ptr = '\0'; + + if (strcmp(user, usr) == 0) { + restricted_user = strdup(user); + // extract program arguments + + fullargv[0] = "firejail"; + int i; + ptr = args; + for (i = 1; i < MAX_ARGS; i++) { + fullargv[i] = ptr; + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + if (*ptr != '\0') { + *ptr ='\0'; + fullargv[i] = strdup(fullargv[i]); + if (fullargv[i] == NULL) { + fprintf(stderr, "Error: cannot allocate memory\n"); + exit(1); + } + ptr++; + while (*ptr == ' ' || *ptr == '\t') + ptr++; + if (*ptr != '\0') + continue; + } + fullargv[i] = strdup(fullargv[i]); + fclose(fp); + return i + 1; + } + fprintf(stderr, "Error: too many program arguments in users.conf line %d\n", lineno); + exit(1); + } + } + fclose(fp); + + return 0; +} + diff --git a/src/firejail/rlimit.c b/src/firejail/rlimit.c new file mode 100644 index 000000000..6c755a08d --- /dev/null +++ b/src/firejail/rlimit.c @@ -0,0 +1,62 @@ +/* + * 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 "firejail.h" +#include +#include + +void set_rlimits(void) { + // resource limits + struct rlimit rl; + if (arg_rlimit_nofile) { + rl.rlim_cur = (rlim_t) cfg.rlimit_nofile; + rl.rlim_max = (rlim_t) cfg.rlimit_nofile; + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) + errExit("setrlimit"); + if (arg_debug) + printf("Config rlimit: number of open file descriptors %u\n", cfg.rlimit_nofile); + } + + if (arg_rlimit_nproc) { + rl.rlim_cur = (rlim_t) cfg.rlimit_nproc; + rl.rlim_max = (rlim_t) cfg.rlimit_nproc; + if (setrlimit(RLIMIT_NPROC, &rl) == -1) + errExit("setrlimit"); + if (arg_debug) + printf("Config rlimit: number of processes %u\n", cfg.rlimit_nproc); + } + + if (arg_rlimit_fsize) { + rl.rlim_cur = (rlim_t) cfg.rlimit_fsize; + rl.rlim_max = (rlim_t) cfg.rlimit_fsize; + if (setrlimit(RLIMIT_FSIZE, &rl) == -1) + errExit("setrlimit"); + if (arg_debug) + printf("Config rlimit: maximum file size %u\n", cfg.rlimit_fsize); + } + + if (arg_rlimit_sigpending) { + rl.rlim_cur = (rlim_t) cfg.rlimit_sigpending; + rl.rlim_max = (rlim_t) cfg.rlimit_sigpending; + if (setrlimit(RLIMIT_SIGPENDING, &rl) == -1) + errExit("setrlimit"); + if (arg_debug) + printf("Config rlimit: maximum number of signals pending %u\n", cfg.rlimit_sigpending); + } +} diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c new file mode 100644 index 000000000..3f5fb51fa --- /dev/null +++ b/src/firejail/sandbox.c @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2014, 2015 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 "firejail.h" +#include +#include +#include +#include +#include +#include + +#include +#ifndef CLONE_NEWUSER +#define CLONE_NEWUSER 0x10000000 +#endif + +static void set_caps(void) { + if (arg_caps_drop_all) + caps_drop_all(); + else if (arg_caps_drop) + caps_drop_list(arg_caps_list); + else if (arg_caps_keep) + caps_keep_list(arg_caps_list); + else if (arg_caps_default_filter) + caps_default_filter(); +} + +void save_nogroups(void) { + if (arg_nogroups == 0) + return; + + char *fname; + if (asprintf(&fname, "%s/groups", MNT_DIR) == -1) + errExit("asprintf"); + FILE *fp = fopen(fname, "w"); + if (fp) { + fprintf(fp, "\n"); + fclose(fp); + if (chown(fname, 0, 0) < 0) + errExit("chown"); + } + else { + fprintf(stderr, "Error: cannot save nogroups state\n"); + free(fname); + exit(1); + } + + free(fname); +} + +static void sandbox_if_up(Bridge *br) { + assert(br); + if (!br->configured) + return; + + char *dev = br->devsandbox; + net_if_up(dev); + + if (br->arg_ip_none == 1); // do nothing + else if (br->arg_ip_none == 0 && br->macvlan == 0) { + if (br->ipsandbox == br->ip) { + fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); + exit(1); + } + + // just assign the address + assert(br->ipsandbox); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); + net_if_ip(dev, br->ipsandbox, br->mask); + net_if_up(dev); + } + else if (br->arg_ip_none == 0 && br->macvlan == 1) { + // reassign the macvlan address + if (br->ipsandbox == 0) + // ip address assigned by arp-scan for a macvlan device + br->ipsandbox = arp_assign(dev, br); //br->ip, br->mask); + else { + if (br->ipsandbox == br->ip) { + fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); + exit(1); + } + + uint32_t rv = arp_check(dev, br->ipsandbox, br->ip); + if (rv) { + fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use.\n", PRINT_IP(br->ipsandbox)); + exit(1); + } + } + + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); + net_if_ip(dev, br->ipsandbox, br->mask); + net_if_up(dev); + } +} + +static void chk_chroot(void) { + // if we are starting firejail inside some other container technology, we don't care about this + char *mycont = getenv("container"); + if (mycont) + return; + + // check if this is a regular chroot + struct stat s; + if (stat("/", &s) == 0) { + if (s.st_ino != 2) + return; + } + + fprintf(stderr, "Error: cannot mount filesystem as slave\n"); + exit(1); +} + +int sandbox(void* sandbox_arg) { + pid_t child_pid = getpid(); + if (arg_debug) + printf("Initializing child process\n"); + + // close each end of the unused pipes + close(parent_to_child_fds[1]); + close(child_to_parent_fds[0]); + + // wait for parent to do base setup + wait_for_other(parent_to_child_fds[0]); + + if (arg_debug && child_pid == 1) + printf("PID namespace installed\n"); + + //**************************** + // set hostname + //**************************** + if (cfg.hostname) { + if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0) + errExit("sethostname"); + } + + //**************************** + // mount namespace + //**************************** + // mount events are not forwarded between the host the sandbox + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { + chk_chroot(); + } + + //**************************** + // netfilter + //**************************** + if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter + netfilter(arg_netfilter_file); + } + + //**************************** + // trace pre-install + //**************************** + if (arg_trace) + fs_trace_preload(); + + //**************************** + // configure filesystem + //**************************** +#ifdef HAVE_CHROOT + if (cfg.chrootdir) { + fs_chroot(cfg.chrootdir); + // force caps and seccomp if not started as root + if (getuid() != 0) { + // force default seccomp inside the chroot, no keep or drop list + // the list build on top of the default drop list is kept intact + arg_seccomp = 1; + if (arg_seccomp_list_drop) { + free(arg_seccomp_list_drop); + arg_seccomp_list_drop = NULL; + } + if (arg_seccomp_list_keep) { + free(arg_seccomp_list_keep); + arg_seccomp_list_keep = NULL; + } + + // disable all capabilities + if (arg_caps_default_filter || arg_caps_list) + fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n"); + arg_caps_drop_all = 1; + + // drop all supplementary groups; /etc/group file inside chroot + // is controlled by a regular usr + arg_nogroups = 1; + printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); + } + + //**************************** + // trace pre-install, this time inside chroot + //**************************** + if (arg_trace) + fs_trace_preload(); + } + else +#endif + if (arg_overlay) + fs_overlayfs(); + else + fs_basic_fs(); + + + //**************************** + // set hostname in /etc/hostname + //**************************** + if (cfg.hostname) { + fs_hostname(cfg.hostname); + } + + //**************************** + // apply the profile file + //**************************** + if (cfg.profile) + fs_blacklist(cfg.homedir); + + //**************************** + // private mode + //**************************** + if (arg_private) { + if (cfg.home_private) // --private= + fs_private_homedir(); + else if (cfg.home_private_keep) // --private.keep= + fs_private_home_list(); + else // --private + fs_private(); + } + + if (arg_private_dev) + fs_private_dev(); + + //**************************** + // install trace + //**************************** + if (arg_trace) + fs_trace(); + + //**************************** + // update /proc, /dev, /boot directorymy + //**************************** + fs_proc_sys_dev_boot(); + + //**************************** + // networking + //**************************** + if (arg_nonetwork) { + net_if_up("lo"); + } + else if (any_bridge_configured()) { + // configure lo and eth0...eth3 + net_if_up("lo"); + + if (mac_not_zero(cfg.bridge0.macsandbox)) + net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); + sandbox_if_up(&cfg.bridge0); + + if (mac_not_zero(cfg.bridge1.macsandbox)) + net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); + sandbox_if_up(&cfg.bridge1); + + if (mac_not_zero(cfg.bridge2.macsandbox)) + net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); + sandbox_if_up(&cfg.bridge2); + + if (mac_not_zero(cfg.bridge3.macsandbox)) + net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); + sandbox_if_up(&cfg.bridge3); + + // add a default route + if (cfg.defaultgw) { + // set the default route + if (net_add_route(0, 0, cfg.defaultgw)) + fprintf(stderr, "Warning: cannot configure default route\n"); + } + + if (arg_debug) + printf("Network namespace enabled\n"); + } + + // if any dns server is configured, it is time to set it now + fs_resolvconf(); + + // print network configuration + if (any_bridge_configured() || cfg.defaultgw || cfg.dns1) { + printf("\n"); + if (any_bridge_configured()) + net_ifprint(); + if (cfg.defaultgw != 0) + printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); + if (cfg.dns1 != 0) + printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); + if (cfg.dns2 != 0) + printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); + if (cfg.dns3 != 0) + printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); + printf("\n"); + } + + + + //**************************** + // start executable + //**************************** + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died + 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"); + } + } + } + + // set environment + // 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"); + if (arg_zsh && setenv("SHELL", "/usr/bin/zsh", 1) < 0) + errExit("setenv"); + if (arg_csh && setenv("SHELL", "/bin/csh", 1) < 0) + errExit("setenv"); + if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0) + errExit("setenv"); + // 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"); + + + // set capabilities + if (!arg_noroot) + set_caps(); + + // set rlimits + set_rlimits(); + + // set seccomp +#ifdef HAVE_SECCOMP + // if a keep list is available, disregard the drop list + if (arg_seccomp == 1) { + if (arg_seccomp_list_keep) + seccomp_filter_keep(); // this will also save the fmyilter to MNT_DIR/seccomp file + else + seccomp_filter_drop(); // this will also save the filter to MNT_DIR/seccomp file + } +#endif + + // set cpu affinity + if (cfg.cpus) { + save_cpu(); // save cpu affinity mask to MNT_DIR/cpu file + set_cpu_affinity(); + } + + // save cgroup in MNT_DIR/cgroup file + if (cfg.cgroup) + save_cgroup(); + + //**************************************** + // drop privileges or create a new user namespace + //**************************************** + save_nogroups(); + if (arg_noroot) { + int rv = unshare(CLONE_NEWUSER); + if (rv == -1) { + fprintf(stderr, "Warning: cannot mount a new user namespace\n"); + perror("unshare"); + drop_privs(arg_nogroups); + } + } + else + drop_privs(arg_nogroups); + + // notify parent that new user namespace has been created so a proper + // UID/GID map can be setup + notify_other(child_to_parent_fds[1]); + close(child_to_parent_fds[1]); + + // wait for parent to finish setting up a proper UID/GID map + wait_for_other(parent_to_child_fds[0]); + close(parent_to_child_fds[0]); + + // somehow, the new user namespace resets capabilities; + // we need to do them again + if (arg_noroot) { + set_caps(); + if (arg_debug) + printf("User namespace (noroot) installed\n"); + } + + + //**************************************** + // start the program without using a shell + //**************************************** + if (arg_shell_none) { + if (arg_debug) { + int i; + for (i = cfg.original_program_index; i < cfg.original_argc; i++) { + if (cfg.original_argv[i] == NULL) + break; + printf("execvp argument %d: %s\n", i - cfg.original_program_index, cfg.original_argv[i]); + } + } + + if (!arg_command) + printf("Child process initialized\n"); + execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index + 1]); + } + //**************************************** + // start the program using a shell + //**************************************** + else { + // choose the shell requested by the user, or use bash as default + char *sh; + if (cfg.shell) + sh = cfg.shell; + else if (arg_zsh) + sh = "/usr/bin/zsh"; + else if (arg_csh) + sh = "/bin/csh"; + else + sh = "/bin/bash"; + + char *arg[5]; + int index = 0; + arg[index++] = sh; + arg[index++] = "-c"; + assert(cfg.command_line); + if (arg_debug) + printf("Starting %s\n", cfg.command_line); + if (arg_doubledash) + arg[index++] = "--"; + arg[index++] = cfg.command_line; + arg[index] = NULL; + assert(index < 5); + + if (arg_debug) { + char *msg; + if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1) + errExit("asprintf"); + logmsg(msg); + free(msg); + } + + if (arg_debug) { + int i; + for (i = 0; i < 5; i++) { + if (arg[i] == NULL) + break; + printf("execvp argument %d: %s\n", i, arg[i]); + } + } + + if (!arg_command) + printf("Child process initialized\n"); + execvp(sh, arg); + } + + + perror("execvp"); + return 0; +} diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c new file mode 100644 index 000000000..c03eb6848 --- /dev/null +++ b/src/firejail/seccomp.c @@ -0,0 +1,658 @@ +/* + * 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. +*/ + +/* default seccomp filter + // seccomp + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL, + BLACKLIST(SYS_mount), // mount/unmount filesystems + BLACKLIST(SYS_umount2), + BLACKLIST(SYS_ptrace), // trace processes + BLACKLIST(SYS_kexec_load), // loading a different kernel + BLACKLIST(SYS_open_by_handle_at), // open by handle + BLACKLIST(SYS_init_module), // kernel module handling +#ifdef SYS_finit_module // introduced in 2013 + BLACKLIST(SYS_finit_module), +#endif + BLACKLIST(SYS_delete_module), + BLACKLIST(SYS_iopl), // io permisions +#ifdef SYS_ioperm + BLACKLIST(SYS_ioperm), +#endif +SYS_iopl + BLACKLIST(SYS_iopl), // io permisions +#endif +#ifdef SYS_ni_syscall), // new io permisions call on arm devices + BLACKLIST(SYS_ni_syscall), +#endif + BLACKLIST(SYS_swapon), // swap on/off + BLACKLIST(SYS_swapoff), + BLACKLIST(SYS_syslog), // kernel printk control + RETURN_ALLOW + }; +*/ +#ifdef HAVE_SECCOMP +#include "firejail.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#ifndef PR_SET_NO_NEW_PRIVS +# define PR_SET_NO_NEW_PRIVS 38 +#endif + +#if HAVE_SECCOMP_H +#include +#else +#define SECCOMP_MODE_FILTER 2 +#define SECCOMP_RET_KILL 0x00000000U +#define SECCOMP_RET_TRAP 0x00030000U +#define SECCOMP_RET_ALLOW 0x7fff0000U +#define SECCOMP_RET_ERRNO 0x00050000U +#define SECCOMP_RET_DATA 0x0000ffffU +struct seccomp_data { + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; +}; +#endif + +#if defined(__i386__) +# define ARCH_NR AUDIT_ARCH_I386 +#elif defined(__x86_64__) +# define ARCH_NR AUDIT_ARCH_X86_64 +#elif defined(__arm__) +# define ARCH_NR AUDIT_ARCH_ARM +#else +# warning "Platform does not support seccomp filter yet" +# define ARCH_NR 0 +#endif + + +#define VALIDATE_ARCHITECTURE \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) + +#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + (offsetof(struct seccomp_data, nr))) + +#define BLACKLIST(syscall_nr) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) + +#define WHITELIST(syscall_nr) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) + +#define RETURN_ALLOW \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) + +#define KILL_PROCESS \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) + +#define SECSIZE 128 // initial filter size +static struct sock_filter *sfilter = NULL; +static int sfilter_alloc_size = 0; +static int sfilter_index = 0; + +// debug filter +void filter_debug(void) { + // start filter + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL + }; + + // print sizes + printf("SECCOMP Filter:\n"); + if (sfilter == NULL) { + printf("SECCOMP filter not allocated\n"); + return; + } + if (sfilter_index < 4) + return; + + // test the start of the filter + if (memcmp(sfilter, filter, sizeof(filter)) == 0) { + printf(" VALIDATE_ARCHITECTURE\n"); + printf(" EXAMINE_SYSCAL\n"); + } + + // loop trough blacklists + int i = 4; + while (i < sfilter_index) { + // minimal parsing! + unsigned char *ptr = (unsigned char *) &sfilter[i]; + int *nr = (int *) (ptr + 4); + if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { + printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); + i += 2; + } + else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { + printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); + i += 2; + } + else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { + printf(" KILL_PROCESS\n"); + i++; + } + else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { + printf(" RETURN_ALLOW\n"); + i++; + } + else { + printf(" UNKNOWN ENTRY!!!\n"); + i++; + } + } +} + +// initialize filter +static void filter_init(void) { + if (sfilter) { + assert(0); + return; + } + + if (arg_debug) + printf("Initialize seccomp filter\n"); + // allocate a filter of SECSIZE + sfilter = malloc(sizeof(struct sock_filter) * SECSIZE); + if (!sfilter) + errExit("malloc"); + memset(sfilter, 0, sizeof(struct sock_filter) * SECSIZE); + sfilter_alloc_size = SECSIZE; + + // copy the start entries + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL + }; + sfilter_index = sizeof(filter) / sizeof(struct sock_filter); + memcpy(sfilter, filter, sizeof(filter)); +} + +static void filter_realloc(void) { + assert(sfilter); + assert(sfilter_alloc_size); + assert(sfilter_index); + if (arg_debug) + printf("Allocating more seccomp filter entries\n"); + + // allocate the new memory + struct sock_filter *old = sfilter; + sfilter = malloc(sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); + if (!sfilter) + errExit("malloc"); + memset(sfilter, 0, sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); + + // copy old filter + memcpy(sfilter, old, sizeof(struct sock_filter) * sfilter_alloc_size); + sfilter_alloc_size += SECSIZE; +} + +static void filter_add_whitelist(int syscall) { + assert(sfilter); + assert(sfilter_alloc_size); + assert(sfilter_index); + if (arg_debug) + printf("Whitelisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); + + if ((sfilter_index + 2) > sfilter_alloc_size) + filter_realloc(); + + struct sock_filter filter[] = { + WHITELIST(syscall) + }; +#if 0 +{ + int i; + unsigned char *ptr = (unsigned char *) &filter[0]; + for (i = 0; i < sizeof(filter); i++, ptr++) + printf("%x, ", (*ptr) & 0xff); + printf("\n"); +} +#endif + memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); + sfilter_index += sizeof(filter) / sizeof(struct sock_filter); +} + +static void filter_add_blacklist(int syscall) { + assert(sfilter); + assert(sfilter_alloc_size); + assert(sfilter_index); + if (arg_debug) + printf("Blacklisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); + + if ((sfilter_index + 2) > sfilter_alloc_size) + filter_realloc(); + + struct sock_filter filter[] = { + BLACKLIST(syscall) + }; +#if 0 +{ + int i; + unsigned char *ptr = (unsigned char *) &filter[0]; + for (i = 0; i < sizeof(filter); i++, ptr++) + printf("%x, ", (*ptr) & 0xff); + printf("\n"); +} +#endif + memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); + sfilter_index += sizeof(filter) / sizeof(struct sock_filter); +} + +static void filter_end_blacklist(void) { + assert(sfilter); + assert(sfilter_alloc_size); + assert(sfilter_index); + if (arg_debug) + printf("Ending syscall filter\n"); + + if ((sfilter_index + 2) > sfilter_alloc_size) + filter_realloc(); + + struct sock_filter filter[] = { + RETURN_ALLOW + }; +#if 0 +{ + int i; + unsigned char *ptr = (unsigned char *) &filter[0]; + for (i = 0; i < sizeof(filter); i++, ptr++) + printf("%x, ", (*ptr) & 0xff); + printf("\n"); +} +#endif + memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); + sfilter_index += sizeof(filter) / sizeof(struct sock_filter); +} + +static void filter_end_whitelist(void) { + assert(sfilter); + assert(sfilter_alloc_size); + assert(sfilter_index); + if (arg_debug) + printf("Ending syscall filter\n"); + + if ((sfilter_index + 2) > sfilter_alloc_size) + filter_realloc(); + + struct sock_filter filter[] = { + KILL_PROCESS + }; +#if 0 +{ + int i; + unsigned char *ptr = (unsigned char *) &filter[0]; + for (i = 0; i < sizeof(filter); i++, ptr++) + printf("%x, ", (*ptr) & 0xff); + printf("\n"); +} +#endif + memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); + sfilter_index += sizeof(filter) / sizeof(struct sock_filter); +} + + +// save seccomp filter in /tmp/firejail/mnt/seccomp +static void write_seccomp_file(void) { + fs_build_mnt_dir(); + assert(sfilter); + + char *fname; + if (asprintf(&fname, "%s/seccomp", MNT_DIR) == -1) + errExit("asprintf"); + int fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + if (fd == -1) + errExit("open"); + + if (arg_debug) + printf("Save seccomp filter, size %lu bytes\n", sfilter_index * sizeof(struct sock_filter)); + errno = 0; + ssize_t sz = write(fd, sfilter, sfilter_index * sizeof(struct sock_filter)); + if (sz != (sfilter_index * sizeof(struct sock_filter))) { + fprintf(stderr, "Error: cannot save seccomp filter\n"); + exit(1); + } + close(fd); + if (chown(fname, 0, 0) < 0) + errExit("chown"); + free(fname); +} + +// read seccomp filter from /tmp/firejail/mnt/seccomp +static void read_seccomp_file(char *file_name) { + assert(sfilter == NULL && sfilter_index == 0); + + char *fname; + if (file_name) + fname = file_name; + else { + if (asprintf(&fname, "%s/seccomp", MNT_DIR) == -1) + errExit("asprintf"); + } + + // check file + struct stat s; + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: seccomp file not found\n"); + exit(1); + } + ssize_t sz = s.st_size; + if (sz == 0 || (sz % sizeof(struct sock_filter)) != 0) { + fprintf(stderr, "Error: invalid seccomp file\n"); + exit(1); + } + sfilter = malloc(sz); + if (!sfilter) + errExit("malloc"); + + // read file + /* coverity[toctou] */ + int fd = open(fname,O_RDONLY); + if (fd == -1) + errExit("open"); + errno = 0; + ssize_t size = read(fd, sfilter, sz); + if (size != sz) { + fprintf(stderr, "Error: invalid seccomp file\n"); + exit(1); + } + sfilter_index = sz / sizeof(struct sock_filter); + + if (arg_debug) + printf("Read seccomp filter, size %lu bytes\n", sfilter_index * sizeof(struct sock_filter)); + + close(fd); + free(fname); + + if (arg_debug) + filter_debug(); +} + + +// drop filter for seccomp option +int seccomp_filter_drop(void) { + filter_init(); + + // default seccomp + if (arg_seccomp_list_drop == NULL) { +#ifdef SYS_mount + filter_add_blacklist(SYS_mount); +#endif +#ifdef SYS_umount2 + filter_add_blacklist(SYS_umount2); +#endif +#ifdef SYS_ptrace + filter_add_blacklist(SYS_ptrace); +#endif +#ifdef SYS_kexec_load + filter_add_blacklist(SYS_kexec_load); +#endif +#ifdef SYS_open_by_handle_at + filter_add_blacklist(SYS_open_by_handle_at); +#endif +#ifdef SYS_init_module + filter_add_blacklist(SYS_init_module); +#endif +#ifdef SYS_finit_module // introduced in 2013 + filter_add_blacklist(SYS_finit_module); +#endif +#ifdef SYS_delete_module + filter_add_blacklist(SYS_delete_module); +#endif +#ifdef SYS_iopl + filter_add_blacklist(SYS_iopl); +#endif +#ifdef SYS_ioperm + filter_add_blacklist(SYS_ioperm); +#endif +#ifdef SYS_ni_syscall // new io permisions call on arm devices + filter_add_blacklist(SYS_ni_syscall); +#endif +#ifdef SYS_swapon + filter_add_blacklist(SYS_swapon); +#endif +#ifdef SYS_swapoff + filter_add_blacklist(SYS_swapoff); +#endif +#ifdef SYS_syslog + filter_add_blacklist(SYS_syslog); +#endif +#ifdef SYS_process_vm_readv + filter_add_blacklist(SYS_process_vm_readv); +#endif +#ifdef SYS_process_vm_writev + filter_add_blacklist(SYS_process_vm_writev); +#endif +#ifdef SYS_mknod + filter_add_blacklist(SYS_mknod); +#endif + + // new syscalls in 0.9,23 +#ifdef SYS_sysfs + filter_add_blacklist(SYS_sysfs); +#endif +#ifdef SYS__sysctl + filter_add_blacklist(SYS__sysctl); +#endif +#ifdef SYS_adjtimex + filter_add_blacklist(SYS_adjtimex); +#endif +#ifdef SYS_clock_adjtime + filter_add_blacklist(SYS_clock_adjtime); +#endif +#ifdef SYS_lookup_dcookie + filter_add_blacklist(SYS_lookup_dcookie); +#endif +#ifdef SYS_perf_event_open + filter_add_blacklist(SYS_perf_event_open); +#endif +#ifdef SYS_fanotify_init + filter_add_blacklist(SYS_fanotify_init); +#endif +#ifdef SYS_kcmp + filter_add_blacklist(SYS_kcmp); +#endif + } + + // default seccomp filter with additional drop list + if (arg_seccomp_list && arg_seccomp_list_drop == NULL) { + if (syscall_check_list(arg_seccomp_list, filter_add_blacklist)) { + fprintf(stderr, "Error: cannot load seccomp filter\n"); + exit(1); + } + } + // drop list + else if (arg_seccomp_list == NULL && arg_seccomp_list_drop) { + if (syscall_check_list(arg_seccomp_list_drop, filter_add_blacklist)) { + fprintf(stderr, "Error: cannot load seccomp filter\n"); + exit(1); + } + } + + + filter_end_blacklist(); + if (arg_debug) + filter_debug(); + + // save seccomp filter in /tmp/firejail/mnt/seccomp + // in order to use it in --join operations + write_seccomp_file(); + + + struct sock_fprog prog = { + .len = sfilter_index, + .filter = sfilter, + }; + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); + return 1; + } + else if (arg_debug) { + printf("seccomp enabled\n"); + } + + return 0; +} + +// keep filter for seccomp option +int seccomp_filter_keep(void) { + filter_init(); + + // these 4 syscalls are used by firejail after the seccomp filter is initialized + filter_add_whitelist(SYS_setuid); + filter_add_whitelist(SYS_setgid); + filter_add_whitelist(SYS_setgroups); + filter_add_whitelist(SYS_dup); + + // apply keep list + if (arg_seccomp_list_keep) { + if (syscall_check_list(arg_seccomp_list_keep, filter_add_whitelist)) { + fprintf(stderr, "Error: cannot load seccomp filter\n"); + exit(1); + } + } + + filter_end_whitelist(); + if (arg_debug) + filter_debug(); + + // save seccomp filter in /tmp/firejail/mnt/seccomp + // in order to use it in --join operations + write_seccomp_file(); + + + struct sock_fprog prog = { + .len = sfilter_index, + .filter = sfilter, + }; + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); + return 1; + } + else if (arg_debug) { + printf("seccomp enabled\n"); + } + + return 0; +} + + + +void seccomp_set(void) { + // read seccomp filter from /tmp/firejail/mnt/seccomp + read_seccomp_file(NULL); + + // apply filter + struct sock_fprog prog = { + .len = sfilter_index, + .filter = sfilter, + }; + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); + return; + } + else if (arg_debug) { + printf("seccomp enabled\n"); + } +} + +void seccomp_print_filter_name(const char *name) { + if (!name || strlen(name) == 0) { + fprintf(stderr, "Error: invalid sandbox name\n"); + exit(1); + } + pid_t pid; + if (name2pid(name, &pid)) { + fprintf(stderr, "Error: cannot find sandbox %s\n", name); + exit(1); + } + + seccomp_print_filter(pid); +} + +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); +} + +#endif // HAVE_SECCOMP + diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c new file mode 100644 index 000000000..b666996df --- /dev/null +++ b/src/firejail/shutdown.c @@ -0,0 +1,98 @@ +/* + * 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 "firejail.h" +#include +#include +#include +#include + +void shut_name(const char *name) { + if (!name || strlen(name) == 0) { + fprintf(stderr, "Error: invalid sandbox name\n"); + exit(1); + } + + pid_t pid; + if (name2pid(name, &pid)) { + fprintf(stderr, "Error: cannot find sandbox %s\n", name); + exit(1); + } + + shut(pid); +} + +void shut(pid_t pid) { + pid_t parent = pid; + // if the pid is that of a firejail process, use the pid of a child process inside the sandbox + 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) { + 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) { + 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); + sleep(2); + + // if the process is still running, terminate it using SIGKILL + // try to open stat file + char *file; + if (asprintf(&file, "/proc/%u/status", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) + return; + fclose(fp); + + // kill the process and also the parent + printf("Sending SIGKILL to %u\n", pid); + kill(pid, SIGKILL); + if (parent != pid) { + printf("Sending SIGKILL to %u\n", parent); + kill(parent, SIGKILL); + } +} diff --git a/src/firejail/syscall.c b/src/firejail/syscall.c new file mode 100644 index 000000000..50bff7f5a --- /dev/null +++ b/src/firejail/syscall.c @@ -0,0 +1,4942 @@ +/* + * 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. +*/ + +#ifdef HAVE_SECCOMP +#include "firejail.h" +#include + +typedef struct { + char *name; + int nr; +} SyscallEntry; + +static SyscallEntry syslist[] = { +// +// code generated using tools/extract-syscall +// +#ifndef _SYSCALL_H +#endif +#if !defined __x86_64__ +#ifdef SYS__llseek +#ifdef __NR__llseek + {"_llseek", __NR__llseek}, +#endif +#endif +#ifdef SYS__newselect +#ifdef __NR__newselect + {"_newselect", __NR__newselect}, +#endif +#endif +#ifdef SYS__sysctl +#ifdef __NR__sysctl + {"_sysctl", __NR__sysctl}, +#endif +#endif +#ifdef SYS_access +#ifdef __NR_access + {"access", __NR_access}, +#endif +#endif +#ifdef SYS_acct +#ifdef __NR_acct + {"acct", __NR_acct}, +#endif +#endif +#ifdef SYS_add_key +#ifdef __NR_add_key + {"add_key", __NR_add_key}, +#endif +#endif +#ifdef SYS_adjtimex +#ifdef __NR_adjtimex + {"adjtimex", __NR_adjtimex}, +#endif +#endif +#ifdef SYS_afs_syscall +#ifdef __NR_afs_syscall + {"afs_syscall", __NR_afs_syscall}, +#endif +#endif +#ifdef SYS_alarm +#ifdef __NR_alarm + {"alarm", __NR_alarm}, +#endif +#endif +#ifdef SYS_bdflush +#ifdef __NR_bdflush + {"bdflush", __NR_bdflush}, +#endif +#endif +#ifdef SYS_break +#ifdef __NR_break + {"break", __NR_break}, +#endif +#endif +#ifdef SYS_brk +#ifdef __NR_brk + {"brk", __NR_brk}, +#endif +#endif +#ifdef SYS_capget +#ifdef __NR_capget + {"capget", __NR_capget}, +#endif +#endif +#ifdef SYS_capset +#ifdef __NR_capset + {"capset", __NR_capset}, +#endif +#endif +#ifdef SYS_chdir +#ifdef __NR_chdir + {"chdir", __NR_chdir}, +#endif +#endif +#ifdef SYS_chmod +#ifdef __NR_chmod + {"chmod", __NR_chmod}, +#endif +#endif +#ifdef SYS_chown +#ifdef __NR_chown + {"chown", __NR_chown}, +#endif +#endif +#ifdef SYS_chown32 +#ifdef __NR_chown32 + {"chown32", __NR_chown32}, +#endif +#endif +#ifdef SYS_chroot +#ifdef __NR_chroot + {"chroot", __NR_chroot}, +#endif +#endif +#ifdef SYS_clock_adjtime +#ifdef __NR_clock_adjtime + {"clock_adjtime", __NR_clock_adjtime}, +#endif +#endif +#ifdef SYS_clock_getres +#ifdef __NR_clock_getres + {"clock_getres", __NR_clock_getres}, +#endif +#endif +#ifdef SYS_clock_gettime +#ifdef __NR_clock_gettime + {"clock_gettime", __NR_clock_gettime}, +#endif +#endif +#ifdef SYS_clock_nanosleep +#ifdef __NR_clock_nanosleep + {"clock_nanosleep", __NR_clock_nanosleep}, +#endif +#endif +#ifdef SYS_clock_settime +#ifdef __NR_clock_settime + {"clock_settime", __NR_clock_settime}, +#endif +#endif +#ifdef SYS_clone +#ifdef __NR_clone + {"clone", __NR_clone}, +#endif +#endif +#ifdef SYS_close +#ifdef __NR_close + {"close", __NR_close}, +#endif +#endif +#ifdef SYS_creat +#ifdef __NR_creat + {"creat", __NR_creat}, +#endif +#endif +#ifdef SYS_create_module +#ifdef __NR_create_module + {"create_module", __NR_create_module}, +#endif +#endif +#ifdef SYS_delete_module +#ifdef __NR_delete_module + {"delete_module", __NR_delete_module}, +#endif +#endif +#ifdef SYS_dup +#ifdef __NR_dup + {"dup", __NR_dup}, +#endif +#endif +#ifdef SYS_dup2 +#ifdef __NR_dup2 + {"dup2", __NR_dup2}, +#endif +#endif +#ifdef SYS_dup3 +#ifdef __NR_dup3 + {"dup3", __NR_dup3}, +#endif +#endif +#ifdef SYS_epoll_create +#ifdef __NR_epoll_create + {"epoll_create", __NR_epoll_create}, +#endif +#endif +#ifdef SYS_epoll_create1 +#ifdef __NR_epoll_create1 + {"epoll_create1", __NR_epoll_create1}, +#endif +#endif +#ifdef SYS_epoll_ctl +#ifdef __NR_epoll_ctl + {"epoll_ctl", __NR_epoll_ctl}, +#endif +#endif +#ifdef SYS_epoll_pwait +#ifdef __NR_epoll_pwait + {"epoll_pwait", __NR_epoll_pwait}, +#endif +#endif +#ifdef SYS_epoll_wait +#ifdef __NR_epoll_wait + {"epoll_wait", __NR_epoll_wait}, +#endif +#endif +#ifdef SYS_eventfd +#ifdef __NR_eventfd + {"eventfd", __NR_eventfd}, +#endif +#endif +#ifdef SYS_eventfd2 +#ifdef __NR_eventfd2 + {"eventfd2", __NR_eventfd2}, +#endif +#endif +#ifdef SYS_execve +#ifdef __NR_execve + {"execve", __NR_execve}, +#endif +#endif +#ifdef SYS_exit +#ifdef __NR_exit + {"exit", __NR_exit}, +#endif +#endif +#ifdef SYS_exit_group +#ifdef __NR_exit_group + {"exit_group", __NR_exit_group}, +#endif +#endif +#ifdef SYS_faccessat +#ifdef __NR_faccessat + {"faccessat", __NR_faccessat}, +#endif +#endif +#ifdef SYS_fadvise64 +#ifdef __NR_fadvise64 + {"fadvise64", __NR_fadvise64}, +#endif +#endif +#ifdef SYS_fadvise64_64 +#ifdef __NR_fadvise64_64 + {"fadvise64_64", __NR_fadvise64_64}, +#endif +#endif +#ifdef SYS_fallocate +#ifdef __NR_fallocate + {"fallocate", __NR_fallocate}, +#endif +#endif +#ifdef SYS_fanotify_init +#ifdef __NR_fanotify_init + {"fanotify_init", __NR_fanotify_init}, +#endif +#endif +#ifdef SYS_fanotify_mark +#ifdef __NR_fanotify_mark + {"fanotify_mark", __NR_fanotify_mark}, +#endif +#endif +#ifdef SYS_fchdir +#ifdef __NR_fchdir + {"fchdir", __NR_fchdir}, +#endif +#endif +#ifdef SYS_fchmod +#ifdef __NR_fchmod + {"fchmod", __NR_fchmod}, +#endif +#endif +#ifdef SYS_fchmodat +#ifdef __NR_fchmodat + {"fchmodat", __NR_fchmodat}, +#endif +#endif +#ifdef SYS_fchown +#ifdef __NR_fchown + {"fchown", __NR_fchown}, +#endif +#endif +#ifdef SYS_fchown32 +#ifdef __NR_fchown32 + {"fchown32", __NR_fchown32}, +#endif +#endif +#ifdef SYS_fchownat +#ifdef __NR_fchownat + {"fchownat", __NR_fchownat}, +#endif +#endif +#ifdef SYS_fcntl +#ifdef __NR_fcntl + {"fcntl", __NR_fcntl}, +#endif +#endif +#ifdef SYS_fcntl64 +#ifdef __NR_fcntl64 + {"fcntl64", __NR_fcntl64}, +#endif +#endif +#ifdef SYS_fdatasync +#ifdef __NR_fdatasync + {"fdatasync", __NR_fdatasync}, +#endif +#endif +#ifdef SYS_fgetxattr +#ifdef __NR_fgetxattr + {"fgetxattr", __NR_fgetxattr}, +#endif +#endif +#ifdef SYS_finit_module +#ifdef __NR_finit_module + {"finit_module", __NR_finit_module}, +#endif +#endif +#ifdef SYS_flistxattr +#ifdef __NR_flistxattr + {"flistxattr", __NR_flistxattr}, +#endif +#endif +#ifdef SYS_flock +#ifdef __NR_flock + {"flock", __NR_flock}, +#endif +#endif +#ifdef SYS_fork +#ifdef __NR_fork + {"fork", __NR_fork}, +#endif +#endif +#ifdef SYS_fremovexattr +#ifdef __NR_fremovexattr + {"fremovexattr", __NR_fremovexattr}, +#endif +#endif +#ifdef SYS_fsetxattr +#ifdef __NR_fsetxattr + {"fsetxattr", __NR_fsetxattr}, +#endif +#endif +#ifdef SYS_fstat +#ifdef __NR_fstat + {"fstat", __NR_fstat}, +#endif +#endif +#ifdef SYS_fstat64 +#ifdef __NR_fstat64 + {"fstat64", __NR_fstat64}, +#endif +#endif +#ifdef SYS_fstatat64 +#ifdef __NR_fstatat64 + {"fstatat64", __NR_fstatat64}, +#endif +#endif +#ifdef SYS_fstatfs +#ifdef __NR_fstatfs + {"fstatfs", __NR_fstatfs}, +#endif +#endif +#ifdef SYS_fstatfs64 +#ifdef __NR_fstatfs64 + {"fstatfs64", __NR_fstatfs64}, +#endif +#endif +#ifdef SYS_fsync +#ifdef __NR_fsync + {"fsync", __NR_fsync}, +#endif +#endif +#ifdef SYS_ftime +#ifdef __NR_ftime + {"ftime", __NR_ftime}, +#endif +#endif +#ifdef SYS_ftruncate +#ifdef __NR_ftruncate + {"ftruncate", __NR_ftruncate}, +#endif +#endif +#ifdef SYS_ftruncate64 +#ifdef __NR_ftruncate64 + {"ftruncate64", __NR_ftruncate64}, +#endif +#endif +#ifdef SYS_futex +#ifdef __NR_futex + {"futex", __NR_futex}, +#endif +#endif +#ifdef SYS_futimesat +#ifdef __NR_futimesat + {"futimesat", __NR_futimesat}, +#endif +#endif +#ifdef SYS_get_kernel_syms +#ifdef __NR_get_kernel_syms + {"get_kernel_syms", __NR_get_kernel_syms}, +#endif +#endif +#ifdef SYS_get_mempolicy +#ifdef __NR_get_mempolicy + {"get_mempolicy", __NR_get_mempolicy}, +#endif +#endif +#ifdef SYS_get_robust_list +#ifdef __NR_get_robust_list + {"get_robust_list", __NR_get_robust_list}, +#endif +#endif +#ifdef SYS_get_thread_area +#ifdef __NR_get_thread_area + {"get_thread_area", __NR_get_thread_area}, +#endif +#endif +#ifdef SYS_getcpu +#ifdef __NR_getcpu + {"getcpu", __NR_getcpu}, +#endif +#endif +#ifdef SYS_getcwd +#ifdef __NR_getcwd + {"getcwd", __NR_getcwd}, +#endif +#endif +#ifdef SYS_getdents +#ifdef __NR_getdents + {"getdents", __NR_getdents}, +#endif +#endif +#ifdef SYS_getdents64 +#ifdef __NR_getdents64 + {"getdents64", __NR_getdents64}, +#endif +#endif +#ifdef SYS_getegid +#ifdef __NR_getegid + {"getegid", __NR_getegid}, +#endif +#endif +#ifdef SYS_getegid32 +#ifdef __NR_getegid32 + {"getegid32", __NR_getegid32}, +#endif +#endif +#ifdef SYS_geteuid +#ifdef __NR_geteuid + {"geteuid", __NR_geteuid}, +#endif +#endif +#ifdef SYS_geteuid32 +#ifdef __NR_geteuid32 + {"geteuid32", __NR_geteuid32}, +#endif +#endif +#ifdef SYS_getgid +#ifdef __NR_getgid + {"getgid", __NR_getgid}, +#endif +#endif +#ifdef SYS_getgid32 +#ifdef __NR_getgid32 + {"getgid32", __NR_getgid32}, +#endif +#endif +#ifdef SYS_getgroups +#ifdef __NR_getgroups + {"getgroups", __NR_getgroups}, +#endif +#endif +#ifdef SYS_getgroups32 +#ifdef __NR_getgroups32 + {"getgroups32", __NR_getgroups32}, +#endif +#endif +#ifdef SYS_getitimer +#ifdef __NR_getitimer + {"getitimer", __NR_getitimer}, +#endif +#endif +#ifdef SYS_getpgid +#ifdef __NR_getpgid + {"getpgid", __NR_getpgid}, +#endif +#endif +#ifdef SYS_getpgrp +#ifdef __NR_getpgrp + {"getpgrp", __NR_getpgrp}, +#endif +#endif +#ifdef SYS_getpid +#ifdef __NR_getpid + {"getpid", __NR_getpid}, +#endif +#endif +#ifdef SYS_getpmsg +#ifdef __NR_getpmsg + {"getpmsg", __NR_getpmsg}, +#endif +#endif +#ifdef SYS_getppid +#ifdef __NR_getppid + {"getppid", __NR_getppid}, +#endif +#endif +#ifdef SYS_getpriority +#ifdef __NR_getpriority + {"getpriority", __NR_getpriority}, +#endif +#endif +#ifdef SYS_getresgid +#ifdef __NR_getresgid + {"getresgid", __NR_getresgid}, +#endif +#endif +#ifdef SYS_getresgid32 +#ifdef __NR_getresgid32 + {"getresgid32", __NR_getresgid32}, +#endif +#endif +#ifdef SYS_getresuid +#ifdef __NR_getresuid + {"getresuid", __NR_getresuid}, +#endif +#endif +#ifdef SYS_getresuid32 +#ifdef __NR_getresuid32 + {"getresuid32", __NR_getresuid32}, +#endif +#endif +#ifdef SYS_getrlimit +#ifdef __NR_getrlimit + {"getrlimit", __NR_getrlimit}, +#endif +#endif +#ifdef SYS_getrusage +#ifdef __NR_getrusage + {"getrusage", __NR_getrusage}, +#endif +#endif +#ifdef SYS_getsid +#ifdef __NR_getsid + {"getsid", __NR_getsid}, +#endif +#endif +#ifdef SYS_gettid +#ifdef __NR_gettid + {"gettid", __NR_gettid}, +#endif +#endif +#ifdef SYS_gettimeofday +#ifdef __NR_gettimeofday + {"gettimeofday", __NR_gettimeofday}, +#endif +#endif +#ifdef SYS_getuid +#ifdef __NR_getuid + {"getuid", __NR_getuid}, +#endif +#endif +#ifdef SYS_getuid32 +#ifdef __NR_getuid32 + {"getuid32", __NR_getuid32}, +#endif +#endif +#ifdef SYS_getxattr +#ifdef __NR_getxattr + {"getxattr", __NR_getxattr}, +#endif +#endif +#ifdef SYS_gtty +#ifdef __NR_gtty + {"gtty", __NR_gtty}, +#endif +#endif +#ifdef SYS_idle +#ifdef __NR_idle + {"idle", __NR_idle}, +#endif +#endif +#ifdef SYS_init_module +#ifdef __NR_init_module + {"init_module", __NR_init_module}, +#endif +#endif +#ifdef SYS_inotify_add_watch +#ifdef __NR_inotify_add_watch + {"inotify_add_watch", __NR_inotify_add_watch}, +#endif +#endif +#ifdef SYS_inotify_init +#ifdef __NR_inotify_init + {"inotify_init", __NR_inotify_init}, +#endif +#endif +#ifdef SYS_inotify_init1 +#ifdef __NR_inotify_init1 + {"inotify_init1", __NR_inotify_init1}, +#endif +#endif +#ifdef SYS_inotify_rm_watch +#ifdef __NR_inotify_rm_watch + {"inotify_rm_watch", __NR_inotify_rm_watch}, +#endif +#endif +#ifdef SYS_io_cancel +#ifdef __NR_io_cancel + {"io_cancel", __NR_io_cancel}, +#endif +#endif +#ifdef SYS_io_destroy +#ifdef __NR_io_destroy + {"io_destroy", __NR_io_destroy}, +#endif +#endif +#ifdef SYS_io_getevents +#ifdef __NR_io_getevents + {"io_getevents", __NR_io_getevents}, +#endif +#endif +#ifdef SYS_io_setup +#ifdef __NR_io_setup + {"io_setup", __NR_io_setup}, +#endif +#endif +#ifdef SYS_io_submit +#ifdef __NR_io_submit + {"io_submit", __NR_io_submit}, +#endif +#endif +#ifdef SYS_ioctl +#ifdef __NR_ioctl + {"ioctl", __NR_ioctl}, +#endif +#endif +#ifdef SYS_ioperm +#ifdef __NR_ioperm + {"ioperm", __NR_ioperm}, +#endif +#endif +#ifdef SYS_iopl +#ifdef __NR_iopl + {"iopl", __NR_iopl}, +#endif +#endif +#ifdef SYS_ioprio_get +#ifdef __NR_ioprio_get + {"ioprio_get", __NR_ioprio_get}, +#endif +#endif +#ifdef SYS_ioprio_set +#ifdef __NR_ioprio_set + {"ioprio_set", __NR_ioprio_set}, +#endif +#endif +#ifdef SYS_ipc +#ifdef __NR_ipc + {"ipc", __NR_ipc}, +#endif +#endif +#ifdef SYS_kcmp +#ifdef __NR_kcmp + {"kcmp", __NR_kcmp}, +#endif +#endif +#ifdef SYS_kexec_load +#ifdef __NR_kexec_load + {"kexec_load", __NR_kexec_load}, +#endif +#endif +#ifdef SYS_keyctl +#ifdef __NR_keyctl + {"keyctl", __NR_keyctl}, +#endif +#endif +#ifdef SYS_kill +#ifdef __NR_kill + {"kill", __NR_kill}, +#endif +#endif +#ifdef SYS_lchown +#ifdef __NR_lchown + {"lchown", __NR_lchown}, +#endif +#endif +#ifdef SYS_lchown32 +#ifdef __NR_lchown32 + {"lchown32", __NR_lchown32}, +#endif +#endif +#ifdef SYS_lgetxattr +#ifdef __NR_lgetxattr + {"lgetxattr", __NR_lgetxattr}, +#endif +#endif +#ifdef SYS_link +#ifdef __NR_link + {"link", __NR_link}, +#endif +#endif +#ifdef SYS_linkat +#ifdef __NR_linkat + {"linkat", __NR_linkat}, +#endif +#endif +#ifdef SYS_listxattr +#ifdef __NR_listxattr + {"listxattr", __NR_listxattr}, +#endif +#endif +#ifdef SYS_llistxattr +#ifdef __NR_llistxattr + {"llistxattr", __NR_llistxattr}, +#endif +#endif +#ifdef SYS_lock +#ifdef __NR_lock + {"lock", __NR_lock}, +#endif +#endif +#ifdef SYS_lookup_dcookie +#ifdef __NR_lookup_dcookie + {"lookup_dcookie", __NR_lookup_dcookie}, +#endif +#endif +#ifdef SYS_lremovexattr +#ifdef __NR_lremovexattr + {"lremovexattr", __NR_lremovexattr}, +#endif +#endif +#ifdef SYS_lseek +#ifdef __NR_lseek + {"lseek", __NR_lseek}, +#endif +#endif +#ifdef SYS_lsetxattr +#ifdef __NR_lsetxattr + {"lsetxattr", __NR_lsetxattr}, +#endif +#endif +#ifdef SYS_lstat +#ifdef __NR_lstat + {"lstat", __NR_lstat}, +#endif +#endif +#ifdef SYS_lstat64 +#ifdef __NR_lstat64 + {"lstat64", __NR_lstat64}, +#endif +#endif +#ifdef SYS_madvise +#ifdef __NR_madvise + {"madvise", __NR_madvise}, +#endif +#endif +#ifdef SYS_mbind +#ifdef __NR_mbind + {"mbind", __NR_mbind}, +#endif +#endif +#ifdef SYS_migrate_pages +#ifdef __NR_migrate_pages + {"migrate_pages", __NR_migrate_pages}, +#endif +#endif +#ifdef SYS_mincore +#ifdef __NR_mincore + {"mincore", __NR_mincore}, +#endif +#endif +#ifdef SYS_mkdir +#ifdef __NR_mkdir + {"mkdir", __NR_mkdir}, +#endif +#endif +#ifdef SYS_mkdirat +#ifdef __NR_mkdirat + {"mkdirat", __NR_mkdirat}, +#endif +#endif +#ifdef SYS_mknod +#ifdef __NR_mknod + {"mknod", __NR_mknod}, +#endif +#endif +#ifdef SYS_mknodat +#ifdef __NR_mknodat + {"mknodat", __NR_mknodat}, +#endif +#endif +#ifdef SYS_mlock +#ifdef __NR_mlock + {"mlock", __NR_mlock}, +#endif +#endif +#ifdef SYS_mlockall +#ifdef __NR_mlockall + {"mlockall", __NR_mlockall}, +#endif +#endif +#ifdef SYS_mmap +#ifdef __NR_mmap + {"mmap", __NR_mmap}, +#endif +#endif +#ifdef SYS_mmap2 +#ifdef __NR_mmap2 + {"mmap2", __NR_mmap2}, +#endif +#endif +#ifdef SYS_modify_ldt +#ifdef __NR_modify_ldt + {"modify_ldt", __NR_modify_ldt}, +#endif +#endif +#ifdef SYS_mount +#ifdef __NR_mount + {"mount", __NR_mount}, +#endif +#endif +#ifdef SYS_move_pages +#ifdef __NR_move_pages + {"move_pages", __NR_move_pages}, +#endif +#endif +#ifdef SYS_mprotect +#ifdef __NR_mprotect + {"mprotect", __NR_mprotect}, +#endif +#endif +#ifdef SYS_mpx +#ifdef __NR_mpx + {"mpx", __NR_mpx}, +#endif +#endif +#ifdef SYS_mq_getsetattr +#ifdef __NR_mq_getsetattr + {"mq_getsetattr", __NR_mq_getsetattr}, +#endif +#endif +#ifdef SYS_mq_notify +#ifdef __NR_mq_notify + {"mq_notify", __NR_mq_notify}, +#endif +#endif +#ifdef SYS_mq_open +#ifdef __NR_mq_open + {"mq_open", __NR_mq_open}, +#endif +#endif +#ifdef SYS_mq_timedreceive +#ifdef __NR_mq_timedreceive + {"mq_timedreceive", __NR_mq_timedreceive}, +#endif +#endif +#ifdef SYS_mq_timedsend +#ifdef __NR_mq_timedsend + {"mq_timedsend", __NR_mq_timedsend}, +#endif +#endif +#ifdef SYS_mq_unlink +#ifdef __NR_mq_unlink + {"mq_unlink", __NR_mq_unlink}, +#endif +#endif +#ifdef SYS_mremap +#ifdef __NR_mremap + {"mremap", __NR_mremap}, +#endif +#endif +#ifdef SYS_msync +#ifdef __NR_msync + {"msync", __NR_msync}, +#endif +#endif +#ifdef SYS_munlock +#ifdef __NR_munlock + {"munlock", __NR_munlock}, +#endif +#endif +#ifdef SYS_munlockall +#ifdef __NR_munlockall + {"munlockall", __NR_munlockall}, +#endif +#endif +#ifdef SYS_munmap +#ifdef __NR_munmap + {"munmap", __NR_munmap}, +#endif +#endif +#ifdef SYS_name_to_handle_at +#ifdef __NR_name_to_handle_at + {"name_to_handle_at", __NR_name_to_handle_at}, +#endif +#endif +#ifdef SYS_nanosleep +#ifdef __NR_nanosleep + {"nanosleep", __NR_nanosleep}, +#endif +#endif +#ifdef SYS_nfsservctl +#ifdef __NR_nfsservctl + {"nfsservctl", __NR_nfsservctl}, +#endif +#endif +#ifdef SYS_nice +#ifdef __NR_nice + {"nice", __NR_nice}, +#endif +#endif +#ifdef SYS_oldfstat +#ifdef __NR_oldfstat + {"oldfstat", __NR_oldfstat}, +#endif +#endif +#ifdef SYS_oldlstat +#ifdef __NR_oldlstat + {"oldlstat", __NR_oldlstat}, +#endif +#endif +#ifdef SYS_oldolduname +#ifdef __NR_oldolduname + {"oldolduname", __NR_oldolduname}, +#endif +#endif +#ifdef SYS_oldstat +#ifdef __NR_oldstat + {"oldstat", __NR_oldstat}, +#endif +#endif +#ifdef SYS_olduname +#ifdef __NR_olduname + {"olduname", __NR_olduname}, +#endif +#endif +#ifdef SYS_open +#ifdef __NR_open + {"open", __NR_open}, +#endif +#endif +#ifdef SYS_open_by_handle_at +#ifdef __NR_open_by_handle_at + {"open_by_handle_at", __NR_open_by_handle_at}, +#endif +#endif +#ifdef SYS_openat +#ifdef __NR_openat + {"openat", __NR_openat}, +#endif +#endif +#ifdef SYS_pause +#ifdef __NR_pause + {"pause", __NR_pause}, +#endif +#endif +#ifdef SYS_perf_event_open +#ifdef __NR_perf_event_open + {"perf_event_open", __NR_perf_event_open}, +#endif +#endif +#ifdef SYS_personality +#ifdef __NR_personality + {"personality", __NR_personality}, +#endif +#endif +#ifdef SYS_pipe +#ifdef __NR_pipe + {"pipe", __NR_pipe}, +#endif +#endif +#ifdef SYS_pipe2 +#ifdef __NR_pipe2 + {"pipe2", __NR_pipe2}, +#endif +#endif +#ifdef SYS_pivot_root +#ifdef __NR_pivot_root + {"pivot_root", __NR_pivot_root}, +#endif +#endif +#ifdef SYS_poll +#ifdef __NR_poll + {"poll", __NR_poll}, +#endif +#endif +#ifdef SYS_ppoll +#ifdef __NR_ppoll + {"ppoll", __NR_ppoll}, +#endif +#endif +#ifdef SYS_prctl +#ifdef __NR_prctl + {"prctl", __NR_prctl}, +#endif +#endif +#ifdef SYS_pread64 +#ifdef __NR_pread64 + {"pread64", __NR_pread64}, +#endif +#endif +#ifdef SYS_preadv +#ifdef __NR_preadv + {"preadv", __NR_preadv}, +#endif +#endif +#ifdef SYS_prlimit64 +#ifdef __NR_prlimit64 + {"prlimit64", __NR_prlimit64}, +#endif +#endif +#ifdef SYS_process_vm_readv +#ifdef __NR_process_vm_readv + {"process_vm_readv", __NR_process_vm_readv}, +#endif +#endif +#ifdef SYS_process_vm_writev +#ifdef __NR_process_vm_writev + {"process_vm_writev", __NR_process_vm_writev}, +#endif +#endif +#ifdef SYS_prof +#ifdef __NR_prof + {"prof", __NR_prof}, +#endif +#endif +#ifdef SYS_profil +#ifdef __NR_profil + {"profil", __NR_profil}, +#endif +#endif +#ifdef SYS_pselect6 +#ifdef __NR_pselect6 + {"pselect6", __NR_pselect6}, +#endif +#endif +#ifdef SYS_ptrace +#ifdef __NR_ptrace + {"ptrace", __NR_ptrace}, +#endif +#endif +#ifdef SYS_putpmsg +#ifdef __NR_putpmsg + {"putpmsg", __NR_putpmsg}, +#endif +#endif +#ifdef SYS_pwrite64 +#ifdef __NR_pwrite64 + {"pwrite64", __NR_pwrite64}, +#endif +#endif +#ifdef SYS_pwritev +#ifdef __NR_pwritev + {"pwritev", __NR_pwritev}, +#endif +#endif +#ifdef SYS_query_module +#ifdef __NR_query_module + {"query_module", __NR_query_module}, +#endif +#endif +#ifdef SYS_quotactl +#ifdef __NR_quotactl + {"quotactl", __NR_quotactl}, +#endif +#endif +#ifdef SYS_read +#ifdef __NR_read + {"read", __NR_read}, +#endif +#endif +#ifdef SYS_readahead +#ifdef __NR_readahead + {"readahead", __NR_readahead}, +#endif +#endif +#ifdef SYS_readdir +#ifdef __NR_readdir + {"readdir", __NR_readdir}, +#endif +#endif +#ifdef SYS_readlink +#ifdef __NR_readlink + {"readlink", __NR_readlink}, +#endif +#endif +#ifdef SYS_readlinkat +#ifdef __NR_readlinkat + {"readlinkat", __NR_readlinkat}, +#endif +#endif +#ifdef SYS_readv +#ifdef __NR_readv + {"readv", __NR_readv}, +#endif +#endif +#ifdef SYS_reboot +#ifdef __NR_reboot + {"reboot", __NR_reboot}, +#endif +#endif +#ifdef SYS_recvmmsg +#ifdef __NR_recvmmsg + {"recvmmsg", __NR_recvmmsg}, +#endif +#endif +#ifdef SYS_remap_file_pages +#ifdef __NR_remap_file_pages + {"remap_file_pages", __NR_remap_file_pages}, +#endif +#endif +#ifdef SYS_removexattr +#ifdef __NR_removexattr + {"removexattr", __NR_removexattr}, +#endif +#endif +#ifdef SYS_rename +#ifdef __NR_rename + {"rename", __NR_rename}, +#endif +#endif +#ifdef SYS_renameat +#ifdef __NR_renameat + {"renameat", __NR_renameat}, +#endif +#endif +#ifdef SYS_request_key +#ifdef __NR_request_key + {"request_key", __NR_request_key}, +#endif +#endif +#ifdef SYS_restart_syscall +#ifdef __NR_restart_syscall + {"restart_syscall", __NR_restart_syscall}, +#endif +#endif +#ifdef SYS_rmdir +#ifdef __NR_rmdir + {"rmdir", __NR_rmdir}, +#endif +#endif +#ifdef SYS_rt_sigaction +#ifdef __NR_rt_sigaction + {"rt_sigaction", __NR_rt_sigaction}, +#endif +#endif +#ifdef SYS_rt_sigpending +#ifdef __NR_rt_sigpending + {"rt_sigpending", __NR_rt_sigpending}, +#endif +#endif +#ifdef SYS_rt_sigprocmask +#ifdef __NR_rt_sigprocmask + {"rt_sigprocmask", __NR_rt_sigprocmask}, +#endif +#endif +#ifdef SYS_rt_sigqueueinfo +#ifdef __NR_rt_sigqueueinfo + {"rt_sigqueueinfo", __NR_rt_sigqueueinfo}, +#endif +#endif +#ifdef SYS_rt_sigreturn +#ifdef __NR_rt_sigreturn + {"rt_sigreturn", __NR_rt_sigreturn}, +#endif +#endif +#ifdef SYS_rt_sigsuspend +#ifdef __NR_rt_sigsuspend + {"rt_sigsuspend", __NR_rt_sigsuspend}, +#endif +#endif +#ifdef SYS_rt_sigtimedwait +#ifdef __NR_rt_sigtimedwait + {"rt_sigtimedwait", __NR_rt_sigtimedwait}, +#endif +#endif +#ifdef SYS_rt_tgsigqueueinfo +#ifdef __NR_rt_tgsigqueueinfo + {"rt_tgsigqueueinfo", __NR_rt_tgsigqueueinfo}, +#endif +#endif +#ifdef SYS_sched_get_priority_max +#ifdef __NR_sched_get_priority_max + {"sched_get_priority_max", __NR_sched_get_priority_max}, +#endif +#endif +#ifdef SYS_sched_get_priority_min +#ifdef __NR_sched_get_priority_min + {"sched_get_priority_min", __NR_sched_get_priority_min}, +#endif +#endif +#ifdef SYS_sched_getaffinity +#ifdef __NR_sched_getaffinity + {"sched_getaffinity", __NR_sched_getaffinity}, +#endif +#endif +#ifdef SYS_sched_getparam +#ifdef __NR_sched_getparam + {"sched_getparam", __NR_sched_getparam}, +#endif +#endif +#ifdef SYS_sched_getscheduler +#ifdef __NR_sched_getscheduler + {"sched_getscheduler", __NR_sched_getscheduler}, +#endif +#endif +#ifdef SYS_sched_rr_get_interval +#ifdef __NR_sched_rr_get_interval + {"sched_rr_get_interval", __NR_sched_rr_get_interval}, +#endif +#endif +#ifdef SYS_sched_setaffinity +#ifdef __NR_sched_setaffinity + {"sched_setaffinity", __NR_sched_setaffinity}, +#endif +#endif +#ifdef SYS_sched_setparam +#ifdef __NR_sched_setparam + {"sched_setparam", __NR_sched_setparam}, +#endif +#endif +#ifdef SYS_sched_setscheduler +#ifdef __NR_sched_setscheduler + {"sched_setscheduler", __NR_sched_setscheduler}, +#endif +#endif +#ifdef SYS_sched_yield +#ifdef __NR_sched_yield + {"sched_yield", __NR_sched_yield}, +#endif +#endif +#ifdef SYS_select +#ifdef __NR_select + {"select", __NR_select}, +#endif +#endif +#ifdef SYS_sendfile +#ifdef __NR_sendfile + {"sendfile", __NR_sendfile}, +#endif +#endif +#ifdef SYS_sendfile64 +#ifdef __NR_sendfile64 + {"sendfile64", __NR_sendfile64}, +#endif +#endif +#ifdef SYS_sendmmsg +#ifdef __NR_sendmmsg + {"sendmmsg", __NR_sendmmsg}, +#endif +#endif +#ifdef SYS_set_mempolicy +#ifdef __NR_set_mempolicy + {"set_mempolicy", __NR_set_mempolicy}, +#endif +#endif +#ifdef SYS_set_robust_list +#ifdef __NR_set_robust_list + {"set_robust_list", __NR_set_robust_list}, +#endif +#endif +#ifdef SYS_set_thread_area +#ifdef __NR_set_thread_area + {"set_thread_area", __NR_set_thread_area}, +#endif +#endif +#ifdef SYS_set_tid_address +#ifdef __NR_set_tid_address + {"set_tid_address", __NR_set_tid_address}, +#endif +#endif +#ifdef SYS_setdomainname +#ifdef __NR_setdomainname + {"setdomainname", __NR_setdomainname}, +#endif +#endif +#ifdef SYS_setfsgid +#ifdef __NR_setfsgid + {"setfsgid", __NR_setfsgid}, +#endif +#endif +#ifdef SYS_setfsgid32 +#ifdef __NR_setfsgid32 + {"setfsgid32", __NR_setfsgid32}, +#endif +#endif +#ifdef SYS_setfsuid +#ifdef __NR_setfsuid + {"setfsuid", __NR_setfsuid}, +#endif +#endif +#ifdef SYS_setfsuid32 +#ifdef __NR_setfsuid32 + {"setfsuid32", __NR_setfsuid32}, +#endif +#endif +#ifdef SYS_setgid +#ifdef __NR_setgid + {"setgid", __NR_setgid}, +#endif +#endif +#ifdef SYS_setgid32 +#ifdef __NR_setgid32 + {"setgid32", __NR_setgid32}, +#endif +#endif +#ifdef SYS_setgroups +#ifdef __NR_setgroups + {"setgroups", __NR_setgroups}, +#endif +#endif +#ifdef SYS_setgroups32 +#ifdef __NR_setgroups32 + {"setgroups32", __NR_setgroups32}, +#endif +#endif +#ifdef SYS_sethostname +#ifdef __NR_sethostname + {"sethostname", __NR_sethostname}, +#endif +#endif +#ifdef SYS_setitimer +#ifdef __NR_setitimer + {"setitimer", __NR_setitimer}, +#endif +#endif +#ifdef SYS_setns +#ifdef __NR_setns + {"setns", __NR_setns}, +#endif +#endif +#ifdef SYS_setpgid +#ifdef __NR_setpgid + {"setpgid", __NR_setpgid}, +#endif +#endif +#ifdef SYS_setpriority +#ifdef __NR_setpriority + {"setpriority", __NR_setpriority}, +#endif +#endif +#ifdef SYS_setregid +#ifdef __NR_setregid + {"setregid", __NR_setregid}, +#endif +#endif +#ifdef SYS_setregid32 +#ifdef __NR_setregid32 + {"setregid32", __NR_setregid32}, +#endif +#endif +#ifdef SYS_setresgid +#ifdef __NR_setresgid + {"setresgid", __NR_setresgid}, +#endif +#endif +#ifdef SYS_setresgid32 +#ifdef __NR_setresgid32 + {"setresgid32", __NR_setresgid32}, +#endif +#endif +#ifdef SYS_setresuid +#ifdef __NR_setresuid + {"setresuid", __NR_setresuid}, +#endif +#endif +#ifdef SYS_setresuid32 +#ifdef __NR_setresuid32 + {"setresuid32", __NR_setresuid32}, +#endif +#endif +#ifdef SYS_setreuid +#ifdef __NR_setreuid + {"setreuid", __NR_setreuid}, +#endif +#endif +#ifdef SYS_setreuid32 +#ifdef __NR_setreuid32 + {"setreuid32", __NR_setreuid32}, +#endif +#endif +#ifdef SYS_setrlimit +#ifdef __NR_setrlimit + {"setrlimit", __NR_setrlimit}, +#endif +#endif +#ifdef SYS_setsid +#ifdef __NR_setsid + {"setsid", __NR_setsid}, +#endif +#endif +#ifdef SYS_settimeofday +#ifdef __NR_settimeofday + {"settimeofday", __NR_settimeofday}, +#endif +#endif +#ifdef SYS_setuid +#ifdef __NR_setuid + {"setuid", __NR_setuid}, +#endif +#endif +#ifdef SYS_setuid32 +#ifdef __NR_setuid32 + {"setuid32", __NR_setuid32}, +#endif +#endif +#ifdef SYS_setxattr +#ifdef __NR_setxattr + {"setxattr", __NR_setxattr}, +#endif +#endif +#ifdef SYS_sgetmask +#ifdef __NR_sgetmask + {"sgetmask", __NR_sgetmask}, +#endif +#endif +#ifdef SYS_sigaction +#ifdef __NR_sigaction + {"sigaction", __NR_sigaction}, +#endif +#endif +#ifdef SYS_sigaltstack +#ifdef __NR_sigaltstack + {"sigaltstack", __NR_sigaltstack}, +#endif +#endif +#ifdef SYS_signal +#ifdef __NR_signal + {"signal", __NR_signal}, +#endif +#endif +#ifdef SYS_signalfd +#ifdef __NR_signalfd + {"signalfd", __NR_signalfd}, +#endif +#endif +#ifdef SYS_signalfd4 +#ifdef __NR_signalfd4 + {"signalfd4", __NR_signalfd4}, +#endif +#endif +#ifdef SYS_sigpending +#ifdef __NR_sigpending + {"sigpending", __NR_sigpending}, +#endif +#endif +#ifdef SYS_sigprocmask +#ifdef __NR_sigprocmask + {"sigprocmask", __NR_sigprocmask}, +#endif +#endif +#ifdef SYS_sigreturn +#ifdef __NR_sigreturn + {"sigreturn", __NR_sigreturn}, +#endif +#endif +#ifdef SYS_sigsuspend +#ifdef __NR_sigsuspend + {"sigsuspend", __NR_sigsuspend}, +#endif +#endif +#ifdef SYS_socketcall +#ifdef __NR_socketcall + {"socketcall", __NR_socketcall}, +#endif +#endif +#ifdef SYS_splice +#ifdef __NR_splice + {"splice", __NR_splice}, +#endif +#endif +#ifdef SYS_ssetmask +#ifdef __NR_ssetmask + {"ssetmask", __NR_ssetmask}, +#endif +#endif +#ifdef SYS_stat +#ifdef __NR_stat + {"stat", __NR_stat}, +#endif +#endif +#ifdef SYS_stat64 +#ifdef __NR_stat64 + {"stat64", __NR_stat64}, +#endif +#endif +#ifdef SYS_statfs +#ifdef __NR_statfs + {"statfs", __NR_statfs}, +#endif +#endif +#ifdef SYS_statfs64 +#ifdef __NR_statfs64 + {"statfs64", __NR_statfs64}, +#endif +#endif +#ifdef SYS_stime +#ifdef __NR_stime + {"stime", __NR_stime}, +#endif +#endif +#ifdef SYS_stty +#ifdef __NR_stty + {"stty", __NR_stty}, +#endif +#endif +#ifdef SYS_swapoff +#ifdef __NR_swapoff + {"swapoff", __NR_swapoff}, +#endif +#endif +#ifdef SYS_swapon +#ifdef __NR_swapon + {"swapon", __NR_swapon}, +#endif +#endif +#ifdef SYS_symlink +#ifdef __NR_symlink + {"symlink", __NR_symlink}, +#endif +#endif +#ifdef SYS_symlinkat +#ifdef __NR_symlinkat + {"symlinkat", __NR_symlinkat}, +#endif +#endif +#ifdef SYS_sync +#ifdef __NR_sync + {"sync", __NR_sync}, +#endif +#endif +#ifdef SYS_sync_file_range +#ifdef __NR_sync_file_range + {"sync_file_range", __NR_sync_file_range}, +#endif +#endif +#ifdef SYS_syncfs +#ifdef __NR_syncfs + {"syncfs", __NR_syncfs}, +#endif +#endif +#ifdef SYS_sysfs +#ifdef __NR_sysfs + {"sysfs", __NR_sysfs}, +#endif +#endif +#ifdef SYS_sysinfo +#ifdef __NR_sysinfo + {"sysinfo", __NR_sysinfo}, +#endif +#endif +#ifdef SYS_syslog +#ifdef __NR_syslog + {"syslog", __NR_syslog}, +#endif +#endif +#ifdef SYS_tee +#ifdef __NR_tee + {"tee", __NR_tee}, +#endif +#endif +#ifdef SYS_tgkill +#ifdef __NR_tgkill + {"tgkill", __NR_tgkill}, +#endif +#endif +#ifdef SYS_time +#ifdef __NR_time + {"time", __NR_time}, +#endif +#endif +#ifdef SYS_timer_create +#ifdef __NR_timer_create + {"timer_create", __NR_timer_create}, +#endif +#endif +#ifdef SYS_timer_delete +#ifdef __NR_timer_delete + {"timer_delete", __NR_timer_delete}, +#endif +#endif +#ifdef SYS_timer_getoverrun +#ifdef __NR_timer_getoverrun + {"timer_getoverrun", __NR_timer_getoverrun}, +#endif +#endif +#ifdef SYS_timer_gettime +#ifdef __NR_timer_gettime + {"timer_gettime", __NR_timer_gettime}, +#endif +#endif +#ifdef SYS_timer_settime +#ifdef __NR_timer_settime + {"timer_settime", __NR_timer_settime}, +#endif +#endif +#ifdef SYS_timerfd_create +#ifdef __NR_timerfd_create + {"timerfd_create", __NR_timerfd_create}, +#endif +#endif +#ifdef SYS_timerfd_gettime +#ifdef __NR_timerfd_gettime + {"timerfd_gettime", __NR_timerfd_gettime}, +#endif +#endif +#ifdef SYS_timerfd_settime +#ifdef __NR_timerfd_settime + {"timerfd_settime", __NR_timerfd_settime}, +#endif +#endif +#ifdef SYS_times +#ifdef __NR_times + {"times", __NR_times}, +#endif +#endif +#ifdef SYS_tkill +#ifdef __NR_tkill + {"tkill", __NR_tkill}, +#endif +#endif +#ifdef SYS_truncate +#ifdef __NR_truncate + {"truncate", __NR_truncate}, +#endif +#endif +#ifdef SYS_truncate64 +#ifdef __NR_truncate64 + {"truncate64", __NR_truncate64}, +#endif +#endif +#ifdef SYS_ugetrlimit +#ifdef __NR_ugetrlimit + {"ugetrlimit", __NR_ugetrlimit}, +#endif +#endif +#ifdef SYS_ulimit +#ifdef __NR_ulimit + {"ulimit", __NR_ulimit}, +#endif +#endif +#ifdef SYS_umask +#ifdef __NR_umask + {"umask", __NR_umask}, +#endif +#endif +#ifdef SYS_umount +#ifdef __NR_umount + {"umount", __NR_umount}, +#endif +#endif +#ifdef SYS_umount2 +#ifdef __NR_umount2 + {"umount2", __NR_umount2}, +#endif +#endif +#ifdef SYS_uname +#ifdef __NR_uname + {"uname", __NR_uname}, +#endif +#endif +#ifdef SYS_unlink +#ifdef __NR_unlink + {"unlink", __NR_unlink}, +#endif +#endif +#ifdef SYS_unlinkat +#ifdef __NR_unlinkat + {"unlinkat", __NR_unlinkat}, +#endif +#endif +#ifdef SYS_unshare +#ifdef __NR_unshare + {"unshare", __NR_unshare}, +#endif +#endif +#ifdef SYS_uselib +#ifdef __NR_uselib + {"uselib", __NR_uselib}, +#endif +#endif +#ifdef SYS_ustat +#ifdef __NR_ustat + {"ustat", __NR_ustat}, +#endif +#endif +#ifdef SYS_utime +#ifdef __NR_utime + {"utime", __NR_utime}, +#endif +#endif +#ifdef SYS_utimensat +#ifdef __NR_utimensat + {"utimensat", __NR_utimensat}, +#endif +#endif +#ifdef SYS_utimes +#ifdef __NR_utimes + {"utimes", __NR_utimes}, +#endif +#endif +#ifdef SYS_vfork +#ifdef __NR_vfork + {"vfork", __NR_vfork}, +#endif +#endif +#ifdef SYS_vhangup +#ifdef __NR_vhangup + {"vhangup", __NR_vhangup}, +#endif +#endif +#ifdef SYS_vm86 +#ifdef __NR_vm86 + {"vm86", __NR_vm86}, +#endif +#endif +#ifdef SYS_vm86old +#ifdef __NR_vm86old + {"vm86old", __NR_vm86old}, +#endif +#endif +#ifdef SYS_vmsplice +#ifdef __NR_vmsplice + {"vmsplice", __NR_vmsplice}, +#endif +#endif +#ifdef SYS_vserver +#ifdef __NR_vserver + {"vserver", __NR_vserver}, +#endif +#endif +#ifdef SYS_wait4 +#ifdef __NR_wait4 + {"wait4", __NR_wait4}, +#endif +#endif +#ifdef SYS_waitid +#ifdef __NR_waitid + {"waitid", __NR_waitid}, +#endif +#endif +#ifdef SYS_waitpid +#ifdef __NR_waitpid + {"waitpid", __NR_waitpid}, +#endif +#endif +#ifdef SYS_write +#ifdef __NR_write + {"write", __NR_write}, +#endif +#endif +#ifdef SYS_writev +#ifdef __NR_writev + {"writev", __NR_writev}, +#endif +#endif +#endif +#if defined __x86_64__ && defined __LP64__ +#ifdef SYS__sysctl +#ifdef __NR__sysctl + {"_sysctl", __NR__sysctl}, +#endif +#endif +#ifdef SYS_accept +#ifdef __NR_accept + {"accept", __NR_accept}, +#endif +#endif +#ifdef SYS_accept4 +#ifdef __NR_accept4 + {"accept4", __NR_accept4}, +#endif +#endif +#ifdef SYS_access +#ifdef __NR_access + {"access", __NR_access}, +#endif +#endif +#ifdef SYS_acct +#ifdef __NR_acct + {"acct", __NR_acct}, +#endif +#endif +#ifdef SYS_add_key +#ifdef __NR_add_key + {"add_key", __NR_add_key}, +#endif +#endif +#ifdef SYS_adjtimex +#ifdef __NR_adjtimex + {"adjtimex", __NR_adjtimex}, +#endif +#endif +#ifdef SYS_afs_syscall +#ifdef __NR_afs_syscall + {"afs_syscall", __NR_afs_syscall}, +#endif +#endif +#ifdef SYS_alarm +#ifdef __NR_alarm + {"alarm", __NR_alarm}, +#endif +#endif +#ifdef SYS_arch_prctl +#ifdef __NR_arch_prctl + {"arch_prctl", __NR_arch_prctl}, +#endif +#endif +#ifdef SYS_bind +#ifdef __NR_bind + {"bind", __NR_bind}, +#endif +#endif +#ifdef SYS_brk +#ifdef __NR_brk + {"brk", __NR_brk}, +#endif +#endif +#ifdef SYS_capget +#ifdef __NR_capget + {"capget", __NR_capget}, +#endif +#endif +#ifdef SYS_capset +#ifdef __NR_capset + {"capset", __NR_capset}, +#endif +#endif +#ifdef SYS_chdir +#ifdef __NR_chdir + {"chdir", __NR_chdir}, +#endif +#endif +#ifdef SYS_chmod +#ifdef __NR_chmod + {"chmod", __NR_chmod}, +#endif +#endif +#ifdef SYS_chown +#ifdef __NR_chown + {"chown", __NR_chown}, +#endif +#endif +#ifdef SYS_chroot +#ifdef __NR_chroot + {"chroot", __NR_chroot}, +#endif +#endif +#ifdef SYS_clock_adjtime +#ifdef __NR_clock_adjtime + {"clock_adjtime", __NR_clock_adjtime}, +#endif +#endif +#ifdef SYS_clock_getres +#ifdef __NR_clock_getres + {"clock_getres", __NR_clock_getres}, +#endif +#endif +#ifdef SYS_clock_gettime +#ifdef __NR_clock_gettime + {"clock_gettime", __NR_clock_gettime}, +#endif +#endif +#ifdef SYS_clock_nanosleep +#ifdef __NR_clock_nanosleep + {"clock_nanosleep", __NR_clock_nanosleep}, +#endif +#endif +#ifdef SYS_clock_settime +#ifdef __NR_clock_settime + {"clock_settime", __NR_clock_settime}, +#endif +#endif +#ifdef SYS_clone +#ifdef __NR_clone + {"clone", __NR_clone}, +#endif +#endif +#ifdef SYS_close +#ifdef __NR_close + {"close", __NR_close}, +#endif +#endif +#ifdef SYS_connect +#ifdef __NR_connect + {"connect", __NR_connect}, +#endif +#endif +#ifdef SYS_creat +#ifdef __NR_creat + {"creat", __NR_creat}, +#endif +#endif +#ifdef SYS_create_module +#ifdef __NR_create_module + {"create_module", __NR_create_module}, +#endif +#endif +#ifdef SYS_delete_module +#ifdef __NR_delete_module + {"delete_module", __NR_delete_module}, +#endif +#endif +#ifdef SYS_dup +#ifdef __NR_dup + {"dup", __NR_dup}, +#endif +#endif +#ifdef SYS_dup2 +#ifdef __NR_dup2 + {"dup2", __NR_dup2}, +#endif +#endif +#ifdef SYS_dup3 +#ifdef __NR_dup3 + {"dup3", __NR_dup3}, +#endif +#endif +#ifdef SYS_epoll_create +#ifdef __NR_epoll_create + {"epoll_create", __NR_epoll_create}, +#endif +#endif +#ifdef SYS_epoll_create1 +#ifdef __NR_epoll_create1 + {"epoll_create1", __NR_epoll_create1}, +#endif +#endif +#ifdef SYS_epoll_ctl +#ifdef __NR_epoll_ctl + {"epoll_ctl", __NR_epoll_ctl}, +#endif +#endif +#ifdef SYS_epoll_ctl_old +#ifdef __NR_epoll_ctl_old + {"epoll_ctl_old", __NR_epoll_ctl_old}, +#endif +#endif +#ifdef SYS_epoll_pwait +#ifdef __NR_epoll_pwait + {"epoll_pwait", __NR_epoll_pwait}, +#endif +#endif +#ifdef SYS_epoll_wait +#ifdef __NR_epoll_wait + {"epoll_wait", __NR_epoll_wait}, +#endif +#endif +#ifdef SYS_epoll_wait_old +#ifdef __NR_epoll_wait_old + {"epoll_wait_old", __NR_epoll_wait_old}, +#endif +#endif +#ifdef SYS_eventfd +#ifdef __NR_eventfd + {"eventfd", __NR_eventfd}, +#endif +#endif +#ifdef SYS_eventfd2 +#ifdef __NR_eventfd2 + {"eventfd2", __NR_eventfd2}, +#endif +#endif +#ifdef SYS_execve +#ifdef __NR_execve + {"execve", __NR_execve}, +#endif +#endif +#ifdef SYS_exit +#ifdef __NR_exit + {"exit", __NR_exit}, +#endif +#endif +#ifdef SYS_exit_group +#ifdef __NR_exit_group + {"exit_group", __NR_exit_group}, +#endif +#endif +#ifdef SYS_faccessat +#ifdef __NR_faccessat + {"faccessat", __NR_faccessat}, +#endif +#endif +#ifdef SYS_fadvise64 +#ifdef __NR_fadvise64 + {"fadvise64", __NR_fadvise64}, +#endif +#endif +#ifdef SYS_fallocate +#ifdef __NR_fallocate + {"fallocate", __NR_fallocate}, +#endif +#endif +#ifdef SYS_fanotify_init +#ifdef __NR_fanotify_init + {"fanotify_init", __NR_fanotify_init}, +#endif +#endif +#ifdef SYS_fanotify_mark +#ifdef __NR_fanotify_mark + {"fanotify_mark", __NR_fanotify_mark}, +#endif +#endif +#ifdef SYS_fchdir +#ifdef __NR_fchdir + {"fchdir", __NR_fchdir}, +#endif +#endif +#ifdef SYS_fchmod +#ifdef __NR_fchmod + {"fchmod", __NR_fchmod}, +#endif +#endif +#ifdef SYS_fchmodat +#ifdef __NR_fchmodat + {"fchmodat", __NR_fchmodat}, +#endif +#endif +#ifdef SYS_fchown +#ifdef __NR_fchown + {"fchown", __NR_fchown}, +#endif +#endif +#ifdef SYS_fchownat +#ifdef __NR_fchownat + {"fchownat", __NR_fchownat}, +#endif +#endif +#ifdef SYS_fcntl +#ifdef __NR_fcntl + {"fcntl", __NR_fcntl}, +#endif +#endif +#ifdef SYS_fdatasync +#ifdef __NR_fdatasync + {"fdatasync", __NR_fdatasync}, +#endif +#endif +#ifdef SYS_fgetxattr +#ifdef __NR_fgetxattr + {"fgetxattr", __NR_fgetxattr}, +#endif +#endif +#ifdef SYS_finit_module +#ifdef __NR_finit_module + {"finit_module", __NR_finit_module}, +#endif +#endif +#ifdef SYS_flistxattr +#ifdef __NR_flistxattr + {"flistxattr", __NR_flistxattr}, +#endif +#endif +#ifdef SYS_flock +#ifdef __NR_flock + {"flock", __NR_flock}, +#endif +#endif +#ifdef SYS_fork +#ifdef __NR_fork + {"fork", __NR_fork}, +#endif +#endif +#ifdef SYS_fremovexattr +#ifdef __NR_fremovexattr + {"fremovexattr", __NR_fremovexattr}, +#endif +#endif +#ifdef SYS_fsetxattr +#ifdef __NR_fsetxattr + {"fsetxattr", __NR_fsetxattr}, +#endif +#endif +#ifdef SYS_fstat +#ifdef __NR_fstat + {"fstat", __NR_fstat}, +#endif +#endif +#ifdef SYS_fstatfs +#ifdef __NR_fstatfs + {"fstatfs", __NR_fstatfs}, +#endif +#endif +#ifdef SYS_fsync +#ifdef __NR_fsync + {"fsync", __NR_fsync}, +#endif +#endif +#ifdef SYS_ftruncate +#ifdef __NR_ftruncate + {"ftruncate", __NR_ftruncate}, +#endif +#endif +#ifdef SYS_futex +#ifdef __NR_futex + {"futex", __NR_futex}, +#endif +#endif +#ifdef SYS_futimesat +#ifdef __NR_futimesat + {"futimesat", __NR_futimesat}, +#endif +#endif +#ifdef SYS_get_kernel_syms +#ifdef __NR_get_kernel_syms + {"get_kernel_syms", __NR_get_kernel_syms}, +#endif +#endif +#ifdef SYS_get_mempolicy +#ifdef __NR_get_mempolicy + {"get_mempolicy", __NR_get_mempolicy}, +#endif +#endif +#ifdef SYS_get_robust_list +#ifdef __NR_get_robust_list + {"get_robust_list", __NR_get_robust_list}, +#endif +#endif +#ifdef SYS_get_thread_area +#ifdef __NR_get_thread_area + {"get_thread_area", __NR_get_thread_area}, +#endif +#endif +#ifdef SYS_getcpu +#ifdef __NR_getcpu + {"getcpu", __NR_getcpu}, +#endif +#endif +#ifdef SYS_getcwd +#ifdef __NR_getcwd + {"getcwd", __NR_getcwd}, +#endif +#endif +#ifdef SYS_getdents +#ifdef __NR_getdents + {"getdents", __NR_getdents}, +#endif +#endif +#ifdef SYS_getdents64 +#ifdef __NR_getdents64 + {"getdents64", __NR_getdents64}, +#endif +#endif +#ifdef SYS_getegid +#ifdef __NR_getegid + {"getegid", __NR_getegid}, +#endif +#endif +#ifdef SYS_geteuid +#ifdef __NR_geteuid + {"geteuid", __NR_geteuid}, +#endif +#endif +#ifdef SYS_getgid +#ifdef __NR_getgid + {"getgid", __NR_getgid}, +#endif +#endif +#ifdef SYS_getgroups +#ifdef __NR_getgroups + {"getgroups", __NR_getgroups}, +#endif +#endif +#ifdef SYS_getitimer +#ifdef __NR_getitimer + {"getitimer", __NR_getitimer}, +#endif +#endif +#ifdef SYS_getpeername +#ifdef __NR_getpeername + {"getpeername", __NR_getpeername}, +#endif +#endif +#ifdef SYS_getpgid +#ifdef __NR_getpgid + {"getpgid", __NR_getpgid}, +#endif +#endif +#ifdef SYS_getpgrp +#ifdef __NR_getpgrp + {"getpgrp", __NR_getpgrp}, +#endif +#endif +#ifdef SYS_getpid +#ifdef __NR_getpid + {"getpid", __NR_getpid}, +#endif +#endif +#ifdef SYS_getpmsg +#ifdef __NR_getpmsg + {"getpmsg", __NR_getpmsg}, +#endif +#endif +#ifdef SYS_getppid +#ifdef __NR_getppid + {"getppid", __NR_getppid}, +#endif +#endif +#ifdef SYS_getpriority +#ifdef __NR_getpriority + {"getpriority", __NR_getpriority}, +#endif +#endif +#ifdef SYS_getresgid +#ifdef __NR_getresgid + {"getresgid", __NR_getresgid}, +#endif +#endif +#ifdef SYS_getresuid +#ifdef __NR_getresuid + {"getresuid", __NR_getresuid}, +#endif +#endif +#ifdef SYS_getrlimit +#ifdef __NR_getrlimit + {"getrlimit", __NR_getrlimit}, +#endif +#endif +#ifdef SYS_getrusage +#ifdef __NR_getrusage + {"getrusage", __NR_getrusage}, +#endif +#endif +#ifdef SYS_getsid +#ifdef __NR_getsid + {"getsid", __NR_getsid}, +#endif +#endif +#ifdef SYS_getsockname +#ifdef __NR_getsockname + {"getsockname", __NR_getsockname}, +#endif +#endif +#ifdef SYS_getsockopt +#ifdef __NR_getsockopt + {"getsockopt", __NR_getsockopt}, +#endif +#endif +#ifdef SYS_gettid +#ifdef __NR_gettid + {"gettid", __NR_gettid}, +#endif +#endif +#ifdef SYS_gettimeofday +#ifdef __NR_gettimeofday + {"gettimeofday", __NR_gettimeofday}, +#endif +#endif +#ifdef SYS_getuid +#ifdef __NR_getuid + {"getuid", __NR_getuid}, +#endif +#endif +#ifdef SYS_getxattr +#ifdef __NR_getxattr + {"getxattr", __NR_getxattr}, +#endif +#endif +#ifdef SYS_init_module +#ifdef __NR_init_module + {"init_module", __NR_init_module}, +#endif +#endif +#ifdef SYS_inotify_add_watch +#ifdef __NR_inotify_add_watch + {"inotify_add_watch", __NR_inotify_add_watch}, +#endif +#endif +#ifdef SYS_inotify_init +#ifdef __NR_inotify_init + {"inotify_init", __NR_inotify_init}, +#endif +#endif +#ifdef SYS_inotify_init1 +#ifdef __NR_inotify_init1 + {"inotify_init1", __NR_inotify_init1}, +#endif +#endif +#ifdef SYS_inotify_rm_watch +#ifdef __NR_inotify_rm_watch + {"inotify_rm_watch", __NR_inotify_rm_watch}, +#endif +#endif +#ifdef SYS_io_cancel +#ifdef __NR_io_cancel + {"io_cancel", __NR_io_cancel}, +#endif +#endif +#ifdef SYS_io_destroy +#ifdef __NR_io_destroy + {"io_destroy", __NR_io_destroy}, +#endif +#endif +#ifdef SYS_io_getevents +#ifdef __NR_io_getevents + {"io_getevents", __NR_io_getevents}, +#endif +#endif +#ifdef SYS_io_setup +#ifdef __NR_io_setup + {"io_setup", __NR_io_setup}, +#endif +#endif +#ifdef SYS_io_submit +#ifdef __NR_io_submit + {"io_submit", __NR_io_submit}, +#endif +#endif +#ifdef SYS_ioctl +#ifdef __NR_ioctl + {"ioctl", __NR_ioctl}, +#endif +#endif +#ifdef SYS_ioperm +#ifdef __NR_ioperm + {"ioperm", __NR_ioperm}, +#endif +#endif +#ifdef SYS_iopl +#ifdef __NR_iopl + {"iopl", __NR_iopl}, +#endif +#endif +#ifdef SYS_ioprio_get +#ifdef __NR_ioprio_get + {"ioprio_get", __NR_ioprio_get}, +#endif +#endif +#ifdef SYS_ioprio_set +#ifdef __NR_ioprio_set + {"ioprio_set", __NR_ioprio_set}, +#endif +#endif +#ifdef SYS_kcmp +#ifdef __NR_kcmp + {"kcmp", __NR_kcmp}, +#endif +#endif +#ifdef SYS_kexec_load +#ifdef __NR_kexec_load + {"kexec_load", __NR_kexec_load}, +#endif +#endif +#ifdef SYS_keyctl +#ifdef __NR_keyctl + {"keyctl", __NR_keyctl}, +#endif +#endif +#ifdef SYS_kill +#ifdef __NR_kill + {"kill", __NR_kill}, +#endif +#endif +#ifdef SYS_lchown +#ifdef __NR_lchown + {"lchown", __NR_lchown}, +#endif +#endif +#ifdef SYS_lgetxattr +#ifdef __NR_lgetxattr + {"lgetxattr", __NR_lgetxattr}, +#endif +#endif +#ifdef SYS_link +#ifdef __NR_link + {"link", __NR_link}, +#endif +#endif +#ifdef SYS_linkat +#ifdef __NR_linkat + {"linkat", __NR_linkat}, +#endif +#endif +#ifdef SYS_listen +#ifdef __NR_listen + {"listen", __NR_listen}, +#endif +#endif +#ifdef SYS_listxattr +#ifdef __NR_listxattr + {"listxattr", __NR_listxattr}, +#endif +#endif +#ifdef SYS_llistxattr +#ifdef __NR_llistxattr + {"llistxattr", __NR_llistxattr}, +#endif +#endif +#ifdef SYS_lookup_dcookie +#ifdef __NR_lookup_dcookie + {"lookup_dcookie", __NR_lookup_dcookie}, +#endif +#endif +#ifdef SYS_lremovexattr +#ifdef __NR_lremovexattr + {"lremovexattr", __NR_lremovexattr}, +#endif +#endif +#ifdef SYS_lseek +#ifdef __NR_lseek + {"lseek", __NR_lseek}, +#endif +#endif +#ifdef SYS_lsetxattr +#ifdef __NR_lsetxattr + {"lsetxattr", __NR_lsetxattr}, +#endif +#endif +#ifdef SYS_lstat +#ifdef __NR_lstat + {"lstat", __NR_lstat}, +#endif +#endif +#ifdef SYS_madvise +#ifdef __NR_madvise + {"madvise", __NR_madvise}, +#endif +#endif +#ifdef SYS_mbind +#ifdef __NR_mbind + {"mbind", __NR_mbind}, +#endif +#endif +#ifdef SYS_migrate_pages +#ifdef __NR_migrate_pages + {"migrate_pages", __NR_migrate_pages}, +#endif +#endif +#ifdef SYS_mincore +#ifdef __NR_mincore + {"mincore", __NR_mincore}, +#endif +#endif +#ifdef SYS_mkdir +#ifdef __NR_mkdir + {"mkdir", __NR_mkdir}, +#endif +#endif +#ifdef SYS_mkdirat +#ifdef __NR_mkdirat + {"mkdirat", __NR_mkdirat}, +#endif +#endif +#ifdef SYS_mknod +#ifdef __NR_mknod + {"mknod", __NR_mknod}, +#endif +#endif +#ifdef SYS_mknodat +#ifdef __NR_mknodat + {"mknodat", __NR_mknodat}, +#endif +#endif +#ifdef SYS_mlock +#ifdef __NR_mlock + {"mlock", __NR_mlock}, +#endif +#endif +#ifdef SYS_mlockall +#ifdef __NR_mlockall + {"mlockall", __NR_mlockall}, +#endif +#endif +#ifdef SYS_mmap +#ifdef __NR_mmap + {"mmap", __NR_mmap}, +#endif +#endif +#ifdef SYS_modify_ldt +#ifdef __NR_modify_ldt + {"modify_ldt", __NR_modify_ldt}, +#endif +#endif +#ifdef SYS_mount +#ifdef __NR_mount + {"mount", __NR_mount}, +#endif +#endif +#ifdef SYS_move_pages +#ifdef __NR_move_pages + {"move_pages", __NR_move_pages}, +#endif +#endif +#ifdef SYS_mprotect +#ifdef __NR_mprotect + {"mprotect", __NR_mprotect}, +#endif +#endif +#ifdef SYS_mq_getsetattr +#ifdef __NR_mq_getsetattr + {"mq_getsetattr", __NR_mq_getsetattr}, +#endif +#endif +#ifdef SYS_mq_notify +#ifdef __NR_mq_notify + {"mq_notify", __NR_mq_notify}, +#endif +#endif +#ifdef SYS_mq_open +#ifdef __NR_mq_open + {"mq_open", __NR_mq_open}, +#endif +#endif +#ifdef SYS_mq_timedreceive +#ifdef __NR_mq_timedreceive + {"mq_timedreceive", __NR_mq_timedreceive}, +#endif +#endif +#ifdef SYS_mq_timedsend +#ifdef __NR_mq_timedsend + {"mq_timedsend", __NR_mq_timedsend}, +#endif +#endif +#ifdef SYS_mq_unlink +#ifdef __NR_mq_unlink + {"mq_unlink", __NR_mq_unlink}, +#endif +#endif +#ifdef SYS_mremap +#ifdef __NR_mremap + {"mremap", __NR_mremap}, +#endif +#endif +#ifdef SYS_msgctl +#ifdef __NR_msgctl + {"msgctl", __NR_msgctl}, +#endif +#endif +#ifdef SYS_msgget +#ifdef __NR_msgget + {"msgget", __NR_msgget}, +#endif +#endif +#ifdef SYS_msgrcv +#ifdef __NR_msgrcv + {"msgrcv", __NR_msgrcv}, +#endif +#endif +#ifdef SYS_msgsnd +#ifdef __NR_msgsnd + {"msgsnd", __NR_msgsnd}, +#endif +#endif +#ifdef SYS_msync +#ifdef __NR_msync + {"msync", __NR_msync}, +#endif +#endif +#ifdef SYS_munlock +#ifdef __NR_munlock + {"munlock", __NR_munlock}, +#endif +#endif +#ifdef SYS_munlockall +#ifdef __NR_munlockall + {"munlockall", __NR_munlockall}, +#endif +#endif +#ifdef SYS_munmap +#ifdef __NR_munmap + {"munmap", __NR_munmap}, +#endif +#endif +#ifdef SYS_name_to_handle_at +#ifdef __NR_name_to_handle_at + {"name_to_handle_at", __NR_name_to_handle_at}, +#endif +#endif +#ifdef SYS_nanosleep +#ifdef __NR_nanosleep + {"nanosleep", __NR_nanosleep}, +#endif +#endif +#ifdef SYS_newfstatat +#ifdef __NR_newfstatat + {"newfstatat", __NR_newfstatat}, +#endif +#endif +#ifdef SYS_nfsservctl +#ifdef __NR_nfsservctl + {"nfsservctl", __NR_nfsservctl}, +#endif +#endif +#ifdef SYS_open +#ifdef __NR_open + {"open", __NR_open}, +#endif +#endif +#ifdef SYS_open_by_handle_at +#ifdef __NR_open_by_handle_at + {"open_by_handle_at", __NR_open_by_handle_at}, +#endif +#endif +#ifdef SYS_openat +#ifdef __NR_openat + {"openat", __NR_openat}, +#endif +#endif +#ifdef SYS_pause +#ifdef __NR_pause + {"pause", __NR_pause}, +#endif +#endif +#ifdef SYS_perf_event_open +#ifdef __NR_perf_event_open + {"perf_event_open", __NR_perf_event_open}, +#endif +#endif +#ifdef SYS_personality +#ifdef __NR_personality + {"personality", __NR_personality}, +#endif +#endif +#ifdef SYS_pipe +#ifdef __NR_pipe + {"pipe", __NR_pipe}, +#endif +#endif +#ifdef SYS_pipe2 +#ifdef __NR_pipe2 + {"pipe2", __NR_pipe2}, +#endif +#endif +#ifdef SYS_pivot_root +#ifdef __NR_pivot_root + {"pivot_root", __NR_pivot_root}, +#endif +#endif +#ifdef SYS_poll +#ifdef __NR_poll + {"poll", __NR_poll}, +#endif +#endif +#ifdef SYS_ppoll +#ifdef __NR_ppoll + {"ppoll", __NR_ppoll}, +#endif +#endif +#ifdef SYS_prctl +#ifdef __NR_prctl + {"prctl", __NR_prctl}, +#endif +#endif +#ifdef SYS_pread64 +#ifdef __NR_pread64 + {"pread64", __NR_pread64}, +#endif +#endif +#ifdef SYS_preadv +#ifdef __NR_preadv + {"preadv", __NR_preadv}, +#endif +#endif +#ifdef SYS_prlimit64 +#ifdef __NR_prlimit64 + {"prlimit64", __NR_prlimit64}, +#endif +#endif +#ifdef SYS_process_vm_readv +#ifdef __NR_process_vm_readv + {"process_vm_readv", __NR_process_vm_readv}, +#endif +#endif +#ifdef SYS_process_vm_writev +#ifdef __NR_process_vm_writev + {"process_vm_writev", __NR_process_vm_writev}, +#endif +#endif +#ifdef SYS_pselect6 +#ifdef __NR_pselect6 + {"pselect6", __NR_pselect6}, +#endif +#endif +#ifdef SYS_ptrace +#ifdef __NR_ptrace + {"ptrace", __NR_ptrace}, +#endif +#endif +#ifdef SYS_putpmsg +#ifdef __NR_putpmsg + {"putpmsg", __NR_putpmsg}, +#endif +#endif +#ifdef SYS_pwrite64 +#ifdef __NR_pwrite64 + {"pwrite64", __NR_pwrite64}, +#endif +#endif +#ifdef SYS_pwritev +#ifdef __NR_pwritev + {"pwritev", __NR_pwritev}, +#endif +#endif +#ifdef SYS_query_module +#ifdef __NR_query_module + {"query_module", __NR_query_module}, +#endif +#endif +#ifdef SYS_quotactl +#ifdef __NR_quotactl + {"quotactl", __NR_quotactl}, +#endif +#endif +#ifdef SYS_read +#ifdef __NR_read + {"read", __NR_read}, +#endif +#endif +#ifdef SYS_readahead +#ifdef __NR_readahead + {"readahead", __NR_readahead}, +#endif +#endif +#ifdef SYS_readlink +#ifdef __NR_readlink + {"readlink", __NR_readlink}, +#endif +#endif +#ifdef SYS_readlinkat +#ifdef __NR_readlinkat + {"readlinkat", __NR_readlinkat}, +#endif +#endif +#ifdef SYS_readv +#ifdef __NR_readv + {"readv", __NR_readv}, +#endif +#endif +#ifdef SYS_reboot +#ifdef __NR_reboot + {"reboot", __NR_reboot}, +#endif +#endif +#ifdef SYS_recvfrom +#ifdef __NR_recvfrom + {"recvfrom", __NR_recvfrom}, +#endif +#endif +#ifdef SYS_recvmmsg +#ifdef __NR_recvmmsg + {"recvmmsg", __NR_recvmmsg}, +#endif +#endif +#ifdef SYS_recvmsg +#ifdef __NR_recvmsg + {"recvmsg", __NR_recvmsg}, +#endif +#endif +#ifdef SYS_remap_file_pages +#ifdef __NR_remap_file_pages + {"remap_file_pages", __NR_remap_file_pages}, +#endif +#endif +#ifdef SYS_removexattr +#ifdef __NR_removexattr + {"removexattr", __NR_removexattr}, +#endif +#endif +#ifdef SYS_rename +#ifdef __NR_rename + {"rename", __NR_rename}, +#endif +#endif +#ifdef SYS_renameat +#ifdef __NR_renameat + {"renameat", __NR_renameat}, +#endif +#endif +#ifdef SYS_request_key +#ifdef __NR_request_key + {"request_key", __NR_request_key}, +#endif +#endif +#ifdef SYS_restart_syscall +#ifdef __NR_restart_syscall + {"restart_syscall", __NR_restart_syscall}, +#endif +#endif +#ifdef SYS_rmdir +#ifdef __NR_rmdir + {"rmdir", __NR_rmdir}, +#endif +#endif +#ifdef SYS_rt_sigaction +#ifdef __NR_rt_sigaction + {"rt_sigaction", __NR_rt_sigaction}, +#endif +#endif +#ifdef SYS_rt_sigpending +#ifdef __NR_rt_sigpending + {"rt_sigpending", __NR_rt_sigpending}, +#endif +#endif +#ifdef SYS_rt_sigprocmask +#ifdef __NR_rt_sigprocmask + {"rt_sigprocmask", __NR_rt_sigprocmask}, +#endif +#endif +#ifdef SYS_rt_sigqueueinfo +#ifdef __NR_rt_sigqueueinfo + {"rt_sigqueueinfo", __NR_rt_sigqueueinfo}, +#endif +#endif +#ifdef SYS_rt_sigreturn +#ifdef __NR_rt_sigreturn + {"rt_sigreturn", __NR_rt_sigreturn}, +#endif +#endif +#ifdef SYS_rt_sigsuspend +#ifdef __NR_rt_sigsuspend + {"rt_sigsuspend", __NR_rt_sigsuspend}, +#endif +#endif +#ifdef SYS_rt_sigtimedwait +#ifdef __NR_rt_sigtimedwait + {"rt_sigtimedwait", __NR_rt_sigtimedwait}, +#endif +#endif +#ifdef SYS_rt_tgsigqueueinfo +#ifdef __NR_rt_tgsigqueueinfo + {"rt_tgsigqueueinfo", __NR_rt_tgsigqueueinfo}, +#endif +#endif +#ifdef SYS_sched_get_priority_max +#ifdef __NR_sched_get_priority_max + {"sched_get_priority_max", __NR_sched_get_priority_max}, +#endif +#endif +#ifdef SYS_sched_get_priority_min +#ifdef __NR_sched_get_priority_min + {"sched_get_priority_min", __NR_sched_get_priority_min}, +#endif +#endif +#ifdef SYS_sched_getaffinity +#ifdef __NR_sched_getaffinity + {"sched_getaffinity", __NR_sched_getaffinity}, +#endif +#endif +#ifdef SYS_sched_getparam +#ifdef __NR_sched_getparam + {"sched_getparam", __NR_sched_getparam}, +#endif +#endif +#ifdef SYS_sched_getscheduler +#ifdef __NR_sched_getscheduler + {"sched_getscheduler", __NR_sched_getscheduler}, +#endif +#endif +#ifdef SYS_sched_rr_get_interval +#ifdef __NR_sched_rr_get_interval + {"sched_rr_get_interval", __NR_sched_rr_get_interval}, +#endif +#endif +#ifdef SYS_sched_setaffinity +#ifdef __NR_sched_setaffinity + {"sched_setaffinity", __NR_sched_setaffinity}, +#endif +#endif +#ifdef SYS_sched_setparam +#ifdef __NR_sched_setparam + {"sched_setparam", __NR_sched_setparam}, +#endif +#endif +#ifdef SYS_sched_setscheduler +#ifdef __NR_sched_setscheduler + {"sched_setscheduler", __NR_sched_setscheduler}, +#endif +#endif +#ifdef SYS_sched_yield +#ifdef __NR_sched_yield + {"sched_yield", __NR_sched_yield}, +#endif +#endif +#ifdef SYS_security +#ifdef __NR_security + {"security", __NR_security}, +#endif +#endif +#ifdef SYS_select +#ifdef __NR_select + {"select", __NR_select}, +#endif +#endif +#ifdef SYS_semctl +#ifdef __NR_semctl + {"semctl", __NR_semctl}, +#endif +#endif +#ifdef SYS_semget +#ifdef __NR_semget + {"semget", __NR_semget}, +#endif +#endif +#ifdef SYS_semop +#ifdef __NR_semop + {"semop", __NR_semop}, +#endif +#endif +#ifdef SYS_semtimedop +#ifdef __NR_semtimedop + {"semtimedop", __NR_semtimedop}, +#endif +#endif +#ifdef SYS_sendfile +#ifdef __NR_sendfile + {"sendfile", __NR_sendfile}, +#endif +#endif +#ifdef SYS_sendmmsg +#ifdef __NR_sendmmsg + {"sendmmsg", __NR_sendmmsg}, +#endif +#endif +#ifdef SYS_sendmsg +#ifdef __NR_sendmsg + {"sendmsg", __NR_sendmsg}, +#endif +#endif +#ifdef SYS_sendto +#ifdef __NR_sendto + {"sendto", __NR_sendto}, +#endif +#endif +#ifdef SYS_set_mempolicy +#ifdef __NR_set_mempolicy + {"set_mempolicy", __NR_set_mempolicy}, +#endif +#endif +#ifdef SYS_set_robust_list +#ifdef __NR_set_robust_list + {"set_robust_list", __NR_set_robust_list}, +#endif +#endif +#ifdef SYS_set_thread_area +#ifdef __NR_set_thread_area + {"set_thread_area", __NR_set_thread_area}, +#endif +#endif +#ifdef SYS_set_tid_address +#ifdef __NR_set_tid_address + {"set_tid_address", __NR_set_tid_address}, +#endif +#endif +#ifdef SYS_setdomainname +#ifdef __NR_setdomainname + {"setdomainname", __NR_setdomainname}, +#endif +#endif +#ifdef SYS_setfsgid +#ifdef __NR_setfsgid + {"setfsgid", __NR_setfsgid}, +#endif +#endif +#ifdef SYS_setfsuid +#ifdef __NR_setfsuid + {"setfsuid", __NR_setfsuid}, +#endif +#endif +#ifdef SYS_setgid +#ifdef __NR_setgid + {"setgid", __NR_setgid}, +#endif +#endif +#ifdef SYS_setgroups +#ifdef __NR_setgroups + {"setgroups", __NR_setgroups}, +#endif +#endif +#ifdef SYS_sethostname +#ifdef __NR_sethostname + {"sethostname", __NR_sethostname}, +#endif +#endif +#ifdef SYS_setitimer +#ifdef __NR_setitimer + {"setitimer", __NR_setitimer}, +#endif +#endif +#ifdef SYS_setns +#ifdef __NR_setns + {"setns", __NR_setns}, +#endif +#endif +#ifdef SYS_setpgid +#ifdef __NR_setpgid + {"setpgid", __NR_setpgid}, +#endif +#endif +#ifdef SYS_setpriority +#ifdef __NR_setpriority + {"setpriority", __NR_setpriority}, +#endif +#endif +#ifdef SYS_setregid +#ifdef __NR_setregid + {"setregid", __NR_setregid}, +#endif +#endif +#ifdef SYS_setresgid +#ifdef __NR_setresgid + {"setresgid", __NR_setresgid}, +#endif +#endif +#ifdef SYS_setresuid +#ifdef __NR_setresuid + {"setresuid", __NR_setresuid}, +#endif +#endif +#ifdef SYS_setreuid +#ifdef __NR_setreuid + {"setreuid", __NR_setreuid}, +#endif +#endif +#ifdef SYS_setrlimit +#ifdef __NR_setrlimit + {"setrlimit", __NR_setrlimit}, +#endif +#endif +#ifdef SYS_setsid +#ifdef __NR_setsid + {"setsid", __NR_setsid}, +#endif +#endif +#ifdef SYS_setsockopt +#ifdef __NR_setsockopt + {"setsockopt", __NR_setsockopt}, +#endif +#endif +#ifdef SYS_settimeofday +#ifdef __NR_settimeofday + {"settimeofday", __NR_settimeofday}, +#endif +#endif +#ifdef SYS_setuid +#ifdef __NR_setuid + {"setuid", __NR_setuid}, +#endif +#endif +#ifdef SYS_setxattr +#ifdef __NR_setxattr + {"setxattr", __NR_setxattr}, +#endif +#endif +#ifdef SYS_shmat +#ifdef __NR_shmat + {"shmat", __NR_shmat}, +#endif +#endif +#ifdef SYS_shmctl +#ifdef __NR_shmctl + {"shmctl", __NR_shmctl}, +#endif +#endif +#ifdef SYS_shmdt +#ifdef __NR_shmdt + {"shmdt", __NR_shmdt}, +#endif +#endif +#ifdef SYS_shmget +#ifdef __NR_shmget + {"shmget", __NR_shmget}, +#endif +#endif +#ifdef SYS_shutdown +#ifdef __NR_shutdown + {"shutdown", __NR_shutdown}, +#endif +#endif +#ifdef SYS_sigaltstack +#ifdef __NR_sigaltstack + {"sigaltstack", __NR_sigaltstack}, +#endif +#endif +#ifdef SYS_signalfd +#ifdef __NR_signalfd + {"signalfd", __NR_signalfd}, +#endif +#endif +#ifdef SYS_signalfd4 +#ifdef __NR_signalfd4 + {"signalfd4", __NR_signalfd4}, +#endif +#endif +#ifdef SYS_socket +#ifdef __NR_socket + {"socket", __NR_socket}, +#endif +#endif +#ifdef SYS_socketpair +#ifdef __NR_socketpair + {"socketpair", __NR_socketpair}, +#endif +#endif +#ifdef SYS_splice +#ifdef __NR_splice + {"splice", __NR_splice}, +#endif +#endif +#ifdef SYS_stat +#ifdef __NR_stat + {"stat", __NR_stat}, +#endif +#endif +#ifdef SYS_statfs +#ifdef __NR_statfs + {"statfs", __NR_statfs}, +#endif +#endif +#ifdef SYS_swapoff +#ifdef __NR_swapoff + {"swapoff", __NR_swapoff}, +#endif +#endif +#ifdef SYS_swapon +#ifdef __NR_swapon + {"swapon", __NR_swapon}, +#endif +#endif +#ifdef SYS_symlink +#ifdef __NR_symlink + {"symlink", __NR_symlink}, +#endif +#endif +#ifdef SYS_symlinkat +#ifdef __NR_symlinkat + {"symlinkat", __NR_symlinkat}, +#endif +#endif +#ifdef SYS_sync +#ifdef __NR_sync + {"sync", __NR_sync}, +#endif +#endif +#ifdef SYS_sync_file_range +#ifdef __NR_sync_file_range + {"sync_file_range", __NR_sync_file_range}, +#endif +#endif +#ifdef SYS_syncfs +#ifdef __NR_syncfs + {"syncfs", __NR_syncfs}, +#endif +#endif +#ifdef SYS_sysfs +#ifdef __NR_sysfs + {"sysfs", __NR_sysfs}, +#endif +#endif +#ifdef SYS_sysinfo +#ifdef __NR_sysinfo + {"sysinfo", __NR_sysinfo}, +#endif +#endif +#ifdef SYS_syslog +#ifdef __NR_syslog + {"syslog", __NR_syslog}, +#endif +#endif +#ifdef SYS_tee +#ifdef __NR_tee + {"tee", __NR_tee}, +#endif +#endif +#ifdef SYS_tgkill +#ifdef __NR_tgkill + {"tgkill", __NR_tgkill}, +#endif +#endif +#ifdef SYS_time +#ifdef __NR_time + {"time", __NR_time}, +#endif +#endif +#ifdef SYS_timer_create +#ifdef __NR_timer_create + {"timer_create", __NR_timer_create}, +#endif +#endif +#ifdef SYS_timer_delete +#ifdef __NR_timer_delete + {"timer_delete", __NR_timer_delete}, +#endif +#endif +#ifdef SYS_timer_getoverrun +#ifdef __NR_timer_getoverrun + {"timer_getoverrun", __NR_timer_getoverrun}, +#endif +#endif +#ifdef SYS_timer_gettime +#ifdef __NR_timer_gettime + {"timer_gettime", __NR_timer_gettime}, +#endif +#endif +#ifdef SYS_timer_settime +#ifdef __NR_timer_settime + {"timer_settime", __NR_timer_settime}, +#endif +#endif +#ifdef SYS_timerfd_create +#ifdef __NR_timerfd_create + {"timerfd_create", __NR_timerfd_create}, +#endif +#endif +#ifdef SYS_timerfd_gettime +#ifdef __NR_timerfd_gettime + {"timerfd_gettime", __NR_timerfd_gettime}, +#endif +#endif +#ifdef SYS_timerfd_settime +#ifdef __NR_timerfd_settime + {"timerfd_settime", __NR_timerfd_settime}, +#endif +#endif +#ifdef SYS_times +#ifdef __NR_times + {"times", __NR_times}, +#endif +#endif +#ifdef SYS_tkill +#ifdef __NR_tkill + {"tkill", __NR_tkill}, +#endif +#endif +#ifdef SYS_truncate +#ifdef __NR_truncate + {"truncate", __NR_truncate}, +#endif +#endif +#ifdef SYS_tuxcall +#ifdef __NR_tuxcall + {"tuxcall", __NR_tuxcall}, +#endif +#endif +#ifdef SYS_umask +#ifdef __NR_umask + {"umask", __NR_umask}, +#endif +#endif +#ifdef SYS_umount2 +#ifdef __NR_umount2 + {"umount2", __NR_umount2}, +#endif +#endif +#ifdef SYS_uname +#ifdef __NR_uname + {"uname", __NR_uname}, +#endif +#endif +#ifdef SYS_unlink +#ifdef __NR_unlink + {"unlink", __NR_unlink}, +#endif +#endif +#ifdef SYS_unlinkat +#ifdef __NR_unlinkat + {"unlinkat", __NR_unlinkat}, +#endif +#endif +#ifdef SYS_unshare +#ifdef __NR_unshare + {"unshare", __NR_unshare}, +#endif +#endif +#ifdef SYS_uselib +#ifdef __NR_uselib + {"uselib", __NR_uselib}, +#endif +#endif +#ifdef SYS_ustat +#ifdef __NR_ustat + {"ustat", __NR_ustat}, +#endif +#endif +#ifdef SYS_utime +#ifdef __NR_utime + {"utime", __NR_utime}, +#endif +#endif +#ifdef SYS_utimensat +#ifdef __NR_utimensat + {"utimensat", __NR_utimensat}, +#endif +#endif +#ifdef SYS_utimes +#ifdef __NR_utimes + {"utimes", __NR_utimes}, +#endif +#endif +#ifdef SYS_vfork +#ifdef __NR_vfork + {"vfork", __NR_vfork}, +#endif +#endif +#ifdef SYS_vhangup +#ifdef __NR_vhangup + {"vhangup", __NR_vhangup}, +#endif +#endif +#ifdef SYS_vmsplice +#ifdef __NR_vmsplice + {"vmsplice", __NR_vmsplice}, +#endif +#endif +#ifdef SYS_vserver +#ifdef __NR_vserver + {"vserver", __NR_vserver}, +#endif +#endif +#ifdef SYS_wait4 +#ifdef __NR_wait4 + {"wait4", __NR_wait4}, +#endif +#endif +#ifdef SYS_waitid +#ifdef __NR_waitid + {"waitid", __NR_waitid}, +#endif +#endif +#ifdef SYS_write +#ifdef __NR_write + {"write", __NR_write}, +#endif +#endif +#ifdef SYS_writev +#ifdef __NR_writev + {"writev", __NR_writev}, +#endif +#endif +#endif +#if defined __x86_64__ && defined __ILP32__ +#ifdef SYS_accept +#ifdef __NR_accept + {"accept", __NR_accept}, +#endif +#endif +#ifdef SYS_accept4 +#ifdef __NR_accept4 + {"accept4", __NR_accept4}, +#endif +#endif +#ifdef SYS_access +#ifdef __NR_access + {"access", __NR_access}, +#endif +#endif +#ifdef SYS_acct +#ifdef __NR_acct + {"acct", __NR_acct}, +#endif +#endif +#ifdef SYS_add_key +#ifdef __NR_add_key + {"add_key", __NR_add_key}, +#endif +#endif +#ifdef SYS_adjtimex +#ifdef __NR_adjtimex + {"adjtimex", __NR_adjtimex}, +#endif +#endif +#ifdef SYS_afs_syscall +#ifdef __NR_afs_syscall + {"afs_syscall", __NR_afs_syscall}, +#endif +#endif +#ifdef SYS_alarm +#ifdef __NR_alarm + {"alarm", __NR_alarm}, +#endif +#endif +#ifdef SYS_arch_prctl +#ifdef __NR_arch_prctl + {"arch_prctl", __NR_arch_prctl}, +#endif +#endif +#ifdef SYS_bind +#ifdef __NR_bind + {"bind", __NR_bind}, +#endif +#endif +#ifdef SYS_brk +#ifdef __NR_brk + {"brk", __NR_brk}, +#endif +#endif +#ifdef SYS_capget +#ifdef __NR_capget + {"capget", __NR_capget}, +#endif +#endif +#ifdef SYS_capset +#ifdef __NR_capset + {"capset", __NR_capset}, +#endif +#endif +#ifdef SYS_chdir +#ifdef __NR_chdir + {"chdir", __NR_chdir}, +#endif +#endif +#ifdef SYS_chmod +#ifdef __NR_chmod + {"chmod", __NR_chmod}, +#endif +#endif +#ifdef SYS_chown +#ifdef __NR_chown + {"chown", __NR_chown}, +#endif +#endif +#ifdef SYS_chroot +#ifdef __NR_chroot + {"chroot", __NR_chroot}, +#endif +#endif +#ifdef SYS_clock_adjtime +#ifdef __NR_clock_adjtime + {"clock_adjtime", __NR_clock_adjtime}, +#endif +#endif +#ifdef SYS_clock_getres +#ifdef __NR_clock_getres + {"clock_getres", __NR_clock_getres}, +#endif +#endif +#ifdef SYS_clock_gettime +#ifdef __NR_clock_gettime + {"clock_gettime", __NR_clock_gettime}, +#endif +#endif +#ifdef SYS_clock_nanosleep +#ifdef __NR_clock_nanosleep + {"clock_nanosleep", __NR_clock_nanosleep}, +#endif +#endif +#ifdef SYS_clock_settime +#ifdef __NR_clock_settime + {"clock_settime", __NR_clock_settime}, +#endif +#endif +#ifdef SYS_clone +#ifdef __NR_clone + {"clone", __NR_clone}, +#endif +#endif +#ifdef SYS_close +#ifdef __NR_close + {"close", __NR_close}, +#endif +#endif +#ifdef SYS_connect +#ifdef __NR_connect + {"connect", __NR_connect}, +#endif +#endif +#ifdef SYS_creat +#ifdef __NR_creat + {"creat", __NR_creat}, +#endif +#endif +#ifdef SYS_delete_module +#ifdef __NR_delete_module + {"delete_module", __NR_delete_module}, +#endif +#endif +#ifdef SYS_dup +#ifdef __NR_dup + {"dup", __NR_dup}, +#endif +#endif +#ifdef SYS_dup2 +#ifdef __NR_dup2 + {"dup2", __NR_dup2}, +#endif +#endif +#ifdef SYS_dup3 +#ifdef __NR_dup3 + {"dup3", __NR_dup3}, +#endif +#endif +#ifdef SYS_epoll_create +#ifdef __NR_epoll_create + {"epoll_create", __NR_epoll_create}, +#endif +#endif +#ifdef SYS_epoll_create1 +#ifdef __NR_epoll_create1 + {"epoll_create1", __NR_epoll_create1}, +#endif +#endif +#ifdef SYS_epoll_ctl +#ifdef __NR_epoll_ctl + {"epoll_ctl", __NR_epoll_ctl}, +#endif +#endif +#ifdef SYS_epoll_pwait +#ifdef __NR_epoll_pwait + {"epoll_pwait", __NR_epoll_pwait}, +#endif +#endif +#ifdef SYS_epoll_wait +#ifdef __NR_epoll_wait + {"epoll_wait", __NR_epoll_wait}, +#endif +#endif +#ifdef SYS_eventfd +#ifdef __NR_eventfd + {"eventfd", __NR_eventfd}, +#endif +#endif +#ifdef SYS_eventfd2 +#ifdef __NR_eventfd2 + {"eventfd2", __NR_eventfd2}, +#endif +#endif +#ifdef SYS_execve +#ifdef __NR_execve + {"execve", __NR_execve}, +#endif +#endif +#ifdef SYS_exit +#ifdef __NR_exit + {"exit", __NR_exit}, +#endif +#endif +#ifdef SYS_exit_group +#ifdef __NR_exit_group + {"exit_group", __NR_exit_group}, +#endif +#endif +#ifdef SYS_faccessat +#ifdef __NR_faccessat + {"faccessat", __NR_faccessat}, +#endif +#endif +#ifdef SYS_fadvise64 +#ifdef __NR_fadvise64 + {"fadvise64", __NR_fadvise64}, +#endif +#endif +#ifdef SYS_fallocate +#ifdef __NR_fallocate + {"fallocate", __NR_fallocate}, +#endif +#endif +#ifdef SYS_fanotify_init +#ifdef __NR_fanotify_init + {"fanotify_init", __NR_fanotify_init}, +#endif +#endif +#ifdef SYS_fanotify_mark +#ifdef __NR_fanotify_mark + {"fanotify_mark", __NR_fanotify_mark}, +#endif +#endif +#ifdef SYS_fchdir +#ifdef __NR_fchdir + {"fchdir", __NR_fchdir}, +#endif +#endif +#ifdef SYS_fchmod +#ifdef __NR_fchmod + {"fchmod", __NR_fchmod}, +#endif +#endif +#ifdef SYS_fchmodat +#ifdef __NR_fchmodat + {"fchmodat", __NR_fchmodat}, +#endif +#endif +#ifdef SYS_fchown +#ifdef __NR_fchown + {"fchown", __NR_fchown}, +#endif +#endif +#ifdef SYS_fchownat +#ifdef __NR_fchownat + {"fchownat", __NR_fchownat}, +#endif +#endif +#ifdef SYS_fcntl +#ifdef __NR_fcntl + {"fcntl", __NR_fcntl}, +#endif +#endif +#ifdef SYS_fdatasync +#ifdef __NR_fdatasync + {"fdatasync", __NR_fdatasync}, +#endif +#endif +#ifdef SYS_fgetxattr +#ifdef __NR_fgetxattr + {"fgetxattr", __NR_fgetxattr}, +#endif +#endif +#ifdef SYS_finit_module +#ifdef __NR_finit_module + {"finit_module", __NR_finit_module}, +#endif +#endif +#ifdef SYS_flistxattr +#ifdef __NR_flistxattr + {"flistxattr", __NR_flistxattr}, +#endif +#endif +#ifdef SYS_flock +#ifdef __NR_flock + {"flock", __NR_flock}, +#endif +#endif +#ifdef SYS_fork +#ifdef __NR_fork + {"fork", __NR_fork}, +#endif +#endif +#ifdef SYS_fremovexattr +#ifdef __NR_fremovexattr + {"fremovexattr", __NR_fremovexattr}, +#endif +#endif +#ifdef SYS_fsetxattr +#ifdef __NR_fsetxattr + {"fsetxattr", __NR_fsetxattr}, +#endif +#endif +#ifdef SYS_fstat +#ifdef __NR_fstat + {"fstat", __NR_fstat}, +#endif +#endif +#ifdef SYS_fstatfs +#ifdef __NR_fstatfs + {"fstatfs", __NR_fstatfs}, +#endif +#endif +#ifdef SYS_fsync +#ifdef __NR_fsync + {"fsync", __NR_fsync}, +#endif +#endif +#ifdef SYS_ftruncate +#ifdef __NR_ftruncate + {"ftruncate", __NR_ftruncate}, +#endif +#endif +#ifdef SYS_futex +#ifdef __NR_futex + {"futex", __NR_futex}, +#endif +#endif +#ifdef SYS_futimesat +#ifdef __NR_futimesat + {"futimesat", __NR_futimesat}, +#endif +#endif +#ifdef SYS_get_mempolicy +#ifdef __NR_get_mempolicy + {"get_mempolicy", __NR_get_mempolicy}, +#endif +#endif +#ifdef SYS_get_robust_list +#ifdef __NR_get_robust_list + {"get_robust_list", __NR_get_robust_list}, +#endif +#endif +#ifdef SYS_getcpu +#ifdef __NR_getcpu + {"getcpu", __NR_getcpu}, +#endif +#endif +#ifdef SYS_getcwd +#ifdef __NR_getcwd + {"getcwd", __NR_getcwd}, +#endif +#endif +#ifdef SYS_getdents +#ifdef __NR_getdents + {"getdents", __NR_getdents}, +#endif +#endif +#ifdef SYS_getdents64 +#ifdef __NR_getdents64 + {"getdents64", __NR_getdents64}, +#endif +#endif +#ifdef SYS_getegid +#ifdef __NR_getegid + {"getegid", __NR_getegid}, +#endif +#endif +#ifdef SYS_geteuid +#ifdef __NR_geteuid + {"geteuid", __NR_geteuid}, +#endif +#endif +#ifdef SYS_getgid +#ifdef __NR_getgid + {"getgid", __NR_getgid}, +#endif +#endif +#ifdef SYS_getgroups +#ifdef __NR_getgroups + {"getgroups", __NR_getgroups}, +#endif +#endif +#ifdef SYS_getitimer +#ifdef __NR_getitimer + {"getitimer", __NR_getitimer}, +#endif +#endif +#ifdef SYS_getpeername +#ifdef __NR_getpeername + {"getpeername", __NR_getpeername}, +#endif +#endif +#ifdef SYS_getpgid +#ifdef __NR_getpgid + {"getpgid", __NR_getpgid}, +#endif +#endif +#ifdef SYS_getpgrp +#ifdef __NR_getpgrp + {"getpgrp", __NR_getpgrp}, +#endif +#endif +#ifdef SYS_getpid +#ifdef __NR_getpid + {"getpid", __NR_getpid}, +#endif +#endif +#ifdef SYS_getpmsg +#ifdef __NR_getpmsg + {"getpmsg", __NR_getpmsg}, +#endif +#endif +#ifdef SYS_getppid +#ifdef __NR_getppid + {"getppid", __NR_getppid}, +#endif +#endif +#ifdef SYS_getpriority +#ifdef __NR_getpriority + {"getpriority", __NR_getpriority}, +#endif +#endif +#ifdef SYS_getresgid +#ifdef __NR_getresgid + {"getresgid", __NR_getresgid}, +#endif +#endif +#ifdef SYS_getresuid +#ifdef __NR_getresuid + {"getresuid", __NR_getresuid}, +#endif +#endif +#ifdef SYS_getrlimit +#ifdef __NR_getrlimit + {"getrlimit", __NR_getrlimit}, +#endif +#endif +#ifdef SYS_getrusage +#ifdef __NR_getrusage + {"getrusage", __NR_getrusage}, +#endif +#endif +#ifdef SYS_getsid +#ifdef __NR_getsid + {"getsid", __NR_getsid}, +#endif +#endif +#ifdef SYS_getsockname +#ifdef __NR_getsockname + {"getsockname", __NR_getsockname}, +#endif +#endif +#ifdef SYS_getsockopt +#ifdef __NR_getsockopt + {"getsockopt", __NR_getsockopt}, +#endif +#endif +#ifdef SYS_gettid +#ifdef __NR_gettid + {"gettid", __NR_gettid}, +#endif +#endif +#ifdef SYS_gettimeofday +#ifdef __NR_gettimeofday + {"gettimeofday", __NR_gettimeofday}, +#endif +#endif +#ifdef SYS_getuid +#ifdef __NR_getuid + {"getuid", __NR_getuid}, +#endif +#endif +#ifdef SYS_getxattr +#ifdef __NR_getxattr + {"getxattr", __NR_getxattr}, +#endif +#endif +#ifdef SYS_init_module +#ifdef __NR_init_module + {"init_module", __NR_init_module}, +#endif +#endif +#ifdef SYS_inotify_add_watch +#ifdef __NR_inotify_add_watch + {"inotify_add_watch", __NR_inotify_add_watch}, +#endif +#endif +#ifdef SYS_inotify_init +#ifdef __NR_inotify_init + {"inotify_init", __NR_inotify_init}, +#endif +#endif +#ifdef SYS_inotify_init1 +#ifdef __NR_inotify_init1 + {"inotify_init1", __NR_inotify_init1}, +#endif +#endif +#ifdef SYS_inotify_rm_watch +#ifdef __NR_inotify_rm_watch + {"inotify_rm_watch", __NR_inotify_rm_watch}, +#endif +#endif +#ifdef SYS_io_cancel +#ifdef __NR_io_cancel + {"io_cancel", __NR_io_cancel}, +#endif +#endif +#ifdef SYS_io_destroy +#ifdef __NR_io_destroy + {"io_destroy", __NR_io_destroy}, +#endif +#endif +#ifdef SYS_io_getevents +#ifdef __NR_io_getevents + {"io_getevents", __NR_io_getevents}, +#endif +#endif +#ifdef SYS_io_setup +#ifdef __NR_io_setup + {"io_setup", __NR_io_setup}, +#endif +#endif +#ifdef SYS_io_submit +#ifdef __NR_io_submit + {"io_submit", __NR_io_submit}, +#endif +#endif +#ifdef SYS_ioctl +#ifdef __NR_ioctl + {"ioctl", __NR_ioctl}, +#endif +#endif +#ifdef SYS_ioperm +#ifdef __NR_ioperm + {"ioperm", __NR_ioperm}, +#endif +#endif +#ifdef SYS_iopl +#ifdef __NR_iopl + {"iopl", __NR_iopl}, +#endif +#endif +#ifdef SYS_ioprio_get +#ifdef __NR_ioprio_get + {"ioprio_get", __NR_ioprio_get}, +#endif +#endif +#ifdef SYS_ioprio_set +#ifdef __NR_ioprio_set + {"ioprio_set", __NR_ioprio_set}, +#endif +#endif +#ifdef SYS_kcmp +#ifdef __NR_kcmp + {"kcmp", __NR_kcmp}, +#endif +#endif +#ifdef SYS_kexec_load +#ifdef __NR_kexec_load + {"kexec_load", __NR_kexec_load}, +#endif +#endif +#ifdef SYS_keyctl +#ifdef __NR_keyctl + {"keyctl", __NR_keyctl}, +#endif +#endif +#ifdef SYS_kill +#ifdef __NR_kill + {"kill", __NR_kill}, +#endif +#endif +#ifdef SYS_lchown +#ifdef __NR_lchown + {"lchown", __NR_lchown}, +#endif +#endif +#ifdef SYS_lgetxattr +#ifdef __NR_lgetxattr + {"lgetxattr", __NR_lgetxattr}, +#endif +#endif +#ifdef SYS_link +#ifdef __NR_link + {"link", __NR_link}, +#endif +#endif +#ifdef SYS_linkat +#ifdef __NR_linkat + {"linkat", __NR_linkat}, +#endif +#endif +#ifdef SYS_listen +#ifdef __NR_listen + {"listen", __NR_listen}, +#endif +#endif +#ifdef SYS_listxattr +#ifdef __NR_listxattr + {"listxattr", __NR_listxattr}, +#endif +#endif +#ifdef SYS_llistxattr +#ifdef __NR_llistxattr + {"llistxattr", __NR_llistxattr}, +#endif +#endif +#ifdef SYS_lookup_dcookie +#ifdef __NR_lookup_dcookie + {"lookup_dcookie", __NR_lookup_dcookie}, +#endif +#endif +#ifdef SYS_lremovexattr +#ifdef __NR_lremovexattr + {"lremovexattr", __NR_lremovexattr}, +#endif +#endif +#ifdef SYS_lseek +#ifdef __NR_lseek + {"lseek", __NR_lseek}, +#endif +#endif +#ifdef SYS_lsetxattr +#ifdef __NR_lsetxattr + {"lsetxattr", __NR_lsetxattr}, +#endif +#endif +#ifdef SYS_lstat +#ifdef __NR_lstat + {"lstat", __NR_lstat}, +#endif +#endif +#ifdef SYS_madvise +#ifdef __NR_madvise + {"madvise", __NR_madvise}, +#endif +#endif +#ifdef SYS_mbind +#ifdef __NR_mbind + {"mbind", __NR_mbind}, +#endif +#endif +#ifdef SYS_migrate_pages +#ifdef __NR_migrate_pages + {"migrate_pages", __NR_migrate_pages}, +#endif +#endif +#ifdef SYS_mincore +#ifdef __NR_mincore + {"mincore", __NR_mincore}, +#endif +#endif +#ifdef SYS_mkdir +#ifdef __NR_mkdir + {"mkdir", __NR_mkdir}, +#endif +#endif +#ifdef SYS_mkdirat +#ifdef __NR_mkdirat + {"mkdirat", __NR_mkdirat}, +#endif +#endif +#ifdef SYS_mknod +#ifdef __NR_mknod + {"mknod", __NR_mknod}, +#endif +#endif +#ifdef SYS_mknodat +#ifdef __NR_mknodat + {"mknodat", __NR_mknodat}, +#endif +#endif +#ifdef SYS_mlock +#ifdef __NR_mlock + {"mlock", __NR_mlock}, +#endif +#endif +#ifdef SYS_mlockall +#ifdef __NR_mlockall + {"mlockall", __NR_mlockall}, +#endif +#endif +#ifdef SYS_mmap +#ifdef __NR_mmap + {"mmap", __NR_mmap}, +#endif +#endif +#ifdef SYS_modify_ldt +#ifdef __NR_modify_ldt + {"modify_ldt", __NR_modify_ldt}, +#endif +#endif +#ifdef SYS_mount +#ifdef __NR_mount + {"mount", __NR_mount}, +#endif +#endif +#ifdef SYS_move_pages +#ifdef __NR_move_pages + {"move_pages", __NR_move_pages}, +#endif +#endif +#ifdef SYS_mprotect +#ifdef __NR_mprotect + {"mprotect", __NR_mprotect}, +#endif +#endif +#ifdef SYS_mq_getsetattr +#ifdef __NR_mq_getsetattr + {"mq_getsetattr", __NR_mq_getsetattr}, +#endif +#endif +#ifdef SYS_mq_notify +#ifdef __NR_mq_notify + {"mq_notify", __NR_mq_notify}, +#endif +#endif +#ifdef SYS_mq_open +#ifdef __NR_mq_open + {"mq_open", __NR_mq_open}, +#endif +#endif +#ifdef SYS_mq_timedreceive +#ifdef __NR_mq_timedreceive + {"mq_timedreceive", __NR_mq_timedreceive}, +#endif +#endif +#ifdef SYS_mq_timedsend +#ifdef __NR_mq_timedsend + {"mq_timedsend", __NR_mq_timedsend}, +#endif +#endif +#ifdef SYS_mq_unlink +#ifdef __NR_mq_unlink + {"mq_unlink", __NR_mq_unlink}, +#endif +#endif +#ifdef SYS_mremap +#ifdef __NR_mremap + {"mremap", __NR_mremap}, +#endif +#endif +#ifdef SYS_msgctl +#ifdef __NR_msgctl + {"msgctl", __NR_msgctl}, +#endif +#endif +#ifdef SYS_msgget +#ifdef __NR_msgget + {"msgget", __NR_msgget}, +#endif +#endif +#ifdef SYS_msgrcv +#ifdef __NR_msgrcv + {"msgrcv", __NR_msgrcv}, +#endif +#endif +#ifdef SYS_msgsnd +#ifdef __NR_msgsnd + {"msgsnd", __NR_msgsnd}, +#endif +#endif +#ifdef SYS_msync +#ifdef __NR_msync + {"msync", __NR_msync}, +#endif +#endif +#ifdef SYS_munlock +#ifdef __NR_munlock + {"munlock", __NR_munlock}, +#endif +#endif +#ifdef SYS_munlockall +#ifdef __NR_munlockall + {"munlockall", __NR_munlockall}, +#endif +#endif +#ifdef SYS_munmap +#ifdef __NR_munmap + {"munmap", __NR_munmap}, +#endif +#endif +#ifdef SYS_name_to_handle_at +#ifdef __NR_name_to_handle_at + {"name_to_handle_at", __NR_name_to_handle_at}, +#endif +#endif +#ifdef SYS_nanosleep +#ifdef __NR_nanosleep + {"nanosleep", __NR_nanosleep}, +#endif +#endif +#ifdef SYS_newfstatat +#ifdef __NR_newfstatat + {"newfstatat", __NR_newfstatat}, +#endif +#endif +#ifdef SYS_open +#ifdef __NR_open + {"open", __NR_open}, +#endif +#endif +#ifdef SYS_open_by_handle_at +#ifdef __NR_open_by_handle_at + {"open_by_handle_at", __NR_open_by_handle_at}, +#endif +#endif +#ifdef SYS_openat +#ifdef __NR_openat + {"openat", __NR_openat}, +#endif +#endif +#ifdef SYS_pause +#ifdef __NR_pause + {"pause", __NR_pause}, +#endif +#endif +#ifdef SYS_perf_event_open +#ifdef __NR_perf_event_open + {"perf_event_open", __NR_perf_event_open}, +#endif +#endif +#ifdef SYS_personality +#ifdef __NR_personality + {"personality", __NR_personality}, +#endif +#endif +#ifdef SYS_pipe +#ifdef __NR_pipe + {"pipe", __NR_pipe}, +#endif +#endif +#ifdef SYS_pipe2 +#ifdef __NR_pipe2 + {"pipe2", __NR_pipe2}, +#endif +#endif +#ifdef SYS_pivot_root +#ifdef __NR_pivot_root + {"pivot_root", __NR_pivot_root}, +#endif +#endif +#ifdef SYS_poll +#ifdef __NR_poll + {"poll", __NR_poll}, +#endif +#endif +#ifdef SYS_ppoll +#ifdef __NR_ppoll + {"ppoll", __NR_ppoll}, +#endif +#endif +#ifdef SYS_prctl +#ifdef __NR_prctl + {"prctl", __NR_prctl}, +#endif +#endif +#ifdef SYS_pread64 +#ifdef __NR_pread64 + {"pread64", __NR_pread64}, +#endif +#endif +#ifdef SYS_preadv +#ifdef __NR_preadv + {"preadv", __NR_preadv}, +#endif +#endif +#ifdef SYS_prlimit64 +#ifdef __NR_prlimit64 + {"prlimit64", __NR_prlimit64}, +#endif +#endif +#ifdef SYS_process_vm_readv +#ifdef __NR_process_vm_readv + {"process_vm_readv", __NR_process_vm_readv}, +#endif +#endif +#ifdef SYS_process_vm_writev +#ifdef __NR_process_vm_writev + {"process_vm_writev", __NR_process_vm_writev}, +#endif +#endif +#ifdef SYS_pselect6 +#ifdef __NR_pselect6 + {"pselect6", __NR_pselect6}, +#endif +#endif +#ifdef SYS_ptrace +#ifdef __NR_ptrace + {"ptrace", __NR_ptrace}, +#endif +#endif +#ifdef SYS_putpmsg +#ifdef __NR_putpmsg + {"putpmsg", __NR_putpmsg}, +#endif +#endif +#ifdef SYS_pwrite64 +#ifdef __NR_pwrite64 + {"pwrite64", __NR_pwrite64}, +#endif +#endif +#ifdef SYS_pwritev +#ifdef __NR_pwritev + {"pwritev", __NR_pwritev}, +#endif +#endif +#ifdef SYS_quotactl +#ifdef __NR_quotactl + {"quotactl", __NR_quotactl}, +#endif +#endif +#ifdef SYS_read +#ifdef __NR_read + {"read", __NR_read}, +#endif +#endif +#ifdef SYS_readahead +#ifdef __NR_readahead + {"readahead", __NR_readahead}, +#endif +#endif +#ifdef SYS_readlink +#ifdef __NR_readlink + {"readlink", __NR_readlink}, +#endif +#endif +#ifdef SYS_readlinkat +#ifdef __NR_readlinkat + {"readlinkat", __NR_readlinkat}, +#endif +#endif +#ifdef SYS_readv +#ifdef __NR_readv + {"readv", __NR_readv}, +#endif +#endif +#ifdef SYS_reboot +#ifdef __NR_reboot + {"reboot", __NR_reboot}, +#endif +#endif +#ifdef SYS_recvfrom +#ifdef __NR_recvfrom + {"recvfrom", __NR_recvfrom}, +#endif +#endif +#ifdef SYS_recvmmsg +#ifdef __NR_recvmmsg + {"recvmmsg", __NR_recvmmsg}, +#endif +#endif +#ifdef SYS_recvmsg +#ifdef __NR_recvmsg + {"recvmsg", __NR_recvmsg}, +#endif +#endif +#ifdef SYS_remap_file_pages +#ifdef __NR_remap_file_pages + {"remap_file_pages", __NR_remap_file_pages}, +#endif +#endif +#ifdef SYS_removexattr +#ifdef __NR_removexattr + {"removexattr", __NR_removexattr}, +#endif +#endif +#ifdef SYS_rename +#ifdef __NR_rename + {"rename", __NR_rename}, +#endif +#endif +#ifdef SYS_renameat +#ifdef __NR_renameat + {"renameat", __NR_renameat}, +#endif +#endif +#ifdef SYS_request_key +#ifdef __NR_request_key + {"request_key", __NR_request_key}, +#endif +#endif +#ifdef SYS_restart_syscall +#ifdef __NR_restart_syscall + {"restart_syscall", __NR_restart_syscall}, +#endif +#endif +#ifdef SYS_rmdir +#ifdef __NR_rmdir + {"rmdir", __NR_rmdir}, +#endif +#endif +#ifdef SYS_rt_sigaction +#ifdef __NR_rt_sigaction + {"rt_sigaction", __NR_rt_sigaction}, +#endif +#endif +#ifdef SYS_rt_sigpending +#ifdef __NR_rt_sigpending + {"rt_sigpending", __NR_rt_sigpending}, +#endif +#endif +#ifdef SYS_rt_sigprocmask +#ifdef __NR_rt_sigprocmask + {"rt_sigprocmask", __NR_rt_sigprocmask}, +#endif +#endif +#ifdef SYS_rt_sigqueueinfo +#ifdef __NR_rt_sigqueueinfo + {"rt_sigqueueinfo", __NR_rt_sigqueueinfo}, +#endif +#endif +#ifdef SYS_rt_sigreturn +#ifdef __NR_rt_sigreturn + {"rt_sigreturn", __NR_rt_sigreturn}, +#endif +#endif +#ifdef SYS_rt_sigsuspend +#ifdef __NR_rt_sigsuspend + {"rt_sigsuspend", __NR_rt_sigsuspend}, +#endif +#endif +#ifdef SYS_rt_sigtimedwait +#ifdef __NR_rt_sigtimedwait + {"rt_sigtimedwait", __NR_rt_sigtimedwait}, +#endif +#endif +#ifdef SYS_rt_tgsigqueueinfo +#ifdef __NR_rt_tgsigqueueinfo + {"rt_tgsigqueueinfo", __NR_rt_tgsigqueueinfo}, +#endif +#endif +#ifdef SYS_sched_get_priority_max +#ifdef __NR_sched_get_priority_max + {"sched_get_priority_max", __NR_sched_get_priority_max}, +#endif +#endif +#ifdef SYS_sched_get_priority_min +#ifdef __NR_sched_get_priority_min + {"sched_get_priority_min", __NR_sched_get_priority_min}, +#endif +#endif +#ifdef SYS_sched_getaffinity +#ifdef __NR_sched_getaffinity + {"sched_getaffinity", __NR_sched_getaffinity}, +#endif +#endif +#ifdef SYS_sched_getparam +#ifdef __NR_sched_getparam + {"sched_getparam", __NR_sched_getparam}, +#endif +#endif +#ifdef SYS_sched_getscheduler +#ifdef __NR_sched_getscheduler + {"sched_getscheduler", __NR_sched_getscheduler}, +#endif +#endif +#ifdef SYS_sched_rr_get_interval +#ifdef __NR_sched_rr_get_interval + {"sched_rr_get_interval", __NR_sched_rr_get_interval}, +#endif +#endif +#ifdef SYS_sched_setaffinity +#ifdef __NR_sched_setaffinity + {"sched_setaffinity", __NR_sched_setaffinity}, +#endif +#endif +#ifdef SYS_sched_setparam +#ifdef __NR_sched_setparam + {"sched_setparam", __NR_sched_setparam}, +#endif +#endif +#ifdef SYS_sched_setscheduler +#ifdef __NR_sched_setscheduler + {"sched_setscheduler", __NR_sched_setscheduler}, +#endif +#endif +#ifdef SYS_sched_yield +#ifdef __NR_sched_yield + {"sched_yield", __NR_sched_yield}, +#endif +#endif +#ifdef SYS_security +#ifdef __NR_security + {"security", __NR_security}, +#endif +#endif +#ifdef SYS_select +#ifdef __NR_select + {"select", __NR_select}, +#endif +#endif +#ifdef SYS_semctl +#ifdef __NR_semctl + {"semctl", __NR_semctl}, +#endif +#endif +#ifdef SYS_semget +#ifdef __NR_semget + {"semget", __NR_semget}, +#endif +#endif +#ifdef SYS_semop +#ifdef __NR_semop + {"semop", __NR_semop}, +#endif +#endif +#ifdef SYS_semtimedop +#ifdef __NR_semtimedop + {"semtimedop", __NR_semtimedop}, +#endif +#endif +#ifdef SYS_sendfile +#ifdef __NR_sendfile + {"sendfile", __NR_sendfile}, +#endif +#endif +#ifdef SYS_sendmmsg +#ifdef __NR_sendmmsg + {"sendmmsg", __NR_sendmmsg}, +#endif +#endif +#ifdef SYS_sendmsg +#ifdef __NR_sendmsg + {"sendmsg", __NR_sendmsg}, +#endif +#endif +#ifdef SYS_sendto +#ifdef __NR_sendto + {"sendto", __NR_sendto}, +#endif +#endif +#ifdef SYS_set_mempolicy +#ifdef __NR_set_mempolicy + {"set_mempolicy", __NR_set_mempolicy}, +#endif +#endif +#ifdef SYS_set_robust_list +#ifdef __NR_set_robust_list + {"set_robust_list", __NR_set_robust_list}, +#endif +#endif +#ifdef SYS_set_tid_address +#ifdef __NR_set_tid_address + {"set_tid_address", __NR_set_tid_address}, +#endif +#endif +#ifdef SYS_setdomainname +#ifdef __NR_setdomainname + {"setdomainname", __NR_setdomainname}, +#endif +#endif +#ifdef SYS_setfsgid +#ifdef __NR_setfsgid + {"setfsgid", __NR_setfsgid}, +#endif +#endif +#ifdef SYS_setfsuid +#ifdef __NR_setfsuid + {"setfsuid", __NR_setfsuid}, +#endif +#endif +#ifdef SYS_setgid +#ifdef __NR_setgid + {"setgid", __NR_setgid}, +#endif +#endif +#ifdef SYS_setgroups +#ifdef __NR_setgroups + {"setgroups", __NR_setgroups}, +#endif +#endif +#ifdef SYS_sethostname +#ifdef __NR_sethostname + {"sethostname", __NR_sethostname}, +#endif +#endif +#ifdef SYS_setitimer +#ifdef __NR_setitimer + {"setitimer", __NR_setitimer}, +#endif +#endif +#ifdef SYS_setns +#ifdef __NR_setns + {"setns", __NR_setns}, +#endif +#endif +#ifdef SYS_setpgid +#ifdef __NR_setpgid + {"setpgid", __NR_setpgid}, +#endif +#endif +#ifdef SYS_setpriority +#ifdef __NR_setpriority + {"setpriority", __NR_setpriority}, +#endif +#endif +#ifdef SYS_setregid +#ifdef __NR_setregid + {"setregid", __NR_setregid}, +#endif +#endif +#ifdef SYS_setresgid +#ifdef __NR_setresgid + {"setresgid", __NR_setresgid}, +#endif +#endif +#ifdef SYS_setresuid +#ifdef __NR_setresuid + {"setresuid", __NR_setresuid}, +#endif +#endif +#ifdef SYS_setreuid +#ifdef __NR_setreuid + {"setreuid", __NR_setreuid}, +#endif +#endif +#ifdef SYS_setrlimit +#ifdef __NR_setrlimit + {"setrlimit", __NR_setrlimit}, +#endif +#endif +#ifdef SYS_setsid +#ifdef __NR_setsid + {"setsid", __NR_setsid}, +#endif +#endif +#ifdef SYS_setsockopt +#ifdef __NR_setsockopt + {"setsockopt", __NR_setsockopt}, +#endif +#endif +#ifdef SYS_settimeofday +#ifdef __NR_settimeofday + {"settimeofday", __NR_settimeofday}, +#endif +#endif +#ifdef SYS_setuid +#ifdef __NR_setuid + {"setuid", __NR_setuid}, +#endif +#endif +#ifdef SYS_setxattr +#ifdef __NR_setxattr + {"setxattr", __NR_setxattr}, +#endif +#endif +#ifdef SYS_shmat +#ifdef __NR_shmat + {"shmat", __NR_shmat}, +#endif +#endif +#ifdef SYS_shmctl +#ifdef __NR_shmctl + {"shmctl", __NR_shmctl}, +#endif +#endif +#ifdef SYS_shmdt +#ifdef __NR_shmdt + {"shmdt", __NR_shmdt}, +#endif +#endif +#ifdef SYS_shmget +#ifdef __NR_shmget + {"shmget", __NR_shmget}, +#endif +#endif +#ifdef SYS_shutdown +#ifdef __NR_shutdown + {"shutdown", __NR_shutdown}, +#endif +#endif +#ifdef SYS_sigaltstack +#ifdef __NR_sigaltstack + {"sigaltstack", __NR_sigaltstack}, +#endif +#endif +#ifdef SYS_signalfd +#ifdef __NR_signalfd + {"signalfd", __NR_signalfd}, +#endif +#endif +#ifdef SYS_signalfd4 +#ifdef __NR_signalfd4 + {"signalfd4", __NR_signalfd4}, +#endif +#endif +#ifdef SYS_socket +#ifdef __NR_socket + {"socket", __NR_socket}, +#endif +#endif +#ifdef SYS_socketpair +#ifdef __NR_socketpair + {"socketpair", __NR_socketpair}, +#endif +#endif +#ifdef SYS_splice +#ifdef __NR_splice + {"splice", __NR_splice}, +#endif +#endif +#ifdef SYS_stat +#ifdef __NR_stat + {"stat", __NR_stat}, +#endif +#endif +#ifdef SYS_statfs +#ifdef __NR_statfs + {"statfs", __NR_statfs}, +#endif +#endif +#ifdef SYS_swapoff +#ifdef __NR_swapoff + {"swapoff", __NR_swapoff}, +#endif +#endif +#ifdef SYS_swapon +#ifdef __NR_swapon + {"swapon", __NR_swapon}, +#endif +#endif +#ifdef SYS_symlink +#ifdef __NR_symlink + {"symlink", __NR_symlink}, +#endif +#endif +#ifdef SYS_symlinkat +#ifdef __NR_symlinkat + {"symlinkat", __NR_symlinkat}, +#endif +#endif +#ifdef SYS_sync +#ifdef __NR_sync + {"sync", __NR_sync}, +#endif +#endif +#ifdef SYS_sync_file_range +#ifdef __NR_sync_file_range + {"sync_file_range", __NR_sync_file_range}, +#endif +#endif +#ifdef SYS_syncfs +#ifdef __NR_syncfs + {"syncfs", __NR_syncfs}, +#endif +#endif +#ifdef SYS_sysfs +#ifdef __NR_sysfs + {"sysfs", __NR_sysfs}, +#endif +#endif +#ifdef SYS_sysinfo +#ifdef __NR_sysinfo + {"sysinfo", __NR_sysinfo}, +#endif +#endif +#ifdef SYS_syslog +#ifdef __NR_syslog + {"syslog", __NR_syslog}, +#endif +#endif +#ifdef SYS_tee +#ifdef __NR_tee + {"tee", __NR_tee}, +#endif +#endif +#ifdef SYS_tgkill +#ifdef __NR_tgkill + {"tgkill", __NR_tgkill}, +#endif +#endif +#ifdef SYS_time +#ifdef __NR_time + {"time", __NR_time}, +#endif +#endif +#ifdef SYS_timer_create +#ifdef __NR_timer_create + {"timer_create", __NR_timer_create}, +#endif +#endif +#ifdef SYS_timer_delete +#ifdef __NR_timer_delete + {"timer_delete", __NR_timer_delete}, +#endif +#endif +#ifdef SYS_timer_getoverrun +#ifdef __NR_timer_getoverrun + {"timer_getoverrun", __NR_timer_getoverrun}, +#endif +#endif +#ifdef SYS_timer_gettime +#ifdef __NR_timer_gettime + {"timer_gettime", __NR_timer_gettime}, +#endif +#endif +#ifdef SYS_timer_settime +#ifdef __NR_timer_settime + {"timer_settime", __NR_timer_settime}, +#endif +#endif +#ifdef SYS_timerfd_create +#ifdef __NR_timerfd_create + {"timerfd_create", __NR_timerfd_create}, +#endif +#endif +#ifdef SYS_timerfd_gettime +#ifdef __NR_timerfd_gettime + {"timerfd_gettime", __NR_timerfd_gettime}, +#endif +#endif +#ifdef SYS_timerfd_settime +#ifdef __NR_timerfd_settime + {"timerfd_settime", __NR_timerfd_settime}, +#endif +#endif +#ifdef SYS_times +#ifdef __NR_times + {"times", __NR_times}, +#endif +#endif +#ifdef SYS_tkill +#ifdef __NR_tkill + {"tkill", __NR_tkill}, +#endif +#endif +#ifdef SYS_truncate +#ifdef __NR_truncate + {"truncate", __NR_truncate}, +#endif +#endif +#ifdef SYS_tuxcall +#ifdef __NR_tuxcall + {"tuxcall", __NR_tuxcall}, +#endif +#endif +#ifdef SYS_umask +#ifdef __NR_umask + {"umask", __NR_umask}, +#endif +#endif +#ifdef SYS_umount2 +#ifdef __NR_umount2 + {"umount2", __NR_umount2}, +#endif +#endif +#ifdef SYS_uname +#ifdef __NR_uname + {"uname", __NR_uname}, +#endif +#endif +#ifdef SYS_unlink +#ifdef __NR_unlink + {"unlink", __NR_unlink}, +#endif +#endif +#ifdef SYS_unlinkat +#ifdef __NR_unlinkat + {"unlinkat", __NR_unlinkat}, +#endif +#endif +#ifdef SYS_unshare +#ifdef __NR_unshare + {"unshare", __NR_unshare}, +#endif +#endif +#ifdef SYS_ustat +#ifdef __NR_ustat + {"ustat", __NR_ustat}, +#endif +#endif +#ifdef SYS_utime +#ifdef __NR_utime + {"utime", __NR_utime}, +#endif +#endif +#ifdef SYS_utimensat +#ifdef __NR_utimensat + {"utimensat", __NR_utimensat}, +#endif +#endif +#ifdef SYS_utimes +#ifdef __NR_utimes + {"utimes", __NR_utimes}, +#endif +#endif +#ifdef SYS_vfork +#ifdef __NR_vfork + {"vfork", __NR_vfork}, +#endif +#endif +#ifdef SYS_vhangup +#ifdef __NR_vhangup + {"vhangup", __NR_vhangup}, +#endif +#endif +#ifdef SYS_vmsplice +#ifdef __NR_vmsplice + {"vmsplice", __NR_vmsplice}, +#endif +#endif +#ifdef SYS_wait4 +#ifdef __NR_wait4 + {"wait4", __NR_wait4}, +#endif +#endif +#ifdef SYS_waitid +#ifdef __NR_waitid + {"waitid", __NR_waitid}, +#endif +#endif +#ifdef SYS_write +#ifdef __NR_write + {"write", __NR_write}, +#endif +#endif +#ifdef SYS_writev +#ifdef __NR_writev + {"writev", __NR_writev}, +#endif +#endif +#endif + +// +// end of generated code +// +}; // end of syslist + +const char *syscall_find_nr(int nr) { + int i; + int elems = sizeof(syslist) / sizeof(syslist[0]); + for (i = 0; i < elems; i++) { + if (nr == syslist[i].nr) + return syslist[i].name; + } + + return "unknown"; +} + +// return -1 if error, or syscall number +static int syscall_find_name(const char *name) { + int i; + int elems = sizeof(syslist) / sizeof(syslist[0]); + for (i = 0; i < elems; i++) { + if (strcmp(name, syslist[i].name) == 0) + return syslist[i].nr; + } + + return -1; +} + +// return 1 if error, 0 if OK +int syscall_check_list(const char *slist, void (*callback)(int)) { + // don't allow empty lists + if (slist == NULL || *slist == '\0') { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); + return -1; + } + + // work on a copy of the string + char *str = strdup(slist); + if (!str) + errExit("strdup"); + + char *ptr = str; + char *start = str; + while (*ptr != '\0') { + if (islower(*ptr) || isdigit(*ptr) || *ptr == '_') + ; + else if (*ptr == ',') { + *ptr = '\0'; + int nr = syscall_find_name(start); + if (nr == -1) + fprintf(stderr, "Warning: syscall %s not found\n", start); + else if (callback != NULL) + callback(nr); + + start = ptr + 1; + } + ptr++; + } + if (*start != '\0') { + int nr = syscall_find_name(start); + if (nr == -1) + fprintf(stderr, "Warning: syscall %s not found\n", start); + else if (callback != NULL) + callback(nr); + } + + free(str); + return 0; +} + +void syscall_print(void) { + int i; + int elems = sizeof(syslist) / sizeof(syslist[0]); + for (i = 0; i < elems; i++) { + printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); + } + printf("\n"); +} + +#endif // HAVE_SECCOMP diff --git a/src/firejail/usage.c b/src/firejail/usage.c new file mode 100644 index 000000000..71ae203ff --- /dev/null +++ b/src/firejail/usage.c @@ -0,0 +1,312 @@ +/* + * 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 "firejail.h" + +void usage(void) { + printf("firejail - version %s\n\n", VERSION); + printf("Firejail is a SUID sandbox program that reduces the risk of security breaches by\n"); + printf("restricting the running environment of untrusted applications using Linux\n"); + printf("namespaces. It includes a sandbox profile for Mozilla Firefox.\n\n"); + printf("\n"); + printf("Usage: firejail [options] [program and arguments]\n\n"); + printf("\n"); + printf("Without any options, the sandbox consists of a filesystem chroot build from the\n"); + printf("current system directories mounted read-only, and new PID and IPC\n"); + printf("namespaces. If no program is specified as an argument, /bin/bash is started by\n"); + printf("default in the sandbox.\n\n"); + printf("\n"); + printf("Options:\n\n"); + printf("\t-- - signal the end of options and disables further option processing.\n\n"); + printf("\t--bandwidth=name - set bandwidth limits for the sandbox identified\n"); + printf("\t\tby name, see Traffic Shaping section for more details.\n\n"); + printf("\t--bandwidth=pid - set bandwidth limits for the sandbox identified\n"); + printf("\t\tby PID, see Traffic Shaping section for more details.\n\n"); +#ifdef HAVE_BIND + printf("\t--bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n\n"); + printf("\t--bind=filename1,dirname2 - mount-bind filename1 on top of filename2.\n\n"); +#endif + printf("\t--blacklist=dirname_or_filename - blacklist directory or file.\n\n"); + printf("\t-c - execute command and exit.\n\n"); + printf("\t--caps - enable default Linux capabilities filter. The filter disables\n"); + printf("\t\tCAP_SYS_MODULE, CAP_SYS_RAWIO, CAP_SYS_BOOT, CAP_SYS_NICE,\n"); + printf("\t\tCAP_SYS_TTY_CONFIG, CAP_SYSLOG, CAP_MKNOD, CAP_SYS_ADMIN.\n\n"); + printf("\t--caps.drop=all - drop all capabilities.\n\n"); + printf("\t--caps.drop=capability,capability,capability - blacklist Linux\n"); + printf("\t\tcapabilities filter.\n\n"); + printf("\t--caps.keep=capability,capability,capability - whitelist Linux\n"); + printf("\t\tcapabilities filter.\n\n"); + printf("\t--caps.print=name - print the caps filter for the sandbox identified\n"); + printf("\t\tby name.\n\n"); + printf("\t--caps.print=pid - print the caps filter for the sandbox identified\n"); + printf("\t\tby PID.\n\n"); + printf("\t--cgroup=tasks-file - place the sandbox in the specified control group.\n"); + printf("\t\ttasks-file is the full path of cgroup tasks file.\n"); + printf("\t\tExample: --cgroup=/sys/fs/cgroup/g1/tasks\n\n"); +#ifdef HAVE_CHROOT + printf("\t--chroot=dirname - chroot into dirname directory.\n\n"); +#endif + printf("\t--cpu=cpu-number,cpu-number - set cpu affinity.\n"); + printf("\t\tExample: cpu=0,1,2\n\n"); + printf("\t--csh - use /bin/csh as default shell.\n\n"); + printf("\t--debug - print sandbox debug messages.\n\n"); + printf("\t--debug-syscalls - print all recognized system calls in the current\n"); + printf("\t\tFirejail software build and exit.\n\n"); + printf("\t--debug-caps - print all recognized capabilities in the current\n"); + printf("\t\tFirejail software build and exit.\n\n"); + printf("\t--defaultgw=address - use this address as default gateway in the new\n"); + printf("\t\tnetwork namespace.\n\n"); + printf("\t--dns=address - set a DNS server for the sandbox. Up to three DNS\n"); + printf("\t\tservers can be defined.\n\n"); + printf("\t--dns.print=name - print DNS configuration for the sandbox identified\n"); + printf("\t\tby name.\n\n"); + printf("\t--dns.print=pid - print DNS configuration of the sandbox identified.\n"); + printf("\t\tby PID.\n\n"); + printf("\t--help, -? - this help screen.\n\n"); + printf("\t--ip=address - set interface IP address.\n\n"); + printf("\t--ip=none - no IP address and no default gateway address are configured\n"); + printf("\t\tin the new network namespace. Use this option in case you intend\n"); + printf("\t\tto start an external DHCP client in the sandbox.\n\n"); + printf("\t--iprange=address,address - configure an IP address in this range\n\n"); + printf("\t--ipc-namespace - enable a new IPC namespace if the sandbox was started\n"); + printf("\t\tas a regular user. IPC namespace is enabled by default only if\n"); + printf("\t\tthe sandbox is started as root.\n\n"); + printf("\t--join=name - join the sandbox identified by name.\n\n"); + printf("\t--join=pid - join the sandbox identified by PID.\n\n"); + printf("\t--list - list all sandboxes.\n\n"); + printf("\t--mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n\n"); + printf("\t--name=name - set sandbox hostname.\n\n"); + printf("\t--net=bridgename - enable network namespaces and connect to this bridge\n"); + printf("\t\tdevice. Unless specified with option --ip and --defaultgw, an\n"); + printf("\t\tIP address and a default gateway will be assigned automatically\n"); + printf("\t\tto the sandbox. The IP address is checked using ARP before\n"); + printf("\t\tassignment. The IP address assigned as default gateway is the\n"); + printf("\t\tbridge device IP address. Up to four --net devices can\n"); + printf("\t\tbe defined. Mixing bridge and macvlan devices is allowed.\n\n"); + printf("\t--net=ethernet_interface - enable network namespaces and connect\n"); + printf("\t\tto this ethernet_interface using the standard Linux macvlan\n"); + printf("\t\tdriver. Unless specified with option --ip and --defaultgw, an\n"); + printf("\t\tIP address and a default gateway will be assigned automatically\n"); + printf("\t\tto the sandbox. The IP address is checked using ARP before\n"); + printf("\t\tassignment. The IP address assigned as default gateway is the\n"); + printf("\t\tdefault gateway of the host. Up to four --net devices can\n"); + printf("\t\tbe defined. Mixing bridge and macvlan devices is allowed.\n\n"); + printf("\t--net=none - enable a new, unconnected network namespace.\n\n"); + + printf("\t--netfilter - enable the default client network filter in the new\n"); + printf("\t\tnetwork namespace:\n\n"); + printf("\t\t*filter\n"); + printf("\t\t:INPUT DROP [0:0]\n"); + printf("\t\t:FORWARD DROP [0:0]\n"); + printf("\t\t:OUTPUT ACCEPT [0:0]\n"); + printf("\t\t-A INPUT -i lo -j ACCEPT\n"); + printf("\t\t-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"); + printf("\t\t-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT\n"); + printf("\t\t-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT\n"); + printf("\t\t-A INPUT -p icmp --icmp-type echo-request -j ACCEPT \n"); + printf("\t\tCOMMIT\n\n"); + printf("\t--netfilter=filename - enable the network filter specified by\n"); + printf("\t\tfilename in the new network namespace. The filter file format\n"); + printf("\t\tis the format of iptables-save and iptable-restore commands.\n\n"); + + printf("\t--netstats - monitor network statistics for sandboxes creating a new\n"); + printf("\t\tnetwork namespace.\n\n"); + printf("\t--nogroups - disable supplementary groups. Without this option,\n"); + printf("\t\tsupplementary groups are enabled for the user starting the\n"); + printf("\t\tsandbox. For root user supplementary groups are always\n"); + printf("\t\tdisabled.\n\n"); + + printf("\t--noroot - install a user namespace with a single user - the current\n"); + printf("\t\tuser. root user does not exist in the new namespace. This option\n"); + printf("\t\tis not supported for --chroot and --overlay configurations.\n\n"); + + printf("\t--output=logfile - stdout logging and log rotation. Copy stdout to\n"); + printf("\t\tlogfile, and keep the size of the file under 500KB using log\n"); + printf("\t\trotation. Five files with prefixes .1 to .5 are used in\n"); + printf("\t\trotation.\n\n"); + printf("\t--overlay - mount a filesystem overlay on top of the current filesystem.\n"); + printf("\t\t(OverlayFS support is required in Linux kernel for this option\n"); + printf("\t\tto work)\n\n"); + + printf("\t--private - mount new /root and /home/user directories in temporary\n"); + printf("\t\tfilesystems. All modifications are discarded when the sandbox is\n"); + printf("\t\tclosed.\n\n"); + printf("\t--private=directory - use directory as user home.\n\n"); + printf("\t--private.keep=file,directory - build a new user home in a temporary\n"); + printf("\t\tfilesystem, and copy the files and directories in the list in\n"); + printf("\t\tthe new home. All modifications are discarded when the sandbox\n"); + printf("\t\tis closed.\n\n"); + printf("\t--private-dev - create a new /dev directory. Only null, full, zero, tty,\n"); + printf("\t\tpst, ptms, random, urandom and shm devices are available.\n\n"); + + printf("\t--profile=filename - use a custom profile.\n\n"); + printf("\t--read-only=dirname_or_filename - set directory or file read-only.\n\n"); + printf("\t--rlimit-fsize=number - set the maximum file size that can be created\n"); + printf("\t\tby a process.\n\n"); + printf("\t--rlimit-nofile=number - set the maximum number of files that can be\n"); + printf("\t\topened by a process.\n\n"); + printf("\t--rlimit-nproc=number - set the maximum number of processes that can be\n"); + printf("\t\tcreated for the real user ID of the calling process.\n\n"); + printf("\t--rlimit-sigpending=number - set the maximum number of pending signals\n"); + printf("\t\tfor a process.\n\n"); + + printf("\t--scan - ARP-scan all the networks from inside a network namespace.\n"); + printf("\t\tThis makes it possible to detect macvlan kernel device drivers\n"); + printf("\t\trunning on the current host.\n\n"); + +#ifdef HAVE_SECCOMP + printf("\t--seccomp - enable seccomp filter and blacklist the syscalls in the\n"); + printf("\t\tlist. The default list is as follows: mount, umount2,\n"); + printf("\t\tptrace, kexec_load, open_by_handle_at, init_module,\n"); + printf("\t\tfinit_module, delete_module, iopl, ioperm, swapon, swapoff,\n"); + printf("\t\tmknode, syslog, process_vm_readv and process_vm_writev\n"); + printf("\t\tsysfs,_sysctl, adjtimex, clock_adjtime, lookup_dcookie,\n"); + printf("\t\tperf_event_open, fanotify_init and kcmp.\n\n"); + + printf("\t--seccomp=syscall,syscall,syscall - enable seccomp filter, blacklist the\n"); + printf("\t\tdefault syscall list and the syscalls specified by the command.\n\n"); + + printf("\t--seccomp.drop=syscall,syscall,syscall - enable seccomp filter, and\n"); + printf("\t\tblacklist the syscalls specified by the command.\n\n"); + + printf("\t--seccomp.keep=syscall,syscall,syscall - enable seccomp filter, and\n"); + printf("\t\twhitelist the syscalls specified by the command.\n\n"); + + printf("\t--seccomp.print=name - print the seccomp filter for the sandbox\n"); + printf("\t\tidentified by name.\n\n"); + printf("\t--seccomp.print=pid - print the seccomp filter for the sandbox\n"); + printf("\t\tidentified by PID.\n\n"); +#endif + + printf("\t--shell=none - run the program directly without a user shell.\n\n"); + printf("\t--shell=program - set default user shell.\n\n"); + printf("\t--shutdown=name - shutdown the sandbox identified by name.\n\n"); + printf("\t--shutdown=pid - shutdown the sandbox identified by PID.\n\n"); + printf("\t--tmpfs=dirname - mount a tmpfs filesystem on directory dirname.\n\n"); + printf("\t--top - monitor the most CPU-intensive sandboxes.\n\n"); + printf("\t--trace - trace open, access and connect system calls.\n\n"); + printf("\t--tree - print a tree of all sandboxed processes.\n\n"); + printf("\t--version - print program version and exit.\n\n"); + printf("\t--zsh - use /usr/bin/zsh as default shell.\n\n"); + printf("\n"); + printf("\n"); + + + printf("Traffic Shaping\n\n"); + + printf("Network bandwidth is an expensive resource shared among all sandboxes\n"); + printf("running on a system. Traffic shaping allows the user to increase network\n"); + printf("performance by controlling the amount of data that flows into and out of the\n"); + printf("sandboxes. Firejail implements a simple rate-limiting shaper based on Linux\n"); + printf("command tc. The shaper works at sandbox level, and can be used only for\n"); + printf("sandboxes configured with new network namespaces.\n\n"); + + printf("Set rate-limits:\n"); + printf("\tfirejail --bandwidth={name|pid} set network-name down-speed up-speed\n\n"); + printf("Clear rate-limits:\n"); + printf("\tfirejail --bandwidth={name|pid} clear network-name\n\n"); + printf("Status:\n"); + printf("\tfirejail --bandwidth={name|pid} status\n\n"); + printf("where:\n"); + printf("\tname - sandbox name\n"); + printf("\tpid - sandbox pid\n"); + printf("\tnetwork-name - network name as used by --net option\n"); + printf("\tdown-speed - download speed in KB/s (decimal kilobyte per second)\n"); + printf("\tup-speed - upload speed in KB/s (decimal kilobyte per second)\n"); + printf("\n"); + printf("Example:\n"); + printf("\t$ firejail --name=mybrowser --net=eth0 firefox &\n"); + printf("\t$ firejail --bandwidth=mybrowser set eth0 80 20\n"); + printf("\t$ firejail --bandwidth=mybrowser status\n"); + printf("\t$ firejail --bandwidth=mybrowser clear eth0\n"); + printf("\n"); + printf("\n"); + + + + printf("Monitoring\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("\n"); + printf("Profile files\n\n"); + printf("Several command line configuration options can be passed to the program using\n"); + printf("profile files. Default Firejail profile files are stored in /etc/firejail\n"); + printf("directory, user profile files are stored in ~/.config/firejail directory. See\n"); + printf("man 5 firejail-profile for more information.\n\n"); + printf("\n"); + printf("Restricted shell\n\n"); + printf("To configure a restricted shell, replace /bin/bash with /usr/bin/firejail i\n"); + printf("/etc/password file for each user that needs to be restricted.\n"); + printf("Alternatively, you can specify /usr/bin/firejail in adduser command:\n\n"); + printf(" adduser --shell /usr/bin/firejail username\n\n"); + printf("Arguments to be passed to firejail executable upon login are declared in\n"); + printf("/etc/firejail/login.users file.\n\n"); + printf("\n"); + printf("Examples:\n\n"); + printf(" $ firejail\n"); + printf(" start a regular /bin/bash session in sandbox\n"); + printf(" $ firejail firefox\n"); + printf(" start Mozilla Firefox\n"); + printf(" $ firejail --seccomp firefox\n"); + printf(" start Mozilla Firefox in a seccomp sandbox\n"); + printf(" $ firejail --caps firefox\n"); + printf(" start Mozilla Firefox in a Linux capabilities sandbox\n"); + printf(" $ firejail --debug firefox\n"); + printf(" debug Firefox sandbox\n"); + printf(" $ firejail --private\n"); + printf(" start a /bin/bash session with a new tmpfs home directory\n"); + printf(" $ firejail --net=br0 ip=10.10.20.10\n"); + printf(" start a /bin/bash session in a new network namespace; the session is\n"); + printf(" connected to the main network using br0 bridge device, an IP address\n"); + printf(" of 10.10.20.10 is assigned to the sandbox\n"); + printf(" $ firejail --net=br0 --net=br1 --net=br2\n"); + printf(" start a /bin/bash session in a new network namespace and connect it\n"); + printf(" to br0, br1, and br2 host bridge devices\n"); + printf(" $ firejail --list\n"); + printf(" list all running sandboxes\n"); + printf("\n"); + printf("License GPL version 2 or later\n"); + printf("Homepage: http://firejail.sourceforge.net\n"); + printf("\n"); +} diff --git a/src/firejail/util.c b/src/firejail/util.c new file mode 100644 index 000000000..2c50caf17 --- /dev/null +++ b/src/firejail/util.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2014, 2015 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 "firejail.h" +#include +#include +#include +#include +#include +#include + +#define MAX_GROUPS 1024 +// drop privileges +// - for root group or if nogroups is set, supplementary groups are not configured +void drop_privs(int nogroups) { + gid_t gid = getgid(); + + // configure supplementary groups + if (gid == 0 || nogroups) { + if (setgroups(0, NULL) < 0) + errExit("setgroups"); + if (arg_debug) + printf("Username %s, no supplementary groups\n", cfg.username); + } + else { + assert(cfg.username); + gid_t groups[MAX_GROUPS]; + int ngroups = MAX_GROUPS; + int rv = getgrouplist(cfg.username, gid, groups, &ngroups); + + if (arg_debug && rv) { + printf("Username %s, groups ", cfg.username); + int i; + for (i = 0; i < ngroups; i++) + printf("%u, ", groups[i]); + printf("\n"); + } + + if (rv == -1) { + fprintf(stderr, "Warning: cannot extract supplementary group list, dropping them\n"); + if (setgroups(0, NULL) < 0) + errExit("setgroups"); + } + else { + rv = setgroups(ngroups, groups); + if (rv) { + fprintf(stderr, "Warning: cannot set supplementary group list, dropping them\n"); + if (setgroups(0, NULL) < 0) + errExit("setgroups"); + } + } + } + + // set uid/gid + if (setgid(getgid()) < 0) + errExit("setgid/getgid"); + if (setuid(getuid()) < 0) + errExit("setuid/getuid"); +} + + +void logsignal(int s) { + if (!arg_debug) + return; + + openlog("firejail", LOG_NDELAY | LOG_PID, LOG_USER); + syslog(LOG_INFO, "Signal %d caught", s); + closelog(); +} + + +void logmsg(const char *msg) { + if (!arg_debug) + return; + + openlog("firejail", LOG_NDELAY | LOG_PID, LOG_USER); + syslog(LOG_INFO, "%s\n", msg); + closelog(); +} + + +void logargs(int argc, char **argv) { + if (!arg_debug) + return; + + int i; + int len = 0; + + // calculate message length + for (i = 0; i < argc; i++) + len += strlen(argv[i]) + 1; // + ' ' + + // build message + char msg[len + 1]; + char *ptr = msg; + for (i = 0; i < argc; i++) { + sprintf(ptr, "%s ", argv[i]); + ptr += strlen(ptr); + } + + // log message + logmsg(msg); +} + + +void logerr(const char *msg) { + if (!arg_debug) + return; + + openlog("firejail", LOG_NDELAY | LOG_PID, LOG_USER); + syslog(LOG_ERR, "%s\n", msg); + closelog(); +} + + +// return -1 if error, 0 if no error +int copy_file(const char *srcname, const char *destname) { + assert(srcname); + assert(destname); + + // open source + int src = open(srcname, O_RDONLY); + if (src < 0) { + fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname); + return -1; + } + + // open destination + int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (dst < 0) { + fprintf(stderr, "Warning: cannot open %s, file not copied\n", destname); + close(src); + return -1; + } + + // copy + ssize_t len; + static const int BUFLEN = 1024; + unsigned char buf[BUFLEN]; + while ((len = read(src, buf, BUFLEN)) > 0) { + int done = 0; + while (done != len) { + int rv = write(dst, buf + done, len - done); + if (rv == -1) { + close(src); + close(dst); + return -1; + } + + done += rv; + } + } + + close(src); + close(dst); + return 0; +} + + +char *get_link(const char *fname) { + assert(fname); + struct stat sb; + char *linkname; + ssize_t r; + + if (lstat(fname, &sb) == -1) + return NULL; + + linkname = malloc(sb.st_size + 1); + if (linkname == NULL) + return NULL; + memset(linkname, 0, sb.st_size + 1); + + r = readlink(fname, linkname, sb.st_size + 1); + if (r < 0) { + free(linkname); + return NULL; + } + return linkname; +} + + +// return 1 if the file is a directory +int is_dir(const char *fname) { + assert(fname); + if (*fname == '\0') + return 0; + + // if fname doesn't end in '/', add one + int rv; + struct stat s; + if (fname[strlen(fname) - 1] == '/') + rv = stat(fname, &s); + else { + char *tmp; + if (asprintf(&tmp, "%s/", fname) == -1) { + fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); + errExit("asprintf"); + } + rv = stat(tmp, &s); + free(tmp); + } + + if (rv == -1) + return 0; + + if (S_ISDIR(s.st_mode)) + return 1; + + return 0; +} + +// return 1 if the file is a link +int is_link(const char *fname) { + assert(fname); + if (*fname == '\0') + return 0; + + struct stat s; + if (lstat(fname, &s) == 0) { + if (S_ISLNK(s.st_mode)) + return 1; + } + + return 0; +} + + +// remove multiple spaces and return allocated memory +char *line_remove_spaces(const char *buf) { + assert(buf); + if (strlen(buf) == 0) + return NULL; + + // allocate memory for the new string + char *rv = malloc(strlen(buf) + 1); + if (rv == NULL) + errExit("malloc"); + + // remove space at start of line + const char *ptr1 = buf; + while (*ptr1 == ' ' || *ptr1 == '\t') + ptr1++; + + // copy data and remove additional spaces + char *ptr2 = rv; + int state = 0; + while (*ptr1 != '\0') { + if (*ptr1 == '\n' || *ptr1 == '\r') + break; + + if (state == 0) { + if (*ptr1 != ' ' && *ptr1 != '\t') + *ptr2++ = *ptr1++; + else { + *ptr2++ = ' '; + ptr1++; + state = 1; + } + } + else { // state == 1 + while (*ptr1 == ' ' || *ptr1 == '\t') + ptr1++; + state = 0; + } + } + + // strip last blank character if any + if (*(ptr2 - 1) == ' ') + --ptr2; + *ptr2 = '\0'; + // if (arg_debug) + // printf("Processing line #%s#\n", rv); + + return rv; +} + + +char *split_comma(char *str) { + if (str == NULL || *str == '\0') + return NULL; + char *ptr = strchr(str, ','); + if (!ptr) + return NULL; + *ptr = '\0'; + ptr++; + if (*ptr == '\0') + return NULL; + return ptr; +} + + +int not_unsigned(const char *str) { + int rv = 0; + const char *ptr = str; + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') { + if (!isdigit(*ptr)) { + rv = 1; + break; + } + ptr++; + } + + return rv; +} + + +#define BUFLEN 4096 +// find the first child for this parent; return 1 if error +int find_child(pid_t parent, pid_t *child) { + *child = 0; // use it to flag a found child + + 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 (*child == 0 && (entry = readdir(dir))) { + pid_t pid = strtol(entry->d_name, &end, 10); + if (end == entry->d_name || *end) + continue; + if (pid == parent) + continue; + + // 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); + continue; + } + + // look for firejail executable name + char buf[BUFLEN]; + while (fgets(buf, BUFLEN - 1, fp)) { + if (strncmp(buf, "PPid:", 5) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + if (parent == atoi(ptr)) + *child = pid; + break; // stop reading the file + } + } + fclose(fp); + free(file); + } + closedir(dir); + + return (*child)? 0:1; // 0 = found, 1 = not found +} + + + +void extract_command_name(const char *str) { + assert(str); + cfg.command_name = strdup(str); + if (!cfg.command_name) + errExit("strdup"); + + // restrict the command name to the first word + char *ptr = cfg.command_name; + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + *ptr = '\0'; + + // remove the path: /usr/bin/firefox becomes firefox + ptr = strrchr(cfg.command_name, '/'); + if (ptr) { + ptr++; + if (*ptr == '\0') { + fprintf(stderr, "Error: invalid command name\n"); + exit(1); + } + + char *tmp = strdup(ptr); + if (!tmp) + errExit("strdup"); + free(cfg.command_name); + cfg.command_name = tmp; + } +} + + +void update_map(char *mapping, char *map_file) { + int fd, j; + size_t map_len; /* Length of 'mapping' */ + + /* Replace commas in mapping string with newlines */ + + map_len = strlen(mapping); + for (j = 0; j < map_len; j++) + if (mapping[j] == ',') + mapping[j] = '\n'; + + fd = open(map_file, O_RDWR); + if (fd == -1) { + fprintf(stderr, "Error: cannot open %s: %s\n", map_file, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (write(fd, mapping, map_len) != map_len) { + fprintf(stderr, "Error: cannot write to %s: %s\n", map_file, strerror(errno)); + exit(EXIT_FAILURE); + } + + close(fd); +} + + +void wait_for_other(int fd) { + //**************************** + // wait for the parent to be initialized + //**************************** + char childstr[BUFLEN + 1]; + int newfd = dup(fd); + if (newfd == -1) + errExit("dup"); + FILE* stream; + stream = fdopen(newfd, "r"); + *childstr = '\0'; + if (fgets(childstr, BUFLEN, stream)) { + // remove \n) + char *ptr = childstr; + while(*ptr !='\0' && *ptr != '\n') + ptr++; + if (*ptr == '\0') + errExit("fgets"); + *ptr = '\0'; + } + else { + fprintf(stderr, "Error: cannot establish communication with the parent, exiting...\n"); + exit(1); + } + fclose(stream); +} + + +void notify_other(int fd) { + FILE* stream; + int newfd = dup(fd); + if (newfd == -1) + errExit("dup"); + stream = fdopen(newfd, "w"); + fprintf(stream, "%u\n", getpid()); + fflush(stream); + fclose(stream); +} diff --git a/src/firejail/veth.c b/src/firejail/veth.c new file mode 100644 index 000000000..ecc31f18d --- /dev/null +++ b/src/firejail/veth.c @@ -0,0 +1,191 @@ +/* code based on iproute2 ip/iplink.c, modified to be included in firejail project + * + * Original source code: + * + * Information: + * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 + * + * Download: + * http://www.kernel.org/pub/linux/utils/net/iproute2/ + * + * Repository: + * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git + * + * License: GPL v2 + * + * Original copyright header + * + * iplink.c "ip link". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + /* + * 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 "firejail.h" +#include "../include/libnetlink.h" +#include + +struct iplink_req +{ + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; +}; + +static struct rtnl_handle rth = { .fd = -1 }; + +int net_create_veth(const char *dev, const char *nsdev, unsigned pid) { + int len; + struct iplink_req req; + + if (arg_debug) + printf("create veth %s/%s/%u\n", dev, nsdev, pid); + assert(dev); + assert(nsdev); + assert(pid); + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "cannot open netlink\n"); + exit(1); + } + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = 0; + + if (dev) { + len = strlen(dev) + 1; + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, len); + } + + struct rtattr *linkinfo = NLMSG_TAIL(&req.n); + addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "veth", strlen("veth")); + + struct rtattr * data = NLMSG_TAIL(&req.n); + addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); + + struct rtattr * peerdata = NLMSG_TAIL(&req.n); + addattr_l (&req.n, sizeof(req), VETH_INFO_PEER, NULL, 0); + req.n.nlmsg_len += sizeof(struct ifinfomsg); + + // place the link in the child namespace + addattr_l (&req.n, sizeof(req), IFLA_NET_NS_PID, &pid, 4); + + if (nsdev) { + int len = strlen(nsdev) + 1; + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, nsdev, len); + } + peerdata->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)peerdata; + + data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; + linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; + + // send message + if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) + exit(2); + + return 0; +} + + +int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { + int len; + struct iplink_req req; + if (arg_debug) + printf("create macvlan %s, parent %s\n", dev, parent); + assert(dev); + assert(parent); + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "cannot open netlink\n"); + exit(1); + } + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = 0; + + // we start with the parent + int parent_ifindex = 2; + addattr_l(&req.n, sizeof(req), IFLA_LINK, &parent_ifindex, 4); + + // add new interface name + len = strlen(dev) + 1; + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, len); + + // place the interface in child namespace + addattr_l (&req.n, sizeof(req), IFLA_NET_NS_PID, &pid, 4); + + + // add link info for the new interface + struct rtattr *linkinfo = NLMSG_TAIL(&req.n); + addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "macvlan", strlen("macvlan")); + + // set macvlan bridge mode + struct rtattr * data = NLMSG_TAIL(&req.n); + addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); + int macvlan_type = MACVLAN_MODE_BRIDGE; + addattr_l (&req.n, sizeof(req), IFLA_INFO_KIND, &macvlan_type, 4); + + data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; +// req.n.nlmsg_len += sizeof(struct ifinfomsg); + + + data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; + linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; + + // send message + if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) + exit(2); + + return 0; +} + +/* +int main(int argc, char **argv) { + printf("Hello\n"); + + + char *dev = argv[3]; + char *nsdev = argv[8]; + unsigned pid; + sscanf(argv[10], "%u", &pid); + + + net_create_veth(dev, nsdev, pid); + + return 0; +} +*/ \ No newline at end of file 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"); +} diff --git a/src/fshaper/fshaper.sh b/src/fshaper/fshaper.sh new file mode 100755 index 000000000..4045fd5a4 --- /dev/null +++ b/src/fshaper/fshaper.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +usage() { + echo "Usage:" + echo " fshaper.sh --status" + echo " fshaper.sh --clear device" + echo " fshaper.sh --set device download-speed upload-speed" +} + +if [ "$1" = "--status" ]; then + /sbin/tc -s qdisc ls + /sbin/tc -s class ls + exit +fi + +if [ "$1" = "--clear" ]; then + if [ $# -ne 2 ]; then + echo "Error: invalid command" + usage + exit + fi + + DEV=$2 + echo "Removing bandwith limits" + /sbin/tc qdisc del dev $DEV root 2> /dev/null > /dev/null + /sbin/tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null + exit + +fi + +if [ "$1" = "--set" ]; then + DEV=$2 + echo "Removing bandwith limit" + /sbin/tc qdisc del dev $DEV ingress #2> /dev/null > /dev/null + + if [ $# -ne 4 ]; then + echo "Error: missing parameters" + usage + exit + fi + + DEV=$2 + echo "Configuring interface $DEV " + + IN=$3 + IN=$((${IN} * 8)) + echo "Download speed ${IN}kbps" + + OUT=$4 + OUT=$((${OUT} * 8)) + echo "Upload speed ${OUT}kbps" + + echo "cleaning limits" + /sbin/tc qdisc del dev $DEV root 2> /dev/null > /dev/null + /sbin/tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null + + echo "configuring tc ingress" + /sbin/tc qdisc add dev $DEV handle ffff: ingress #2> /dev/null > /dev/null + /sbin/tc filter add dev $DEV parent ffff: protocol ip prio 50 u32 match ip src \ + 0.0.0.0/0 police rate ${IN}kbit burst 10k drop flowid :1 #2> /dev/null > /dev/null + + echo "configuring tc egress" + /sbin/tc qdisc add dev $DEV root tbf rate ${OUT}kbit latency 25ms burst 10k #2> /dev/null > /dev/null + exit +fi + +echo "Error: missing parameters" +usage +exit 1 diff --git a/src/ftee/Makefile.in b/src/ftee/Makefile.in new file mode 100644 index 000000000..6911f0a3c --- /dev/null +++ b/src/ftee/Makefile.in @@ -0,0 +1,24 @@ +all: ftee + +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)"' -DPREFIX='"$(PREFIX)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security +LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread + +%.o : %.c $(H_FILE_LIST) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +ftee: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + +clean:; rm -f *.o ftee + +distclean: clean + rm -fr Makefile + diff --git a/src/ftee/ftee.h b/src/ftee/ftee.h new file mode 100644 index 000000000..a28cc5bb5 --- /dev/null +++ b/src/ftee/ftee.h @@ -0,0 +1,24 @@ +/* + * 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 FTEE_H +#define FTEE_H +#include "../include/common.h" + +#endif \ No newline at end of file diff --git a/src/ftee/main.c b/src/ftee/main.c new file mode 100644 index 000000000..4dca3cf48 --- /dev/null +++ b/src/ftee/main.c @@ -0,0 +1,228 @@ +#include "ftee.h" +#include +#include +#include +#include +#define MAXBUF 512 + +static unsigned char buf[MAXBUF]; + +static FILE *out_fp = NULL; +static int out_cnt = 0; +static int out_max = 500 * 1024; + +static void log_close(void) { + if (out_fp) { + fclose(out_fp); + out_fp = NULL; + } +} + +static void log_rotate(const char *fname) { + struct stat s; + int index = strlen(fname); + char *name1 = malloc(index + 2 + 1); + char *name2 = malloc(index + 2 + 1); + if (!name1 || !name2) + errExit("malloc"); + strcpy(name1, fname); + strcpy(name2, fname); + fflush(0); + + // delete filename.5 + sprintf(name1 + index, ".5"); + if (stat(name1, &s) == 0) { + int rv = unlink(name1); + if (rv == -1) + perror("unlink"); + } + + // move files 1 to 4 down one position + sprintf(name2 + index, ".4"); + if (stat(name2, &s) == 0) { + int rv = rename(name2, name1); + if (rv == -1) + perror("rename"); + } + + sprintf(name1 + index, ".3"); + if (stat(name1, &s) == 0) { + int rv = rename(name1, name2); + if (rv == -1) + perror("rename"); + } + + sprintf(name2 + index, ".2"); + if (stat(name2, &s) == 0) { + /* coverity[toctou] */ + int rv = rename(name2, name1); + if (rv == -1) + perror("rename"); + } + + sprintf(name1 + index, ".1"); + if (stat(name1, &s) == 0) { + int rv = rename(name1, name2); + if (rv == -1) + perror("rename"); + } + + // move the first file + if (out_fp) + fclose(out_fp); + + out_fp = NULL; + if (stat(fname, &s) == 0) { + int rv = rename(fname, name1); + if (rv == -1) + perror("rename"); + } + + free(name1); + free(name2); +} + +static void log_write(const unsigned char *str, int len, const char *fname) { + assert(fname); + + if (out_fp == NULL) { + out_fp = fopen(fname, "w"); + if (!out_fp) { + fprintf(stderr, "Error: cannot open log file %s\n", fname); + exit(1); + } + out_cnt = 0; + } + + // rotate files + out_cnt += len; + if (out_cnt >= out_max) { + log_rotate(fname); + + // reopen the first file + if (out_fp) + fclose(out_fp); + out_fp = fopen(fname, "w"); + if (!out_fp) { + fprintf(stderr, "Error: cannot open log file %s\n", fname); + exit(1); + } + out_cnt = len; + } + + fwrite(str, len, 1, out_fp); + fflush(0); +} + + +// return 1 if the file is a directory +static int is_dir(const char *fname) { + assert(fname); + if (*fname == '\0') + return 0; + + // if fname doesn't end in '/', add one + int rv; + struct stat s; + if (fname[strlen(fname) - 1] == '/') + rv = stat(fname, &s); + else { + char *tmp; + if (asprintf(&tmp, "%s/", fname) == -1) { + fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); + exit(1); + } + rv = stat(tmp, &s); + free(tmp); + } + + if (rv == -1) + return 0; + + if (S_ISDIR(s.st_mode)) + return 1; + + return 0; +} + +// return 1 if the file is a link +static int is_link(const char *fname) { + assert(fname); + if (*fname == '\0') + return 0; + + struct stat s; + if (lstat(fname, &s) == 0) { + if (S_ISLNK(s.st_mode)) + return 1; + } + + return 0; +} + + + + + +static void usage(void) { + printf("Usage: ftee filename\n"); +} + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Error: please provide a filename to store the program output\n"); + usage(); + exit(1); + } + char *fname = argv[1]; + + + // do not accept directories, links, and files with ".." + if (strstr(fname, "..") || is_link(fname) || is_dir(fname)) { + fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n"); + exit(1); + } + + struct stat s; + if (stat(fname, &s) == 0) { + // check permissions + if (s.st_uid != getuid() || s.st_gid != getgid()) { + fprintf(stderr, "Error: the output file needs to be owned by the current user.\n"); + exit(1); + } + + // check hard links + if (s.st_nlink != 1) { + fprintf(stderr, "Error: no hard links allowed.\n"); + exit(1); + } + } + + // check if we can append to this file + /* coverity[toctou] */ + FILE *fp = fopen(fname, "a"); + if (!fp) { + fprintf(stderr, "Error: cannot open output file %s\n", fname); + exit(1); + } + fclose(fp); + + + // preserve the last log file + log_rotate(fname); + + setvbuf (stdout, NULL, _IONBF, 0); + while(1) { + int n = read(0, buf, sizeof(buf)); + if (n < 0 && errno == EINTR) + continue; + if (n <= 0) + break; + + fwrite(buf, n, 1, stdout); + log_write(buf, n, fname); + } + + log_close(); + return 0; +} diff --git a/src/include/common.h b/src/include/common.h new file mode 100644 index 000000000..7ce1e9290 --- /dev/null +++ b/src/include/common.h @@ -0,0 +1,115 @@ +/* + * 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 COMMON_H +#define COMMON_H +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define errExit(msg) do { char msgout[500]; sprintf(msgout, "Error %s:%s(%d)", msg, __FUNCTION__, __LINE__); perror(msgout); exit(1);} while (0) + +// macro to print ip addresses in a printf statement +#define PRINT_IP(A) \ +((int) (((A) >> 24) & 0xFF)), ((int) (((A) >> 16) & 0xFF)), ((int) (((A) >> 8) & 0xFF)), ((int) ( (A) & 0xFF)) + +// macro to print a mac addresses in a printf statement +#define PRINT_MAC(A) \ +((unsigned) (*(A)) & 0xff), ((unsigned) (*((A) + 1) & 0xff)), ((unsigned) (*((A) + 2) & 0xff)), \ +((unsigned) (*((A) + 3)) & 0xff), ((unsigned) (*((A) + 4) & 0xff)), ((unsigned) (*((A) + 5)) & 0xff) + +// the number of bits in a network mask +static inline uint8_t mask2bits(uint32_t mask) { + uint32_t tmp = 0x80000000; + int i; + uint8_t rv = 0; + + for (i = 0; i < 32; i++, tmp >>= 1) { + if (tmp & mask) + rv++; + else + break; + } + return rv; +} + +// read an IPv4 address and convert it to uint32_t +static inline int atoip(const char *str, uint32_t *ip) { + unsigned a, b, c, d; + + if (sscanf(str, "%u.%u.%u.%u", &a, &b, &c, &d) != 4 || a > 255 || b > 255 || c > 255 || d > 255) + return 1; + + *ip = a * 0x1000000 + b * 0x10000 + c * 0x100 + d; + return 0; +} + +// verify an ip address is in the network range given by ifip and mask +static inline char *in_netrange(uint32_t ip, uint32_t ifip, uint32_t ifmask) { + if ((ip & ifmask) != (ifip & ifmask)) + return "Error: the IP address is not in the interface range\n"; + else if ((ip & ifmask) == ip) + return "Error: the IP address is a network address\n"; + else if ((ip | ~ifmask) == ip) + return "Error: the IP address is a network address\n"; + return NULL; +} + +// read a mac address +static inline int atomac(char *str, unsigned char macAddr[6]) { + unsigned mac[6]; + + if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) + return 1; + + int i; + for (i = 0; i < 6; i++) { + if (mac[i] > 0xff) + return 1; + + macAddr[i] = (unsigned char) mac[i]; + } + + return 0; +} + +// check a mac address is configured +static inline int mac_not_zero(const unsigned char mac[6]) { + int i; + for (i = 0; i < 6; i++) { + if (mac[i] != 0) + return 1; + } + + return 0; +} + +int join_namespace(pid_t pid, char *type); +int name2pid(const char *name, pid_t *pid); +char *pid_proc_comm(const pid_t pid); +char *pid_proc_cmdline(const pid_t pid); +#endif diff --git a/src/include/libnetlink.h b/src/include/libnetlink.h new file mode 100644 index 000000000..e9cd6b186 --- /dev/null +++ b/src/include/libnetlink.h @@ -0,0 +1,163 @@ +/* file extracted from iproute2 software package + * + * Original source code: + * + * Information: + * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 + * + * Download: + * http://www.kernel.org/pub/linux/utils/net/iproute2/ + * + * Repository: + * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git + * + * License: GPL v2 + */ + + +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; +}; + +extern int rcvbuf; + +extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); +extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol); +extern void rtnl_close(struct rtnl_handle *rth); +extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); +extern int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int fam, int type, + __u32 filt_mask); +extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); + +typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, + struct nlmsghdr *n, void *); + +struct rtnl_dump_filter_arg +{ + rtnl_filter_t filter; + void *arg1; +}; + +extern int rtnl_dump_filter_l(struct rtnl_handle *rth, + const struct rtnl_dump_filter_arg *arg); +extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, + void *arg); +extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer); +extern int rtnl_send(struct rtnl_handle *rth, const void *buf, int); +extern int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int); + +extern int addattr(struct nlmsghdr *n, int maxlen, int type); +extern int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data); +extern int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data); +extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +extern int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data); +extern int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *data); + +extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen); +extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); +extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type); +extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest); +extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len); +extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest); +extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen); + +extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags); +extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len); + +#define parse_rtattr_nested(tb, max, rta) \ + (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) + +#define parse_rtattr_nested_compat(tb, max, rta, data, len) \ + ({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \ + __parse_rtattr_nested_compat(tb, max, rta, len); }) + +static inline __u8 rta_getattr_u8(const struct rtattr *rta) +{ + return *(__u8 *)RTA_DATA(rta); +} +static inline __u16 rta_getattr_u16(const struct rtattr *rta) +{ + return *(__u16 *)RTA_DATA(rta); +} +static inline __u32 rta_getattr_u32(const struct rtattr *rta) +{ + return *(__u32 *)RTA_DATA(rta); +} +static inline __u64 rta_getattr_u64(const struct rtattr *rta) +{ + __u64 tmp; + memcpy(&tmp, RTA_DATA(rta), sizeof(__u64)); + return tmp; +} +static inline const char *rta_getattr_str(const struct rtattr *rta) +{ + return (const char *)RTA_DATA(rta); +} + +extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, + void *jarg); +extern int rtnl_from_file(FILE *, rtnl_filter_t handler, + void *jarg); + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +#ifndef IFA_RTA +#define IFA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#endif +#ifndef IFA_PAYLOAD +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) +#endif + +#ifndef IFLA_RTA +#define IFLA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#endif +#ifndef IFLA_PAYLOAD +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) +#endif + +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif +#ifndef NDA_PAYLOAD +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) +#endif + +#ifndef NDTA_RTA +#define NDTA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg)))) +#endif +#ifndef NDTA_PAYLOAD +#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) +#endif + +#endif /* __LIBNETLINK_H__ */ + diff --git a/src/include/pid.h b/src/include/pid.h new file mode 100644 index 000000000..aaadaa542 --- /dev/null +++ b/src/include/pid.h @@ -0,0 +1,58 @@ +/* + * 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 PID_H +#define PID_H +extern int max_pids; + + +#define _GNU_SOURCE +#include +#include +#include +typedef struct { + short level; // -1 not a firejail process, 0 not investigated yet, 1 firejail process, > 1 firejail child + unsigned char zombie; + pid_t parent; + uid_t uid; + char *user; + char *cmd; + unsigned utime; + unsigned stime; + unsigned long long rx; // network rx, bytes + unsigned long long tx; // networking tx, bytes + unsigned rx_delta; + unsigned tx_delta; +} Process; +//extern Process pids[max_pids]; +extern Process *pids; + +// pid functions +void pid_getmem(unsigned pid, unsigned *rss, unsigned *shared); +void pid_get_cpu_time(unsigned pid, unsigned *utime, unsigned *stime); +unsigned long long pid_get_start_time(unsigned pid); +uid_t pid_get_uid(pid_t pid); +char *pid_get_user_name(uid_t uid); +// print functions +void pid_print_tree(unsigned index, unsigned parent, int nowrap); +void pid_print_list(unsigned index, int nowrap); +void pid_store_cpu(unsigned index, unsigned parent, unsigned *utime, unsigned *stime); +void pid_read(pid_t mon_pid); + +#endif diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in new file mode 100644 index 000000000..6e6be1910 --- /dev/null +++ b/src/lib/Makefile.in @@ -0,0 +1,20 @@ +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 -fPIC -Wformat -Wformat-security +LDFLAGS:=-pic -Wl,-z,relro -Wl,-z,now + +all: $(OBJS) + +%.o : %.c $(H_FILE_LIST) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +clean:; rm -f $(OBJS) + +distclean: clean + rm -fr Makefile diff --git a/src/lib/common.c b/src/lib/common.c new file mode 100644 index 000000000..6d928abbb --- /dev/null +++ b/src/lib/common.c @@ -0,0 +1,192 @@ +/* + * 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. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/common.h" + +int join_namespace(pid_t pid, char *type) { + char *path; + if (asprintf(&path, "/proc/%u/ns/%s", pid, type) == -1) + errExit("asprintf"); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + free(path); + fprintf(stderr, "Error: cannot open /proc/%u/ns/%s.\n", pid, type); + return -1; + } + + if (syscall(__NR_setns, fd, 0) < 0) { + free(path); + fprintf(stderr, "Error: cannot join namespace %s.\n", type); + close(fd); + return -1; + } + + close(fd); + free(path); + return 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); + 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; +} + +#define BUFLEN 4096 +char *pid_proc_comm(const pid_t pid) { + // open /proc/pid/cmdline file + char *fname; + int fd; + if (asprintf(&fname, "/proc/%d//comm", pid) == -1) + return NULL; + if ((fd = open(fname, O_RDONLY)) < 0) { + free(fname); + return NULL; + } + free(fname); + + // read file + unsigned char buffer[BUFLEN]; + ssize_t len; + if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { + close(fd); + return NULL; + } + buffer[len] = '\0'; + close(fd); + + // return a malloc copy of the command line + char *rv = strdup((char *) buffer); + if (strlen(rv) == 0) { + free(rv); + return NULL; + } + return rv; +} + +char *pid_proc_cmdline(const pid_t pid) { + // open /proc/pid/cmdline file + char *fname; + int fd; + if (asprintf(&fname, "/proc/%d/cmdline", pid) == -1) + return NULL; + if ((fd = open(fname, O_RDONLY)) < 0) { + free(fname); + return NULL; + } + free(fname); + + // read file + unsigned char buffer[BUFLEN]; + ssize_t len; + if ((len = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { + close(fd); + return NULL; + } + buffer[len] = '\0'; + close(fd); + + // clean data + int i; + for (i = 0; i < len; i++) { + if (buffer[i] == '\0') + buffer[i] = ' '; + if (buffer[i] >= 0x80) // execv in progress!!! + return NULL; + } + + // return a malloc copy of the command line + char *rv = strdup((char *) buffer); + if (strlen(rv) == 0) { + free(rv); + return NULL; + } + return rv; +} diff --git a/src/lib/libnetlink.c b/src/lib/libnetlink.c new file mode 100644 index 000000000..264632a01 --- /dev/null +++ b/src/lib/libnetlink.c @@ -0,0 +1,803 @@ +/* file extracted from iproute2 software package + * + * Original source code: + * + * Information: + * http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 + * + * Download: + * http://www.kernel.org/pub/linux/utils/net/iproute2/ + * + * Repository: + * git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git + * + * License: GPL v2 + * + * Original copyright header + * + * libnetlink.c RTnetlink service routines. + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/libnetlink.h" + +int rcvbuf = 1024 * 1024; + +void rtnl_close(struct rtnl_handle *rth) +{ + if (rth->fd >= 0) { + close(rth->fd); + rth->fd = -1; + } +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, + int protocol) +{ + socklen_t addr_len; + int sndbuf = 32768; + + memset(rth, 0, sizeof(*rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF); +} + +int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type, + __u32 filt_mask) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + /* attribute has to be NLMSG aligned */ + struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO))); + __u32 ext_filter_mask; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.ifm.ifi_family = family; + + req.ext_req.rta_type = IFLA_EXT_MASK; + req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32)); + req.ext_filter_mask = filt_mask; + + return send(rth->fd, (void*)&req, sizeof(req), 0); +} + +int rtnl_send(struct rtnl_handle *rth, const void *buf, int len) +{ + return send(rth->fd, buf, len, 0); +} + +int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len) +{ + struct nlmsghdr *h; + int status; + char resp[1024]; + + status = send(rth->fd, buf, len, 0); + if (status < 0) + return status; + + /* Check for immediate errors */ + status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK); + if (status < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + + for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status); + h = NLMSG_NEXT(h, status)) { + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + fprintf(stderr, "ERROR truncated\n"); + else + errno = -err->error; + return -1; + } + } + + return 0; +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec iov[2] = { + { .iov_base = &nlh, .iov_len = sizeof(nlh) }, + { .iov_base = req, .iov_len = len } + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = 2, + }; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter_l(struct rtnl_handle *rth, + const struct rtnl_dump_filter_arg *arg) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + int dump_intr = 0; + + iov.iov_base = buf; + while (1) { + int status; + const struct rtnl_dump_filter_arg *a; + int found_done = 0; + int msglen = 0; + + iov.iov_len = sizeof(buf); + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -1; + } + + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + + for (a = arg; a->filter; a++) { + struct nlmsghdr *h = (struct nlmsghdr*)buf; + msglen = status; + + while (NLMSG_OK(h, msglen)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) + goto skip_it; + + if (h->nlmsg_flags & NLM_F_DUMP_INTR) + dump_intr = 1; + + if (h->nlmsg_type == NLMSG_DONE) { + found_done = 1; + break; /* process next filter */ + } + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, + "ERROR truncated\n"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + return -1; + } + err = a->filter(&nladdr, h, a->arg1); + if (err < 0) + return err; + +skip_it: + h = NLMSG_NEXT(h, msglen); + } + } + + if (found_done) { + if (dump_intr) + fprintf(stderr, + "Dump was interrupted and may be inconsistent.\n"); + return 0; + } + + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (msglen) { + fprintf(stderr, "!!!Remnant of size %d\n", msglen); + exit(1); + } + } +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1) +{ + const struct rtnl_dump_filter_arg a[2] = { + { .filter = filter, .arg1 = arg1, }, + { .filter = NULL, .arg1 = NULL, }, + }; + + return rtnl_dump_filter_l(rth, a); +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { + .iov_base = (void*) n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + + if (answer == NULL) + n->nlmsg_flags |= NLM_F_ACK; + + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + perror("Cannot talk to rtnetlink"); + return -1; + } + + memset(buf,0,sizeof(buf)); + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l < 0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + if (nladdr.nl_pid != peer || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq != seq) { + /* Don't forget to skip that message. */ + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { + fprintf(stderr, "ERROR truncated\n"); + } else { + if (!err->error) { + if (answer) + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "RTNETLINK answers: %s\n", strerror(-err->error)); + errno = -err->error; + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "Unexpected reply!!!\n"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_listen(struct rtnl_handle *rtnl, + rtnl_filter_t handler, + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[8192]; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + iov.iov_base = buf; + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + if (errno == ENOBUFS) + continue; + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler, + void *jarg) +{ + int status; + struct sockaddr_nl nladdr; + char buf[8192]; + struct nlmsghdr *h = (void*)buf; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + while (1) { + int err, len; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("rtnl_from_file: fread"); + return -1; + } + if (status == 0) + return 0; + + len = h->nlmsg_len; + l = len - sizeof(*h); + + if (l<0 || len>sizeof(buf)) { + fprintf(stderr, "!!!malformed message: len=%d @%lu\n", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status < 0) { + perror("rtnl_from_file: fread"); + return -1; + } + if (status < l) { + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + } +} + +int addattr(struct nlmsghdr *n, int maxlen, int type) +{ + return addattr_l(n, maxlen, type, NULL, 0); +} + +int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u8)); +} + +int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u16)); +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u32)); +} + +int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u64)); +} + +int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *str) +{ + return addattr_l(n, maxlen, type, str, strlen(str)+1); +} + + + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + +#if 0 +printf("%d: %s\n", __LINE__, __FUNCTION__); +printf("\ttype %d - ", type); +if (type == IFLA_LINK) { + printf("IFLA_LINK\n"); + int i; + printf("\tdata - "); + for (i = 0; i < alen; i++) + printf("%02x, ", *((unsigned char *)data + i)); + printf("\n"); +} +else if (type == IFLA_IFNAME) { + printf("IFLA_IFNAME\n"); + printf("\tdata - #%s#\n", data); +} +else if (type == IFLA_LINKINFO) printf("IFLA_LINKINFO\n"); +else if (type == IFLA_ADDRESS) { + printf("IFLA_ADDRESS or IFLA_INFO_KIND\n"); + int i; + printf("\tdata - "); + for (i = 0; i < alen; i++) + printf("%02x, ", *((unsigned char *)data + i)); + printf("\n"); +} +else if (type == IFLA_BROADCAST) printf("IFLA_BROADCAST or IFLA_INFO_DATA\n"); + +printf("\tdata length: %d\n", alen); +#endif + + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return 0; +} + +#if 0 +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ +printf("%s: adding type %d, length %d ", __FUNCTION__, type, alen); +if (type == IFLA_INFO_KIND) { +if (alen) + printf("(IFLA_INFO_KIND %s)\n", (char *)data); +else +printf("(VETH_INFO_PEER)\n"); +} +else if (type == IFLA_IFNAME) { +printf("(IFLA_IFNAME %s)\n", (char *) data); +} +else if (type == IFLA_NET_NS_PID) { +printf("(IFLA_NET_NS_PID %u)\n", *((unsigned *) data)); +} +else if (type == IFLA_LINKINFO) +printf("(IFLA_LINKINFO)\n"); +else if (type == IFLA_INFO_DATA) +printf("(IFLA_INFO_DATA)\n"); +else + printf("\n"); + + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return 0; +} +#endif + +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) +{ + if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { + fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + + memcpy(NLMSG_TAIL(n), data, len); + memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); + return 0; +} + +struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; + return n->nlmsg_len; +} + +struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, + const void *data, int len) +{ + struct rtattr *start = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, data, len); + addattr_nest(n, maxlen, type); + return start; +} + +int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start) +{ + struct rtattr *nest = (void *)start + NLMSG_ALIGN(start->rta_len); + + start->rta_len = (void *)NLMSG_TAIL(n) - (void *)start; + addattr_nest_end(n, nest); + return n->nlmsg_len; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len); + return 0; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + return parse_rtattr_flags(tb, max, rta, len, 0); +} + +int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags) +{ + unsigned short type; + + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + type = rta->rta_type & ~flags; + if ((type <= max) && (!tb[type])) + tb[type] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return 0; +} + +int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + int i = 0; + + memset(tb, 0, sizeof(struct rtattr *) * max); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max && i < max) + tb[i++] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return i; +} + +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, + int len) +{ + if (RTA_PAYLOAD(rta) < len) + return -1; + if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) { + rta = RTA_DATA(rta) + RTA_ALIGN(len); + return parse_rtattr_nested(tb, max, rta); + } + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + return 0; +} diff --git a/src/lib/pid.c b/src/lib/pid.c new file mode 100644 index 000000000..a0261ead2 --- /dev/null +++ b/src/lib/pid.c @@ -0,0 +1,392 @@ +/* + * 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 "../include/common.h" +#include "../include/pid.h" +#include +#include +#include +#include +#include + +#define PIDS_BUFLEN 4096 +//Process pids[max_pids]; +Process *pids = NULL; +int max_pids=32769; +#define PIDS_BUFLEN 4096 + +// get the memory associated with this pid +void pid_getmem(unsigned pid, unsigned *rss, unsigned *shared) { + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/statm", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return; + } + free(file); + + unsigned a, b, c; + if (3 != fscanf(fp, "%u %u %u", &a, &b, &c)) { + fclose(fp); + return; + } + *rss += b; + *shared += c; + fclose(fp); +} + + +void pid_get_cpu_time(unsigned pid, unsigned *utime, unsigned *stime) { + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/stat", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return; + } + free(file); + + char line[PIDS_BUFLEN]; + if (fgets(line, PIDS_BUFLEN - 1, fp)) { + char *ptr = line; + // jump 13 fields + int i; + for (i = 0; i < 13; i++) { + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + if (*ptr == '\0') + goto myexit; + ptr++; + } + if (2 != sscanf(ptr, "%u %u", utime, stime)) + goto myexit; + } + +myexit: + fclose(fp); +} + +unsigned long long pid_get_start_time(unsigned pid) { + // open stat file + char *file; + if (asprintf(&file, "/proc/%u/stat", pid) == -1) { + perror("asprintf"); + exit(1); + } + FILE *fp = fopen(file, "r"); + if (!fp) { + free(file); + return 0; + } + free(file); + + char line[PIDS_BUFLEN]; + unsigned long long retval = 0; + if (fgets(line, PIDS_BUFLEN - 1, fp)) { + char *ptr = line; + // jump 21 fields + int i; + for (i = 0; i < 21; i++) { + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') + ptr++; + if (*ptr == '\0') + goto myexit; + ptr++; + } + if (1 != sscanf(ptr, "%llu", &retval)) + goto myexit; + } + +myexit: + fclose(fp); + return retval; +} + +char *pid_get_user_name(uid_t uid) { + struct passwd *pw = getpwuid(uid); + if (pw) + return strdup(pw->pw_name); + return NULL; +} + +uid_t pid_get_uid(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, "Uid:", 4) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') + goto doexit; + + rv = atoi(ptr); + break; // break regardless! + } + } +doexit: + fclose(fp); + free(file); + return rv; +} + +static void print_elem(unsigned index, int nowrap) { + // get terminal size + struct winsize sz; + int col = 0; + if (isatty(STDIN_FILENO)) { + if (!ioctl(0, TIOCGWINSZ, &sz)) + col = sz.ws_col; + } + + // indent + char indent[(pids[index].level - 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent)); + indent[(pids[index].level - 1) * 2] = '\0'; + + // get data + uid_t uid = pids[index].uid; + char *cmd = pid_proc_cmdline(index); + char *user = pid_get_user_name(uid); + char *allocated = user; + if (user ==NULL) + user = ""; + if (cmd) { + if (col < 4 || nowrap) + printf("%s%u:%s:%s\n", indent, index, user, cmd); + else { + char *out; + if (asprintf(&out, "%s%u:%s:%s\n", indent, index, user, cmd) == -1) + errExit("asprintf"); + int len = strlen(out); + if (len > col) { + out[col] = '\0'; + out[col - 1] = '\n'; + } + printf("%s", out); + free(out); + } + + free(cmd); + } + else { + if (pids[index].zombie) + printf("%s%u: (zombie)\n", indent, index); + else + printf("%s%u:\n", indent, index); + } + if (allocated) + free(allocated); +} + +// recursivity!!! +void pid_print_tree(unsigned index, unsigned parent, int nowrap) { + print_elem(index, nowrap); + + int i; + for (i = index + 1; i < max_pids; i++) { + if (pids[i].parent == index) + pid_print_tree(i, index, nowrap); + } + + for (i = 0; i < index; i++) { + if (pids[i].parent == index) + pid_print_tree(i, index, nowrap); + } +} + +void pid_print_list(unsigned index, int nowrap) { + print_elem(index, nowrap); +} + +// recursivity!!! +void pid_store_cpu(unsigned index, unsigned parent, unsigned *utime, unsigned *stime) { + if (pids[index].level == 1) { + *utime = 0; + *stime = 0; + } + + unsigned utmp = 0; + unsigned stmp = 0; + 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) + pid_store_cpu(i, index, utime, stime); + } + + if (pids[index].level == 1) { + pids[index].utime = *utime; + pids[index].stime = *stime; + } +} + +// mon_pid: pid of sandbox to be monitored, 0 if all sandboxes are included +void pid_read(pid_t mon_pid) { + if (pids == NULL) { + FILE *fp = fopen("/proc/sys/kernel/pid_max", "r"); + if (fp) { + int val; + if (fscanf(fp, "%d", &val) == 1) { + if (val >= max_pids) + max_pids = val + 1; + } + fclose(fp); + } + pids = malloc(sizeof(Process) * max_pids); + if (pids == NULL) + errExit("malloc"); + } + memset(pids, 0, sizeof(Process) * max_pids); + pid_t mypid = 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); + } + } + + pid_t child = -1; + struct dirent *entry; + char *end; + while (child < 0 && (entry = readdir(dir))) { + pid_t pid = strtol(entry->d_name, &end, 10); + pid %= max_pids; + if (end == entry->d_name || *end) + continue; + if (pid == mypid) + continue; + + // 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); + continue; + } + + // 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') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + + if (mon_pid == 0 && strncmp(ptr, "firejail", 8) == 0) { + pids[pid].level = 1; + } + else if (mon_pid == pid && strncmp(ptr, "firejail", 8) == 0) { + pids[pid].level = 1; + } +// else if (mon_pid == 0 && strncmp(ptr, "lxc-execute", 11) == 0) { +// pids[pid].level = 1; +// } +// else if (mon_pid == pid && strncmp(ptr, "lxc-execute", 11) == 0) { +// pids[pid].level = 1; +// } + else + pids[pid].level = -1; + } + if (strncmp(buf, "State:", 6) == 0) { + if (strstr(buf, "(zombie)")) + pids[pid].zombie = 1; + } + else if (strncmp(buf, "PPid:", 5) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + unsigned parent = atoi(ptr); + parent %= max_pids; + if (pids[parent].level > 0) { + pids[pid].level = pids[parent].level + 1; + } + pids[pid].parent = parent; + } + else if (strncmp(buf, "Uid:", 4) == 0) { + char *ptr = buf + 5; + while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { + ptr++; + } + if (*ptr == '\0') { + fprintf(stderr, "Error: cannot read /proc file\n"); + exit(1); + } + pids[pid].uid = atoi(ptr); + break; + } + } + fclose(fp); + free(file); + } + closedir(dir); + + pid_t pid; + for (pid = 0; pid < max_pids; pid++) { + int parent = pids[pid].parent; + if (pids[parent].level > 0) { + pids[pid].level = pids[parent].level + 1; + } + } +} diff --git a/src/libtrace/Makefile.in b/src/libtrace/Makefile.in new file mode 100644 index 000000000..8848fc08c --- /dev/null +++ b/src/libtrace/Makefile.in @@ -0,0 +1,25 @@ +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 -fPIC -Wformat -Wformat-security +LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now + +all: libtrace.so + +%.o : %.c $(H_FILE_LIST) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +# gcc -shared -fPIC -ldl traceopen.c -o traceopen.so +libtrace.so: $(OBJS) + $(CC) $(LDFLAGS) -shared -fPIC -z relro -o $@ $(OBJS) -ldl + + +clean:; rm -f $(OBJS) libtrace.so + +distclean: clean + rm -fr Makefile diff --git a/src/libtrace/libtrace.c b/src/libtrace/libtrace.c new file mode 100644 index 000000000..a785ec698 --- /dev/null +++ b/src/libtrace/libtrace.c @@ -0,0 +1,609 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// break recursivity on fopen call +typedef FILE *(*orig_fopen_t)(const char *pathname, const char *mode); +static orig_fopen_t orig_fopen = NULL; +typedef FILE *(*orig_fopen64_t)(const char *pathname, const char *mode); +static orig_fopen64_t orig_fopen64 = NULL; + +// +// pid +// +static pid_t mypid = 0; +static inline pid_t pid(void) { + if (!mypid) + mypid = getpid(); + return mypid; +} + +// +// process name +// +#define MAXNAME 16 +static char myname[MAXNAME]; +static int nameinit = 0; +static char *name(void) { + if (!nameinit) { + // initialize the name of the process based on /proc/PID/comm + memset(myname, 0, MAXNAME); + + pid_t p = pid(); + char *fname; + if (asprintf(&fname, "/proc/%u/comm", p) == -1) + return "unknown"; + + // read file + if (!orig_fopen) + orig_fopen = (orig_fopen_t)dlsym(RTLD_NEXT, "fopen"); + FILE *fp = orig_fopen(fname, "r"); + if (!fp) + return "unknown"; + if (fgets(myname, MAXNAME, fp) == NULL) { + fclose(fp); + free(fname); + return "unknown"; + } + + // clean '\n' + char *ptr = strchr(myname, '\n'); + if (ptr) + *ptr = '\0'; + + fclose(fp); + free(fname); + nameinit = 1; + } + + return myname; +} + +// +// network +// +typedef struct { + int val; + char *name; +} XTable; + +static XTable socket_type[] = { +#ifdef SOCK_STREAM + { SOCK_STREAM, "SOCK_STREAM" }, +#endif +#ifdef SOCK_DGRAM + { SOCK_DGRAM, "SOCK_DGRAM" }, +#endif +#ifdef SOCK_RAW + { SOCK_RAW, "SOCK_RAW" }, +#endif +#ifdef SOCK_RDM + { SOCK_RDM, "SOCK_RDM" }, +#endif +#ifdef SOCK_SEQPACKET + { SOCK_SEQPACKET, "SOCK_SEQPACKET" }, +#endif +#ifdef SOCK_DCCP + { SOCK_DCCP, "SOCK_DCCP" }, +#endif + { 0, NULL} // NULL terminated +}; + +static XTable socket_domain[] = { +#ifdef AF_INET + { AF_INET, "AF_INET" }, +#endif +#ifdef AF_INET6 + { AF_INET6, "AF_INET6" }, +#endif +#ifdef AF_LOCAL + { AF_LOCAL, "AF_LOCAL" }, +#endif +#ifdef AF_PACKET + { AF_PACKET, "AF_PACKET" }, +#endif +#ifdef AF_IPX + { AF_IPX, "AF_IPX" }, +#endif +#ifdef AF_NETLINK + { AF_NETLINK, "AF_NETLINK" }, +#endif +#ifdef AF_X25 + { AF_X25, "AF_X25" }, +#endif +#ifdef AF_AX25 + { AF_AX25, "AF_AX25" }, +#endif +#ifdef AF_ATMPVC + { AF_ATMPVC, "AF_ATMPVC" }, +#endif +#ifdef AF_APPLETALK + { AF_APPLETALK, "AF_APPLETALK" }, +#endif + { 0, NULL} // NULL terminated +}; + +static XTable socket_protocol[] = { +#ifdef IPPROTO_IP + { IPPROTO_IP, "IPPROTO_IP" }, +#endif +#ifdef IPPROTO_ICMP + { IPPROTO_ICMP, "IPPROTO_ICMP" }, +#endif +#ifdef IPPROTO_IGMP + { IPPROTO_IGMP, "IPPROTO_IGMP" }, +#endif +#ifdef IPPROTO_IPIP + { IPPROTO_IPIP, "IPPROTO_IPIP" }, +#endif +#ifdef IPPROTO_TCP + { IPPROTO_TCP, "IPPROTO_TCP" }, +#endif +#ifdef IPPROTO_EGP + { IPPROTO_EGP, "IPPROTO_EGP" }, +#endif +#ifdef IPPROTO_PUP + { IPPROTO_PUP, "IPPROTO_PUP" }, +#endif +#ifdef IPPROTO_UDP + { IPPROTO_UDP, "IPPROTO_UDP" }, +#endif +#ifdef IPPROTO_IDP + { IPPROTO_IDP, "IPPROTO_IDP" }, +#endif +#ifdef IPPROTO_DCCP + { IPPROTO_DCCP, "IPPROTO_DCCP" }, +#endif +#ifdef IPPROTO_RSVP + { IPPROTO_RSVP, "IPPROTO_RSVP" }, +#endif +#ifdef IPPROTO_GRE + { IPPROTO_GRE, "IPPROTO_GRE" }, +#endif +#ifdef IPPROTO_IPV6 + { IPPROTO_IPV6, "IPPROTO_IPV6" }, +#endif +#ifdef IPPROTO_ESP + { IPPROTO_ESP, "IPPROTO_ESP" }, +#endif +#ifdef IPPROTO_AH + { IPPROTO_AH, "IPPROTO_AH" }, +#endif +#ifdef IPPROTO_BEETPH + { IPPROTO_BEETPH, "IPPROTO_BEETPH" }, +#endif +#ifdef IPPROTO_PIM + { IPPROTO_PIM, "IPPROTO_PIM" }, +#endif +#ifdef IPPROTO_COMP + { IPPROTO_COMP, "IPPROTO_COMP" }, +#endif +#ifdef IPPROTO_SCTP + { IPPROTO_SCTP, "IPPROTO_SCTP" }, +#endif +#ifdef IPPROTO_UDPLITE + { IPPROTO_UDPLITE, "IPPROTO_UDPLITE" }, +#endif +#ifdef IPPROTO_RAW + { IPPROTO_RAW, "IPPROTO_RAW" }, +#endif + { 0, NULL} // NULL terminated +}; + +static char *translate(XTable *table, int val) { + while (table->name != NULL) { + if (val == table->val) + return table->name; + table++; + } + + return NULL; +} + +static void print_sockaddr(const char *call, const struct sockaddr *addr) { + if (addr->sa_family == AF_INET) { + struct sockaddr_in *a = (struct sockaddr_in *) addr; + printf("%u:%s:%s %s:%u\n", pid(), name(), call, inet_ntoa(a->sin_addr), ntohs(a->sin_port)); + } + else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *a = (struct sockaddr_in6 *) addr; + char str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(a->sin6_addr), str, INET6_ADDRSTRLEN); + printf("%u:%s:%s %s\n", pid(), name(), call, str); + } + else if (addr->sa_family == AF_UNIX) { + struct sockaddr_un *a = (struct sockaddr_un *) addr; + if (a->sun_path[0]) + printf("%u:%s:%s %s\n", pid(), name(), call, a->sun_path); + else + printf("%u:%s:%s @%s\n", pid(), name(), call, a->sun_path + 1); + } + else { + printf("%u:%s:%s family %d\n", pid(), name(), call, addr->sa_family); + } +} + +// +// syscalls +// + +// open +typedef int (*orig_open_t)(const char *pathname, int flags, mode_t mode); +static orig_open_t orig_open = NULL; +int open(const char *pathname, int flags, mode_t mode) { + if (!orig_open) + orig_open = (orig_open_t)dlsym(RTLD_NEXT, "open"); + + int rv = orig_open(pathname, flags, mode); + printf("%u:%s:open %s\n", pid(), name(), pathname); + return rv; +} + +typedef int (*orig_open64_t)(const char *pathname, int flags, mode_t mode); +static orig_open64_t orig_open64 = NULL; +int open64(const char *pathname, int flags, mode_t mode) { + if (!orig_open64) + orig_open64 = (orig_open64_t)dlsym(RTLD_NEXT, "open64"); + + int rv = orig_open64(pathname, flags, mode); + printf("%u:%s:open64 %s\n", pid(), name(), pathname); + return rv; +} + +// openat +typedef int (*orig_openat_t)(int dirfd, const char *pathname, int flags, mode_t mode); +static orig_openat_t orig_openat = NULL; +int openat(int dirfd, const char *pathname, int flags, mode_t mode) { + if (!orig_openat) + orig_openat = (orig_openat_t)dlsym(RTLD_NEXT, "openat"); + + int rv = orig_openat(dirfd, pathname, flags, mode); + printf("%u:%s:openat %s\n", pid(), name(), pathname); + return rv; +} + +typedef int (*orig_openat64_t)(int dirfd, const char *pathname, int flags, mode_t mode); +static orig_openat64_t orig_openat64 = NULL; +int openat64(int dirfd, const char *pathname, int flags, mode_t mode) { + if (!orig_openat64) + orig_openat64 = (orig_openat64_t)dlsym(RTLD_NEXT, "openat64"); + + int rv = orig_openat64(dirfd, pathname, flags, mode); + printf("%u:%s:openat64 %s\n", pid(), name(), pathname); + return rv; +} + + +// fopen +FILE *fopen(const char *pathname, const char *mode) { + if (!orig_fopen) + orig_fopen = (orig_fopen_t)dlsym(RTLD_NEXT, "fopen"); + + FILE *rv = orig_fopen(pathname, mode); + printf("%u:%s:fopen %s\n", pid(), name(), pathname); + return rv; +} + +FILE *fopen64(const char *pathname, const char *mode) { + if (!orig_fopen64) + orig_fopen64 = (orig_fopen_t)dlsym(RTLD_NEXT, "fopen64"); + + FILE *rv = orig_fopen64(pathname, mode); + printf("%u:%s:fopen64 %s\n", pid(), name(), pathname); + return rv; +} + + +// freopen +typedef FILE *(*orig_freopen_t)(const char *pathname, const char *mode, FILE *stream); +static orig_freopen_t orig_freopen = NULL; +FILE *freopen(const char *pathname, const char *mode, FILE *stream) { + if (!orig_freopen) + orig_freopen = (orig_freopen_t)dlsym(RTLD_NEXT, "freopen"); + + FILE *rv = orig_freopen(pathname, mode, stream); + printf("%u:%s:freopen %s\n", pid(), name(), pathname); + return rv; +} + +typedef FILE *(*orig_freopen64_t)(const char *pathname, const char *mode, FILE *stream); +static orig_freopen64_t orig_freopen64 = NULL; +FILE *freopen64(const char *pathname, const char *mode, FILE *stream) { + if (!orig_freopen64) + orig_freopen64 = (orig_freopen64_t)dlsym(RTLD_NEXT, "freopen64"); + + FILE *rv = orig_freopen64(pathname, mode, stream); + printf("%u:%s:freopen64 %s\n", pid(), name(), pathname); + return rv; +} + +// unlink +typedef int (*orig_unlink_t)(const char *pathname); +static orig_unlink_t orig_unlink = NULL; +int unlink(const char *pathname) { + if (!orig_unlink) + orig_unlink = (orig_unlink_t)dlsym(RTLD_NEXT, "unlink"); + + int rv = orig_unlink(pathname); + printf("%u:%s:unlink %s\n", pid(), name(), pathname); + return rv; +} + +typedef int (*orig_unlinkat_t)(int dirfd, const char *pathname, int flags); +static orig_unlinkat_t orig_unlinkat = NULL; +int unlinkat(int dirfd, const char *pathname, int flags) { + if (!orig_unlinkat) + orig_unlinkat = (orig_unlinkat_t)dlsym(RTLD_NEXT, "unlinkat"); + + int rv = orig_unlinkat(dirfd, pathname, flags); + printf("%u:%s:unlinkat %s\n", pid(), name(), pathname); + return rv; +} + +// mkdir/mkdirat/rmdir +typedef int (*orig_mkdir_t)(const char *pathname, mode_t mode); +static orig_mkdir_t orig_mkdir = NULL; +int mkdir(const char *pathname, mode_t mode) { + if (!orig_mkdir) + orig_mkdir = (orig_mkdir_t)dlsym(RTLD_NEXT, "mkdir"); + + int rv = orig_mkdir(pathname, mode); + printf("%u:%s:mkdir %s\n", pid(), name(), pathname); + return rv; +} + +typedef int (*orig_mkdirat_t)(int dirfd, const char *pathname, mode_t mode); +static orig_mkdirat_t orig_mkdirat = NULL; +int mkdirat(int dirfd, const char *pathname, mode_t mode) { + if (!orig_mkdirat) + orig_mkdirat = (orig_mkdirat_t)dlsym(RTLD_NEXT, "mkdirat"); + + int rv = orig_mkdirat(dirfd, pathname, mode); + printf("%u:%s:mkdirat %s\n", pid(), name(), pathname); + return rv; +} + +typedef int (*orig_rmdir_t)(const char *pathname); +static orig_rmdir_t orig_rmdir = NULL; +int rmdir(const char *pathname) { + if (!orig_rmdir) + orig_rmdir = (orig_rmdir_t)dlsym(RTLD_NEXT, "rmdir"); + + int rv = orig_rmdir(pathname); + printf("%u:%s:rmdir %s\n", pid(), name(), pathname); + return rv; +} + +// stat +typedef int (*orig_stat_t)(const char *pathname, struct stat *buf); +static orig_stat_t orig_stat = NULL; +int stat(const char *pathname, struct stat *buf) { + if (!orig_stat) + orig_stat = (orig_stat_t)dlsym(RTLD_NEXT, "stat"); + + int rv = orig_stat(pathname, buf); + printf("%u:%s:stat %s\n", pid(), name(), pathname); + return rv; +} + +typedef int (*orig_stat64_t)(const char *pathname, struct stat64 *buf); +static orig_stat64_t orig_stat64 = NULL; +int stat64(const char *pathname, struct stat64 *buf) { + if (!orig_stat) + orig_stat64 = (orig_stat64_t)dlsym(RTLD_NEXT, "stat"); + + int rv = orig_stat64(pathname, buf); + printf("%u:%s:stat %s\n", pid(), name(), pathname); + return rv; +} + + +// access +typedef int (*orig_access_t)(const char *pathname, int mode); +static orig_access_t orig_access = NULL; +int access(const char *pathname, int mode) { + if (!orig_access) + orig_access = (orig_access_t)dlsym(RTLD_NEXT, "access"); + + int rv = orig_access(pathname, mode); + printf("%u:%s:access %s\n", pid(), name(), pathname); + return rv; +} + + +// connect +typedef int (*orig_connect_t)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static orig_connect_t orig_connect = NULL; +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + if (!orig_connect) + orig_connect = (orig_connect_t)dlsym(RTLD_NEXT, "connect"); + + int rv = orig_connect(sockfd, addr, addrlen); + print_sockaddr("connect", addr); + + return rv; +} + +// socket +typedef int (*orig_socket_t)(int domain, int type, int protocol); +static orig_socket_t orig_socket = NULL; +static char buf[1024]; +int socket(int domain, int type, int protocol) { + if (!orig_socket) + orig_socket = (orig_socket_t)dlsym(RTLD_NEXT, "socket"); + + int rv = orig_socket(domain, type, protocol); + char *ptr = buf; + ptr += sprintf(ptr, "%u:%s:socket ", pid(), name()); + char *str = translate(socket_domain, domain); + if (str == NULL) + ptr += sprintf(ptr, "%d ", domain); + else + ptr += sprintf(ptr, "%s ", str); + + int t = type; // glibc uses higher bits for various other purposes +#ifdef SOCK_CLOEXEC + t &= ~SOCK_CLOEXEC; +#endif +#ifdef SOCK_NONBLOCK + t &= ~SOCK_NONBLOCK; +#endif + str = translate(socket_type, t); + if (str == NULL) + ptr += sprintf(ptr, "%d ", type); + else + ptr += sprintf(ptr, "%s ", str); + + str = translate(socket_protocol, protocol); + if (str == NULL) + ptr += sprintf(ptr, "%d", protocol); + else + ptr += sprintf(ptr, "%s", str); + + printf("%s\n", buf); + return rv; +} + +// bind +typedef int (*orig_bind_t)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static orig_bind_t orig_bind = NULL; +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + if (!orig_bind) + orig_bind = (orig_bind_t)dlsym(RTLD_NEXT, "bind"); + + int rv = orig_bind(sockfd, addr, addrlen); + print_sockaddr("bind", addr); + + return rv; +} + +#if 0 //todo: fix compilation problems +typedef int (*orig_accept_t)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static orig_accept_t orig_accept = NULL; +int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen) { + if (!orig_accept) + orig_accept = (orig_accept_t)dlsym(RTLD_NEXT, "accept"); + + int rv = orig_accept(sockfd, addr, addrlen); + print_sockaddr("accept", addr); + + return rv; +} +#endif + +typedef int (*orig_system_t)(const char *command); +static orig_system_t orig_system = NULL; +int system(const char *command) { + if (!orig_system) + orig_system = (orig_system_t)dlsym(RTLD_NEXT, "system"); + + int rv = orig_system(command); + printf("%u:%s:system %s\n", pid(), name(), command); + + return rv; +} + +typedef int (*orig_setuid_t)(uid_t uid); +static orig_setuid_t orig_setuid = NULL; +int setuid(uid_t uid) { + if (!orig_setuid) + orig_setuid = (orig_setuid_t)dlsym(RTLD_NEXT, "setuid"); + + int rv = orig_setuid(uid); + printf("%u:%s:setuid %d\n", pid(), name(), uid); + + return rv; +} + +typedef int (*orig_setgid_t)(gid_t gid); +static orig_setgid_t orig_setgid = NULL; +int setgid(gid_t gid) { + if (!orig_setgid) + orig_setgid = (orig_setgid_t)dlsym(RTLD_NEXT, "setgid"); + + int rv = orig_setgid(gid); + printf("%u:%s:setgid %d\n", pid(), name(), gid); + + return rv; +} + +typedef int (*orig_setfsuid_t)(uid_t uid); +static orig_setfsuid_t orig_setfsuid = NULL; +int setfsuid(uid_t uid) { + if (!orig_setfsuid) + orig_setfsuid = (orig_setfsuid_t)dlsym(RTLD_NEXT, "setfsuid"); + + int rv = orig_setfsuid(uid); + printf("%u:%s:setfsuid %d\n", pid(), name(), uid); + + return rv; +} + +typedef int (*orig_setfsgid_t)(gid_t gid); +static orig_setfsgid_t orig_setfsgid = NULL; +int setfsgid(gid_t gid) { + if (!orig_setfsgid) + orig_setfsgid = (orig_setfsgid_t)dlsym(RTLD_NEXT, "setfsgid"); + + int rv = orig_setfsgid(gid); + printf("%u:%s:setfsgid %d\n", pid(), name(), gid); + + return rv; +} + +typedef int (*orig_setreuid_t)(uid_t ruid, uid_t euid); +static orig_setreuid_t orig_setreuid = NULL; +int setreuid(uid_t ruid, uid_t euid) { + if (!orig_setreuid) + orig_setreuid = (orig_setreuid_t)dlsym(RTLD_NEXT, "setreuid"); + + int rv = orig_setreuid(ruid, euid); + printf("%u:%s:setreuid %d %d\n", pid(), name(), ruid, euid); + + return rv; +} + +typedef int (*orig_setregid_t)(gid_t rgid, gid_t egid); +static orig_setregid_t orig_setregid = NULL; +int setregid(gid_t rgid, gid_t egid) { + if (!orig_setregid) + orig_setregid = (orig_setregid_t)dlsym(RTLD_NEXT, "setregid"); + + int rv = orig_setregid(rgid, egid); + printf("%u:%s:setregid %d %d\n", pid(), name(), rgid, egid); + + return rv; +} + +typedef int (*orig_setresuid_t)(uid_t ruid, uid_t euid, uid_t suid); +static orig_setresuid_t orig_setresuid = NULL; +int setresuid(uid_t ruid, uid_t euid, uid_t suid) { + if (!orig_setresuid) + orig_setresuid = (orig_setresuid_t)dlsym(RTLD_NEXT, "setresuid"); + + int rv = orig_setresuid(ruid, euid, suid); + printf("%u:%s:setresuid %d %d %d\n", pid(), name(), ruid, euid, suid); + + return rv; +} + +typedef int (*orig_setresgid_t)(gid_t rgid, gid_t egid, gid_t sgid); +static orig_setresgid_t orig_setresgid = NULL; +int setresgid(gid_t rgid, gid_t egid, gid_t sgid) { + if (!orig_setresgid) + orig_setresgid = (orig_setresgid_t)dlsym(RTLD_NEXT, "setresgid"); + + int rv = orig_setresgid(rgid, egid, sgid); + printf("%u:%s:setresgid %d %d %d\n", pid(), name(), rgid, egid, sgid); + + return rv; +} \ No newline at end of file diff --git a/src/man/firejail-login.txt b/src/man/firejail-login.txt new file mode 100644 index 000000000..6613dc044 --- /dev/null +++ b/src/man/firejail-login.txt @@ -0,0 +1,36 @@ +.TH man 5 "MONTH YEAR" "VERSION" "firejail login.users man page" +.SH NAME +login.users \- Login file syntax for Firejail + +.SH DESCRIPTION +/etc/firejail/login.users file describes additional arguments passed to firejail executable +upon user logging into a Firejail restircted shell. Each user entry in the file consists of +a user name followed by the arguments passed to firejail. The format is as follows: + + user_name: arguments + +Example: + + netblue:--debug --net=none + +.SH RESTRICTED SHELL +To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in +/etc/password file for each user that needs to be restricted. Alternatively, +you can specify /usr/bin/firejail in adduser command: + +adduser \-\-shell /usr/bin/firejail username + +.SH FILES +/etc/firejail/login.users + +.SH LICENSE +Firejail 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. +.PP +Homepage: http://firejail.sourceforge.net +.SH SEE ALSO +\&\flfirejail\fR\|(1), +\&\flfiremon\fR\|(1), +\&\flfirejail-profile\fR\|(5) + + + diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt new file mode 100644 index 000000000..46da19ecd --- /dev/null +++ b/src/man/firejail-profile.txt @@ -0,0 +1,181 @@ +.TH man 5 "MONTH YEAR" "VERSION" "firejail profiles man page" +.SH NAME +profile \- Profile file syntax for Firejail + +.SH USAGE +.TP +firejail \-\-profile=filename.profile + +.SH DESCRIPTION +Several Firejail command line configuration options can be passed to the program using +profile files. Default Firejail profile files are stored in /etc/firejail +directory and ~/.config/firejail directory. + +.SH Scripting +Include and comment support: + +.TP +\f\include other.profile +Include other.profile file. +.TP +# this is a comment + +.SH Filesystem +These profile entries define a chroot filesystem built on top of the existing +host filesystem. Each line describes a file element that is removed from +the filesystem (\fBblacklist\fR), a read-only file or directory (\fBread-only\fR), +a tmpfs mounted on top of an existing directory (\fBtmpfs\fR), +or mount-bind a directory or file on top of another directory or file (\fBbind\fR). +Use \fBprivate\fR to set private mode. +File globbing is supported, and PATH and HOME directories are searched. +Examples: +.TP +\f\blacklist /usr/bin +Remove /usr/bin directory. +.TP +\f\blacklist /etc/password +Remove /etc/password file. +.TP +\f\read-only /etc/password +Read-only /etc/password file. +.TP +tmpfs /etc +Mount an empty tmpfs filesystem on top of /etc directory. +.TP +bind /root/config/ssh,/etc/ssh +Mount-bind /root/config/ssh on /etc/ssh. +.TP +\f\blacklist /usr/bin/gcc* +Remove all gcc files in /usr/bin (file globbing). +.TP +\f\blacklist ${PATH}/ifconfig +Remove ifconfig command from the regular path directories. +.TP +\f\blacklist ${HOME}/.ssh +Remove .ssh directory from user home directory. +.TP +\f\private +Mount new /root and /home/user directories in temporary +filesystems. All modifications are discarded when the sandbox is +closed. +.TP +\f\private directory +Use directory as user home. +.TP +\f\private.keep file,directory +Build a new user home in a temporary +filesystem, and copy the files and directories in the list in the +new home. All modifications are discarded when the sandbox is +closed. +.TP +\f\private-dev +Create a new /dev directory. Only null, full, zero, tty, pts, ptmx, random, urandom and shm devices are available. + +.SH Filters +\fBcaps\fR and \fBseccomp\fR enable Linux capabilities and seccomp filters. Examples: + +.TP +caps +Enable default Linux capabilities filter. +.TP +caps.drop all +Blacklist all Linux capabilities. +.TP +caps.drop capability,capability,capability +Blacklist Linux capabilities filter. +.TP +caps.drop capability,capability,capability +Whitelist Linux capabilities filter. +.TP +\f\seccomp +Enable default seccomp filter. +.TP +\f\seccomp syscall,syscall,syscall +Enable seccomp filter and blacklist the system calls in the list on top of default seccomp filter. +.TP +\f\seccomp.drop syscall,syscall,syscall +Enable seccomp filter and blacklist the system calls in the list. +.TP +\f\seccomp.keep syscall,syscall,syscall +Enable seccomp filter and whitelist the system calls in the list. + + +.SH User Namespace +Use \fBnoroot\fR to enable an user namespace. The namespace has only one user, the current user. +There is no root account defined in the namespace. + +.TP +noroot +Enable an user namespace without root user defined. + + +.SH Resource limits +These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. +The limits can be modified inside the sandbox using the regular \fBulimt\fR command. Examples: + +.TP +\f\rlimit-fsize 1024 +Set the maximum file size that can be created by a process to 1024 bytes. +.TP +\f\rlimit-nproc 1000 +Set the maximum number of processes that can be created for the real user ID of the calling process to 1000. +.TP +\f\rlimit-nofile 500 +Set the maximum number of files that can be opened by a process to 500. +.TP +\f\rlimit-sigpending 200 +Set the maximum number of processes that can be created for the real user ID of the calling process to 200. + +.SH CPU Affinity +Set the CPU cores available for this sandbox. Examples: + +.TP +cpu 1,2,3 +Use only CPU cores 0, 1 and 2. + +.SH Control Groups +Place the sandbox in an existing control group specified by the full path of the task file. Example: + +.TP +cgroup /sys/fs/cgroup/g1/tasks +The sandbox is placed in g1 control group. + +.SH User Environment + +.TP +nogroups +Disable supplementary user groups +.TP +shell none +Run the program directly, without a shell. + +.SH Networking +Networking features available in profile files. + +.TP +netfilter +If a new network namespace is created, enabled default network filter. + +.TP +netfilter filename +If a new network namespace is created, enabled the network filter in filename. + +.TP +dns address +Set a DNS server for the sandbox. Up to three DNS servers can be defined. + + +.SH FILES +/etc/firejail/filename.profile, $HOME/.config/firejail/filename.profile + +.SH LICENSE +Firejail 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. +.PP +Homepage: http://firejail.sourceforge.net +.SH SEE ALSO +\&\flfirejail\fR\|(1), +\&\flfiremon\fR\|(1), +\&\flfirejail-login\fR\|(5) + + + diff --git a/src/man/firejail.txt b/src/man/firejail.txt new file mode 100644 index 000000000..51f21975e --- /dev/null +++ b/src/man/firejail.txt @@ -0,0 +1,1196 @@ +.TH man 1 "MONTH YEAR" "VERSION" "firejail man page" +.SH NAME +Firejail \- Linux namespaces sandbox program +.SH SYNOPSIS +Start a sandbox: +.PP +.RS +firejail [OPTIONS] [program and arguments] +.RE +.PP +Network traffic shaping for an existing sandbox: +.PP +.RS +firejail \-\-bandwidth={|} bandwidth-command +.RE +.PP +Monitoring: +.PP +.RS +firejail {\-\-list | \-\-netstats | \-\-top | \-\-tree} +.RE +.PP +Miscellaneous: +.PP +.RS +firejail {\-? | \-\-debug-caps | \-\-debug-syscalls | \-\-help | +.br +\-\-version} +.RE +.SH DESCRIPTION +Firejail is a SUID sandbox program that reduces the risk of security breaches by +restricting the running environment of untrusted applications using Linux +namespaces, seccomp-bpf and Linux capabilities. +It allows a process and all its descendants to have their own private view of the +globally shared kernel resources, such as the network stack, process table, mount table. +Firejail can work in a SELinux or AppArmor environment, +and it is integrated with Linux Control Groups. +.PP +Written in C with virtually no dependencies, the software runs on any Linux computer with a 3.x kernel version +or newer. +It can sandbox any type of processes: servers, graphical applications, and even user login sessions. +The software includes sandbox profiles for a number of more common +Linux programs, such as Mozilla Firefox, Chromium, VLC, Transmission etc. +.SH USAGE +Without any options, the sandbox consists of a chroot filesystem build in a new mount namespace, +and new PID and UTS namespaces. IPC, network and user namespaces can be added using the command line options. +The default Firejail filesystem is based on the host filesystem with the main directories mounted read-only. +Only /home, /tmp and /var directories are writable. +.PP +If no program is specified as an argument, /bin/bash is started by default. +Examples: +.PP +$ firejail [OPTIONS] # starting a /bin/bash shell +.PP +$ firejail [OPTIONS] firefox # starting Mozilla Firefox +.PP +Multiple commands can be run in sandbox using regular bash logic operators: +.PP +$ sudo firejail [OPTIONS] "/etc/init.d/nginx start && sleep inf" +.PP +In the previous example, "sleep inf" command is required in order to keep the session open for the daemon program. + +.SH OPTIONS +.TP +\fB\-\- +Signal the end of options and disables further option processing. +.TP +\fB\-\-bandwidth=name +Set bandwidth limits for the sandbox identified by name, see TRAFFIC SHAPING section for more details. +.TP +\fB\-\-bandwidth=pid +Set bandwidth limits for the sandbox identified by PID, see TRAFFIC SHAPING section for more details. +.TP +\fB\-\-bind=dirname1,dirname2 +Mount-bind dirname1 on top of dirname2. This option is only available when running the sandbox as root. +.br + +.br +Example: +.br +# firejail \-\-bind=/config/www,/var/www +.TP +\fB\-\-bind=filename1,filename2 +Mount-bind filename1 on top of filename2. This option is only available when running as root. +.br + +.br +Example: +.br +# firejail \-\-bind=/config/etc/passwd,/etc/passwd +.TP +\fB\-\-blacklist=dirname_or_filename +Blacklist directory or file. +.br + +.br +Example: +.br +$ firejail \-\-blacklist=/sbin \-\-blacklist=/usr/sbin +.TP +\fB\-c +Execute command and exit. +.TP +\fB\-\-caps +Linux capabilities is a kernel feature designed to split up the root privilege into a set of distinct privileges. +These privileges can be enabled or disabled independently, thus restricting what a process running +as root can do in the system. + +By default root programs run with all capabilities enabled. \-\-caps option disables the following capabilities: +CAP_SYS_MODULE, CAP_SYS_RAWIO, +CAP_SYS_BOOT, CAP_SYS_NICE, CAP_SYS_TTY_CONFIG, CAP_SYSLOG, CAP_MKNOD, CAP_SYS_ADMIN. +The filter is applied to all processes started in the sandbox. +.br + +.br +Example: +.br +$ sudo firejail \-\-caps "/etc/init.d/nginx start && sleep inf" + +.TP +\fB\-\-caps.drop=all +Drop all capabilities for the processes running in the sandbox. This option is recommended for running GUI programs +or any other program that doesn't require root privileges. It is a must-have option for sandboxing untrusted programs +installed from unofficial sources - such as games, Java programs, etc. +.br + +.br +Example: +.br +$ firejail \-\-caps.drop=all warzone2100 + +.TP +\fB\-\-caps.drop=capability,capability,capability +Define a custom blacklist Linux capabilities filter. +.br + +.br +Example: +.br +$ firejail \-\-caps.keep=net_broadcast,net_admin,net_raw + +.TP +\fB\-\-caps.keep=capability,capability,capability +Define a custom whitelist Linux capabilities filter. +.br + +.br +Example: +.br +$ sudo firejail \-\-caps.keep=chown,net_bind_service,setgid,\\ +setuid "/etc/init.d/nginx start && sleep inf" + +.TP +\fB\-\-caps.print=name +Print the caps filter for the sandbox identified by name. +.br + +.br +Example: +.br +$ firejail \-\-name=mygame \-\-caps.drop=all warzone2100 & +.br +[...] +.br +$ firejail \-\-caps.print=mygame + +.TP +\fB\-\-caps.print=pid +Print the caps filter for a sandbox identified by PID. +.br + +.br +Example: +.br +$ firejail \-\-list +.br +3272:netblue:firejail \-\-private firefox +.br +$ firejail \-\-caps.print=3272 + +.TP +\fB\-\-cgroup=tasks-file +Place the sandbox in the specified control group. tasks-file is the full path of cgroup tasks file. +.br + +.br +Example: +.br +# firejail \-\-cgroup=/sys/fs/cgroup/g1/tasks + +.TP +\fB\-\-chroot=dirname +Chroot the sandbox into a root filesystem. If the sandbox is started as a +regular user, default seccomp and capabilities filters are eanbled. +.br + +.br +Example: +.br +$ firejail \-\-chroot=/media/ubuntu warzone2100 + +.TP +\fB\-\-cpu=cpu-number,cpu-number,cpu-number +Set CPU affinity. +.br + +.br +Example: +.br +$ firejail \-\-cpu=0,1 handbrake + +.TP +\fB\-\-csh +Use /bin/csh as default user shell. +.br + +.br +Example: +.br +$ firejail \-\-csh +.TP +\fB\-\-debug\fR +Print debug messages. +.br + +.br +Example: +.br +$ firejail \-\-debug firefox +.TP +\fB\-\-debug-syscalls +Print all recognized system calls in the current Firejail software build and exit. +.br + +.br +Example: +.br +$ firejail \-\-debug-syscalls +.TP +\fB\-\-debug-caps +Print all recognized capabilities in the current Firejail software build and exit. +.br + +.br +Example: +.br +$ firejail \-\-debug-caps +.TP +\fB\-\-defaultgw=address +Use this address as default gateway in the new network namespace. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-defaultgw=10.10.20.1 firefox + +.TP +\fB\-\-dns=address +Set a DNS server for the sandbox. Up to three DNS servers can be defined. +Use this option if you don't trust the DNS setup on your network. +.br + +.br +Example: +.br +$ firejail \-\-dns=8.8.8.8 \-\-dns=8.8.4.4 firefox + +.TP +\fB\-\-dns.print=name +Print DNS configuration for a sandbox identified by name. +.br + +.br +Example: +.br +$ firejail \-\-name=mygame \-\-caps.drop=all warzone2100 & +.br +[...] +.br +$ firejail \-\-dns.print=mygame + +.TP +\fB\-\-dns.print=pid +Print DNS configuration for a sandbox identified by PID. +.br + +.br +Example: +.br +$ firejail \-\-list +.br +3272:netblue:firejail \-\-private firefox +.br +$ firejail \-\-dns.print=3272 + +.TP +\fB\-?\fR, \fB\-\-help\fR +Print options end exit. +.TP +\fB\-\-ip=address +Assign IP addresses to the last network interface defined by a \-\-net option. A +default gateway is assigned by default. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-ip=10.10.20.56 firefox + +.TP +\fB\-\-ip=none +No IP address and no default gateway are configured for the last interface +defined by a \-\-net option. Use this option +in case you intend to start an external DHCP client in the sandbox. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-\ip=none + +.TP +\fB\-\-iprange=address,address +Assign an IP address in the provided range to the last network interface defined by a \-\-net option. A +default gateway is assigned by default. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-\iprange=192.168.1.100,192.168.1.150 + +.TP +\fB\-\-ipc-namespace +Enable a new IPC namespace if the sandbox was started as a regular user. IPC namespace is enabled by default +for sandboxes started as root. +.br + +.br +Example: +.br +$ firejail \-\-ipc-namespace firefox +.TP +\fB\-\-join=name +Join the sandbox identified by name. By default a /bin/bash shell is started after joining the sandbox. +If a program is specified, the program is run in the sandbox. +.br + +.br +Example: +.br +$ firejail \-\-name=mygame \-\-caps.drop=all warzone2100 & +.br +[...] +.br +$ firejail \-\-join=mygame + + +.TP +\fB\-\-join=pid +Join the sandbox identified by PID. By default a /bin/bash shell is started after joining the sandbox. +If a program is specified, the program is run in the sandbox. +.br + +.br +Example: +.br +$ firejail \-\-list +.br +3272:netblue:firejail \-\-private firefox +.br +$ firejail \-\-join=3272 + +.TP +\fB\-\-list +List all sandboxes, see MONITORING section for more details. +.br + +.br +Example: +.br +$ firejail \-\-list +.br +7015:netblue:firejail firefox +.br +7056:netblue:firejail \-\-net=eth0 transmission-gtk +.br +7064:netblue:firejail \-\-noroot xterm +.br +$ +.TP +\fB\-\-mac=address +Assign MAC addresses to the last network interface defined by a \-\-net option. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-mac=00:11:22:33:44:55 firefox + +.TP +\fB\-\-name=name +Set sandbox hostname. Several options, such as \-\-join and \-\-shutdown, can use +this name to identify a sandbox. +.br + +.br +Example: +.br +$ firejail \-\-name=mybrowser firefox + +.TP +\fB\-\-net=bridge_interface +Enable a new network namespace and connect it to this bridge interface. +Unless specified with option \-\-ip and \-\-defaultgw, an IP address and a default gateway will be assigned +automatically to the sandbox. The IP address is verified using ARP before assignment. The address +configured as default gateway is the bridge device IP address. Up to four \-\-net +bridge devices can be defined. Mixing bridge and macvlan devices is allowed. +.br + +.br +Example: +.br +$ sudo brctl addbr br0 +.br +$ sudo ifconfig br0 10.10.20.1/24 +.br +$ sudo brctl addbr br1 +.br +$ sudo ifconfig br1 10.10.30.1/24 +.br +$ firejail \-\-net=br0 \-\-net=br1 + +.TP +\fB\-\-net=ethernet_interface +Enable a new network namespace and connect it +to this ethernet interface using the standard Linux macvlan +driver. Unless specified with option \-\-ip and \-\-defaultgw, an +IP address and a default gateway will be assigned automatically +to the sandbox. The IP address is verified using ARP before +assignment. The address configured as default gateway is the +default gateway of the host. Up to four \-\-net devices can +be defined. Mixing bridge and macvlan devices is allowed. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-ip=192.168.1.80 \-\-dns=8.8.8.8 firefox + +.TP +\fB\-\-net=none +Enable a new, unconnected network namespace. The only interface +available in the new namespace is a new loopback interface (lo). +Use this option to deny +network access to programs that don't really need network access. +.br + +.br +Example: +.br +$ firejail \-\-net=none vlc + +.TP +\fB\-\-netfilter +Enable a default client network filter in the new network namespace. +New network namespaces are created using \-\-net option. If a new network namespaces is not created, +\-\-netfilter option does nothing. +The default filter is as follows: +.br + +.br +*filter +.br +:INPUT DROP [0:0] +.br +:FORWARD DROP [0:0] +.br +:OUTPUT ACCEPT [0:0] +.br +\-A INPUT \-i lo \-j ACCEPT +.br +\-A INPUT \-m state \-\-state RELATED,ESTABLISHED \-j ACCEPT +.br +\-A INPUT \-p icmp \-\-icmp-type destination-unreachable \-j ACCEPT +.br +\-A INPUT \-p icmp \-\-icmp-type time-exceeded \-j ACCEPT +.br +\-A INPUT \-p icmp \-\-icmp-type echo-request \-j ACCEPT +.br +COMMIT +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-netfilter firefox +.TP +\fB\-\-netfilter=filename +Enable the network filter specified by filename in the new network namespace. The filter file format +is the format of iptables-save and iptable-restore commands. +New network namespaces are created using \-\-net option. If a new network namespaces is not created, +\-\-netfilter option does nothing. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-netfilter=myfile firefox +.TP +\fB\-\-netstats +Monitor network namespace statistics, see MONITORING section for more details. +.br + +.br +Example: +.br +$ firejail \-\-netstats +.br +PID User RX(KB/s) TX(KB/s) Command +.br +1294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox +.br +7383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission + + +.TP +\fB\-\-nogroups +Disable supplementary groups. Without this option, supplementary groups are enabled for the user starting the +sandbox. For root user supplementary groups are always disabled. +.br + +.br +Example: +.br +$ id +.br +uid=1000(netblue) gid=1000(netblue) groups=1000(netblue),24(cdrom),25(floppy),27(sudo),29(audio) +.br +$ firejail \-\-nogroups +.br +Parent pid 8704, child pid 8705 +.br +Child process initialized +.br +$ id +.br +uid=1000(netblue) gid=1000(netblue) groups=1000(netblue) +.br +$ + +.TP +\fB\-\-noroot +Install a user namespace with a single user - the current user. +root user does not exist in the new namespace. This option +requires a Linux kernel version 3.8 or newer. The option +is not supported for \-\-chroot and \-\-overlay configurations, +or for sandboxes started as root. +.br + +.br +Example: +.br +$ firejail \-\-noroot +.br +Parent pid 8553, child pid 8554 +.br +Child process initialized +.br +$ ping google.com +.br +ping: icmp open socket: Operation not permitted +.br +$ +.TP +\fB\-\-output=logfile +stdout logging and log rotation. Copy stdout to logfile, and keep the size of the file under 500KB using log +rotation. Five files with prefixes .1 to .5 are used in rotation. +.br + +.br +Example: +.br +$ firejail \-\-output=sandboxlog /bin/bash +.br +[...] +.br +$ ls -l sandboxlog* +.br +-rw-r--r-- 1 netblue netblue 333890 Jun 2 07:48 sadnboxlog +.br +-rw-r--r-- 1 netblue netblue 511488 Jun 2 07:48 sandboxlog.1 +.br +-rw-r--r-- 1 netblue netblue 511488 Jun 2 07:48 sandboxlog.2 +.br +-rw-r--r-- 1 netblue netblue 511488 Jun 2 07:48 sandboxlog.3 +.br +-rw-r--r-- 1 netblue netblue 511488 Jun 2 07:48 sandboxlog.4 +.br +-rw-r--r-- 1 netblue netblue 511488 Jun 2 07:48 sandboxlog.5 + +.TP +\fB\-\-overlay +Mount a filesystem overlay on top of the current filesystem. All filesystem modifications go into the overlay, +and are discarded when the sandbox is closed. +.br + +.br +OverlayFS support is required in Linux kernel for this option to work. +OverlayFS was officially introduced in Linux kernel version 3.18. It was also +available in earlier kernel versions in some distributions such as Ubuntu and OpenSUSE. +.br + +.br +Example: +.br +$ firejail \-\-overlay firefox + +.TP +\fB\-\-private +Mount new /root and /home/user directories in temporary +filesystems. All modifications are discarded when the sandbox is +closed. +.br + +.br +Example: +.br +$ firejail \-\-private firefox +.TP +\fB\-\-private=directory +Use directory as user home. +.br + +.br +Example: +.br +$ firejail \-\-private=/home/netblue/firefox-home firefox + +.TP +\fB\-\-private.keep=file,directory +Build a new user home in a temporary +filesystem, and copy the files and directories in the list in the +new home. All modifications are discarded when the sandbox is +closed. +.br + +.br +Example: +.br +$ firejail \-\-private.keep=.mozilla firefox +.TP +\fB\-\-private-dev +Create a new /dev directory. Only null, full, zero, tty, pts, ptmx, random, urandom and shm devices are available. +.br + +.br +Example: +.br +$ firejail \-\-private-dev +.br +Parent pid 9887, child pid 9888 +.br +Child process initialized +.br +$ ls /dev +.br +full null ptmx pts random shm tty urandom zero +.br +$ +.TP +\fB\-\-profile=filename +Load a custom profile from filename. For filename use an absolute path or a path relative to the current path. +For more information, see PROFILES section below. +.br + +.br +Example: +.br +$ firejail \-\-profile=myprofile +.TP +\fB\-\-read-only=dirname_or_filename +Set directory or file read-only. +.br + +.br +Example: +.br +$ firejail \-\-read-only=~/.mozilla firefox +.TP +\fB\-\-rlimit-fsize=number +Set the maximum file size that can be created by a process. +.TP +\fB\-\-rlimit-nofile=number +Set the maximum number of files that can be opened by a process. +.TP +\fB\-\-rlimit-nproc=number +Set the maximum number of processes that can be created for the real user ID of the calling process. +.TP +\fB\-\-rlimit-sigpending=number +Set the maximum number of pending signals for a process. +.TP +\fB\-\-scan +ARP-scan all the networks from inside a network namespace. +This makes it possible to detect macvlan kernel device drivers running on the current host. +.br + +.br +Example: +.br +$ firejail \-\-net=eth0 \-\-scan +.TP +\fB\-\-seccomp +Enable seccomp filter and blacklist the syscalls in the default list. The default list is as follows: +mount, umount2, ptrace, kexec_load, open_by_handle_at, init_module, finit_module, delete_module, +iopl, ioperm, swapon, swapoff, mknode, syslog, process_vm_readv and process_vm_writev, +sysfs,_sysctl, adjtimex, clock_adjtime, lookup_dcookie, perf_event_open, fanotify_init and kcmp. +.br + +.br +Example: +.br +$ firejail \-\-sccomp +.TP +\fB\-\-seccomp=syscall,syscall,syscall +Enable seccomp filter, blacklist the default list and the syscalls specified by the command. +.br + +.br +Example: +.br +$ firejail \-\-seccomp=utime,utimensat,utimes firefox +.TP +\fB\-\-seccomp.drop=syscall,syscall,syscall +Enable seccomp filter, and blacklist the syscalls specified by the command. +.br + +.br +Example: +.br +$ firejail \-\-seccomp.drop=utime,utimensat,utimes +.TP +\fB\-\-seccomp.keep=syscall,syscall,syscall +Enable seccomp filter, and whitelist the syscalls specified by the command. +.br + +.br +Example: +.br +$ firejail \-\-shell=none \-\-seccomp.keep=poll,select,[...] transmission-gtk +.TP +\fB\-\-seccomp.print=name +Print the seccomp filter for the sandbox started using \-\-name option. +.br + +.br +Example: +.br +$ firejail \-\-name=browser firefox & +.br +$ firejail \-\-seccomp.print=browser +.br +SECCOMP Filter: +.br + VALIDATE_ARCHITECTURE +.br + EXAMINE_SYSCAL +.br + BLACKLIST 165 mount +.br + BLACKLIST 166 umount2 +.br + BLACKLIST 101 ptrace +.br + BLACKLIST 246 kexec_load +.br + BLACKLIST 304 open_by_handle_at +.br + BLACKLIST 175 init_module +.br + BLACKLIST 176 delete_module +.br + BLACKLIST 172 iopl +.br + BLACKLIST 173 ioperm +.br + BLACKLIST 167 swapon +.br + BLACKLIST 168 swapoff +.br + BLACKLIST 103 syslog +.br + BLACKLIST 310 process_vm_readv +.br + BLACKLIST 311 process_vm_writev +.br + BLACKLIST 133 mknod +.br + BLACKLIST 139 sysfs +.br + BLACKLIST 156 _sysctl +.br + BLACKLIST 159 adjtimex +.br + BLACKLIST 305 clock_adjtime +.br + BLACKLIST 212 lookup_dcookie +.br + BLACKLIST 298 perf_event_open +.br + BLACKLIST 300 fanotify_init +.br + RETURN_ALLOW +.br +$ +.TP +\fB\-\-seccomp.print=pid +Print the seccomp filter for the sandbox specified by process ID. Use \-\-list option to get a list of all active sandboxes. +.br + +.br +Example: +.br +$ firejail \-\-list +.br +10786:netblue:firejail \-\-name=browser firefox +$ firejail \-\-seccomp.print=10786 +.br +SECCOMP Filter: +.br + VALIDATE_ARCHITECTURE +.br + EXAMINE_SYSCAL +.br + BLACKLIST 165 mount +.br + BLACKLIST 166 umount2 +.br + BLACKLIST 101 ptrace +.br + BLACKLIST 246 kexec_load +.br + BLACKLIST 304 open_by_handle_at +.br + BLACKLIST 175 init_module +.br + BLACKLIST 176 delete_module +.br + BLACKLIST 172 iopl +.br + BLACKLIST 173 ioperm +.br + BLACKLIST 167 swapon +.br + BLACKLIST 168 swapoff +.br + BLACKLIST 103 syslog +.br + BLACKLIST 310 process_vm_readv +.br + BLACKLIST 311 process_vm_writev +.br + BLACKLIST 133 mknod +.br + BLACKLIST 139 sysfs +.br + BLACKLIST 156 _sysctl +.br + BLACKLIST 159 adjtimex +.br + BLACKLIST 305 clock_adjtime +.br + BLACKLIST 212 lookup_dcookie +.br + BLACKLIST 298 perf_event_open +.br + BLACKLIST 300 fanotify_init +.br + RETURN_ALLOW +.br +$ +.TP +\fB\-\-shell=none +Run the program directly, without a user shell. +.br + +.br +Example: +.br +$ firejail \-\-shell=none script.sh +.TP +\fB\-\-shell=program +Set default user shell. Use this shell to run the application using \-c shell option. +For example "firejail \-\-shell=/bin/dash firefox" will start Mozilla Firefox as "/bin/dash \-c firefox". +By default Bash shell (/bin/bash) is used. Options such as \-\-zsh and \-\-csh can also set the default +shell. +.br + +.br +Example: +$firejail \-\-shell=/bin/dash script.sh +.TP +\fB\-\-shutdown=name +Shutdown the sandbox started using \-\-name option. +.br + +.br +Example: +.br +$ firejail \-\-name=mygame \-\-caps.drop=all warzone2100 & +.br +[...] +.br +$ firejail \-\-shutdown=mygame +.TP +\fB\-\-shutdown=pid +Shutdown the sandbox specified by process ID. Use \-\-list option to get a list of all active sandboxes. +.br + +.br +Example: +.br +$ firejail \-\-list +.br +3272:netblue:firejail \-\-private firefox +.br +$ firejail \-\-shutdown=3272 +.TP +\fB\-\-tmpfs=dirname +Mount a tmpfs filesystem on directory dirname. +.br + +.br +Example: +.br +$ firejail \-\-tmpfs=/var +.TP +\fB\-\-top +Monitor the most CPU-intensive sandboxes, see MONITORING section for more details. +.br + +.br +Example: +.br +$ firejail \-\-top +.TP +\fB\-\-trace +Trace open, access and connect system calls. +.br + +.br +Example: +.br +$ firejail \-\-trace wget -q www.debian.org +.br +Parent pid 11793, child pid 11794 +.br +Child process initialized +.br +1:bash:open /dev/tty +.br +1:wget:fopen64 /etc/wgetrc +.br +1:wget:fopen /etc/hosts +.br +1:wget:socket AF_INET SOCK_DGRAM IPPROTO_IP +.br +1:wget:connect 8.8.8.8:53 +.br +1:wget:socket AF_INET SOCK_STREAM IPPROTO_IP +.br +1:wget:connect 140.211.15.34:80 +.br +1:wget:fopen64 index.html.1 +.br + +.br +parent is shutting down, bye... +.TP +\fB\-\-tree +Print a tree of all sandboxed processes, see MONITORING section for more details. +.br + +.br +Example: +.br +$ firejail \-\-tree +.br +11903:netblue:firejail iceweasel +.br + 11904:netblue:iceweasel +.br + 11957:netblue:/usr/lib/iceweasel/plugin-container +.br +11969:netblue:firejail \-\-net=eth0 transmission-gtk +.br + 11970:netblue:transmission-gtk +.TP +\fB\-\-version +Print program version and exit. +.br + +.br +Example: +.br +$ firejail \-\-version +.br +firejail version 0.9.27 +.TP +\fB\-\-zsh +Use /usr/bin/zsh as default user shell. +.br + +.br +Example: +.br +$ firejakil \-\-zsh +.SH TRAFFIC SHAPING +Network bandwidth is an expensive resource shared among all sandboxes running on a system. +Traffic shaping allows the user to increase network performance by controlling +the amount of data that flows into and out of the sandboxes. + +Firejail implements a simple rate-limiting shaper based on Linux command tc. +The shaper works at sandbox level, and can be used only for sandboxes configured with new network namespaces. + +Set rate-limits: + + firejail --bandwidth={name|pid} set network download upload + +Clear rate-limits: + + firejail --bandwidth={name|pid} clear network + +Status: + + firejail --bandwidth={name|pid} status + +where: +.br + name - sandbox name +.br + pid - sandbox pid +.br + network - network interface as used by \-\-net option +.br + download - download speed in KB/s (kilobyte per second) +.br + upload - upload speed in KB/s (kilobyte per second) + +Example: +.br + $ firejail \-\-name=mybrowser \-\-net=eth0 firefox & +.br + $ firejail \-\-bandwidth=mybrowser set eth0 80 20 +.br + $ firejail \-\-bandwidth=mybrowser status +.br + $ firejail \-\-bandwidth=mybrowser clear eth0 + +.SH MONITORING +Option \-\-list prints a list of all sandboxes. The format +for each process entry is as follows: + + PID:USER:Command + +Option \-\-tree prints the tree of processes running in the sandbox. The format +for each process entry is as follows: + + PID:USER:Command + +Option \-\-top is similar to the UNIX top command, however it applies only to +sandboxes. + +Option \-\-netstats prints network statistics for active sandboxes installing new network namespaces. + + +Listed below are the available fields (columns) in alphabetical +order for \-\-top and \-\-netstat options: + +.TP +Command +Command used to start the sandbox. +.TP +CPU% +CPU usage, the sandbox share of the elapsed CPU time since the +last screen update +.TP +PID +Unique process ID for the task controlling the sandbox. +.TP +Prcs +Number of processes running in sandbox, including the controlling process. +.TP +RES +Resident Memory Size (KiB), sandbox non-swapped physical memory. +It is a sum of the RES values for all processes running in the sandbox. +.TP +RX(KB/s) +Network receive speed. +.TP +SHR +Shared Memory Size (KiB), it reflects memory shared with other +processes. It is a sum of the SHR values for all processes running +in the sandbox, including the controlling process. +.TP +TX(KB/s) +Network transmit speed. +.TP +Uptime +Sandbox running time in hours:minutes:seconds format. +.TP +User +The owner of the sandbox. + +.SH PROFILES +Several command line configuration options can be passed to the program using +profile files. Firejail supports user specified profile files and automatic profile files, +as follows: + +1. Load a specific profile file from a full path, or a path relative to the current directory. +Example: +.PP +.RS +$ firejail --profile=/home/netblue/icecat.profile icecat +.RE + +2. Load a default profile file automatically from ~/.config/firejail or from /etc/firejail, based +on the name of the executable started in the sandbox. Example: +.PP +.RS +$ firejail icecat +.br +Command name #icecat# +.br +.br +Found icecat profile in /home/netblue/.config/firejail directory +.br +Reading profile /home/netblue/.config/firejail/icecat.profile +.br +[...] +.RE + +See man 5 firejail-profile for profile file syntax information. + +.SH RESTRICTED SHELL +To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in +/etc/password file for each user that needs to be restricted. Alternatively, +you can specify /usr/bin/firejail in adduser command: + +adduser \-\-shell /usr/bin/firejail username + +Additional arguments passed to firejail executable upon login are declared in /etc/firejail/login.users file. + +.SH EXAMPLES +.TP +\f\firejail +Start a regular /bin/bash session in sandbox. +.TP +\f\firejail firefox +Start Mozilla Firefox. +.TP +\f\firejail \-\-seccomp firefox +Start Mozilla Firefox in a seccomp sandbox. +.TP +\f\firejail \-\-caps firefox +Start Mozilla Firefox in a Linux capabilities sandbox. +.TP +\f\firejail \-\-debug firefox +Debug Firefox sandbox. +.TP +\f\firejail \-\-private +Start a /bin/bash session with a new tmpfs home directory. +.TP +\f\firejail \-\-net=br0 ip=10.10.20.10 +Start a /bin/bash session in a new network namespace. The session is +connected to the main network using br0 bridge device. An IP address +of 10.10.20.10 is assigned to the sandbox. +.TP +\f\firejail \-\-net=br0 \-\-net=br1 \-\-net=br2 +Start a /bin/bash session in a new network namespace and connect it +to br0, br1, and br2 host bridge devices. +.TP +\f\firejail \-\-list +List all sandboxed processes. +.SH LICENSE +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. +.PP +Homepage: http://firejail.sourceforge.net +.SH SEE ALSO +\&\flfiremon\fR\|(1), +\&\flfirejail-profile\fR\|(5), +\&\flfirejail-login\fR\|(5) + + + diff --git a/src/man/firemon.txt b/src/man/firemon.txt new file mode 100644 index 000000000..b6010f46e --- /dev/null +++ b/src/man/firemon.txt @@ -0,0 +1,107 @@ +.TH man 1 "MONTH YEAR" "VERSION" "firemon man page" +.SH NAME +Firemon \- Monitoring program for processes started in a Firejail sandbox. +.SH SYNOPSIS +firemon [OPTIONS] [PID] +.SH DESCRIPTION +Firemon monitors programs started in a Firejail sandbox. +Without a PID specified, all processes started by Firejail are monitored. Descendants of +these processes are also being monitored. +.SH OPTIONS +.TP +\fB\-\-arp +Print ARP table for each sandbox. +.TP +\fB\-\-caps +Print capabilities configuration for each sandbox. +.TP +\fB\-\-cgroup +Print control group information for each sandbox. +.TP +\fB\-\-cpu +Print CPU affinity for each sandbox. +.TP +\fB\-?\fR, \fB\-\-help\fR +Print options end exit. +.TP +\fB\-\-interface +Print network interface information for each sandbox. +.TP +\fB\-\-list +List all sandboxes. +.TP +\fB\-\-name=name +Print information only about named sandbox. +.TP +\fB\-\-netstats +Monitor network statistics for sandboxes creating a new network namespace. +.TP +\fB\-\-route +Print route table for each sandbox. +.TP +\fB\-\-seccomp +Print seccomp configuration for each sandbox. +.TP +\fB\-\-top +Monitor the most CPU-intensive sandboxes. +.TP +\fB\-\-tree +Print a tree of all sandboxed processes. +.TP +\fB\-\-version +Print program version and exit. + +.PP +Option \-\-list prints a list of all sandboxes. The format +for each entry is as follows: + + PID:USER:Command + +Option \-\-tree prints the tree of processes running in the sandbox. The format +for each process entry is as follows: + + PID:USER:Command + +Option \-\-top is similar to the UNIX top command, however it applies only to +sandboxes. Listed below are the available fields (columns) in alphabetical +order: + +.TP +Command +Command used to start the sandbox. +.TP +CPU% +CPU usage, the sandbox share of the elapsed CPU time since the +last screen update +.TP +PID +Unique process ID for the task controlling the sandbox. +.TP +Prcs +Number of processes running in sandbox, including the controlling process. +.TP +RES +Resident Memory Size (KiB), sandbox non-swapped physical memory. +It is a sum of the RES values for all processes running in the sandbox. +.TP +SHR +Shared Memory Size (KiB), it reflects memory shared with other +processes. It is a sum of the SHR values for all processes running +in the sandbox, including the controlling process. +.TP +Uptime +Sandbox running time in hours:minutes:seconds format. +.TP +User +The owner of the sandbox. + +.SH LICENSE +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. +.PP +Homepage: http://firejail.sourceforge.net +.SH SEE ALSO +\&\flfirejail\fR\|(1), +\&\flfirejail-profile\fR\|(5), +\&\flfirejail-login\fR\|(5) + + diff --git a/src/tools/check-caps.sh b/src/tools/check-caps.sh new file mode 100755 index 000000000..13525677b --- /dev/null +++ b/src/tools/check-caps.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +if [ $# -eq 0 ] +then + echo "Usage: check-caps.sh program-and-arguments" + echo +fi + +set -x + +firejail --caps.drop=chown "$1" +firejail --caps.drop=dac_override "$1" +firejail --caps.drop=dac_read_search "$1" +firejail --caps.drop=fowner "$1" +firejail --caps.drop=fsetid "$1" +firejail --caps.drop=kill "$1" +firejail --caps.drop=setgid "$1" +firejail --caps.drop=setuid "$1" +firejail --caps.drop=setpcap "$1" +firejail --caps.drop=linux_immutable "$1" +firejail --caps.drop=net_bind_service "$1" +firejail --caps.drop=net_broadcast "$1" +firejail --caps.drop=net_admin "$1" +firejail --caps.drop=net_raw "$1" +firejail --caps.drop=ipc_lock "$1" +firejail --caps.drop=ipc_owner "$1" +firejail --caps.drop=sys_module "$1" +firejail --caps.drop=sys_rawio "$1" +firejail --caps.drop=sys_chroot "$1" +firejail --caps.drop=sys_ptrace "$1" +firejail --caps.drop=sys_pacct "$1" +firejail --caps.drop=sys_admin "$1" +firejail --caps.drop=sys_boot "$1" +firejail --caps.drop=sys_nice "$1" +firejail --caps.drop=sys_resource "$1" +firejail --caps.drop=sys_time "$1" +firejail --caps.drop=sys_tty_config "$1" +firejail --caps.drop=mknod "$1" +firejail --caps.drop=lease "$1" +firejail --caps.drop=audit_write "$1" +firejail --caps.drop=audit_control "$1" +firejail --caps.drop=setfcap "$1" +firejail --caps.drop=mac_override "$1" +firejail --caps.drop=mac_admin "$1" +firejail --caps.drop=syslog "$1" +firejail --caps.drop=wake_alarm "$1" diff --git a/src/tools/extract_caps.c b/src/tools/extract_caps.c new file mode 100644 index 000000000..94a062ccb --- /dev/null +++ b/src/tools/extract_caps.c @@ -0,0 +1,83 @@ +/* + * 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 +#include +#include +#include + +#define BUFMAX 4096 + +int main(int argc, char **argv) { + if (argc != 2) { + printf("usage: %s /usr/include/linux/capability.h\n", argv[0]); + return 1; + } + + //open file + FILE *fp = fopen(argv[1], "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open file\n"); + return 1; + } + + // read file + char buf[BUFMAX]; + while (fgets(buf, BUFMAX, fp)) { + // cleanup + char *start = buf; + while (*start == ' ' || *start == '\t') + start++; + char *end = strchr(start, '\n'); + if (end) + *end = '\0'; + + // parsing + if (strncmp(start, "#define CAP_", 12) == 0) { + if (strstr(start, "CAP_LAST_CAP")) + break; + + char *ptr1 = start + 8; + char *ptr2 = ptr1; + while (*ptr2 == ' ' || *ptr2 == '\t') + ptr2++; + while (*ptr2 != ' ' && *ptr2 != '\t') + ptr2++; + *ptr2 = '\0'; + + ptr2 = strdup(ptr1); + assert(ptr2); + ptr2 += 4; + char *ptr3 = ptr2; + while (*ptr3 != '\0') { + *ptr3 = tolower(*ptr3); + ptr3++; + } + + + printf("#ifdef %s\n", ptr1); + printf("\t{\"%s\", %s },\n", ptr2, ptr1); + printf("#endif\n"); + + } + + } + fclose(fp); + return 0; +} diff --git a/src/tools/extract_syscalls.c b/src/tools/extract_syscalls.c new file mode 100644 index 000000000..0e064a49e --- /dev/null +++ b/src/tools/extract_syscalls.c @@ -0,0 +1,91 @@ +/* + * 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 +#include 6 +#include + +#define BUFMAX 4096 + +int main(int argc, char **argv) { + if (argc != 2) { + printf("usage: %s /media/ubuntu/usr/include/x86_64-linux-gnu/bits/syscall.h\n", argv[0]); + return 1; + } + + //open file + FILE *fp = fopen(argv[1], "r"); + if (!fp) { + fprintf(stderr, "Error: cannot open file\n"); + return 1; + } + + // read file + char buf[BUFMAX]; + while (fgets(buf, BUFMAX, fp)) { + // cleanup + char *start = buf; + while (*start == ' ' || *start == '\t') + start++; + char *end = strchr(start, '\n'); + if (end) + *end = '\0'; + + // parsing + if (strncmp(start, "#endif", 6) == 0) + printf("%s\n", start); + if (strncmp(start, "#endif", 6) == 0) + printf("%s\n", start); + else if (strncmp(start, "#if", 3) == 0) + printf("%s\n", start); + else if (strncmp(start, "#define", 7) == 0) { + // extract data + char *ptr1 = strstr(start, "SYS_"); + char *ptr2 = strstr(start, "__NR_"); + if (!ptr1 || !ptr2) { + fprintf(stderr, "Error: cannot parse \"%s\"\n", start); + fclose(fp); + return 1; + } + *(ptr2 - 1) = '\0'; + + char *ptr3 = ptr1; + while (*ptr3 != ' ' && *ptr3 != '\t' && *ptr3 != '\0') + ptr3++; + *ptr3 = '\0'; + ptr3 = ptr2; + while (*ptr3 != ' ' && *ptr3 != '\t' && *ptr3 != '\0') + ptr3++; + *ptr3 = '\0'; + + ptr3 = ptr1; + while (*ptr3 != '_') + ptr3++; + ptr3++; + + printf("#ifdef %s\n", ptr1); + printf("#ifdef %s\n", ptr2); + printf("\t{\"%s\", %s},\n", ptr3, ptr2); + printf("#endif\n"); + printf("#endif\n"); + } + } + fclose(fp); + return 0; +} diff --git a/src/tools/mkcoverit.sh b/src/tools/mkcoverit.sh new file mode 100755 index 000000000..4af84a7a1 --- /dev/null +++ b/src/tools/mkcoverit.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# unpack firejail archive +ARCFIREJAIL=`ls *.tar.bz2| grep firejail` +if [ "$?" -eq 0 ]; +then + echo "preparing $ARCFIREJAIL" + DIRFIREJAIL=`basename $ARCFIREJAIL .tar.bz2` + rm -fr $DIRFIREJAIL + tar -xjvf $ARCFIREJAIL + cd $DIRFIREJAIL + ./configure --prefix=/usr + cd .. +else + echo "Error: firejail source archive missing" + exit 1 +fi + + +# unpack firetools archive +ARCFIRETOOLS=`ls *.tar.bz2 | grep firetools` +if [ "$?" -eq 0 ]; +then + echo "preparing $ARCFIRETOOLS" + DIRFIRETOOLS=`basename $ARCFIRETOOLS .tar.bz2` + rm -fr $DIRFIRETOOLS + tar -xjvf $ARCFIRETOOLS + cd $DIRFIRETOOLS + pwd + ./configure --prefix=/usr + cd .. + +else + echo "Error: firetools source archive missing" + exit 1 +fi + +# move firetools in firejail source tree +mkdir -p $DIRFIREJAIL/extras +mv $DIRFIRETOOLS $DIRFIREJAIL/extras/firetools + +# build +cd $DIRFIREJAIL +cov-build --dir cov-int make -j 4 extras +tar czvf myproject.tgz cov-int diff --git a/src/tools/rvtest.c b/src/tools/rvtest.c new file mode 100644 index 000000000..95050e671 --- /dev/null +++ b/src/tools/rvtest.c @@ -0,0 +1,144 @@ +/* + * 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. +*/ + +// run it as "rvtest 2>/dev/null | grep TESTING" + +#include +#include +#include +#include +#include +#include + +#define MAXBUF 1024 // line buffer +#define TIMEOUT 30 // timeout time in seconds + +static pid_t pid; +static void catch_alarm(int sig) { + kill(pid, SIGTERM); + sleep(1); + kill(pid, SIGKILL); + printf("TESTING ERROR: SIGALARM triggered\n"); + exit(1); +} + +static void usage(void) { + printf("Usage: rvtest testfile\n"); + printf("\n"); + printf("Testfile format:\n"); + printf("\tretval command\n"); + printf("\n"); + printf("Testfile example:\n"); + printf("\n"); + printf("0 firejail --net=none exit\n"); + printf("1 firejail --private=/etc sleep 1\n"); + printf("1 firejail --blablabla\n"); +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "Error: test file missing\n"); + usage(); + return 1; + } + + signal (SIGALRM, catch_alarm); + + // open test file + char *fname = argv[1]; + FILE *fp = fopen(fname, "r"); + + // read test file + char buf[MAXBUF]; + int line = 0; + while (fgets(buf, MAXBUF, fp)) { + line++; + // skip blanks + char *start = buf; + while (*start == ' ' || *start == '\t') + start++; + // remove '\n' + char *ptr = strchr(start, '\n'); + if (ptr) + *ptr ='\0'; + if (*start == '\0') + continue; + + // skip comments + if (*start == '#') + continue; + ptr = strchr(start, '#'); + if (ptr) + *ptr = '\0'; + + // extract exit status + int status; + int rv = sscanf(start, "%d\n", &status); + if (rv != 1) { + fprintf(stderr, "Error: invalid line %d in %s\n", line, fname); + exit(1); + } + + // extract command + char *cmd = strchr(start, ' '); + if (!cmd) { + fprintf(stderr, "Error: invalid line %d in %s\n", line, fname); + exit(1); + } + + // execute command + printf("TESTING %s\n", cmd); + fflush(0); + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(1); + } + + // child + if (pid == 0) { + char *earg[50]; + earg[0] = "/bin/bash"; + earg[1] = "-c"; + earg[2] = cmd; + earg[3] = NULL; + execvp(earg[0], earg); + } + // parent + else { + int exit_status; + + alarm(TIMEOUT); + pid = waitpid(pid, &exit_status, 0); + if (pid == -1) { + perror("waitpid"); + exit(1); + } + + if (WEXITSTATUS(exit_status) != status) + printf("ERROR TESTING: %s\n", cmd); + } + + fflush(0); + } + fclose(fp); + + return 0; +} \ No newline at end of file diff --git a/src/tools/ttytest.c b/src/tools/ttytest.c new file mode 100644 index 000000000..a449bf9ba --- /dev/null +++ b/src/tools/ttytest.c @@ -0,0 +1,36 @@ +#define _XOPEN_SOURCE 600 +#include +#include +#include +#include + +int main(void) { + int fdm; + int rc; + + // initial + system("ls -l /dev/pts"); + + fdm = posix_openpt(O_RDWR); + if (fdm < 0) { + perror("posix_openpt"); + return 1; + } + + rc = grantpt(fdm); + if (rc != 0) { + perror("grantpt"); + return 1; + } + + rc = unlockpt(fdm); + if (rc != 0) { + perror("unlockpt"); + return 1; + } + + // final + system("ls -l /dev/pts"); + + return 0; +} -- cgit v1.2.3-54-g00ecf