Commit 63e9d849 authored by Topi Miettinen's avatar Topi Miettinen

Allow any syscall to be blacklisted (#1447)

Allow any syscall to be blacklisted with aid of LD_PRELOAD library,
libpostexecseccomp.so.

Closes: #1447
parent 28e5585d
all: apps man filters
MYLIBS = src/lib
APPS = src/firejail src/firemon src/firecfg src/libtrace src/libtracelog src/ftee src/faudit src/fnet src/fseccomp src/fcopy src/fldd
APPS = src/firejail src/firemon src/firecfg src/libtrace src/libtracelog src/ftee src/faudit src/fnet src/fseccomp src/fcopy src/fldd src/libpostexecseccomp
MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5
SECCOMP_FILTERS = seccomp seccomp.i386 seccomp.amd64
......@@ -85,6 +85,7 @@ realinstall:
install -m 0755 -d $(DESTDIR)/$(libdir)/firejail
install -c -m 0644 src/libtrace/libtrace.so $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0644 src/libtracelog/libtracelog.so $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0644 src/libpostexecseccomp/libpostexecseccomp.so $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0755 src/ftee/ftee $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0755 src/fshaper/fshaper.sh $(DESTDIR)/$(libdir)/firejail/.
ifeq ($(HAVE_GIT_INSTALL),-DHAVE_GIT_INSTALL)
......@@ -159,11 +160,13 @@ install-strip: all
strip src/firecfg/firecfg
strip src/libtrace/libtrace.so
strip src/libtracelog/libtracelog.so
strip src/libpostexecseccomp/libpostexecseccomp.so
strip src/ftee/ftee
strip src/faudit/faudit
strip src/fnet/fnet
strip src/fseccomp/fseccomp
strip src/fcopy/fcopy
strip src/fldd/fldd
$(MAKE) realinstall
uninstall:
......
......@@ -3823,7 +3823,7 @@ if test "$prefix" = /usr; then
sysconfdir="/etc"
fi
ac_config_files="$ac_config_files Makefile src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile"
ac_config_files="$ac_config_files Makefile src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
......@@ -4545,6 +4545,7 @@ do
"src/faudit/Makefile") CONFIG_FILES="$CONFIG_FILES src/faudit/Makefile" ;;
"src/fseccomp/Makefile") CONFIG_FILES="$CONFIG_FILES src/fseccomp/Makefile" ;;
"src/fldd/Makefile") CONFIG_FILES="$CONFIG_FILES src/fldd/Makefile" ;;
"src/libpostexecseccomp/Makefile") CONFIG_FILES="$CONFIG_FILES src/libpostexecseccomp/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
......
......@@ -177,7 +177,7 @@ fi
AC_OUTPUT(Makefile src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile \
src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile \
src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile)
src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile)
echo
echo "Configuration options:"
......
......@@ -57,6 +57,7 @@
#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_SECCOMP_MDWX "/run/firejail/mnt/seccomp.mdwx" // filter for memory-deny-write-execute
#define RUN_SECCOMP_POSTEXEC "/run/firejail/mnt/seccomp.postexec" // filter for post-exec library
#define PATH_SECCOMP_DEFAULT (LIBDIR "/firejail/seccomp") // default filter built during make
#define PATH_SECCOMP_DEFAULT_DEBUG (LIBDIR "/firejail/seccomp.debug") // default filter built during make
#define PATH_SECCOMP_AMD64 (LIBDIR "/firejail/seccomp.amd64") // amd64 filter built during make
......@@ -305,6 +306,7 @@ extern int arg_overlay_keep; // place overlay diff in a known directory
extern int arg_overlay_reuse; // allow the reuse of overlays
extern int arg_seccomp; // enable default seccomp filter
extern int arg_seccomp_postexec; // need postexec ld.preload library?
extern int arg_caps_default_filter; // enable default capabilities filter
extern int arg_caps_drop; // drop list
......@@ -553,8 +555,6 @@ void caps_drop_dac_override(void);
// syscall.c
const char *syscall_find_nr(int nr);
// return -1 if error, 0 if no error
int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg);
// fs_trace.c
void fs_trace_preload(void);
......
......@@ -63,6 +63,11 @@ void fs_trace(void) {
if (!arg_quiet)
printf("Blacklist violations are logged to syslog\n");
}
if (arg_seccomp_postexec) {
fprintf(fp, "%s/libpostexecseccomp.so\n", prefix);
if (!arg_quiet)
printf("Post-exec seccomp protector enabled\n");
}
SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH);
fclose(fp);
......
......@@ -56,6 +56,7 @@ int arg_overlay_keep = 0; // place overlay diff in a known directory
int arg_overlay_reuse = 0; // allow the reuse of overlays
int arg_seccomp = 0; // enable default seccomp filter
int arg_seccomp_postexec = 0; // need postexec ld.preload library?
int arg_caps_default_filter = 0; // enable default capabilities filter
int arg_caps_drop = 0; // drop list
......
......@@ -85,9 +85,12 @@ void preproc_mount_mnt_dir(void) {
if (arg_memory_deny_write_execute)
copy_file(PATH_SECCOMP_MDWX, RUN_SECCOMP_MDWX, getuid(), getgid(), 0644); // root needed
// as root, create an empty RUN_SECCOMP_PROTOCOL file
// as root, create empty RUN_SECCOMP_PROTOCOL and RUN_SECCOMP_POSTEXEC files
create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644);
if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644))
errExit("set_perms");
create_empty_file_as_root(RUN_SECCOMP_POSTEXEC, 0644);
if (set_perms(RUN_SECCOMP_POSTEXEC, getuid(), getgid(), 0644))
errExit("set_perms");
}
}
......@@ -664,10 +664,15 @@ int sandbox(void* sandbox_arg) {
if (rv)
exit(rv);
}
if (arg_seccomp && (cfg.seccomp_list || cfg.seccomp_list_drop || cfg.seccomp_list_keep))
arg_seccomp_postexec = 1;
#endif
// need ld.so.preload if tracing or seccomp with any non-default lists
bool need_preload = arg_trace || arg_tracelog || arg_seccomp_postexec;
// trace pre-install
if (arg_trace || arg_tracelog)
if (need_preload)
fs_trace_preload();
// store hosts file
......@@ -704,7 +709,7 @@ int sandbox(void* sandbox_arg) {
//****************************
// trace pre-install, this time inside chroot
//****************************
if (arg_trace || arg_tracelog)
if (need_preload)
fs_trace_preload();
}
else
......@@ -767,7 +772,7 @@ int sandbox(void* sandbox_arg) {
else {
fs_private_dir_list("/etc", RUN_ETC_DIR, cfg.etc_private_keep);
// create /etc/ld.so.preload file again
if (arg_trace || arg_tracelog)
if (need_preload)
fs_trace_preload();
}
}
......@@ -903,7 +908,7 @@ int sandbox(void* sandbox_arg) {
//****************************
// install trace
//****************************
if (arg_trace || arg_tracelog)
if (need_preload)
fs_trace();
//****************************
......
......@@ -145,11 +145,11 @@ int seccomp_filter_drop(int enforce_seccomp) {
// 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");
rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7,
PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, RUN_SECCOMP_POSTEXEC, 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);
rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6,
PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, RUN_SECCOMP_POSTEXEC, cfg.seccomp_list);
if (rv)
exit(rv);
}
......@@ -163,11 +163,11 @@ int seccomp_filter_drop(int enforce_seccomp) {
// 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");
rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6,
PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, RUN_SECCOMP_POSTEXEC, cfg.seccomp_list_drop, "allow-debuggers");
else
rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop);
rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5,
PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, RUN_SECCOMP_POSTEXEC, cfg.seccomp_list_drop);
if (rv)
exit(rv);
......@@ -183,9 +183,14 @@ int seccomp_filter_drop(int enforce_seccomp) {
exit(1);
}
if (arg_debug && access(PATH_FSECCOMP, X_OK) == 0)
if (arg_debug && access(PATH_FSECCOMP, X_OK) == 0) {
sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3,
PATH_FSECCOMP, "print", RUN_SECCOMP_CFG);
struct stat st;
if (stat(RUN_SECCOMP_POSTEXEC, &st) != -1 && st.st_size != 0)
sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3,
PATH_FSECCOMP, "print", RUN_SECCOMP_POSTEXEC);
}
return 0;
}
......@@ -196,14 +201,19 @@ int seccomp_filter_keep(void) {
printf("Build drop seccomp filter\n");
// build the seccomp filter as a regular user
sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, cfg.seccomp_list_keep);
sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5,
PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, RUN_SECCOMP_POSTEXEC, cfg.seccomp_list_keep);
if (arg_debug)
printf("seccomp filter configured\n");
if (arg_debug && access(PATH_FSECCOMP, X_OK) == 0)
if (arg_debug && access(PATH_FSECCOMP, X_OK) == 0) {
sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FSECCOMP, "print", RUN_SECCOMP_CFG);
struct stat st;
if (stat(RUN_SECCOMP_POSTEXEC, &st) != -1 && st.st_size != 0)
sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FSECCOMP, "print", RUN_SECCOMP_POSTEXEC);
}
return seccomp_load(RUN_SECCOMP_CFG);
}
......
......@@ -30,8 +30,9 @@ extern int arg_quiet;
// 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_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg, void *ptrarg), int fd, int arg, void *ptrarg);
const char *syscall_find_nr(int nr);
void syscalls_in_list(const char *list, const char *slist, int fd, char **prelist, char **postlist);
// errno.c
void errno_print(void);
......@@ -49,9 +50,9 @@ void seccomp_secondary_32(const char *fname);
// seccomp_file.c
void write_to_file(int fd, const void *data, int size);
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_add_whitelist(int fd, int syscall, int arg, void *ptrarg);
void filter_add_blacklist(int fd, int syscall, int arg, void *ptrarg);
void filter_add_errno(int fd, int syscall, int arg, void *ptrarg);
void filter_end_blacklist(int fd);
void filter_end_whitelist(int fd);
......@@ -59,11 +60,11 @@ void filter_end_whitelist(int fd);
// default list
void seccomp_default(const char *fname, int allow_debuggers);
// drop list
void seccomp_drop(const char *fname, char *list, int allow_debuggers);
void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers);
// default+drop list
void seccomp_default_drop(const char *fname, char *list, int allow_debuggers);
void seccomp_default_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers);
// whitelisted filter
void seccomp_keep(const char *fname, char *list);
void seccomp_keep(const char *fname1, const char *fname2, char *list);
// block writable and executable memory
void memory_deny_write_execute(const char *fname);
......
......@@ -30,11 +30,11 @@ static void usage(void) {
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 drop file1 file2 list\n");
printf("\tfseccomp drop file1 file2 list allow-debuggers\n");
printf("\tfseccomp default drop file1 file2 list\n");
printf("\tfseccomp default drop file1 file2 list allow-debuggers\n");
printf("\tfseccomp keep file1 file2 list\n");
printf("\tfseccomp memory-deny-write-execute file\n");
printf("\tfseccomp print file\n");
}
......@@ -78,16 +78,16 @@ printf("\n");
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 == 5 && strcmp(argv[1], "drop") == 0)
seccomp_drop(argv[2], argv[3], argv[4], 0);
else if (argc == 6 && strcmp(argv[1], "drop") == 0 && strcmp(argv[5], "allow-debuggers") == 0)
seccomp_drop(argv[2], argv[3], argv[4], 1);
else if (argc == 6 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0)
seccomp_default_drop(argv[3], argv[4], argv[5], 0);
else if (argc == 7 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0 && strcmp(argv[6], "allow-debuggers") == 0)
seccomp_default_drop(argv[3], argv[4], argv[5], 1);
else if (argc == 5 && strcmp(argv[1], "keep") == 0)
seccomp_keep(argv[2], argv[3], argv[4]);
else if (argc == 3 && strcmp(argv[1], "memory-deny-write-execute") == 0)
memory_deny_write_execute(argv[2]);
else if (argc == 3 && strcmp(argv[1], "print") == 0)
......
......@@ -27,9 +27,9 @@
static void add_default_list(int fd, int allow_debuggers) {
int r;
if (!allow_debuggers)
r = syscall_check_list("@default-nodebuggers", filter_add_blacklist, fd, 0);
r = syscall_check_list("@default-nodebuggers", filter_add_blacklist, fd, 0, NULL);
else
r = syscall_check_list("@default", filter_add_blacklist, fd, 0);
r = syscall_check_list("@default", filter_add_blacklist, fd, 0, NULL);
assert(r == 0);
//#ifdef SYS_mknod - emoved in 0.9.29 - it breaks Zotero extension
......@@ -56,7 +56,7 @@ void seccomp_default(const char *fname, int allow_debuggers) {
exit(1);
}
// build filter
// build filter (no post-exec filter needed because default list is fine for us)
filter_init(fd);
add_default_list(fd, allow_debuggers);
filter_end_blacklist(fd);
......@@ -66,44 +66,94 @@ void seccomp_default(const char *fname, int allow_debuggers) {
}
// drop list
void seccomp_drop(const char *fname, char *list, int allow_debuggers) {
assert(fname);
void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers) {
assert(fname1);
assert(fname2);
(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);
// open file for pre-exec filter
int fd = open(fname1, 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);
fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname1);
exit(1);
}
// build filter
// build pre-exec filter: don't blacklist any syscalls in @default-keep
filter_init(fd);
char *prelist, *postlist;
syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist);
if (prelist)
if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL)) {
fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n");
exit(1);
}
filter_end_whitelist(fd);
// close file
close(fd);
if (!postlist)
return;
// open file for post-exec filter
fd = open(fname2, 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", fname2);
exit(1);
}
// build post-exec filter: blacklist remaining syscalls
filter_init(fd);
if (syscall_check_list(list, filter_add_blacklist, fd, 0)) {
if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL)) {
fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n");
exit(1);
}
filter_end_blacklist(fd);
filter_end_whitelist(fd);
// close file
close(fd);
}
// default+drop
void seccomp_default_drop(const char *fname, char *list, int allow_debuggers) {
assert(fname);
void seccomp_default_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers) {
assert(fname1);
assert(fname2);
// open file
int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int fd = open(fname1, 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);
fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname1);
exit(1);
}
// build filter
// build pre-exec filter: blacklist @default, don't blacklist
// any listed syscalls in @default-keep
filter_init(fd);
add_default_list(fd, allow_debuggers);
if (syscall_check_list(list, filter_add_blacklist, fd, 0)) {
char *prelist, *postlist;
syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist);
if (prelist)
if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL)) {
fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n");
exit(1);
}
filter_end_blacklist(fd);
// close file
close(fd);
if (!postlist)
return;
// open file for post-exec filter
fd = open(fname2, 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", fname2);
exit(1);
}
// build post-exec filter: blacklist remaining syscalls
filter_init(fd);
if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL)) {
fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n");
exit(1);
}
......@@ -113,22 +163,42 @@ void seccomp_default_drop(const char *fname, char *list, int allow_debuggers) {
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);
void seccomp_keep(const char *fname1, const char *fname2, char *list) {
// open file for pre-exec filter
int fd = open(fname1, 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);
fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname1);
exit(1);
}
// build filter
// build pre-exec filter: whitelist also @default-keep
filter_init(fd);
// these syscalls are used by firejail after the seccomp filter is initialized
int r;
r = syscall_check_list("@default-keep", filter_add_whitelist, fd, 0);
r = syscall_check_list("@default-keep", filter_add_whitelist, fd, 0, NULL);
assert(r == 0);
if (syscall_check_list(list, filter_add_whitelist, fd, 0)) {
if (syscall_check_list(list, filter_add_whitelist, fd, 0, NULL)) {
fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n");
exit(1);
}
filter_end_whitelist(fd);
// close file
close(fd);
// open file for post-exec filter
fd = open(fname2, 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", fname2);
exit(1);
}
// build post-exec filter: whitelist without @default-keep
filter_init(fd);
if (syscall_check_list(list, filter_add_whitelist, fd, 0, NULL)) {
fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n");
exit(1);
}
......
......@@ -60,8 +60,9 @@ void filter_init(int fd) {
write_to_file(fd, filter, sizeof(filter));
}
void filter_add_whitelist(int fd, int syscall, int arg) {
void filter_add_whitelist(int fd, int syscall, int arg, void *ptrarg) {
(void) arg;
(void) ptrarg;
struct sock_filter filter[] = {
WHITELIST(syscall)
......@@ -69,8 +70,9 @@ void filter_add_whitelist(int fd, int syscall, int arg) {
write_to_file(fd, filter, sizeof(filter));
}
void filter_add_blacklist(int fd, int syscall, int arg) {
void filter_add_blacklist(int fd, int syscall, int arg, void *ptrarg) {
(void) arg;
(void) ptrarg;
struct sock_filter filter[] = {
BLACKLIST(syscall)
......@@ -78,7 +80,8 @@ void filter_add_blacklist(int fd, int syscall, int arg) {
write_to_file(fd, filter, sizeof(filter));
}
void filter_add_errno(int fd, int syscall, int arg) {
void filter_add_errno(int fd, int syscall, int arg, void *ptrarg) {
(void) ptrarg;
struct sock_filter filter[] = {
BLACKLIST_ERRNO(syscall, arg)
};
......
......@@ -17,7 +17,9 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
#include "fseccomp.h"
#include <stdio.h>
#include <sys/syscall.h>
typedef struct {
......@@ -30,6 +32,13 @@ typedef struct {
const char * const list;
} SyscallGroupList;
typedef struct {
const char *slist;
char *prelist, *postlist;
bool found;
int syscall;
} SyscallCheckList;
static const SyscallEntry syslist[] = {
//
// code generated using tools/extract-syscall
......@@ -174,6 +183,7 @@ static const SyscallGroupList sysgroups[] = {
},
{ .name = "@default-keep", .list =
"dup,"
"execve,"
"prctl,"
"setgid,"
"setgroups,"
......@@ -449,7 +459,7 @@ error:
}
// 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) {
int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg, void *ptrarg), int fd, int arg, void *ptrarg) {
// don't allow empty lists
if (slist == NULL || *slist == '\0') {
fprintf(stderr, "Error fseccomp: empty syscall lists are not allowed\n");
......@@ -477,7 +487,7 @@ int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall,
fprintf(stderr, "Error fseccomp: unknown syscall group %s\n", ptr);
exit(1);
}
syscall_check_list(new_list, callback, fd, arg);
syscall_check_list(new_list, callback, fd, arg, ptrarg);
}
else {
syscall_process_name(ptr, &syscall_nr, &error_nr);
......@@ -487,9 +497,9 @@ int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall,
}
else if (callback != NULL) {
if (error_nr != -1)
filter_add_errno(fd, syscall_nr, error_nr);
filter_add_errno(fd, syscall_nr, error_nr, ptrarg);
else
callback(fd, syscall_nr, arg);
callback(fd, syscall_nr, arg, ptrarg);
}
}
ptr = strtok_r(NULL, ",", &saveptr);
......@@ -498,3 +508,49 @@ int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall,
free(str);
return 0;
}
static void find_syscall(int fd, int syscall, int arg, void *ptrarg) {
(void)fd;
SyscallCheckList *ptr = ptrarg;
if (syscall == ptr->syscall)
ptr->found = true;
}
// go through list2 and find matches for problem syscall
static void syscall_in_list(int fd, int syscall, int arg, void *ptrarg) {
(void)arg;
SyscallCheckList *ptr = ptrarg;
SyscallCheckList sl;
sl.found = false;
sl.syscall = syscall;
syscall_check_list(ptr->slist, find_syscall, fd, 0, &sl);
// if found in the problem list, add to post-exec list
if (sl.found)
if (ptr->postlist) {
if (asprintf(&ptr->postlist, "%s,%s", ptr->postlist, syscall_find_nr(syscall)) == -1)
errExit("asprintf");
}
else
ptr->postlist = strdup(syscall_find_nr(syscall));
else // no problem, add to pre-exec list
if (ptr->prelist) {
if (asprintf(&ptr->prelist, "%s,%s", ptr->prelist, syscall_find_nr(syscall)) == -1)
errExit("asprintf");
}
else
ptr->prelist = strdup(syscall_find_nr(syscall));
}
// go through list and find matches for syscalls in list @default-keep
void syscalls_in_list(const char *list, const char *slist, int fd, char **prelist, char **postlist) {
SyscallCheckList sl;
// these syscalls are used by firejail after the seccomp filter is initialized
sl.slist = slist;
sl.prelist = NULL;
sl.postlist = NULL;
syscall_check_list(list, syscall_in_list, 0, 0, &sl);
if (!arg_quiet)
printf("list in: %s, check list: %s prelist: %s, postlist: %s\n", list, sl.slist, sl.prelist, sl.postlist);
*prelist = sl.prelist;
*postlist = sl.postlist;
}
CC=@CC@
PREFIX=@prefix@
VERSION=@PACKAGE_VERSION@
NAME=@PACKAGE_NAME@
HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@
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)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIC -Wformat -Wformat-security
LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now
all: libpostexecseccomp.so
%.o : %.c $(H_FILE_LIST)
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
libpostexecseccomp.so: $(OBJS)
$(CC) $(LDFLAGS) -shared -fPIC -z relro -o $@ $(OBJS) -ldl
clean:; rm -f $(OBJS) libpostexecseccomp.so
distclean: clean
rm -fr Makefile
/*
* Copyright (C) 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 "libpostexecseccomp.h"
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
__attribute__((constructor))
static void load_seccomp(void) {
int fd = open(RUN_SECCOMP_POSTEXEC, O_RDONLY);
if (fd == -1)
return;
int size = lseek(fd, 0, SEEK_END);
unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter);
struct sock_filter *filter = MAP_FAILED;
if (size != 0)
filter = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
if (size == 0 || filter == MAP_FAILED)
return;
// install filter