From abe5cb027416771da3d01c9b55d12a8f70618ed8 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Thu, 27 Oct 2016 08:58:48 -0400 Subject: network code split --- src/fnet/Makefile.in | 43 ++++++++++ src/fnet/fnet.h | 40 +++++++++ src/fnet/interface.c | 183 ++++++++++++++++++++++++++++++++++++++++ src/fnet/main.c | 63 ++++++++++++++ src/fnet/veth.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 559 insertions(+) create mode 100644 src/fnet/Makefile.in create mode 100644 src/fnet/fnet.h create mode 100644 src/fnet/interface.c create mode 100644 src/fnet/main.c create mode 100644 src/fnet/veth.c (limited to 'src/fnet') diff --git a/src/fnet/Makefile.in b/src/fnet/Makefile.in new file mode 100644 index 000000000..1bfb4c68d --- /dev/null +++ b/src/fnet/Makefile.in @@ -0,0 +1,43 @@ +all: fnet + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +sysconfdir=@sysconfdir@ + +VERSION=@PACKAGE_VERSION@ +NAME=@PACKAGE_NAME@ +HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ +HAVE_SECCOMP=@HAVE_SECCOMP@ +HAVE_CHROOT=@HAVE_CHROOT@ +HAVE_BIND=@HAVE_BIND@ +HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ +HAVE_NETWORK=@HAVE_NETWORK@ +HAVE_USERNS=@HAVE_USERNS@ +HAVE_X11=@HAVE_X11@ +HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ +HAVE_WHITELIST=@HAVE_WHITELIST@ +HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ +HAVE_APPARMOR=@HAVE_APPARMOR@ +HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ +HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ +EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ + +H_FILE_LIST = $(sort $(wildcard *.[h])) +C_FILE_LIST = $(sort $(wildcard *.c)) +OBJS = $(C_FILE_LIST:.c=.o) +BINOBJS = $(foreach file, $(OBJS), $file) +CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -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) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +fnet: $(OBJS) ../lib/libnetlink.o ../lib/common.o + $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) + +clean:; rm -f *.o fnet + +distclean: clean + rm -fr Makefile + diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h new file mode 100644 index 000000000..58efbbed5 --- /dev/null +++ b/src/fnet/fnet.h @@ -0,0 +1,40 @@ + /* + * Copyright (C) 2014-2016 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 FNET_H +#define FNET_H + +#include +#include +#include +#include +#include "../include/common.h" + +// 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); +int net_move_interface(const char *dev, unsigned pid); + +// interface.c +void net_bridge_add_interface(const char *bridge, const char *dev); +void net_if_up(const char *ifname); +int net_get_mtu(const char *ifname); +void net_set_mtu(const char *ifname, int mtu); + +#endif diff --git a/src/fnet/interface.c b/src/fnet/interface.c new file mode 100644 index 000000000..b1903dd46 --- /dev/null +++ b/src/fnet/interface.c @@ -0,0 +1,183 @@ + /* + * Copyright (C) 2014-2016 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 "fnet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 fnet: invalid network device name %s\n", bridge); + exit(1); + } + + // somehow adding the interface to the bridge resets MTU on bridge device!!! + // workaround: restore MTU on the bridge device + // todo: put a real fix in + int mtu1 = net_get_mtu(bridge); + + 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); + + int mtu2 = net_get_mtu(bridge); + if (mtu1 != mtu2) { + net_set_mtu(bridge, mtu1); + } +} + + +// bring interface up +void net_if_up(const char *ifname) { + if (strlen(ifname) > IFNAMSIZ) { + fprintf(stderr, "Error fnet: 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); + printf("Error fnet: cannot bring up interface %s\n", ifname); + errExit("ioctl"); + } + + ifr.ifr_flags |= IFF_UP; + + // set the new flags + if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { + close(sock); + printf("Error fnet: cannot bring up interface %s\n", ifname); + errExit("ioctl"); + } + + // checking + // read the existing flags + if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { + close(sock); + printf("Error fnet: cannot bring up interface %s\n", ifname); + 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); + printf("Error fnet: cannot bring up interface %s\n", ifname); + errExit("ioctl"); + } + if (ifr.ifr_flags & IFF_RUNNING) + break; + cnt++; + } + + close(sock); +} + +int net_get_mtu(const char *ifname) { + int mtu = 0; + if (strlen(ifname) > IFNAMSIZ) { + fprintf(stderr, "Error fnet: invalid network device name %s\n", ifname); + exit(1); + } + + int s; + struct ifreq ifr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0) + mtu = ifr.ifr_mtu; + close(s); + + + return mtu; +} + +void net_set_mtu(const char *ifname, int mtu) { + if (strlen(ifname) > IFNAMSIZ) { + fprintf(stderr, "Error fnet: invalid network device name %s\n", ifname); + exit(1); + } + + int s; + struct ifreq ifr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_mtu = mtu; + if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) != 0) + fprintf(stderr, "Warning fnet: cannot set mtu for interface %s\n", ifname); + close(s); +} + + diff --git a/src/fnet/main.c b/src/fnet/main.c new file mode 100644 index 000000000..ae780c2ea --- /dev/null +++ b/src/fnet/main.c @@ -0,0 +1,63 @@ + /* + * Copyright (C) 2014-2016 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 +#include +#include +#include +#include + +static void usage(void) { + printf("Usage:\n"); + printf("\tfnet create veth dev1 dev2 bridge child\n"); + printf("\tfnet create macvlan dev parent child\n"); + printf("\tfnet moveif dev proc\n"); +} + +int main(int argc, char **argv) { + if (argc < 2) + return 1; + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { + usage(); + return 0; + } + else if (argc == 7 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "veth") == 0) { + // create veth pair and move one end in the the namespace + net_create_veth(argv[3], argv[4], atoi(argv[6])); + + // connect the ohter veth end to the bridge ... + net_bridge_add_interface(argv[5], argv[3]); + + // ... and bring it up + net_if_up(argv[3]); + } + else if (argc == 6 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "macvlan") == 0) { + net_create_macvlan(argv[3], argv[4], atoi(argv[5])); + } + else if (argc == 4 && strcmp(argv[1], "moveif") == 0) { + net_move_interface(argv[2], atoi(argv[3])); + } + else { + fprintf(stderr, "Error fnet: invalid arguments\n"); + return 1; + } + + return 0; +} diff --git a/src/fnet/veth.c b/src/fnet/veth.c new file mode 100644 index 000000000..d06bc9256 --- /dev/null +++ b/src/fnet/veth.c @@ -0,0 +1,230 @@ +/* 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-2016 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 "fnet.h" +#include "../include/libnetlink.h" +#include +#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; + + 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; + 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; + + // find parent ifindex + int parent_ifindex = if_nametoindex(parent); + if (parent_ifindex <= 0) { + fprintf(stderr, "Error: cannot find network device %s\n", parent); + exit(1); + } + + // add parent + 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; +} + +// move the interface dev in namespace of program pid +// when the interface is moved, netlink does not preserve interface configuration +int net_move_interface(const char *dev, unsigned pid) { + struct iplink_req req; + assert(dev); + + 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; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = 0; + + // find ifindex + int ifindex = if_nametoindex(dev); + if (ifindex <= 0) { + fprintf(stderr, "Error: cannot find interface %s\n", dev); + exit(1); + } + req.i.ifi_index = ifindex; + + // place the interface in child namespace + addattr_l (&req.n, sizeof(req), IFLA_NET_NS_PID, &pid, 4); + + // 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 -- cgit v1.2.3-70-g09d2 From 196a857a11848d0bff33ea1485884fb22bf42da6 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Thu, 27 Oct 2016 21:37:18 -0400 Subject: sbox --- .gitignore | 1 + src/firejail/Makefile.in | 2 +- src/firejail/firejail.h | 12 +++ src/firejail/list.c | 101 ------------------------- src/firejail/main.c | 27 ++++--- src/firejail/network_main.c | 61 +++------------ src/firejail/protocol.c | 2 +- src/firejail/sbox.c | 177 ++++++++++++++++++++++++++++++++++++++++++++ src/firejail/seccomp.c | 2 +- src/firejail/seccomp.h | 149 ------------------------------------- src/fnet/main.c | 11 +++ src/include/seccomp.h | 149 +++++++++++++++++++++++++++++++++++++ 12 files changed, 378 insertions(+), 316 deletions(-) delete mode 100644 src/firejail/list.c create mode 100644 src/firejail/sbox.c delete mode 100644 src/firejail/seccomp.h create mode 100644 src/include/seccomp.h (limited to 'src/fnet') diff --git a/.gitignore b/.gitignore index 0d5979c8b..6acb6775c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ src/firecfg/firecfg src/ftee/ftee src/tags src/faudit/faudit +src/fnet/fnet uids.h diff --git a/src/firejail/Makefile.in b/src/firejail/Makefile.in index 7d4bcb19b..c4c168236 100644 --- a/src/firejail/Makefile.in +++ b/src/firejail/Makefile.in @@ -30,7 +30,7 @@ BINOBJS = $(foreach file, $(OBJS), $file) CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -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) ../include/common.h ../include/euid_common.h ../include/pid.h +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/pid.h ../include/seccomp.h $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 6c0441472..367f599ec 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -25,6 +25,7 @@ // debug restricted shell //#define DEBUG_RESTRICTED_SHELL + // filesystem #define RUN_FIREJAIL_BASEDIR "/run" #define RUN_FIREJAIL_DIR "/run/firejail" @@ -681,6 +682,17 @@ long unsigned int appimage2_size(const char *fname); // cmdline.c void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); +// sbox.c +// programs +#define PATH_FNET (LIBDIR "/firejail/fnet") +#define PATH_FIREMON (PREFIX "/bin/firemon") +// bitmapped filters for sbox_run +#define SBOX_ROOT 1 +#define SBOX_USER 2 +#define SBOX_CAPS 4 +#define SBOX_SECCOMP 8 +int sbox_run(unsigned filter, int num, ...); + #endif diff --git a/src/firejail/list.c b/src/firejail/list.c deleted file mode 100644 index d093a1f85..000000000 --- a/src/firejail/list.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2014-2016 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 - -static void set_privileges(void) { - struct stat s; - if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { - EUID_ROOT(); - - // elevate privileges - if (setreuid(0, 0)) - errExit("setreuid"); - if (setregid(0, 0)) - errExit("setregid"); - } - else - drop_privs(1); -} - -static char *get_firemon_path(const char *cmd) { - assert(cmd); - - // start the argv[0] program in a new sandbox - char *firemon; - if (asprintf(&firemon, "%s/bin/firemon %s", PREFIX, cmd) == -1) - errExit("asprintf"); - - return firemon; -} - -void top(void) { - EUID_ASSERT(); - drop_privs(1); - char *cmd = get_firemon_path("--top"); - - char *arg[4]; - arg[0] = "bash"; - arg[1] = "-c"; - arg[2] = cmd; - arg[3] = NULL; - execvp("/bin/bash", arg); -} - -void netstats(void) { - EUID_ASSERT(); - set_privileges(); - char *cmd = get_firemon_path("--netstats"); - - char *arg[4]; - arg[0] = "bash"; - arg[1] = "-c"; - arg[2] = cmd; - arg[3] = NULL; - execvp("/bin/bash", arg); -} - -void list(void) { - EUID_ASSERT(); - drop_privs(1); - char *cmd = get_firemon_path("--list"); - - char *arg[4]; - arg[0] = "bash"; - arg[1] = "-c"; - arg[2] = cmd; - arg[3] = NULL; - execvp("/bin/bash", arg); -} - -void tree(void) { - EUID_ASSERT(); - drop_privs(1); - char *cmd = get_firemon_path("--tree"); - - char *arg[4]; - arg[0] = "bash"; - arg[1] = "-c"; - arg[2] = cmd; - arg[3] = NULL; - execvp("/bin/bash", arg); -} - diff --git a/src/firejail/main.c b/src/firejail/main.c index f41d5fcd3..15d42a4e0 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -54,9 +54,9 @@ Config cfg; // configuration int arg_private = 0; // mount private /home and /tmp directoryu int arg_private_template = 0; // mount private /home using a template int arg_debug = 0; // print debug messages -int arg_debug_check_filename; // print debug messages for filename checking -int arg_debug_blacklists; // print debug messages for blacklists -int arg_debug_whitelists; // print debug messages for whitelists +int arg_debug_check_filename = 0; // print debug messages for filename checking +int arg_debug_blacklists = 0; // print debug messages for blacklists +int arg_debug_whitelists = 0; // print debug messages for whitelists int arg_nonetwork = 0; // --net=none int arg_command = 0; // -c int arg_overlay = 0; // overlay option @@ -498,27 +498,32 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(0); } else if (strcmp(argv[i], "--list") == 0) { - list(); - exit(0); + int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); + exit(rv); } else if (strcmp(argv[i], "--tree") == 0) { - tree(); - exit(0); + int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); + exit(rv); } else if (strcmp(argv[i], "--top") == 0) { - top(); - exit(0); + int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--top"); + exit(rv); } #ifdef HAVE_NETWORK else if (strcmp(argv[i], "--netstats") == 0) { if (checkcfg(CFG_NETWORK)) { - netstats(); + struct stat s; + int rv; + if (stat("/proc/sys/kernel/grsecurity", &s) == 0) + rv = sbox_run(SBOX_ROOT | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); + else + rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); + exit(rv); } else { fprintf(stderr, "Error: networking features are disabled in Firejail configuration file\n"); exit(1); } - exit(0); } #endif #ifdef HAVE_FILE_TRANSFER diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index d2aa84bb6..35d6dd214 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c @@ -25,49 +25,6 @@ #include #include -static void fnet_run(int num, ...) { - int i; - va_list valist; - va_start(valist, num); - - char *fnet; - if (asprintf(&fnet, "%s/firejail/fnet", LIBDIR) == -1) - errExit("asprintf"); - - char *arg[num + 2]; - arg[0] = fnet; - for (i = 0; i < num; i++) - arg[i + 1] = va_arg(valist, char*); - arg[i + 1] = NULL; - - pid_t child = fork(); - if (child < 0) - errExit("fork"); - if (child == 0) { - // elevate privileges in order to get grsecurity working - if (setreuid(0, 0)) - errExit("setreuid"); - if (setregid(0, 0)) - errExit("setregid"); - - execvp(arg[0], arg); - perror("execl"); - _exit(1); - } - - int status; - if (waitpid(child, &status, 0) == -1 ) { - errExit("waitpid"); - } - if (WIFEXITED(status) && status != 0) { - fprintf(stderr, "Error: cannot run fnet\n"); - exit(1); - } - - va_end(valist); - free(fnet); -} - // configure bridge structure // - extract ip address and mask from the bridge interface void net_configure_bridge(Bridge *br, char *dev_name) { @@ -175,7 +132,7 @@ void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { char *cstr; if (asprintf(&cstr, "%d", child) == -1) errExit("asprintf"); - fnet_run(6, "create", "veth", dev, ifname, br->dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); free(cstr); char *msg; @@ -344,7 +301,7 @@ void network_main(pid_t child) { } else // net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); - fnet_run(5, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); } if (cfg.bridge1.configured) { @@ -352,7 +309,7 @@ void network_main(pid_t child) { net_configure_veth_pair(&cfg.bridge1, "eth1", child); else // net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); - fnet_run(5, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); } if (cfg.bridge2.configured) { @@ -360,7 +317,7 @@ void network_main(pid_t child) { net_configure_veth_pair(&cfg.bridge2, "eth2", child); else // net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); - fnet_run(5, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); } if (cfg.bridge3.configured) { @@ -368,25 +325,25 @@ void network_main(pid_t child) { net_configure_veth_pair(&cfg.bridge3, "eth3", child); else // net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); - fnet_run(5, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); } // move interfaces in sandbox if (cfg.interface0.configured) { // net_move_interface(cfg.interface0.dev, child); - fnet_run(3, "moveif", cfg.interface0.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); } if (cfg.interface1.configured) { // net_move_interface(cfg.interface1.dev, child); - fnet_run(3, "moveif", cfg.interface1.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); } if (cfg.interface2.configured) { // net_move_interface(cfg.interface2.dev, child); - fnet_run(3, "moveif", cfg.interface3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } if (cfg.interface3.configured) { // net_move_interface(cfg.interface3.dev, child); - fnet_run(3, "moveif", cfg.interface3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } free(cstr); diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 1ef5bf13d..6321c703a 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c @@ -47,7 +47,7 @@ #ifdef HAVE_SECCOMP #include "firejail.h" -#include "seccomp.h" +#include "../include/seccomp.h" #include #include diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c new file mode 100644 index 000000000..8c6ace27e --- /dev/null +++ b/src/firejail/sbox.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2014-2016 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/seccomp.h" + +static struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL, + +#if defined(__x86_64__) +#define X32_SYSCALL_BIT 0x40000000 + // handle X32 ABI + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), + RETURN_ERRNO(EPERM), +#endif + + // syscall list +#ifdef SYS_mount + BLACKLIST(SYS_mount), // mount/unmount filesystems +#endif +#ifdef SYS_umount2 + BLACKLIST(SYS_umount2), +#endif +#ifdef SYS_ptrace + BLACKLIST(SYS_ptrace), // trace processes +#endif +#ifdef SYS_kexec_file_load + BLACKLIST(SYS_kexec_file_load), +#endif +#ifdef SYS_kexec_load + BLACKLIST(SYS_kexec_load), // loading a different kernel +#endif +#ifdef SYS_name_to_handle_at + BLACKLIST(SYS_name_to_handle_at), +#endif +#ifdef SYS_open_by_handle_at + BLACKLIST(SYS_open_by_handle_at), // open by handle +#endif +#ifdef SYS_init_module + BLACKLIST(SYS_init_module), // kernel module handling +#endif +#ifdef SYS_finit_module // introduced in 2013 + BLACKLIST(SYS_finit_module), +#endif +#ifdef SYS_create_module + BLACKLIST(SYS_create_module), +#endif +#ifdef SYS_delete_module + BLACKLIST(SYS_delete_module), +#endif +#ifdef SYS_iopl + BLACKLIST(SYS_iopl), // io permissions +#endif +#ifdef SYS_ioperm + BLACKLIST(SYS_ioperm), +#endif +#ifdef SYS_iopl + BLACKLIST(SYS_iopl), // io permissions +#endif +#ifdef SYS_ioprio_set + BLACKLIST(SYS_ioprio_set), +#endif +#ifdef SYS_ni_syscall // new io permissions call on arm devices + BLACKLIST(SYS_ni_syscall), +#endif +#ifdef SYS_swapon + BLACKLIST(SYS_swapon), // swap on/off +#endif +#ifdef SYS_swapoff + BLACKLIST(SYS_swapoff), +#endif +#ifdef SYS_syslog + BLACKLIST(SYS_syslog), // kernel printk control +#endif + RETURN_ALLOW +}; + +static struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), + .filter = filter, +}; + +typedef struct sbox_config { + char *name; + char *path; + unsigned filters; +} SboxConfig; + + +int sbox_run(unsigned filter, int num, ...) { + EUID_ROOT(); + char *path = NULL; + int i; + va_list valist; + va_start(valist, num); + + // build argument list + char *arg[num + 1]; + for (i = 0; i < num; i++) + arg[i] = va_arg(valist, char*); + arg[i] = NULL; + va_end(valist); + +#if 0 +{ +int i; +for (i = 0; i <= num; i++) + printf("#%s# ", arg[i]); +printf("\n"); +} +#endif + pid_t child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + // apply filters + if (filter & SBOX_CAPS) + caps_drop_all(); + + if (filter & SBOX_SECCOMP) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + } + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + perror("prctl(PR_SET_SECCOMP)"); + } + } + + if (filter & SBOX_ROOT) { + // elevate privileges in order to get grsecurity working + if (setreuid(0, 0)) + errExit("setreuid"); + if (setregid(0, 0)) + errExit("setregid"); + } + else if (filter & SBOX_USER) + drop_privs(1); + + execvp(arg[0], arg); + perror("execl"); + _exit(1); + } + + int status; + if (waitpid(child, &status, 0) == -1 ) { + errExit("waitpid"); + } + if (WIFEXITED(status) && status != 0) { + fprintf(stderr, "Error: cannot run fnet\n"); + exit(1); + } + + return status; +} diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 549359d94..09862ec20 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -20,7 +20,7 @@ #ifdef HAVE_SECCOMP #include "firejail.h" -#include "seccomp.h" +#include "../include/seccomp.h" #define SECSIZE 128 // initial filter size static struct sock_filter *sfilter = NULL; diff --git a/src/firejail/seccomp.h b/src/firejail/seccomp.h deleted file mode 100644 index 7d646dd9e..000000000 --- a/src/firejail/seccomp.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2014-2016 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. -*/ - -/* 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 - BLACKLIST(SYS_finit_module), - BLACKLIST(SYS_delete_module), - BLACKLIST(SYS_iopl), // io permissions - BLACKLIST(SYS_ioperm), - BLACKLIST(SYS_iopl), // io permissions - BLACKLIST(SYS_ni_syscall), - BLACKLIST(SYS_swapon), // swap on/off - BLACKLIST(SYS_swapoff), - BLACKLIST(SYS_syslog), // kernel printk control - RETURN_ALLOW - }; - - struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), - .filter = filter, - }; - - - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - perror("prctl(NO_NEW_PRIVS)"); - return 1; - } - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { - perror("prctl"); - return 1; - } -*/ - -#ifndef SECCOMP_H -#define SECCOMP_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_ALLOW) - -#define VALIDATE_ARCHITECTURE_64 \ - BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_X86_64, 1, 0), \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) - -#define VALIDATE_ARCHITECTURE_32 \ - BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_I386, 1, 0), \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) - -#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ - (offsetof(struct seccomp_data, nr))) - -#define EXAMINE_ARGUMENT(nr) BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ - (offsetof(struct seccomp_data, args[nr]))) - -#define ONLY(syscall_nr) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 1, 0), \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) - -#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 BLACKLIST_ERRNO(syscall_nr, nr) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) - -#define RETURN_ALLOW \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) - -#define RETURN_ERRNO(nr) \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) - -#define KILL_PROCESS \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) - -#endif diff --git a/src/fnet/main.c b/src/fnet/main.c index ae780c2ea..88f71c4b3 100644 --- a/src/fnet/main.c +++ b/src/fnet/main.c @@ -31,9 +31,20 @@ static void usage(void) { } int main(int argc, char **argv) { +#if 0 +{ +system("cat /proc/self/status"); +int i; +for (i = 0; i < argc; i++) + printf("*%s* ", argv[i]); +printf("\n"); +} +#endif if (argc < 2) return 1; + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { usage(); return 0; diff --git a/src/include/seccomp.h b/src/include/seccomp.h new file mode 100644 index 000000000..7d646dd9e --- /dev/null +++ b/src/include/seccomp.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2014-2016 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. +*/ + +/* 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 + BLACKLIST(SYS_finit_module), + BLACKLIST(SYS_delete_module), + BLACKLIST(SYS_iopl), // io permissions + BLACKLIST(SYS_ioperm), + BLACKLIST(SYS_iopl), // io permissions + BLACKLIST(SYS_ni_syscall), + BLACKLIST(SYS_swapon), // swap on/off + BLACKLIST(SYS_swapoff), + BLACKLIST(SYS_syslog), // kernel printk control + RETURN_ALLOW + }; + + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), + .filter = filter, + }; + + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + return 1; + } + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + perror("prctl"); + return 1; + } +*/ + +#ifndef SECCOMP_H +#define SECCOMP_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_ALLOW) + +#define VALIDATE_ARCHITECTURE_64 \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_X86_64, 1, 0), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) + +#define VALIDATE_ARCHITECTURE_32 \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_I386, 1, 0), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) + +#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + (offsetof(struct seccomp_data, nr))) + +#define EXAMINE_ARGUMENT(nr) BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + (offsetof(struct seccomp_data, args[nr]))) + +#define ONLY(syscall_nr) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 1, 0), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) + +#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 BLACKLIST_ERRNO(syscall_nr, nr) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) + +#define RETURN_ALLOW \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) + +#define RETURN_ERRNO(nr) \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) + +#define KILL_PROCESS \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) + +#endif -- cgit v1.2.3-70-g09d2 From 0e0df4153e83910f61643b705ecc1e65824b0c0c Mon Sep 17 00:00:00 2001 From: netblue30 Date: Fri, 28 Oct 2016 18:05:59 -0400 Subject: compile cleanup --- src/firejail/appimage_size.c | 5 ++--- src/firejail/sbox.c | 8 ++++++-- src/fnet/main.c | 6 +----- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'src/fnet') diff --git a/src/firejail/appimage_size.c b/src/firejail/appimage_size.c index 64fff6901..3f5c3150c 100644 --- a/src/firejail/appimage_size.c +++ b/src/firejail/appimage_size.c @@ -41,7 +41,6 @@ e_shoff + ( e_shentsize * e_shnum ) = 126584 typedef Elf32_Nhdr Elf_Nhdr; static Elf64_Ehdr ehdr; -static Elf64_Phdr *phdr; #if __BYTE_ORDER == __LITTLE_ENDIAN #define ELFDATANATIVE ELFDATA2LSB @@ -75,7 +74,7 @@ static uint64_t file64_to_cpu(uint64_t val) { // return 0 if error static long unsigned int read_elf32(int fd) { Elf32_Ehdr ehdr32; - ssize_t ret, i; + ssize_t ret; ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); if (ret < 0 || (size_t)ret != sizeof(ehdr)) @@ -92,7 +91,7 @@ static long unsigned int read_elf32(int fd) { // return 0 if error static long unsigned int read_elf64(int fd) { Elf64_Ehdr ehdr64; - ssize_t ret, i; + ssize_t ret; ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); if (ret < 0 || (size_t)ret != sizeof(ehdr)) diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c index d3ef2578c..d1225c3bc 100644 --- a/src/firejail/sbox.c +++ b/src/firejail/sbox.c @@ -23,6 +23,7 @@ #include #include #include + #include #include "../include/seccomp.h" static struct sock_filter filter[] = { @@ -112,7 +113,7 @@ typedef struct sbox_config { int sbox_run(unsigned filter, int num, ...) { EUID_ROOT(); - char *path = NULL; + int i; va_list valist; va_start(valist, num); @@ -159,7 +160,10 @@ printf("\n"); else if (filter & SBOX_USER) drop_privs(1); - execvp(arg[0], arg); + if (arg[0]) // get rid of scan-build warning + execvp(arg[0], arg); + else + assert(0); perror("execl"); _exit(1); } diff --git a/src/fnet/main.c b/src/fnet/main.c index 88f71c4b3..f17287cb9 100644 --- a/src/fnet/main.c +++ b/src/fnet/main.c @@ -17,11 +17,7 @@ * 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 -#include +#include "fnet.h" static void usage(void) { printf("Usage:\n"); -- cgit v1.2.3-70-g09d2 From f898290fd79e0e64d13ceef56fc5960da879d179 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sun, 30 Oct 2016 15:54:05 -0400 Subject: major cleanup --- .gitignore | 1 + README | 2 + src/firejail/appimage.c | 5 - src/firejail/arp.c | 186 +------------------------- src/firejail/firejail.h | 31 +++-- src/firejail/fs.c | 198 ++++----------------------- src/firejail/fs_bin.c | 1 - src/firejail/fs_dev.c | 2 - src/firejail/fs_etc.c | 1 - src/firejail/fs_home.c | 6 - src/firejail/fs_hostname.c | 2 - src/firejail/fs_trace.c | 3 - src/firejail/fs_var.c | 3 - src/firejail/fs_whitelist.c | 4 - src/firejail/main.c | 50 ++----- src/firejail/network.c | 303 +++++------------------------------------- src/firejail/network_main.c | 27 ++-- src/firejail/preproc.c | 110 +++++++++++++++ src/firejail/protocol.c | 2 - src/firejail/pulseaudio.c | 1 - src/firejail/restrict_users.c | 3 - src/firejail/sandbox.c | 285 ++++++++++++++++++++------------------- src/firejail/sbox.c | 23 ++-- src/firejail/seccomp.c | 1 - src/firejail/util.c | 34 +++++ src/fnet/arp.c | 208 +++++++++++++++++++++++++++++ src/fnet/fnet.h | 9 ++ src/fnet/interface.c | 212 +++++++++++++++++++++++++++++ src/fnet/main.c | 39 +++++- src/fseccomp/main.c | 6 +- 30 files changed, 869 insertions(+), 889 deletions(-) create mode 100644 src/firejail/preproc.c create mode 100644 src/fnet/arp.c (limited to 'src/fnet') diff --git a/.gitignore b/.gitignore index 6acb6775c..db523da59 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ src/ftee/ftee src/tags src/faudit/faudit src/fnet/fnet +src/fseccomp/fseccomp uids.h diff --git a/README b/README index cbd15f02a..f0e2ea3e8 100644 --- a/README +++ b/README @@ -86,6 +86,8 @@ valoq (https://github.com/valoq) - added support for /srv in --whitelist feature - Eye of GNOME and Evolution profiles - blacklist suid binaries in disable-common.inc +Vadim A. Misbakh-Soloviov (https://github.com/msva) + - profile fixes Rafael Cavalcanti (https://github.com/rccavalcanti) - chromium profile fixes for Arch Linux Deelvesh Bunjun (https://github.com/DeelveshBunjun) diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 09b242964..322798ee5 100644 --- a/src/firejail/appimage.c +++ b/src/firejail/appimage.c @@ -60,11 +60,6 @@ void appimage_set(const char *appimage_path) { exit(1); } - // populate /run/firejail directory - EUID_ROOT(); - fs_build_firejail_dir(); - EUID_USER(); - // find or allocate a free loop device to use EUID_ROOT(); int cfd = open("/dev/loop-control", O_RDWR); diff --git a/src/firejail/arp.c b/src/firejail/arp.c index fb5e426b0..ddb75905f 100644 --- a/src/firejail/arp.c +++ b/src/firejail/arp.c @@ -40,6 +40,7 @@ typedef struct arp_hdr_t { 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) { @@ -286,189 +287,4 @@ uint32_t arp_assign(const char *dev, Bridge *br) { 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; - uint32_t 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); - - // build 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 incoming packet - if ((unsigned int) 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/firejail.h b/src/firejail/firejail.h index 749656f8b..dcec160fb 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -363,20 +363,19 @@ void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); void net_if_ip6(const char *ifname, const char *addr6); int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6], int *mtu); int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); -void net_ifprint(void); 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]); +void net_config_interface(const char *dev, uint32_t ip, uint32_t mask, int mtu); + +// preproc.c +void preproc_build_firejail_dir(void); +void preproc_mount_mnt_dir(void); +void preproc_build_cp_command(void); +void preproc_delete_cp_command(void) ; +void preproc_remount_mnt_dir(void); // fs.c -// build /run/firejail directory -void fs_build_firejail_dir(void); -// build /run/firejail/mnt directory -void fs_build_mnt_dir(void); -// grab a copy of cp command -void fs_build_cp_command(void); -// delete the temporary cp command -void fs_delete_cp_command(void) ; // blacklist files or directoies by mounting empty files on top of them void fs_blacklist(void); // remount a directory read-only @@ -393,7 +392,6 @@ 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); -void fs_private_tmp(void); // profile.c // find and read the profile specified by name from dir directory @@ -430,8 +428,6 @@ int restricted_shell(const char *user); 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); // util.c void drop_privs(int nogroups); @@ -459,6 +455,8 @@ void invalid_filename(const char *fname); uid_t get_group_id(const char *group); int remove_directory(const char *path); void flush_stdin(void); +void create_empty_dir_as_root(const char *dir, mode_t mode); +void create_empty_file_as_root(const char *dir, mode_t mode); // fs_var.c void fs_var_log(void); // mounting /var/log @@ -687,10 +685,11 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar #define PATH_FIREMON (PREFIX "/bin/firemon") #define PATH_FSECCOMP (LIBDIR "/firejail/fseccomp") // bitmapped filters for sbox_run -#define SBOX_ROOT 1 -#define SBOX_USER 2 -#define SBOX_CAPS 4 -#define SBOX_SECCOMP 8 +#define SBOX_ROOT (1 << 0) +#define SBOX_USER (1 << 1) +#define SBOX_SECCOMP (1 << 2) +#define SBOX_CAPS_NONE (1 << 3) // drop all capabilities +#define SBOX_CAPS_NETWORK (1 << 4) // caps filter for programs running network programs // run sbox int sbox_run(unsigned filter, int num, ...); diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 572b08205..f7fc44c71 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -29,155 +29,8 @@ static void fs_rdwr(const char *dir); -static void create_dir_as_root(const char *dir, mode_t mode) { - assert(dir); - if (arg_debug) - printf("Creating %s directory\n", dir); - - if (mkdir(dir, mode) == -1) - errExit("mkdir"); - if (chmod(dir, mode) == -1) - errExit("chmod"); - - ASSERT_PERMS(dir, 0, 0, mode); -} - -static void create_empty_dir(void) { - struct stat s; - - if (stat(RUN_RO_DIR, &s)) { - /* coverity[toctou] */ - if (mkdir(RUN_RO_DIR, S_IRUSR | S_IXUSR) == -1) - errExit("mkdir"); - if (chmod(RUN_RO_DIR, S_IRUSR | S_IXUSR) == -1) - errExit("chmod"); - ASSERT_PERMS(RUN_RO_DIR, 0, 0, S_IRUSR | S_IXUSR); - } -} - -static void create_empty_file(void) { - struct stat s; - - if (stat(RUN_RO_FILE, &s)) { - /* coverity[toctou] */ - FILE *fp = fopen(RUN_RO_FILE, "w"); - if (!fp) - errExit("fopen"); - - SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); - fclose(fp); - } -} - -// build /run/firejail directory -void fs_build_firejail_dir(void) { - struct stat s; - - // CentOS 6 doesn't have /run directory - if (stat(RUN_FIREJAIL_BASEDIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_BASEDIR, 0755); - } - - // check /run/firejail directory belongs to root end exit if doesn't! - if (stat(RUN_FIREJAIL_DIR, &s) == 0) { - if (s.st_uid != 0 || s.st_gid != 0) { - fprintf(stderr, "Error: non-root %s directory, exiting...\n", RUN_FIREJAIL_DIR); - exit(1); - } - } - else { - create_dir_as_root(RUN_FIREJAIL_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_NETWORK_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_BANDWIDTH_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_NAME_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_NAME_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_X11_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); - } - - create_empty_dir(); - create_empty_file(); -} -// build /run/firejail/mnt directory -static int tmpfs_mounted = 0; -#ifdef HAVE_CHROOT -static void fs_build_remount_mnt_dir(void) { - tmpfs_mounted = 0; - fs_build_mnt_dir(); -} -#endif - -void fs_build_mnt_dir(void) { - struct stat s; - fs_build_firejail_dir(); - - // create /run/firejail/mnt directory - if (stat(RUN_MNT_DIR, &s)) { - create_dir_as_root(RUN_MNT_DIR, 0755); - } - - // ... and mount tmpfs on top of it - if (!tmpfs_mounted) { - // mount tmpfs on top of /run/firejail/mnt - if (arg_debug) - printf("Mounting tmpfs on %s directory\n", RUN_MNT_DIR); - if (mount("tmpfs", RUN_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) - errExit("mounting /run/firejail/mnt"); - tmpfs_mounted = 1; - fs_logger2("tmpfs", RUN_MNT_DIR); - } -} - -// grab a copy of cp command -void fs_build_cp_command(void) { - struct stat s; - fs_build_mnt_dir(); - if (stat(RUN_CP_COMMAND, &s)) { - char* fname = realpath("/bin/cp", NULL); - if (fname == NULL) { - fprintf(stderr, "Error: /bin/cp not found\n"); - exit(1); - } - if (stat(fname, &s)) { - fprintf(stderr, "Error: /bin/cp not found\n"); - exit(1); - } - if (is_link(fname)) { - fprintf(stderr, "Error: invalid /bin/cp file\n"); - exit(1); - } - int rv = copy_file(fname, RUN_CP_COMMAND, 0, 0, 0755); - if (rv) { - fprintf(stderr, "Error: cannot access /bin/cp\n"); - exit(1); - } - ASSERT_PERMS(RUN_CP_COMMAND, 0, 0, 0755); - - free(fname); - } -} - -// delete the temporary cp command -void fs_delete_cp_command(void) { - unlink(RUN_CP_COMMAND); -} - //*********************************************** // process profile file //*********************************************** @@ -202,9 +55,6 @@ static void disable_file(OPERATION op, const char *filename) { assert(op next; } - // create mount points - fs_build_mnt_dir(); - - // /home/user if (home_dir) { // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR diff --git a/src/firejail/main.c b/src/firejail/main.c index e210ceb31..fc86f9651 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -404,7 +404,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { #ifdef HAVE_SECCOMP else if (strcmp(argv[i], "--debug-syscalls") == 0) { if (checkcfg(CFG_SECCOMP)) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-syscalls"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-syscalls"); exit(rv); } else { @@ -414,7 +414,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { } else if (strcmp(argv[i], "--debug-errnos") == 0) { if (checkcfg(CFG_SECCOMP)) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-errnos"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-errnos"); exit(rv); } else { @@ -439,7 +439,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(0); } else if (strcmp(argv[i], "--debug-protocols") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-protocols"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-protocols"); exit(rv); } else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { @@ -499,15 +499,15 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(0); } else if (strcmp(argv[i], "--list") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); exit(rv); } else if (strcmp(argv[i], "--tree") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); exit(rv); } else if (strcmp(argv[i], "--top") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--top"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--top"); exit(rv); } #ifdef HAVE_NETWORK @@ -516,9 +516,9 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { struct stat s; int rv; if (stat("/proc/sys/kernel/grsecurity", &s) == 0) - rv = sbox_run(SBOX_ROOT | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); + rv = sbox_run(SBOX_ROOT | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); else - rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); exit(rv); } else { @@ -855,6 +855,9 @@ int main(int argc, char **argv) { int highest_errno = errno_highest_nr(); #endif + // build /run/firejail directory structure + preproc_build_firejail_dir(); + detect_quiet(argc, argv); detect_allow_debuggers(argc, argv); @@ -957,10 +960,8 @@ int main(int argc, char **argv) { // initialize globals init_cfg(argc, argv); - // check firejail directories EUID_ROOT(); - fs_build_firejail_dir(); bandwidth_del_run_file(sandbox_pid); network_del_run_file(sandbox_pid); delete_name_file(sandbox_pid); @@ -1462,35 +1463,6 @@ int main(int argc, char **argv) { } } -#if 0 // disabled for now, it could be used to overwrite system directories - else if (strncmp(argv[i], "--overlay-path=", 15) == 0) { - if (checkcfg(CFG_OVERLAYFS)) { - if (cfg.chrootdir) { - fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); - exit(1); - } - struct stat s; - if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { - fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); - exit(1); - } - arg_overlay = 1; - arg_overlay_keep = 1; - arg_overlay_reuse = 1; - - char *dirname = argv[i] + 15; - if (dirname == '\0') { - fprintf(stderr, "Error: invalid overlay option\n"); - exit(1); - } - cfg.overlay_dir = expand_home(dirname, cfg.homedir); - } - else { - fprintf(stderr, "Error: overlayfs feature is disabled in Firejail configuration file\n"); - exit(1); - } - } -#endif else if (strcmp(argv[i], "--overlay-tmpfs") == 0) { if (checkcfg(CFG_OVERLAYFS)) { if (cfg.chrootdir) { diff --git a/src/firejail/network.c b/src/firejail/network.c index ac0d86559..6d09d770f 100644 --- a/src/firejail/network.c +++ b/src/firejail/network.c @@ -28,70 +28,6 @@ #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); -} int net_get_mtu(const char *ifname) { int mtu = 0; @@ -190,101 +126,11 @@ void net_if_up(const char *ifname) { 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"); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, + PATH_FNET, "ifup", ifname); +} - // 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); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - - ifr.ifr_flags |= IFF_UP; - - // set the new flags - if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - - // checking - // read the existing flags - if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot bring up interface %s\n", ifname); - 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); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - if (ifr.ifr_flags & IFF_RUNNING) - break; - cnt++; - } - - close(sock); -} - -// bring interface up -void net_if_down(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); - printf("Error: cannot shut down interface %s\n", ifname); - errExit("ioctl"); - } - - ifr.ifr_flags &= ~IFF_UP; - - // set the new flags - if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot shut down interface %s\n", ifname); - errExit("ioctl"); - } - - close(sock); -} - -struct ifreq6 { - struct in6_addr ifr6_addr; - uint32_t ifr6_prefixlen; - unsigned int ifr6_ifindex; -}; // configure interface ipv6 address // ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 void net_if_ip6(const char *ifname, const char *addr6) { @@ -293,107 +139,11 @@ void net_if_ip6(const char *ifname, const char *addr6) { exit(1); } - // extract prefix - unsigned long prefix; - char *ptr; - if ((ptr = strchr(addr6, '/'))) { - prefix = atol(ptr + 1); - if (prefix > 128) { - fprintf(stderr, "Error: invalid prefix for IPv6 address %s\n", addr6); - exit(1); - } - *ptr = '\0'; // mark the end of the address - } - else - prefix = 128; - - // extract address - struct sockaddr_in6 sin6; - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - int rv = inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr); - if (rv <= 0) { - fprintf(stderr, "Error: invalid IPv6 address %s\n", addr6); - exit(1); - } - - // open socket - int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if (sock < 0) { - fprintf(stderr, "Error: IPv6 is not supported on this system\n"); - exit(1); - } - - // find interface index - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { - perror("ioctl SIOGIFINDEX"); - exit(1); - } - - // configure address - struct ifreq6 ifr6; - memset(&ifr6, 0, sizeof(ifr6)); - ifr6.ifr6_prefixlen = prefix; - ifr6.ifr6_ifindex = ifr.ifr_ifindex; - memcpy((char *) &ifr6.ifr6_addr, (char *) &sin6.sin6_addr, sizeof(struct in6_addr)); - if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) { - perror("ioctl SIOCSIFADDR"); - exit(1); - } - - close(sock); -} - -// configure interface ipv4 address -void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) { - if (strlen(ifname) > IFNAMSIZ) { - fprintf(stderr, "Error: invalid network device name %s\n", ifname); - exit(1); - } - if (arg_debug) - printf("configure interface %s\n", ifname); - - 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"); - } - } - - // configure mtu - if (mtu > 0) { - ifr.ifr_mtu = mtu; - if (ioctl( sock, SIOCSIFMTU, &ifr ) < 0) { - close(sock); - errExit("ioctl"); - } - } + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 5, + PATH_FNET, "config", "ipv6", ifname, addr6); - 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; @@ -464,20 +214,15 @@ uint32_t network_get_defaultgw(void) { } int net_config_mac(const char *ifname, const unsigned char mac[6]) { - struct ifreq ifr; - int sock; + char *macstr; + if (asprintf(&macstr, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) == -1) + errExit("asprintf"); - 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); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 5, + PATH_FNET, "config", "mac", ifname, macstr); - if (ioctl(sock, SIOCSIFHWADDR, &ifr) == -1) - errExit("ioctl"); - close(sock); + free(macstr); return 0; } @@ -500,3 +245,27 @@ int net_get_mac(const char *ifname, unsigned char mac[6]) { close(sock); return 0; } + +void net_config_interface(const char *dev, uint32_t ip, uint32_t mask, int mtu) { + assert(dev); + + char *ipstr; + if (asprintf(&ipstr, "%llu", (long long unsigned) ip) == -1) + errExit("asprintf"); + + char *maskstr; + if (asprintf(&maskstr, "%llu", (long long unsigned) mask) == -1) + errExit("asprintf"); + + char *mtustr; + if (asprintf(&mtustr, "%d", mtu) == -1) + errExit("asprintf"); + + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, + PATH_FNET, "config", "interface", dev, ipstr, maskstr, mtustr); + + free(ipstr); + free(maskstr); + free(mtustr); +} + diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index 35d6dd214..8a9c47f0e 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c @@ -128,11 +128,10 @@ void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { else dev = br->veth_name; -// net_create_veth(dev, ifname, child); char *cstr; if (asprintf(&cstr, "%d", child) == -1) errExit("asprintf"); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); free(cstr); char *msg; @@ -300,50 +299,42 @@ void network_main(pid_t child) { net_configure_veth_pair(&cfg.bridge0, "eth0", child); } else -// net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); } if (cfg.bridge1.configured) { if (cfg.bridge1.macvlan == 0) net_configure_veth_pair(&cfg.bridge1, "eth1", child); else -// net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); } if (cfg.bridge2.configured) { if (cfg.bridge2.macvlan == 0) net_configure_veth_pair(&cfg.bridge2, "eth2", child); else -// net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); } if (cfg.bridge3.configured) { if (cfg.bridge3.macvlan == 0) net_configure_veth_pair(&cfg.bridge3, "eth3", child); else -// net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); } // move interfaces in sandbox if (cfg.interface0.configured) { -// net_move_interface(cfg.interface0.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); } if (cfg.interface1.configured) { -// net_move_interface(cfg.interface1.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); } if (cfg.interface2.configured) { -// net_move_interface(cfg.interface2.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } if (cfg.interface3.configured) { -// net_move_interface(cfg.interface3.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } free(cstr); diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c new file mode 100644 index 000000000..6784ff5ac --- /dev/null +++ b/src/firejail/preproc.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014-2016 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 + +static int tmpfs_mounted = 0; + +// build /run/firejail directory +void preproc_build_firejail_dir(void) { + struct stat s; + + // CentOS 6 doesn't have /run directory + if (stat(RUN_FIREJAIL_BASEDIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_BASEDIR, 0755); + } + + if (stat(RUN_FIREJAIL_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_NETWORK_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_BANDWIDTH_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_NAME_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_NAME_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_X11_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); + } + + create_empty_file_as_root(RUN_RO_FILE, S_IRUSR); + create_empty_dir_as_root(RUN_RO_DIR, S_IRUSR); +} + +// build /run/firejail/mnt directory +void preproc_mount_mnt_dir(void) { + struct stat s; + + // mount tmpfs on top of /run/firejail/mnt + if (!tmpfs_mounted) { + if (arg_debug) + printf("Mounting tmpfs on %s directory\n", RUN_MNT_DIR); + if (mount("tmpfs", RUN_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /run/firejail/mnt"); + tmpfs_mounted = 1; + fs_logger2("tmpfs", RUN_MNT_DIR); + } +} + +// grab a copy of cp command +void preproc_build_cp_command(void) { + struct stat s; + preproc_mount_mnt_dir(); + if (stat(RUN_CP_COMMAND, &s)) { + char* fname = realpath("/bin/cp", NULL); + if (fname == NULL) { + fprintf(stderr, "Error: /bin/cp not found\n"); + exit(1); + } + if (stat(fname, &s)) { + fprintf(stderr, "Error: /bin/cp not found\n"); + exit(1); + } + if (is_link(fname)) { + fprintf(stderr, "Error: invalid /bin/cp file\n"); + exit(1); + } + int rv = copy_file(fname, RUN_CP_COMMAND, 0, 0, 0755); + if (rv) { + fprintf(stderr, "Error: cannot access /bin/cp\n"); + exit(1); + } + ASSERT_PERMS(RUN_CP_COMMAND, 0, 0, 0755); + + free(fname); + } +} + +// delete the temporary cp command +void preproc_delete_cp_command(void) { + unlink(RUN_CP_COMMAND); +} diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 43f30e30a..db6c6cad9 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c @@ -70,8 +70,6 @@ void protocol_filter(const char *fname) { void protocol_filter_save(void) { // save protocol filter configuration in PROTOCOL_CFG - fs_build_mnt_dir(); - FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); if (!fp) errExit("fopen"); diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 90997f934..e1a58c1c8 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c @@ -104,7 +104,6 @@ void pulseaudio_init(void) { return; // create the new user pulseaudio directory - fs_build_mnt_dir(); int rv = mkdir(RUN_PULSE_DIR, 0700); (void) rv; // in --chroot mode the directory can already be there if (chown(RUN_PULSE_DIR, getuid(), getgid()) < 0) diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c index 5ef9524d7..57e84e5cc 100644 --- a/src/firejail/restrict_users.c +++ b/src/firejail/restrict_users.c @@ -73,7 +73,6 @@ static void sanitize_home(void) { return; } - fs_build_mnt_dir(); if (mkdir(RUN_WHITELIST_HOME_DIR, 0755) == -1) errExit("mkdir"); @@ -127,7 +126,6 @@ static void sanitize_passwd(void) { FILE *fpin = NULL; FILE *fpout = NULL; - fs_build_mnt_dir(); // open files /* coverity[toctou] */ @@ -261,7 +259,6 @@ static void sanitize_group(void) { FILE *fpin = NULL; FILE *fpout = NULL; - fs_build_mnt_dir(); // open files /* coverity[toctou] */ diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 7a63461ef..a15003d03 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -122,7 +122,7 @@ static void sandbox_if_up(Bridge *br) { assert(br); if (!br->configured) return; - + char *dev = br->devsandbox; net_if_up(dev); @@ -137,8 +137,7 @@ static void sandbox_if_up(Bridge *br) { 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, br->mtu); - net_if_up(dev); + net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); } else if (br->arg_ip_none == 0 && br->macvlan == 1) { // reassign the macvlan address @@ -160,8 +159,7 @@ static void sandbox_if_up(Bridge *br) { 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, br->mtu); - net_if_up(dev); + net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); } if (br->ip6sandbox) @@ -256,32 +254,6 @@ static int monitor_application(pid_t app_pid) { // return the latest exit status. return status; - -#if 0 -// todo: find a way to shut down interfaces before closing the namespace -// the problem is we don't have enough privileges to shutdown interfaces in this moment - // shut down bridge/macvlan interfaces - if (any_bridge_configured()) { - - if (cfg.bridge0.configured) { - printf("Shutting down %s\n", cfg.bridge0.devsandbox); - net_if_down( cfg.bridge0.devsandbox); - } - if (cfg.bridge1.configured) { - printf("Shutting down %s\n", cfg.bridge1.devsandbox); - net_if_down( cfg.bridge1.devsandbox); - } - if (cfg.bridge2.configured) { - printf("Shutting down %s\n", cfg.bridge2.devsandbox); - net_if_down( cfg.bridge2.devsandbox); - } - if (cfg.bridge3.configured) { - printf("Shutting down %s\n", cfg.bridge3.devsandbox); - net_if_down( cfg.bridge3.devsandbox); - } - usleep(20000); // 20 ms sleep - } -#endif } void start_audit(void) { @@ -442,7 +414,8 @@ int sandbox(void* sandbox_arg) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { chk_chroot(); } - + // ... and mount a tmpfs on top of /run/firejail/mnt directory + preproc_mount_mnt_dir(); //**************************** // log sandbox data @@ -459,7 +432,7 @@ int sandbox(void* sandbox_arg) { fs_logger("install mount namespace"); //**************************** - // netfilter etc. + // netfilter //**************************** if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter netfilter(arg_netfilter_file); @@ -468,6 +441,105 @@ int sandbox(void* sandbox_arg) { netfilter6(arg_netfilter6_file); } + //**************************** + // networking + //**************************** + int gw_cfg_failed = 0; // default gw configuration flag + if (arg_nonetwork) { + net_if_up("lo"); + if (arg_debug) + printf("Network namespace enabled, only loopback interface available\n"); + } + else if (any_bridge_configured() || any_interface_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); + + +// todo: this code seems to be dead!!! + // enable interfaces + if (cfg.interface0.configured && cfg.interface0.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); + net_config_interface(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); + } + if (cfg.interface1.configured && cfg.interface1.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); + net_config_interface(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); + } + if (cfg.interface2.configured && cfg.interface2.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); + net_config_interface(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); + } + if (cfg.interface3.configured && cfg.interface3.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); + net_config_interface(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); + } + + // 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"); + gw_cfg_failed = 1; + } + } + + if (arg_debug) + printf("Network namespace enabled\n"); + } + + + // print network configuration + if (!arg_quiet) { + if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { + printf("\n"); + if (any_bridge_configured() || any_interface_configured()) { +// net_ifprint(); + if (arg_scan) + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "printif", "scan"); + else + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, PATH_FNET, "printif", "scan"); + + } + if (cfg.defaultgw != 0) { + if (gw_cfg_failed) + printf("Default gateway configuration failed\n"); + else + 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"); + } + } + // load IBUS env variables if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { // do nothing - there are problems with ibus version 1.5.11 @@ -475,9 +547,34 @@ int sandbox(void* sandbox_arg) { else env_ibus_load(); - // grab a copy of cp command - fs_build_cp_command(); - + //**************************** + // fs pre-processing: + // - copy some commands under /run + // - build seccomp filters + // - create an empty /etc/ld.so.preload + //**************************** + preproc_build_cp_command(); + +#ifdef HAVE_SECCOMP + if (cfg.protocol) { + if (arg_debug) + printf("Build protocol filter: %s\n", cfg.protocol); + // as root, create RUN_SECCOMP_PROTOCOL file + // this is where fseccomp program will store the protocol filter + create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); + if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_PROTOCOL, 0644) == -1) + errExit("chmod"); + + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, + PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); + if (rv) + exit(rv); + } +#endif + // trace pre-install if (arg_trace || arg_tracelog || mask_x11_abstract_socket) fs_trace_preload(); @@ -494,7 +591,7 @@ int sandbox(void* sandbox_arg) { enforce_seccomp = 1; #endif } - + #ifdef HAVE_CHROOT if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); @@ -617,7 +714,6 @@ int sandbox(void* sandbox_arg) { EUID_USER(); profile_add("whitelist /tmp/.X11-unix"); EUID_ROOT(); -// fs_private_tmp(); } } @@ -664,102 +760,17 @@ int sandbox(void* sandbox_arg) { fs_dev_disable_3d(); //**************************** - // networking + // set dns //**************************** - int gw_cfg_failed = 0; // default gw configuration flag - if (arg_nonetwork) { - net_if_up("lo"); - if (arg_debug) - printf("Network namespace enabled, only loopback interface available\n"); - } - else if (any_bridge_configured() || any_interface_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); - - // enable interfaces - if (cfg.interface0.configured && cfg.interface0.ip) { - if (arg_debug) - printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); - net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); - net_if_up(cfg.interface0.dev); - } - if (cfg.interface1.configured && cfg.interface1.ip) { - if (arg_debug) - printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); - net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); - net_if_up(cfg.interface1.dev); - } - if (cfg.interface2.configured && cfg.interface2.ip) { - if (arg_debug) - printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); - net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); - net_if_up(cfg.interface2.dev); - } - if (cfg.interface3.configured && cfg.interface3.ip) { - if (arg_debug) - printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); - net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); - net_if_up(cfg.interface3.dev); - } - - // 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"); - gw_cfg_failed = 1; - } - } - - if (arg_debug) - printf("Network namespace enabled\n"); - } - - // if any dns server is configured, it is time to set it now fs_resolvconf(); + + //**************************** + // fs post-processing + //**************************** + preproc_delete_cp_command(); fs_logger_print(); fs_logger_change_owner(); - // print network configuration - if (!arg_quiet) { - if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { - printf("\n"); - if (any_bridge_configured() || any_interface_configured()) - net_ifprint(); - if (cfg.defaultgw != 0) { - if (gw_cfg_failed) - printf("Default gateway configuration failed\n"); - else - 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"); - } - } - - fs_delete_cp_command(); - //**************************** // set application environment //**************************** @@ -820,21 +831,7 @@ int sandbox(void* sandbox_arg) { // install protocol filter if (cfg.protocol) { if (arg_debug) - printf("Set protocol filter: %s\n", cfg.protocol); - // as root, create RUN_SECCOMP_PROTOCOL file - // this is where fseccomp program will store the protocol filter - int dst = open(RUN_SECCOMP_PROTOCOL, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (dst == -1) - errExit("open"); - close(dst); - if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) - errExit("chown"); - - // build the seccomp filter as a regular user - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 5, - PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); - if (rv) - exit(rv); + printf("Install protocol filter: %s\n", cfg.protocol); protocol_filter(RUN_SECCOMP_PROTOCOL); // install filter protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG } diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c index d1225c3bc..6499b7005 100644 --- a/src/firejail/sbox.c +++ b/src/firejail/sbox.c @@ -125,21 +125,26 @@ int sbox_run(unsigned filter, int num, ...) { arg[i] = NULL; va_end(valist); -//#if 0 -{ -int i; -for (i = 0; i <= num; i++) - printf("#%s# ", arg[i]); -printf("\n"); -} -//#endif + if (arg_debug) { + printf("sbox run: "); + for (i = 0; i <= num; i++) + printf("%s ", arg[i]); + printf("\n"); + } + pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // apply filters - if (filter & SBOX_CAPS) + if (filter & SBOX_CAPS_NONE) { caps_drop_all(); + } + else if (filter & SBOX_CAPS_NETWORK) { + uint64_t set = ((uint64_t) 1) << CAP_NET_ADMIN; + set |= ((uint64_t) 1) << CAP_NET_RAW; + caps_set(set); + } if (filter & SBOX_SECCOMP) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 09862ec20..69be04a03 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -275,7 +275,6 @@ static void filter_end_whitelist(void) { // save seccomp filter in /run/firejail/mnt/seccomp static void write_seccomp_file(void) { - fs_build_mnt_dir(); assert(sfilter); int fd = open(RUN_SECCOMP_CFG, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); diff --git a/src/firejail/util.c b/src/firejail/util.c index 4b2e09953..4384c2b0b 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -696,3 +696,37 @@ void flush_stdin(void) { } } +void create_empty_dir_as_root(const char *dir, mode_t mode) { + assert(dir); + + struct stat s; + if (stat(dir, &s)) { + if (arg_debug) + printf("Creating empty %s directory\n", dir); + if (mkdir(dir, mode) == -1) + errExit("mkdir"); + if (chmod(dir, mode) == -1) + errExit("chmod"); + ASSERT_PERMS(dir, 0, 0, mode); + } + +} + +void create_empty_file_as_root(const char *fname, mode_t mode) { + assert(fname); + struct stat s; + + if (stat(fname, &s)) { + if (arg_debug) + printf("Creating empty %s file\n", fname); + + /* coverity[toctou] */ + FILE *fp = fopen(fname, "w"); + if (!fp) + errExit("fopen"); + + SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); + fclose(fp); + } +} + diff --git a/src/fnet/arp.c b/src/fnet/arp.c new file mode 100644 index 000000000..96684fdf9 --- /dev/null +++ b/src/fnet/arp.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2014-2016 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 "fnet.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; + + +// 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; + uint32_t 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); + + // build 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 incoming packet + if ((unsigned int) 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"); + 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/fnet/fnet.h b/src/fnet/fnet.h index 58efbbed5..0c5e5baef 100644 --- a/src/fnet/fnet.h +++ b/src/fnet/fnet.h @@ -36,5 +36,14 @@ void net_bridge_add_interface(const char *bridge, const char *dev); void net_if_up(const char *ifname); int net_get_mtu(const char *ifname); void net_set_mtu(const char *ifname, int mtu); +void net_ifprint(int scan); +int net_get_mac(const char *ifname, unsigned char mac[6]); +void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); +int net_if_mac(const char *ifname, const unsigned char mac[6]); +void net_if_ip6(const char *ifname, const char *addr6); + + +// arp.c +void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask); #endif diff --git a/src/fnet/interface.c b/src/fnet/interface.c index b1903dd46..67af062bf 100644 --- a/src/fnet/interface.c +++ b/src/fnet/interface.c @@ -180,4 +180,216 @@ void net_set_mtu(const char *ifname, int mtu) { close(s); } +// scan interfaces in current namespace and print IP address/mask for each interface +void net_ifprint(int scan) { + 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 (!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); +} + +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; +} + +// configure interface ipv4 address +void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) { + 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"); + } + } + + // configure mtu + if (mtu > 0) { + ifr.ifr_mtu = mtu; + if (ioctl( sock, SIOCSIFMTU, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + } + + close(sock); + usleep(10000); // sleep 10ms +} + +int net_if_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; +} + +// configure interface ipv6 address +// ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 +struct ifreq6 { + struct in6_addr ifr6_addr; + uint32_t ifr6_prefixlen; + unsigned int ifr6_ifindex; +}; +void net_if_ip6(const char *ifname, const char *addr6) { + if (strchr(addr6, ':') == NULL) { + fprintf(stderr, "Error fnet: invalid IPv6 address %s\n", addr6); + exit(1); + } + + // extract prefix + unsigned long prefix; + char *ptr; + if ((ptr = strchr(addr6, '/'))) { + prefix = atol(ptr + 1); + if (prefix > 128) { + fprintf(stderr, "Error fnet: invalid prefix for IPv6 address %s\n", addr6); + exit(1); + } + *ptr = '\0'; // mark the end of the address + } + else + prefix = 128; + + // extract address + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + int rv = inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr); + if (rv <= 0) { + fprintf(stderr, "Error fnet: invalid IPv6 address %s\n", addr6); + exit(1); + } + + // open socket + int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) { + fprintf(stderr, "Error fnet: IPv6 is not supported on this system\n"); + exit(1); + } + + // find interface index + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { + perror("ioctl SIOGIFINDEX"); + exit(1); + } + + // configure address + struct ifreq6 ifr6; + memset(&ifr6, 0, sizeof(ifr6)); + ifr6.ifr6_prefixlen = prefix; + ifr6.ifr6_ifindex = ifr.ifr_ifindex; + memcpy((char *) &ifr6.ifr6_addr, (char *) &sin6.sin6_addr, sizeof(struct in6_addr)); + if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) { + perror("ioctl SIOCSIFADDR"); + exit(1); + } + + close(sock); +} diff --git a/src/fnet/main.c b/src/fnet/main.c index f17287cb9..4ae9eb6e3 100644 --- a/src/fnet/main.c +++ b/src/fnet/main.c @@ -24,12 +24,18 @@ static void usage(void) { printf("\tfnet create veth dev1 dev2 bridge child\n"); printf("\tfnet create macvlan dev parent child\n"); printf("\tfnet moveif dev proc\n"); + printf("\tfnet printif\n"); + printf("\tfnet printif scan\n"); + printf("\tfnet config interface dev ip mask mtu\n"); + printf("\tfnet config mac addr\n"); + printf("\tfnet config ipv6 dev ipn"); + printf("\tfmet ifup dev\n"); } int main(int argc, char **argv) { #if 0 { -system("cat /proc/self/status"); +//system("cat /proc/self/status"); int i; for (i = 0; i < argc; i++) printf("*%s* ", argv[i]); @@ -45,22 +51,49 @@ printf("\n"); usage(); return 0; } + else if (argc == 3 && strcmp(argv[1], "ifup") == 0) { + net_if_up(argv[2]); + } + else if (argc == 2 && strcmp(argv[1], "printif") == 0) { + net_ifprint(0); + } + else if (argc == 3 && strcmp(argv[1], "printif") == 0 && strcmp(argv[2], "scan") == 0) { + net_ifprint(1); + } else if (argc == 7 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "veth") == 0) { // create veth pair and move one end in the the namespace net_create_veth(argv[3], argv[4], atoi(argv[6])); - // connect the ohter veth end to the bridge ... net_bridge_add_interface(argv[5], argv[3]); - // ... and bring it up net_if_up(argv[3]); } else if (argc == 6 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "macvlan") == 0) { net_create_macvlan(argv[3], argv[4], atoi(argv[5])); } + else if (argc == 7 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "interface") == 0) { + char *dev = argv[3]; + uint32_t ip = (uint32_t) atoll(argv[4]); + uint32_t mask = (uint32_t) atoll(argv[5]); + int mtu = atoi(argv[6]); + // configure interface + net_if_ip(dev, ip, mask, mtu); + // ... and bring it up + net_if_up(dev); + } + else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "mac") == 0) { + unsigned char mac[6]; + if (atomac(argv[4], mac)) { + fprintf(stderr, "Error fnet: invalid mac address %s\n", argv[4]); + } + net_if_mac(argv[3], mac); + } else if (argc == 4 && strcmp(argv[1], "moveif") == 0) { net_move_interface(argv[2], atoi(argv[3])); } + else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) { + net_if_ip6(argv[3], argv[4]); + } else { fprintf(stderr, "Error fnet: invalid arguments\n"); return 1; diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index 59d426a78..f53e2ef8b 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -9,15 +9,15 @@ static void usage(void) { } int main(int argc, char **argv) { -//#if 0 +#if 0 { -//system("cat /proc/self/status"); +system("cat /proc/self/status"); int i; for (i = 0; i < argc; i++) printf("*%s* ", argv[i]); printf("\n"); } -//#endif +#endif if (argc < 2) return 1; -- cgit v1.2.3-70-g09d2 From 72b93c5761b5e42c5742e192f46bac1696c36f4c Mon Sep 17 00:00:00 2001 From: netblue30 Date: Wed, 2 Nov 2016 07:49:01 -0400 Subject: major cleanup --- src/firejail/errno.c | 2 - src/firejail/firejail.h | 24 +- src/firejail/join.c | 6 +- src/firejail/preproc.c | 31 +- src/firejail/protocol.c | 46 --- src/firejail/sandbox.c | 33 +- src/firejail/seccomp.c | 873 +++++++-------------------------------- src/firejail/syscall.c | 105 ----- src/firejail/util.c | 4 +- src/fnet/Makefile.in | 4 +- src/fseccomp/Makefile.in | 4 +- src/fseccomp/errno.c | 43 ++ src/fseccomp/fseccomp.h | 50 +++ src/fseccomp/main.c | 49 +++ src/fseccomp/seccomp.c | 292 +++++++++++++ src/fseccomp/seccomp_file.c | 108 +++++ src/fseccomp/seccomp_print.c | 116 ++++++ src/fseccomp/seccomp_secondary.c | 183 ++++++++ src/fseccomp/syscall.c | 84 ++++ test/filters/filters.sh | 15 +- test/filters/fseccomp.exp | 138 +++++++ 21 files changed, 1281 insertions(+), 929 deletions(-) delete mode 100644 src/firejail/syscall.c create mode 100644 src/fseccomp/seccomp.c create mode 100644 src/fseccomp/seccomp_file.c create mode 100644 src/fseccomp/seccomp_print.c create mode 100644 src/fseccomp/seccomp_secondary.c create mode 100755 test/filters/fseccomp.exp (limited to 'src/fnet') diff --git a/src/firejail/errno.c b/src/firejail/errno.c index 03f10bb14..8215c99a1 100644 --- a/src/firejail/errno.c +++ b/src/firejail/errno.c @@ -17,7 +17,6 @@ * 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 @@ -205,5 +204,4 @@ char *errno_find_nr(int nr) { return "unknown"; } - #endif // HAVE_SECCOMP diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index dcec160fb..cf540ff91 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -25,7 +25,6 @@ // debug restricted shell //#define DEBUG_RESTRICTED_SHELL - // filesystem #define RUN_FIREJAIL_BASEDIR "/run" #define RUN_FIREJAIL_DIR "/run/firejail" @@ -38,8 +37,6 @@ #define RUN_RO_DIR "/run/firejail/firejail.ro.dir" #define RUN_RO_FILE "/run/firejail/firejail.ro.file" #define RUN_MNT_DIR "/run/firejail/mnt" // a tmpfs is mounted on this directory before any of the files below are created -#define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" -#define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" #define RUN_CGROUP_CFG "/run/firejail/mnt/cgroup" #define RUN_CPU_CFG "/run/firejail/mnt/cpu" #define RUN_GROUPS_CFG "/run/firejail/mnt/groups" @@ -50,6 +47,12 @@ #define RUN_BIN_DIR "/run/firejail/mnt/bin" #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" +#define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter +#define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter +#define RUN_SECCOMP_AMD64 "/run/firejail/mnt/seccomp.amd64" // amd64 filter installed on i386 architectures +#define RUN_SECCOMP_I386 "/run/firejail/mnt/seccomp.i386" // i386 filter installed on amd64 architectures + + #define RUN_DEV_DIR "/run/firejail/mnt/dev" #define RUN_DEVLOG_FILE "/run/firejail/mnt/devlog" @@ -76,6 +79,8 @@ #define RUN_GROUP_FILE "/run/firejail/mnt/group" #define RUN_FSLOGGER_FILE "/run/firejail/mnt/fslogger" + + // profiles #define DEFAULT_USER_PROFILE "default" #define DEFAULT_ROOT_PROFILE "server" @@ -491,12 +496,14 @@ void fs_private_home_list(void); // seccomp.c +int seccomp_load(const char *fname); +void seccomp_filter_32(void); +void seccomp_filter_64(void); int seccomp_filter_drop(int enforce_seccomp); int seccomp_filter_keep(void); -void seccomp_set(void); +int seccomp_filter_errno(void); void seccomp_print_filter_name(const char *name); void seccomp_print_filter(pid_t pid); -int seccomp_filter_errno(void); // caps.c int caps_default_filter(void); @@ -591,13 +598,10 @@ void fs_check_bin_list(void); void fs_private_bin_list(void); // protocol.c -void protocol_list(); -void protocol_print_filter_name(const char *name); -void protocol_print_filter(pid_t pid); -void protocol_store(const char *prlist); -void protocol_filter(const char *fname); void protocol_filter_save(void); void protocol_filter_load(const char *fname); +void protocol_print_filter_name(const char *name); +void protocol_print_filter(pid_t pid); // restrict_users.c void restrict_users(void); diff --git a/src/firejail/join.c b/src/firejail/join.c index 9b5fba24d..6f1e9455c 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c @@ -292,16 +292,16 @@ void join(pid_t pid, int argc, char **argv, int index) { if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP - // set protocol filter + // read cfg.protocol from file if (getuid() != 0) protocol_filter_load(RUN_PROTOCOL_CFG); if (cfg.protocol) { // not available for uid 0 - protocol_filter(RUN_SECCOMP_PROTOCOL); + seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter } // set seccomp filter if (apply_seccomp == 1) // not available for uid 0 - seccomp_set(); + seccomp_load(RUN_SECCOMP_CFG); #endif // fix qt 4.8 diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c index 27e06b556..2873571a9 100644 --- a/src/firejail/preproc.c +++ b/src/firejail/preproc.c @@ -66,8 +66,6 @@ void preproc_build_firejail_dir(void) { // build /run/firejail/mnt directory void preproc_mount_mnt_dir(void) { - struct stat s; - // mount tmpfs on top of /run/firejail/mnt if (!tmpfs_mounted) { if (arg_debug) @@ -76,6 +74,35 @@ void preproc_mount_mnt_dir(void) { errExit("mounting /run/firejail/mnt"); tmpfs_mounted = 1; fs_logger2("tmpfs", RUN_MNT_DIR); + + // create all seccomp files + // as root, create RUN_SECCOMP_I386 file + create_empty_file_as_root(RUN_SECCOMP_I386, 0644); + if (chown(RUN_SECCOMP_I386, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_I386, 0644) == -1) + errExit("chmod"); + + // as root, create RUN_SECCOMP_AMD64 file + create_empty_file_as_root(RUN_SECCOMP_AMD64, 0644); + if (chown(RUN_SECCOMP_AMD64, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_AMD64, 0644) == -1) + errExit("chmod"); + + // as root, create RUN_SECCOMP file + create_empty_file_as_root(RUN_SECCOMP_CFG, 0644); + if (chown(RUN_SECCOMP_CFG, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_CFG, 0644) == -1) + errExit("chmod"); + + // as root, create RUN_SECCOMP_PROTOCOL file + create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); + if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_PROTOCOL, 0644) == -1) + errExit("chmod"); } } diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index db6c6cad9..e8e88aee9 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c @@ -22,52 +22,6 @@ #include "firejail.h" #include "../include/seccomp.h" -// install protocol filter -void protocol_filter(const char *fname) { -#ifndef SYS_socket - if (arg_debug) - printf("No support for --protocol on this platform\n"); - return; -#else - assert(fname); - - // check file - struct stat s; - if (stat(fname, &s) == -1) { - fprintf(stderr, "Error: cannot read protocol filter file\n"); - exit(1); - } - int size = s.st_size; - - // read filter - struct sock_filter filter[32]; // big enough - memset(&filter[0], 0, sizeof(filter)); - int src = open(fname, O_RDONLY); - int rd = 0; - while (rd < size) { - int rv = read(src, (unsigned char *) filter + rd, size - rd); - if (rv == -1) { - fprintf(stderr, "Error: cannot read %s file\n", fname); - exit(1); - } - rd += rv; - } - close(src); - - // install filter - unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); - struct sock_fprog prog = { - .len = entries, - .filter = filter, - }; - - 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; - } -#endif -} - void protocol_filter_save(void) { // save protocol filter configuration in PROTOCOL_CFG FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index a15003d03..3942e4da6 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -559,13 +559,6 @@ assert(0); if (cfg.protocol) { if (arg_debug) printf("Build protocol filter: %s\n", cfg.protocol); - // as root, create RUN_SECCOMP_PROTOCOL file - // this is where fseccomp program will store the protocol filter - create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); - if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) - errExit("chown"); - if (chmod(RUN_SECCOMP_PROTOCOL, 0644) == -1) - errExit("chmod"); // build the seccomp filter as a regular user int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, @@ -826,13 +819,23 @@ assert(0); // set rlimits set_rlimits(); - // set seccomp + // set cpu affinity + if (cfg.cpus) { + save_cpu(); // save cpu affinity mask to CPU_CFG file + set_cpu_affinity(); + } + + // save cgroup in CGROUP_CFG file + if (cfg.cgroup) + save_cgroup(); + + // set seccomp //todo: push it down after drop_privs and/or configuring noroot #ifdef HAVE_SECCOMP // install protocol filter if (cfg.protocol) { if (arg_debug) printf("Install protocol filter: %s\n", cfg.protocol); - protocol_filter(RUN_SECCOMP_PROTOCOL); // install filter + seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG } @@ -847,16 +850,6 @@ assert(0); } #endif - // set cpu affinity - if (cfg.cpus) { - save_cpu(); // save cpu affinity mask to CPU_CFG file - set_cpu_affinity(); - } - - // save cgroup in CGROUP_CFG file - if (cfg.cgroup) - save_cgroup(); - //**************************************** // drop privileges or create a new user namespace //**************************************** @@ -929,8 +922,6 @@ assert(0); int status = monitor_application(app_pid); // monitor application flush_stdin(); - - if (WIFEXITED(status)) { // if we had a proper exit, return that exit status return WEXITSTATUS(status); diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 69be04a03..74d29fc9d 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -22,760 +22,203 @@ #include "firejail.h" #include "../include/seccomp.h" -#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 - }; +int seccomp_load(const char *fname) { + assert(fname); - // 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"); + // check file + struct stat s; + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: cannot read protocol filter file\n"); + exit(1); } - - // 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 == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { - int err = *(ptr + 13) << 8 | *(ptr + 12); - printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); - 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++; + int size = s.st_size; + unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); +//printf("size %d, entries %d\n", s.st_size, entries); + + // read filter + struct sock_filter filter[entries]; + memset(&filter[0], 0, sizeof(filter)); + int src = open(fname, O_RDONLY); + int rd = 0; + while (rd < size) { + int rv = read(src, (unsigned char *) filter + rd, size - rd); + if (rv == -1) { + fprintf(stderr, "Error: cannot read %s file\n", fname); + exit(1); } + rd += rv; } -} - -// 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 -#if defined(__x86_64__) -#define X32_SYSCALL_BIT 0x40000000 - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL, - // handle X32 ABI - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), - BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), - RETURN_ERRNO(EPERM) - }; -#else - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL - }; -#endif - 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, int arg) { - (void) arg; - 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); -} + close(src); -static void filter_add_blacklist(int syscall, int arg) { - (void) arg; - 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_add_errno(int syscall, int arg) { - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); -// if (arg_debug) -// printf("Errno syscall %d %d %s\n", syscall, arg, syscall_find_nr(syscall)); - - if ((sfilter_index + 2) > sfilter_alloc_size) - filter_realloc(); - - struct sock_filter filter[] = { - BLACKLIST_ERRNO(syscall, arg) - }; -#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 + // install filter + struct sock_fprog prog = { + .len = entries, + .filter = filter, }; -#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 /run/firejail/mnt/seccomp -static void write_seccomp_file(void) { - assert(sfilter); - - int fd = open(RUN_SECCOMP_CFG, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); - if (fd == -1) - errExit("open"); - if (arg_debug) - printf("Save seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); - errno = 0; - ssize_t sz = write(fd, sfilter, sfilter_index * sizeof(struct sock_filter)); - if (sz != (ssize_t)(sfilter_index * sizeof(struct sock_filter))) { - fprintf(stderr, "Error: cannot save seccomp filter\n"); - exit(1); + 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; } - SET_PERMS_FD(fd, 0, 0, S_IRUSR | S_IWUSR); - close(fd); + + return 0; } -// read seccomp filter from /run/firejail/mnt/seccomp -static void read_seccomp_file(const char *fname) { - assert(sfilter == NULL && sfilter_index == 0); - // check file - struct stat s; - if (stat(fname, &s) == -1) { - fprintf(stderr, "Warning: seccomp file not found\n"); - return; - } - 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 %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); - - close(fd); - - if (arg_debug) - filter_debug(); -} // i386 filter installed on amd64 architectures void seccomp_filter_32(void) { - // hardcoded syscall values - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE_32, - EXAMINE_SYSCALL, - BLACKLIST(21), // mount - BLACKLIST(52), // umount2 -// todo: implement --allow-debuggers - BLACKLIST(26), // ptrace - BLACKLIST(283), // kexec_load - BLACKLIST(341), // name_to_handle_at - BLACKLIST(342), // open_by_handle_at - BLACKLIST(127), // create_module - BLACKLIST(128), // init_module - BLACKLIST(350), // finit_module - BLACKLIST(129), // delete_module - BLACKLIST(110), // iopl - BLACKLIST(101), // ioperm - BLACKLIST(289), // ioprio_set - BLACKLIST(87), // swapon - BLACKLIST(115), // swapoff - BLACKLIST(103), // syslog - BLACKLIST(347), // process_vm_readv - BLACKLIST(348), // process_vm_writev - BLACKLIST(135), // sysfs - BLACKLIST(149), // _sysctl - BLACKLIST(124), // adjtimex - BLACKLIST(343), // clock_adjtime - BLACKLIST(253), // lookup_dcookie - BLACKLIST(336), // perf_event_open - BLACKLIST(338), // fanotify_init - BLACKLIST(349), // kcmp - BLACKLIST(286), // add_key - BLACKLIST(287), // request_key - BLACKLIST(288), // keyctl - BLACKLIST(86), // uselib - BLACKLIST(51), // acct - BLACKLIST(123), // modify_ldt - BLACKLIST(217), // pivot_root - BLACKLIST(245), // io_setup - BLACKLIST(246), // io_destroy - BLACKLIST(247), // io_getevents - BLACKLIST(248), // io_submit - BLACKLIST(249), // io_cancel - BLACKLIST(257), // remap_file_pages - BLACKLIST(274), // mbind - BLACKLIST(275), // get_mempolicy - BLACKLIST(276), // set_mempolicy - BLACKLIST(294), // migrate_pages - BLACKLIST(317), // move_pages - BLACKLIST(316), // vmsplice - BLACKLIST(61), // chroot - BLACKLIST(88), // reboot - BLACKLIST(169), // nfsservctl - BLACKLIST(130), // get_kernel_syms - - RETURN_ALLOW - }; + if (arg_debug) + printf("Build secondary 32-bit filter\n"); - struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), - .filter = filter, - }; + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "secondary", "32", RUN_SECCOMP_I386); + if (rv) + exit(rv); - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - ; - } - else if (arg_debug) { - printf("Dual i386/amd64 seccomp filter configured\n"); + if (seccomp_load(RUN_SECCOMP_I386) == 0) { + if (arg_debug) + printf("Dual i386/amd64 seccomp filter configured\n"); } } // amd64 filter installed on i386 architectures void seccomp_filter_64(void) { - // hardcoded syscall values - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE_64, - EXAMINE_SYSCALL, - BLACKLIST(165), // mount - BLACKLIST(166), // umount2 -// todo: implement --allow-debuggers - BLACKLIST(101), // ptrace - BLACKLIST(246), // kexec_load - BLACKLIST(304), // open_by_handle_at - BLACKLIST(303), // name_to_handle_at - BLACKLIST(174), // create_module - BLACKLIST(175), // init_module - BLACKLIST(313), // finit_module - BLACKLIST(176), // delete_module - BLACKLIST(172), // iopl - BLACKLIST(173), // ioperm - BLACKLIST(251), // ioprio_set - BLACKLIST(167), // swapon - BLACKLIST(168), // swapoff - BLACKLIST(103), // syslog - BLACKLIST(310), // process_vm_readv - BLACKLIST(311), // process_vm_writev - BLACKLIST(139), // sysfs - BLACKLIST(156), // _sysctl - BLACKLIST(159), // adjtimex - BLACKLIST(305), // clock_adjtime - BLACKLIST(212), // lookup_dcookie - BLACKLIST(298), // perf_event_open - BLACKLIST(300), // fanotify_init - BLACKLIST(312), // kcmp - BLACKLIST(248), // add_key - BLACKLIST(249), // request_key - BLACKLIST(250), // keyctl - BLACKLIST(134), // uselib - BLACKLIST(163), // acct - BLACKLIST(154), // modify_ldt - BLACKLIST(155), // pivot_root - BLACKLIST(206), // io_setup - BLACKLIST(207), // io_destroy - BLACKLIST(208), // io_getevents - BLACKLIST(209), // io_submit - BLACKLIST(210), // io_cancel - BLACKLIST(216), // remap_file_pages - BLACKLIST(237), // mbind - BLACKLIST(239), // get_mempolicy - BLACKLIST(238), // set_mempolicy - BLACKLIST(256), // migrate_pages - BLACKLIST(279), // move_pages - BLACKLIST(278), // vmsplice - BLACKLIST(161), // chroot - BLACKLIST(184), // tuxcall - BLACKLIST(169), // reboot - BLACKLIST(180), // nfsservctl - BLACKLIST(177), // get_kernel_syms - - RETURN_ALLOW - }; + if (arg_debug) + printf("Build secondary 64-bit filter\n"); - struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), - .filter = filter, - }; + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "secondary", "64", RUN_SECCOMP_AMD64); + if (rv) + exit(rv); - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - ; - } - else if (arg_debug) { - printf("Dual i386/amd64 seccomp filter configured\n"); + if (seccomp_load(RUN_SECCOMP_AMD64) == 0) { + if (arg_debug) + printf("Dual i386/amd64 seccomp filter configured\n"); } } // drop filter for seccomp option int seccomp_filter_drop(int enforce_seccomp) { - filter_init(); - // default seccomp - if (cfg.seccomp_list_drop == NULL) { + if (cfg.seccomp_list_drop == NULL && cfg.seccomp_list == NULL) { #if defined(__x86_64__) seccomp_filter_32(); #endif #if defined(__i386__) seccomp_filter_64(); #endif - -#ifdef SYS_mount - filter_add_blacklist(SYS_mount, 0); -#endif -#ifdef SYS_umount2 - filter_add_blacklist(SYS_umount2, 0); -#endif - - if (!arg_allow_debuggers) { -#ifdef SYS_ptrace - filter_add_blacklist(SYS_ptrace, 0); -#endif - } - -#ifdef SYS_kexec_load - filter_add_blacklist(SYS_kexec_load, 0); -#endif -#ifdef SYS_kexec_file_load - filter_add_blacklist(SYS_kexec_file_load, 0); -#endif -#ifdef SYS_open_by_handle_at - filter_add_blacklist(SYS_open_by_handle_at, 0); -#endif -#ifdef SYS_name_to_handle_at - filter_add_blacklist(SYS_name_to_handle_at, 0); -#endif -#ifdef SYS_init_module - filter_add_blacklist(SYS_init_module, 0); -#endif -#ifdef SYS_finit_module // introduced in 2013 - filter_add_blacklist(SYS_finit_module, 0); -#endif -#ifdef SYS_create_module - filter_add_blacklist(SYS_create_module, 0); -#endif -#ifdef SYS_delete_module - filter_add_blacklist(SYS_delete_module, 0); -#endif -#ifdef SYS_iopl - filter_add_blacklist(SYS_iopl, 0); -#endif -#ifdef SYS_ioperm - filter_add_blacklist(SYS_ioperm, 0); -#endif -#ifdef SYS_ioprio_set - filter_add_blacklist(SYS_ioprio_set, 0); -#endif -#ifdef SYS_ni_syscall // new io permissions call on arm devices - filter_add_blacklist(SYS_ni_syscall, 0); -#endif -#ifdef SYS_swapon - filter_add_blacklist(SYS_swapon, 0); -#endif -#ifdef SYS_swapoff - filter_add_blacklist(SYS_swapoff, 0); -#endif -#ifdef SYS_syslog - filter_add_blacklist(SYS_syslog, 0); -#endif - if (!arg_allow_debuggers) { -#ifdef SYS_process_vm_readv - filter_add_blacklist(SYS_process_vm_readv, 0); -#endif - } - -#ifdef SYS_process_vm_writev - filter_add_blacklist(SYS_process_vm_writev, 0); -#endif - -// mknod removed in 0.9.29 - it brakes Zotero extension -//#ifdef SYS_mknod -// filter_add_blacklist(SYS_mknod, 0); -//#endif - - // new syscalls in 0.9,23 -#ifdef SYS_sysfs - filter_add_blacklist(SYS_sysfs, 0); -#endif -#ifdef SYS__sysctl - filter_add_blacklist(SYS__sysctl, 0); -#endif -#ifdef SYS_adjtimex - filter_add_blacklist(SYS_adjtimex, 0); -#endif -#ifdef SYS_clock_adjtime - filter_add_blacklist(SYS_clock_adjtime, 0); -#endif -#ifdef SYS_lookup_dcookie - filter_add_blacklist(SYS_lookup_dcookie, 0); -#endif -#ifdef SYS_perf_event_open - filter_add_blacklist(SYS_perf_event_open, 0); -#endif -#ifdef SYS_fanotify_init - filter_add_blacklist(SYS_fanotify_init, 0); -#endif -#ifdef SYS_kcmp - filter_add_blacklist(SYS_kcmp, 0); -#endif - -// 0.9.32 -#ifdef SYS_add_key - filter_add_blacklist(SYS_add_key, 0); -#endif -#ifdef SYS_request_key - filter_add_blacklist(SYS_request_key, 0); -#endif -#ifdef SYS_keyctl - filter_add_blacklist(SYS_keyctl, 0); -#endif -#ifdef SYS_uselib - filter_add_blacklist(SYS_uselib, 0); -#endif -#ifdef SYS_acct - filter_add_blacklist(SYS_acct, 0); -#endif -#ifdef SYS_modify_ldt - filter_add_blacklist(SYS_modify_ldt, 0); -#endif - //#ifdef SYS_unshare - // filter_add_blacklist(SYS_unshare, 0); - //#endif -#ifdef SYS_pivot_root - filter_add_blacklist(SYS_pivot_root, 0); -#endif - //#ifdef SYS_quotactl - // filter_add_blacklist(SYS_quotactl, 0); - //#endif -#ifdef SYS_io_setup - filter_add_blacklist(SYS_io_setup, 0); -#endif -#ifdef SYS_io_destroy - filter_add_blacklist(SYS_io_destroy, 0); -#endif -#ifdef SYS_io_getevents - filter_add_blacklist(SYS_io_getevents, 0); -#endif -#ifdef SYS_io_submit - filter_add_blacklist(SYS_io_submit, 0); -#endif -#ifdef SYS_io_cancel - filter_add_blacklist(SYS_io_cancel, 0); -#endif -#ifdef SYS_remap_file_pages - filter_add_blacklist(SYS_remap_file_pages, 0); -#endif -#ifdef SYS_mbind - filter_add_blacklist(SYS_mbind, 0); -#endif -#ifdef SYS_get_mempolicy - filter_add_blacklist(SYS_get_mempolicy, 0); -#endif -#ifdef SYS_set_mempolicy - filter_add_blacklist(SYS_set_mempolicy, 0); -#endif -#ifdef SYS_migrate_pages - filter_add_blacklist(SYS_migrate_pages, 0); -#endif -#ifdef SYS_move_pages - filter_add_blacklist(SYS_move_pages, 0); -#endif -#ifdef SYS_vmsplice - filter_add_blacklist(SYS_vmsplice, 0); -#endif -#ifdef SYS_chroot - filter_add_blacklist(SYS_chroot, 0); -#endif - //#ifdef SYS_set_robust_list - // filter_add_blacklist(SYS_set_robust_list, 0); - //#endif - //#ifdef SYS_get_robust_list - // filter_add_blacklist(SYS_get_robust_list, 0); - //#endif - - // CHECK_SECCOMP(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone), 1, - // SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER))); - -// 0.9.39 -#ifdef SYS_tuxcall - filter_add_blacklist(SYS_tuxcall, 0); -#endif -#ifdef SYS_reboot - filter_add_blacklist(SYS_reboot, 0); -#endif -#ifdef SYS_nfsservctl - filter_add_blacklist(SYS_nfsservctl, 0); -#endif -#ifdef SYS_get_kernel_syms - filter_add_blacklist(SYS_get_kernel_syms, 0); -#endif - + if (arg_debug) + printf("Build default seccomp filter\n"); + // build the seccomp filter as a regular user + int rv; + if (arg_allow_debuggers) + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "default", RUN_SECCOMP_CFG, "allow-debuggers"); + else + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, + PATH_FSECCOMP, "default", RUN_SECCOMP_CFG); + if (rv) + exit(rv); } // default seccomp filter with additional drop list - if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { - if (syscall_check_list(cfg.seccomp_list, filter_add_blacklist, 0)) { - fprintf(stderr, "Error: cannot load seccomp filter\n"); + else if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { +#if defined(__x86_64__) + seccomp_filter_32(); +#endif +#if defined(__i386__) + seccomp_filter_64(); +#endif + if (arg_debug) + printf("Build default+drop seccomp filter\n"); + if (strlen(cfg.seccomp_list) == 0) { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); exit(1); } + + // build the seccomp filter as a regular user + int rv; + if (arg_allow_debuggers) + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6, + PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list, "allow-debuggers"); + else + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, + PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list); + if (rv) + exit(rv); } - // drop list + + // drop list without defaults - secondary filters are not installed else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { - if (syscall_check_list(cfg.seccomp_list_drop, filter_add_blacklist, 0)) { - fprintf(stderr, "Error: cannot load seccomp filter\n"); + if (arg_debug) + printf("Build drop seccomp filter\n"); + if (strlen(cfg.seccomp_list_drop) == 0) { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); exit(1); } - } - - - filter_end_blacklist(); - if (arg_debug) - filter_debug(); - - // save seccomp filter in /run/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)) { - if (enforce_seccomp) { - fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); - exit(1); - } + // build the seccomp filter as a regular user + int rv; + if (arg_allow_debuggers) + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, + PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop, "allow-debuggers"); else - fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop); - return 1; + if (rv) + exit(rv); + } + else { + assert(0); } - return 0; + // load the filter + if (seccomp_load(RUN_SECCOMP_CFG) == 0) { + if (arg_debug) + printf("seccomp filter configured\n"); + } + else if (enforce_seccomp) { + fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); + exit(1); + } + + if (arg_debug) + sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, + PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); + + return seccomp_load(RUN_SECCOMP_CFG); } // 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, 0); - filter_add_whitelist(SYS_setgid, 0); - filter_add_whitelist(SYS_setgroups, 0); - filter_add_whitelist(SYS_dup, 0); - - // apply keep list - if (cfg.seccomp_list_keep) { - if (syscall_check_list(cfg.seccomp_list_keep, filter_add_whitelist, 0)) { - fprintf(stderr, "Error: cannot load seccomp filter\n"); - exit(1); - } + if (arg_debug) + printf("Build drop seccomp filter\n"); + if (strlen(cfg.seccomp_list_keep) == 0) { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); + exit(1); } - filter_end_whitelist(); + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, cfg.seccomp_list_keep); + if (rv) + exit(rv); if (arg_debug) - filter_debug(); - - // save seccomp filter in /run/firejail/mnt/seccomp - // in order to use it in --join operations - write_seccomp_file(); - + printf("seccomp filter configured\n"); - 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; + + return seccomp_load(RUN_SECCOMP_CFG); } // errno filter for seccomp option int seccomp_filter_errno(void) { +#if 0 //todo: disabled temporarely, bring it back int i; int higest_errno = errno_highest_nr(); filter_init(); @@ -798,42 +241,11 @@ int seccomp_filter_errno(void) { // save seccomp filter in /run/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 seccomp_load(RUN_SECCOMP_CFG); +#else +printf("*** --seccomp. is temporarily disabled, it will be brought back soon ***\n"); return 0; -} - - - -void seccomp_set(void) { - // read seccomp filter from /runp/firejail/mnt/seccomp - read_seccomp_file(RUN_SECCOMP_CFG); - - // 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"); - } +#endif } void seccomp_print_filter_name(const char *name) { @@ -890,10 +302,11 @@ void seccomp_print_filter(pid_t pid) { exit(1); } - // read and print the filter - read_seccomp_file(fname); - drop_privs(1); - filter_debug(); + // read and print the filter - run this as root, the user doesn't have access + int rv = sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, + PATH_FSECCOMP, "print", fname); + if (rv) + exit(rv); free(fname); exit(0); diff --git a/src/firejail/syscall.c b/src/firejail/syscall.c deleted file mode 100644 index f405f23c8..000000000 --- a/src/firejail/syscall.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2014-2016 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. -*/ - -#ifdef HAVE_SECCOMP -#include "firejail.h" -#include - -typedef struct { - char *name; - int nr; -} SyscallEntry; - -static SyscallEntry syslist[] = { -// -// code generated using tools/extract-syscall -// -#include "../include/syscall.h" -// -// 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 syscall, int arg), int arg) { - // 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, arg); - - 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, arg); - } - - free(str); - return 0; -} - -#endif // HAVE_SECCOMP diff --git a/src/firejail/util.c b/src/firejail/util.c index 8d3b9d3cd..9752504e5 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -721,13 +721,13 @@ void create_empty_file_as_root(const char *fname, mode_t mode) { if (arg_debug) printf("Creating empty %s file\n", fname); - /* coverity[toctou] */ FILE *fp = fopen(fname, "w"); if (!fp) errExit("fopen"); - SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); fclose(fp); + if (chmod(fname, mode) == -1) + errExit("chmod"); } } diff --git a/src/fnet/Makefile.in b/src/fnet/Makefile.in index 1bfb4c68d..b515d2333 100644 --- a/src/fnet/Makefile.in +++ b/src/fnet/Makefile.in @@ -30,11 +30,11 @@ BINOBJS = $(foreach file, $(OBJS), $file) CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -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) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/libnetlink.h $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ fnet: $(OBJS) ../lib/libnetlink.o ../lib/common.o - $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o $(LIBS) $(EXTRA_LDFLAGS) clean:; rm -f *.o fnet diff --git a/src/fseccomp/Makefile.in b/src/fseccomp/Makefile.in index e7edd1b8f..110d2c95f 100644 --- a/src/fseccomp/Makefile.in +++ b/src/fseccomp/Makefile.in @@ -30,11 +30,11 @@ BINOBJS = $(foreach file, $(OBJS), $file) CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -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) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h ../include/syscall.h +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ fseccomp: $(OBJS) ../lib/libnetlink.o ../lib/common.o - $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) clean:; rm -f *.o fseccomp diff --git a/src/fseccomp/errno.c b/src/fseccomp/errno.c index 625f484bd..dbee916d4 100644 --- a/src/fseccomp/errno.c +++ b/src/fseccomp/errno.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" #include @@ -151,6 +170,30 @@ static ErrnoEntry errnolist[] = { #endif }; +int errno_find_name(const char *name) { + int i; + int elems = sizeof(errnolist) / sizeof(errnolist[0]); + for (i = 0; i < elems; i++) { + if (strcasecmp(name, errnolist[i].name) == 0) + return errnolist[i].nr; + } + + return -1; +} + +char *errno_find_nr(int nr) { + int i; + int elems = sizeof(errnolist) / sizeof(errnolist[0]); + for (i = 0; i < elems; i++) { + if (nr == errnolist[i].nr) + return errnolist[i].name; + } + + return "unknown"; +} + + + void errno_print(void) { int i; int elems = sizeof(errnolist) / sizeof(errnolist[0]); diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h index 57757ea6c..504f1c23f 100644 --- a/src/fseccomp/fseccomp.h +++ b/src/fseccomp/fseccomp.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 FSECCOMP_H #define FSECCOMP_H #include @@ -8,11 +27,42 @@ // syscall.c void syscall_print(void); +int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg); +int syscall_find_name(const char *name); +char *syscall_find_nr(int nr); // errno.c void errno_print(void); +int errno_find_name(const char *name); +char *errno_find_nr(int nr); // protocol.c void protocol_print(void); void protocol_build_filter(const char *prlist, const char *fname); + +// seccomp_secondary.c +void seccomp_secondary_64(const char *fname); +void seccomp_secondary_32(const char *fname); + +// seccomp_file.c +void filter_init(int fd); +void filter_add_whitelist(int fd, int syscall, int arg); +void filter_add_blacklist(int fd, int syscall, int arg); +void filter_add_errno(int fd, int syscall, int arg); +void filter_end_blacklist(int fd); +void filter_end_whitelist(int fd); + +// seccomp.c +// default list +void seccomp_default(const char *fname, int allow_debuggers); +// drop list +void seccomp_drop(const char *fname, char *list, int allow_debuggers); +// default+drop list +void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); +// whitelisted filter +void seccomp_keep(const char *fname, char *list); + +// seccomp_print +void filter_print(const char *fname); + #endif diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index f53e2ef8b..22b13bcd9 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" static void usage(void) { @@ -6,6 +25,16 @@ static void usage(void) { printf("\tfseccomp debug-errnos\n"); printf("\tfseccomp debug-protocols\n"); printf("\tfseccomp protocol build list file\n"); + printf("\tfseccomp secondary 64 file\n"); + printf("\tfseccomp secondary 32 file\n"); + printf("\tfseccomp default file\n"); + printf("\tfseccomp default file allow-debuggers\n"); + printf("\tfseccomp drop file list\n"); + printf("\tfseccomp drop file list allow-debuggers\n"); + printf("\tfseccomp default drop file list\n"); + printf("\tfseccomp default drop file list allow-debuggers\n"); + printf("\tfseccomp keep file list\n"); + printf("\tfseccomp print file\n"); } int main(int argc, char **argv) { @@ -33,6 +62,26 @@ printf("\n"); protocol_print(); else if (argc == 5 && strcmp(argv[1], "protocol") == 0 && strcmp(argv[2], "build") == 0) protocol_build_filter(argv[3], argv[4]); + else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "64") == 0) + seccomp_secondary_64(argv[3]); + else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "32") == 0) + seccomp_secondary_32(argv[3]); + else if (argc == 3 && strcmp(argv[1], "default") == 0) + seccomp_default(argv[2], 0); + else if (argc == 4 && strcmp(argv[1], "default") == 0 && strcmp(argv[3], "allow-debuggers") == 0) + seccomp_default(argv[2], 1); + else if (argc == 4 && strcmp(argv[1], "drop") == 0) + seccomp_drop(argv[2], argv[3], 0); + else if (argc == 5 && strcmp(argv[1], "drop") == 0 && strcmp(argv[4], "allow-debuggers") == 0) + seccomp_drop(argv[2], argv[3], 1); + else if (argc == 5 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0) + seccomp_default_drop(argv[3], argv[4], 0); + else if (argc == 6 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0 && strcmp(argv[5], "allow-debuggers") == 0) + seccomp_default_drop(argv[3], argv[4], 1); + else if (argc == 4 && strcmp(argv[1], "keep") == 0) + seccomp_keep(argv[2], argv[3]); + else if (argc == 3 && strcmp(argv[1], "print") == 0) + filter_print(argv[2]); else { fprintf(stderr, "Error fseccomp: invalid arguments\n"); return 1; diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c new file mode 100644 index 000000000..cc6edc8ca --- /dev/null +++ b/src/fseccomp/seccomp.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +static void add_default_list(int fd, int allow_debuggers) { +#ifdef SYS_mount + filter_add_blacklist(fd, SYS_mount, 0); +#endif +#ifdef SYS_umount2 + filter_add_blacklist(fd, SYS_umount2, 0); +#endif + + if (!allow_debuggers) { +#ifdef SYS_ptrace + filter_add_blacklist(fd, SYS_ptrace, 0); +#endif + } + +#ifdef SYS_kexec_load + filter_add_blacklist(fd, SYS_kexec_load, 0); +#endif +#ifdef SYS_kexec_file_load + filter_add_blacklist(fd, SYS_kexec_file_load, 0); +#endif +#ifdef SYS_open_by_handle_at + filter_add_blacklist(fd, SYS_open_by_handle_at, 0); +#endif +#ifdef SYS_name_to_handle_at + filter_add_blacklist(fd, SYS_name_to_handle_at, 0); +#endif +#ifdef SYS_init_module + filter_add_blacklist(fd, SYS_init_module, 0); +#endif +#ifdef SYS_finit_module + filter_add_blacklist(fd, SYS_finit_module, 0); +#endif +#ifdef SYS_create_module + filter_add_blacklist(fd, SYS_create_module, 0); +#endif +#ifdef SYS_delete_module + filter_add_blacklist(fd, SYS_delete_module, 0); +#endif +#ifdef SYS_iopl + filter_add_blacklist(fd, SYS_iopl, 0); +#endif +#ifdef SYS_ioperm + filter_add_blacklist(fd, SYS_ioperm, 0); +#endif +#ifdef SYS_ioprio_set + filter_add_blacklist(fd, SYS_ioprio_set, 0); +#endif +#ifdef SYS_ni_syscall + filter_add_blacklist(fd, SYS_ni_syscall, 0); +#endif +#ifdef SYS_swapon + filter_add_blacklist(fd, SYS_swapon, 0); +#endif +#ifdef SYS_swapoff + filter_add_blacklist(fd, SYS_swapoff, 0); +#endif +#ifdef SYS_syslog + filter_add_blacklist(fd, SYS_syslog, 0); +#endif + + if (!allow_debuggers) { +#ifdef SYS_process_vm_readv + filter_add_blacklist(fd, SYS_process_vm_readv, 0); +#endif + } + +#ifdef SYS_process_vm_writev + filter_add_blacklist(fd, SYS_process_vm_writev, 0); +#endif + + // mknod removed in 0.9.29 - it brakes Zotero extension + //#ifdef SYS_mknod + // filter_add_blacklist(SYS_mknod, 0); + //#endif + +#ifdef SYS_sysfs + filter_add_blacklist(fd, SYS_sysfs, 0); +#endif +#ifdef SYS__sysctl + filter_add_blacklist(fd, SYS__sysctl, 0); +#endif +#ifdef SYS_adjtimex + filter_add_blacklist(fd, SYS_adjtimex, 0); +#endif +#ifdef SYS_clock_adjtime + filter_add_blacklist(fd, SYS_clock_adjtime, 0); +#endif +#ifdef SYS_lookup_dcookie + filter_add_blacklist(fd, SYS_lookup_dcookie, 0); +#endif +#ifdef SYS_perf_event_open + filter_add_blacklist(fd, SYS_perf_event_open, 0); +#endif +#ifdef SYS_fanotify_init + filter_add_blacklist(fd, SYS_fanotify_init, 0); +#endif +#ifdef SYS_kcmp + filter_add_blacklist(fd, SYS_kcmp, 0); +#endif +#ifdef SYS_add_key + filter_add_blacklist(fd, SYS_add_key, 0); +#endif +#ifdef SYS_request_key + filter_add_blacklist(fd, SYS_request_key, 0); +#endif +#ifdef SYS_keyctl + filter_add_blacklist(fd, SYS_keyctl, 0); +#endif +#ifdef SYS_uselib + filter_add_blacklist(fd, SYS_uselib, 0); +#endif +#ifdef SYS_acct + filter_add_blacklist(fd, SYS_acct, 0); +#endif +#ifdef SYS_modify_ldt + filter_add_blacklist(fd, SYS_modify_ldt, 0); +#endif +#ifdef SYS_pivot_root + filter_add_blacklist(fd, SYS_pivot_root, 0); +#endif +#ifdef SYS_io_setup + filter_add_blacklist(fd, SYS_io_setup, 0); +#endif +#ifdef SYS_io_destroy + filter_add_blacklist(fd, SYS_io_destroy, 0); +#endif +#ifdef SYS_io_getevents + filter_add_blacklist(fd, SYS_io_getevents, 0); +#endif +#ifdef SYS_io_submit + filter_add_blacklist(fd, SYS_io_submit, 0); +#endif +#ifdef SYS_io_cancel + filter_add_blacklist(fd, SYS_io_cancel, 0); +#endif +#ifdef SYS_remap_file_pages + filter_add_blacklist(fd, SYS_remap_file_pages, 0); +#endif +#ifdef SYS_mbind + filter_add_blacklist(fd, SYS_mbind, 0); +#endif +#ifdef SYS_get_mempolicy + filter_add_blacklist(fd, SYS_get_mempolicy, 0); +#endif +#ifdef SYS_set_mempolicy + filter_add_blacklist(fd, SYS_set_mempolicy, 0); +#endif +#ifdef SYS_migrate_pages + filter_add_blacklist(fd, SYS_migrate_pages, 0); +#endif +#ifdef SYS_move_pages + filter_add_blacklist(fd, SYS_move_pages, 0); +#endif +#ifdef SYS_vmsplice + filter_add_blacklist(fd, SYS_vmsplice, 0); +#endif +#ifdef SYS_chroot + filter_add_blacklist(fd, SYS_chroot, 0); +#endif +#ifdef SYS_tuxcall + filter_add_blacklist(fd, SYS_tuxcall, 0); +#endif +#ifdef SYS_reboot + filter_add_blacklist(fd, SYS_reboot, 0); +#endif +#ifdef SYS_nfsservctl + filter_add_blacklist(fd, SYS_nfsservctl, 0); +#endif +#ifdef SYS_get_kernel_syms + filter_add_blacklist(fd, SYS_get_kernel_syms, 0); +#endif +} + +// default list +void seccomp_default(const char *fname, int allow_debuggers) { + assert(fname); + + // open file + int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + // build filter + filter_init(fd); + add_default_list(fd, allow_debuggers); + filter_end_blacklist(fd); + + // close file + close(fd); +} + +// drop list +void seccomp_drop(const char *fname, char *list, int allow_debuggers) { + assert(fname); + (void) allow_debuggers; // todo: to implemnet it + + // open file + int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + // build filter + filter_init(fd); + if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { + fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); + exit(1); + } + filter_end_blacklist(fd); + + // close file + close(fd); +} + +// default+drop +void seccomp_default_drop(const char *fname, char *list, int allow_debuggers) { + assert(fname); + + // open file + int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + // build filter + filter_init(fd); + add_default_list(fd, allow_debuggers); + if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { + fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); + exit(1); + } + filter_end_blacklist(fd); + + // close file + close(fd); +} + +void seccomp_keep(const char *fname, char *list) { + // open file + int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + // build filter + filter_init(fd); + // these 4 syscalls are used by firejail after the seccomp filter is initialized + filter_add_whitelist(fd, SYS_setuid, 0); + filter_add_whitelist(fd, SYS_setgid, 0); + filter_add_whitelist(fd, SYS_setgroups, 0); + filter_add_whitelist(fd, SYS_dup, 0); + filter_add_whitelist(fd, SYS_prctl, 0); + + if (syscall_check_list(list, filter_add_whitelist, fd, 0)) { + fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); + exit(1); + } + + filter_end_whitelist(fd); + + // close file + close(fd); +} + diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c new file mode 100644 index 000000000..10ef9dd31 --- /dev/null +++ b/src/fseccomp/seccomp_file.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +static void write_to_file(int fd, void *data, int size) { + assert(data); + assert(size); + + int written = 0; + while (written < size) { + int rv = write(fd, (unsigned char *) data + written, size - written); + if (rv == -1) { + fprintf(stderr, "Error fseccomp: cannot write seccomp file\n"); + exit(1); + } + written += rv; + } +} + +void filter_init(int fd) { +#if defined(__x86_64__) +#define X32_SYSCALL_BIT 0x40000000 + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL, + // handle X32 ABI + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), + RETURN_ERRNO(EPERM) + }; +#else + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL + }; +#endif + +#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 + + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_add_whitelist(int fd, int syscall, int arg) { + (void) arg; + + struct sock_filter filter[] = { + WHITELIST(syscall) + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_add_blacklist(int fd, int syscall, int arg) { + (void) arg; + + struct sock_filter filter[] = { + BLACKLIST(syscall) + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_add_errno(int fd, int syscall, int arg) { + struct sock_filter filter[] = { + BLACKLIST_ERRNO(syscall, arg) + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_end_blacklist(int fd) { + struct sock_filter filter[] = { + RETURN_ALLOW + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_end_whitelist(int fd) { + struct sock_filter filter[] = { + KILL_PROCESS + }; + write_to_file(fd, filter, sizeof(filter)); +} + diff --git a/src/fseccomp/seccomp_print.c b/src/fseccomp/seccomp_print.c new file mode 100644 index 000000000..7dc983b12 --- /dev/null +++ b/src/fseccomp/seccomp_print.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +static struct sock_filter *filter = NULL; +static int filter_cnt = 0; + +static void load_seccomp(const char *fname) { + assert(fname); + + // check file + struct stat s; + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error fseccomp: cannot read protocol filter file\n"); + exit(1); + } + int size = s.st_size; + unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); + filter_cnt = entries; +//printf("size %d, entries %d\n", s.st_size, entries); + + filter = malloc(sizeof(struct sock_filter) * entries); + if (!filter) + errExit("malloc"); + + // read filter + memset(filter, 0, sizeof(struct sock_filter) * entries); + int src = open(fname, O_RDONLY); + int rd = 0; + while (rd < size) { + int rv = read(src, (unsigned char *) filter + rd, size - rd); + if (rv == -1) { + fprintf(stderr, "Error fseccomp: cannot read %s file\n", fname); + exit(1); + } + rd += rv; + } + close(src); +} + +// debug filter +void filter_print(const char *fname) { + assert(fname); + load_seccomp(fname); + + // start filter + struct sock_filter start[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL + }; + + // print sizes + printf("SECCOMP Filter:\n"); + + // test the start of the filter + if (memcmp(&start[0], filter, sizeof(start)) == 0) { + printf(" VALIDATE_ARCHITECTURE\n"); + printf(" EXAMINE_SYSCAL\n"); + } + else { + printf("Invalid seccomp filter %s\n", fname); + return; + } + + // loop trough blacklists + int i = 4; + while (i < filter_cnt) { + // minimal parsing! + unsigned char *ptr = (unsigned char *) &filter[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 == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { + int err = *(ptr + 13) << 8 | *(ptr + 12); + printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); + 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++; + } + } +} diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c new file mode 100644 index 000000000..a856e5aef --- /dev/null +++ b/src/fseccomp/seccomp_secondary.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +void seccomp_secondary_64(const char *fname) { + // hardcoded syscall values + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE_64, + EXAMINE_SYSCALL, + BLACKLIST(165), // mount + BLACKLIST(166), // umount2 +// todo: implement --allow-debuggers + BLACKLIST(101), // ptrace + BLACKLIST(246), // kexec_load + BLACKLIST(304), // open_by_handle_at + BLACKLIST(303), // name_to_handle_at + BLACKLIST(174), // create_module + BLACKLIST(175), // init_module + BLACKLIST(313), // finit_module + BLACKLIST(176), // delete_module + BLACKLIST(172), // iopl + BLACKLIST(173), // ioperm + BLACKLIST(251), // ioprio_set + BLACKLIST(167), // swapon + BLACKLIST(168), // swapoff + BLACKLIST(103), // syslog + BLACKLIST(310), // process_vm_readv + BLACKLIST(311), // process_vm_writev + BLACKLIST(139), // sysfs + BLACKLIST(156), // _sysctl + BLACKLIST(159), // adjtimex + BLACKLIST(305), // clock_adjtime + BLACKLIST(212), // lookup_dcookie + BLACKLIST(298), // perf_event_open + BLACKLIST(300), // fanotify_init + BLACKLIST(312), // kcmp + BLACKLIST(248), // add_key + BLACKLIST(249), // request_key + BLACKLIST(250), // keyctl + BLACKLIST(134), // uselib + BLACKLIST(163), // acct + BLACKLIST(154), // modify_ldt + BLACKLIST(155), // pivot_root + BLACKLIST(206), // io_setup + BLACKLIST(207), // io_destroy + BLACKLIST(208), // io_getevents + BLACKLIST(209), // io_submit + BLACKLIST(210), // io_cancel + BLACKLIST(216), // remap_file_pages + BLACKLIST(237), // mbind + BLACKLIST(239), // get_mempolicy + BLACKLIST(238), // set_mempolicy + BLACKLIST(256), // migrate_pages + BLACKLIST(279), // move_pages + BLACKLIST(278), // vmsplice + BLACKLIST(161), // chroot + BLACKLIST(184), // tuxcall + BLACKLIST(169), // reboot + BLACKLIST(180), // nfsservctl + BLACKLIST(177), // get_kernel_syms + + RETURN_ALLOW + }; + + // save filter to file + int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (dst < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + int size = (int) sizeof(filter); + int written = 0; + while (written < size) { + int rv = write(dst, (unsigned char *) filter + written, size - written); + if (rv == -1) { + fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); + exit(1); + } + written += rv; + } + close(dst); +} + +// i386 filter installed on amd64 architectures +void seccomp_secondary_32(const char *fname) { + // hardcoded syscall values + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE_32, + EXAMINE_SYSCALL, + BLACKLIST(21), // mount + BLACKLIST(52), // umount2 +// todo: implement --allow-debuggers + BLACKLIST(26), // ptrace + BLACKLIST(283), // kexec_load + BLACKLIST(341), // name_to_handle_at + BLACKLIST(342), // open_by_handle_at + BLACKLIST(127), // create_module + BLACKLIST(128), // init_module + BLACKLIST(350), // finit_module + BLACKLIST(129), // delete_module + BLACKLIST(110), // iopl + BLACKLIST(101), // ioperm + BLACKLIST(289), // ioprio_set + BLACKLIST(87), // swapon + BLACKLIST(115), // swapoff + BLACKLIST(103), // syslog + BLACKLIST(347), // process_vm_readv + BLACKLIST(348), // process_vm_writev + BLACKLIST(135), // sysfs + BLACKLIST(149), // _sysctl + BLACKLIST(124), // adjtimex + BLACKLIST(343), // clock_adjtime + BLACKLIST(253), // lookup_dcookie + BLACKLIST(336), // perf_event_open + BLACKLIST(338), // fanotify_init + BLACKLIST(349), // kcmp + BLACKLIST(286), // add_key + BLACKLIST(287), // request_key + BLACKLIST(288), // keyctl + BLACKLIST(86), // uselib + BLACKLIST(51), // acct + BLACKLIST(123), // modify_ldt + BLACKLIST(217), // pivot_root + BLACKLIST(245), // io_setup + BLACKLIST(246), // io_destroy + BLACKLIST(247), // io_getevents + BLACKLIST(248), // io_submit + BLACKLIST(249), // io_cancel + BLACKLIST(257), // remap_file_pages + BLACKLIST(274), // mbind + BLACKLIST(275), // get_mempolicy + BLACKLIST(276), // set_mempolicy + BLACKLIST(294), // migrate_pages + BLACKLIST(317), // move_pages + BLACKLIST(316), // vmsplice + BLACKLIST(61), // chroot + BLACKLIST(88), // reboot + BLACKLIST(169), // nfsservctl + BLACKLIST(130), // get_kernel_syms + + RETURN_ALLOW + }; + + // save filter to file + int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (dst < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + int size = (int) sizeof(filter); + int written = 0; + while (written < size) { + int rv = write(dst, (unsigned char *) filter + written, size - written); + if (rv == -1) { + fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); + exit(1); + } + written += rv; + } + close(dst); +} + diff --git a/src/fseccomp/syscall.c b/src/fseccomp/syscall.c index c67d45598..e2052efde 100644 --- a/src/fseccomp/syscall.c +++ b/src/fseccomp/syscall.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" #include @@ -16,6 +35,29 @@ static SyscallEntry syslist[] = { // }; // end of syslist +// return -1 if error, or syscall number +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; +} + +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"; +} + void syscall_print(void) { int i; int elems = sizeof(syslist) / sizeof(syslist[0]); @@ -24,3 +66,45 @@ void syscall_print(void) { } printf("\n"); } + +// return 1 if error, 0 if OK +int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg) { + // 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(fd, nr, arg); + + 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(fd, nr, arg); + } + + free(str); + return 0; +} diff --git a/test/filters/filters.sh b/test/filters/filters.sh index 5093c8614..5c7c98b3e 100755 --- a/test/filters/filters.sh +++ b/test/filters/filters.sh @@ -12,11 +12,21 @@ echo "TESTING: noroot (test/filters/noroot.exp)" echo "TESTING: capabilities (test/filters/caps.exp)" ./caps.exp +rm -f seccomp-test-file +if [ "$(uname -m)" = "x86_64" ]; then + echo "TESTING: fseccomp (test/filters/fseccomp.exp)" + ./fseccomp.exp +else + echo "TESTING SKIP: fseccomp test implemented only for x86_64" +fi +rm -f seccomp-test-file + + if [ "$(uname -m)" = "x86_64" ]; then echo "TESTING: protocol (test/filters/protocol.exp)" ./protocol.exp else - echo "TESTING SKIP: protocol, not running on x86_64" + echo "TESTING SKIP: protocol, running only on x86_64" fi echo "TESTING: seccomp bad empty (test/filters/seccomp-bad-empty.exp)" @@ -50,9 +60,6 @@ echo "TESTING: seccomp chmod profile - seccomp lists (test/filters/seccomp-chmod echo "TESTING: seccomp empty (test/filters/seccomp-empty.exp)" ./seccomp-empty.exp -echo "TESTING: seccomp bad empty (test/filters/seccomp-bad-empty.exp)" -./seccomp-bad-empty.exp - if [ "$(uname -m)" = "x86_64" ]; then echo "TESTING: seccomp dual filter (test/filters/seccomp-dualfilter.exp)" ./seccomp-dualfilter.exp diff --git a/test/filters/fseccomp.exp b/test/filters/fseccomp.exp new file mode 100755 index 000000000..8a9a8f9dc --- /dev/null +++ b/test/filters/fseccomp.exp @@ -0,0 +1,138 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +after 100 +send -- "/usr/lib/firejail/fseccomp debug-syscalls\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "1 - write" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp debug-errnos\r" +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "1 - EPERM" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp debug-protocols\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "unix, inet, inet6, netlink, packet," +} + +after 100 +send -- "/usr/lib/firejail/fseccomp protocol build unix,inet seccomp-test-file\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 4.1\n";exit} + "WHITELIST 41 socket" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp secondary 64 seccomp-test-file\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "BLACKLIST 165 mount" +} +expect { + timeout {puts "TESTING ERROR 5.2\n";exit} + "BLACKLIST 166 umount2" +} +expect { + timeout {puts "TESTING ERROR 5.3\n";exit} + "RETURN_ALLOW" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp default seccomp-test-file\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "BLACKLIST 165 mount" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "BLACKLIST 166 umount2" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "RETURN_ALLOW" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp drop seccomp-test-file chmod,chown\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 7.1\n";exit} + "BLACKLIST 165 mount" {puts "TESTING ERROR 7.2\n";exit} + "BLACKLIST 166 umount2" {puts "TESTING ERROR 7.3\n";exit} + "BLACKLIST 90 chmod" +} +expect { + timeout {puts "TESTING ERROR 7.4\n";exit} + "BLACKLIST 92 chown" +} +expect { + timeout {puts "TESTING ERROR 7.5\n";exit} + "RETURN_ALLOW" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp default drop seccomp-test-file chmod,chown\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 8.1\n";exit} + "BLACKLIST 165 mount" +} +expect { + timeout {puts "TESTING ERROR 8.2\n";exit} + "BLACKLIST 166 umount2" +} +expect { + timeout {puts "TESTING ERROR 8.3\n";exit} + "BLACKLIST 90 chmod" +} +expect { + timeout {puts "TESTING ERROR 8.4\n";exit} + "BLACKLIST 92 chown" +} +expect { + timeout {puts "TESTING ERROR 8.5\n";exit} + "RETURN_ALLOW" +} +after 100 +send -- "/usr/lib/firejail/fseccomp keep seccomp-test-file chmod,chown\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 9.1\n";exit} + "WHITELIST 90 chmod" +} +expect { + timeout {puts "TESTING ERROR 9.2\n";exit} + "WHITELIST 92 chown" +} +expect { + timeout {puts "TESTING ERROR 9.3\n";exit} + "KILL_PROCESS" +} + + + +after 100 +puts "\nall done\n" + -- cgit v1.2.3-70-g09d2