From e2ff818e3c4414ca19cc8533b1bc3b7afd755758 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Tue, 2 Jan 2018 09:08:12 -0500 Subject: optimize default seccomp filters --- src/fsec-optimize/Makefile.in | 45 +++++++++++++ src/fsec-optimize/fsec_optimize.h | 30 +++++++++ src/fsec-optimize/main.c | 94 ++++++++++++++++++++++++++ src/fsec-optimize/optimizer.c | 136 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+) create mode 100644 src/fsec-optimize/Makefile.in create mode 100644 src/fsec-optimize/fsec_optimize.h create mode 100644 src/fsec-optimize/main.c create mode 100644 src/fsec-optimize/optimizer.c (limited to 'src') diff --git a/src/fsec-optimize/Makefile.in b/src/fsec-optimize/Makefile.in new file mode 100644 index 000000000..6ddbfc075 --- /dev/null +++ b/src/fsec-optimize/Makefile.in @@ -0,0 +1,45 @@ +all: fsec-optimize + +CC=@CC@ +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@ +HAVE_GCOV=@HAVE_GCOV@ +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)"' $(HAVE_GCOV) -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/seccomp.h ../include/syscall.h + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +fsec-optimize: $(OBJS) ../lib/libnetlink.o + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) + +clean:; rm -f *.o fsec-optimize *.gcov *.gcda *.gcno + +distclean: clean + rm -fr Makefile diff --git a/src/fsec-optimize/fsec_optimize.h b/src/fsec-optimize/fsec_optimize.h new file mode 100644 index 000000000..ff4d53ab2 --- /dev/null +++ b/src/fsec-optimize/fsec_optimize.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2017 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 FSEC_OPTIMIZE_H +#define FSEC_OPTIMIZE_H +#include "../include/common.h" +#include "../include/seccomp.h" +#include + +// optimize.c +struct sock_filter *duplicate(struct sock_filter *filter, int entries); +int optimize(struct sock_filter * filter, int entries); + +#endif \ No newline at end of file diff --git a/src/fsec-optimize/main.c b/src/fsec-optimize/main.c new file mode 100644 index 000000000..2c11b91ef --- /dev/null +++ b/src/fsec-optimize/main.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014-2017 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 "fsec_optimize.h" + +static void usage(void) { + printf("Usage:\n"); + printf("\tfsec-optimize file - optimize seccomp filter\n"); +} + +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) { + usage(); + return 1; + } + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { + usage(); + return 0; + } + + char *fname = argv[1]; + + // open input file + int fd = open(fname, O_RDONLY); + if (fd == -1) + goto errexit; + + // calculate the number of entries + int size = lseek(fd, 0, SEEK_END); + if (size == -1) // todo: check maximum size of seccomp filter (4KB?) + goto errexit; + unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); + + // read filter + struct sock_filter *filter = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (filter == MAP_FAILED) + goto errexit; + close(fd); + + // duplicate the filter memory and unmap the file + struct sock_filter *outfilter = duplicate(filter, entries); + if (munmap(filter, size) == -1) + perror("Error un-mmapping the file"); + + // optimize filter + entries = optimize(outfilter, entries); + + // write the new file and free memory + fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0755); + if (fd == -1) { + fprintf(stderr, "Error: cannot open output file\n"); + return 1; + } + size = write(fd, outfilter, entries * sizeof(struct sock_filter)); + if (size != entries * sizeof(struct sock_filter)) { + fprintf(stderr, "Error: cannot write output file\n"); + return 1; + } + close(fd); + free(outfilter); + + return 0; +errexit: + close(fd); + fprintf(stderr, "Error: cannot read %s\n", fname); + exit(1); + +} diff --git a/src/fsec-optimize/optimizer.c b/src/fsec-optimize/optimizer.c new file mode 100644 index 000000000..8e61935d3 --- /dev/null +++ b/src/fsec-optimize/optimizer.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014-2017 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 "fsec_optimize.h" + +// From /usr/include/linux/filter.h +//struct sock_filter { /* Filter block */ +// __u16 code; /* Actual filter code */ +// __u8 jt; /* Jump true */ +// __u8 jf; /* Jump false */ +// __u32 k; /* Generic multiuse field */ +//}; + + +#define LIMIT_BLACKLISTS 4 // we optimize blacklists only if we have more than + +static inline int is_blacklist(struct sock_filter *bpf) { + if (bpf->code == BPF_JMP + BPF_JEQ + BPF_K && + (bpf + 1)->code == BPF_RET + BPF_K && + (bpf + 1)->k == SECCOMP_RET_KILL ) + return 1; + return 0; +} + +static int count_blacklists(struct sock_filter *filter, int entries) { + int cnt = 0; + int i; + + for (i = 0; i < (entries - 1); i++, filter++) { // is_blacklist works on two consecutive lines; using entries - 1 + if (is_blacklist(filter)) + cnt++; + } + + return cnt; +} + +typedef struct { + int to_remove; + int to_fix_jumps; +} Action; + +static int optimize_blacklists(struct sock_filter *filter, int entries) { + assert(entries); + assert(filter); + int i; + int j; + + // step1: extract information + Action action[entries]; + memset(&action[0], 0, sizeof(Action) * entries); + int remove_cnt = 0; + for (i = 0; i < (entries - 1); i++) { // is_blacklist works on two consecutive lines; using entries - 1 + if (is_blacklist(filter + i)) { + action[i]. to_fix_jumps = 1; + i++; + action[i].to_remove = 1; + remove_cnt++; + } + } + + // step2: remove lines + struct sock_filter *filter_step2 = duplicate(filter, entries); + Action action_step2[entries]; + memset(&action_step2[0], 0, sizeof(Action) * entries); + for (i = 0, j = 0; i < entries; i++) { + if (!action[i].to_remove) { + memcpy(&filter_step2[j], &filter[i], sizeof(struct sock_filter)); + memcpy(&action_step2[j], &action[i], sizeof(Action)); + j++; + } + else { + // do nothing, we are removing this line + } + } + + // step 3: add the new ret KILL, and recalculate entries + filter_step2[j].code = BPF_RET + BPF_K; + filter_step2[j].k == SECCOMP_RET_KILL; + entries = j + 1; + + // step 4: recalculate jumps + for (i = 0; i < entries; i++) { + if (action_step2[i].to_fix_jumps) { + filter_step2[i].jt = entries - i - 2; + filter_step2[i].jf = 0; + } + } + + // update + memcpy(filter, filter_step2, entries * sizeof(struct sock_filter)); + free(filter_step2); + return entries; +} + +int optimize(struct sock_filter *filter, int entries) { + assert(filter); + assert(entries); + + //********************************** + // optimize blacklist statements + //********************************** + // count "ret KILL" + int cnt = count_blacklists(filter, entries); + if (cnt > LIMIT_BLACKLISTS) + entries = optimize_blacklists(filter, entries); + return entries; +} + +struct sock_filter *duplicate(struct sock_filter *filter, int entries) { + int len = sizeof(struct sock_filter) * entries; + struct sock_filter *rv = malloc(len); + if (!rv) { + errExit("malloc"); + exit(1); + } + + memcpy(rv, filter, len); + return rv; +} + -- cgit v1.2.3-54-g00ecf