From 63e9d849f662d1a494c6396d4a439cd4c91dfa7e Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Sun, 13 Aug 2017 14:07:31 +0300 Subject: Allow any syscall to be blacklisted (#1447) Allow any syscall to be blacklisted with aid of LD_PRELOAD library, libpostexecseccomp.so. Closes: #1447 --- src/fseccomp/fseccomp.h | 15 +++--- src/fseccomp/main.c | 30 +++++------ src/fseccomp/seccomp.c | 118 +++++++++++++++++++++++++++++++++++--------- src/fseccomp/seccomp_file.c | 9 ++-- src/fseccomp/syscall.c | 64 ++++++++++++++++++++++-- 5 files changed, 183 insertions(+), 53 deletions(-) (limited to 'src/fseccomp') diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h index 0db670380..144b612ae 100644 --- a/src/fseccomp/fseccomp.h +++ b/src/fseccomp/fseccomp.h @@ -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); diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index 3d95d5bb2..3bf7de0fa 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -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) diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c index a3db46aad..577f3fdc9 100644 --- a/src/fseccomp/seccomp.c +++ b/src/fseccomp/seccomp.c @@ -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); } diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c index 16ffd5302..2d5ee115d 100644 --- a/src/fseccomp/seccomp_file.c +++ b/src/fseccomp/seccomp_file.c @@ -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) }; diff --git a/src/fseccomp/syscall.c b/src/fseccomp/syscall.c index 5893a2ea8..b9e6d995b 100644 --- a/src/fseccomp/syscall.c +++ b/src/fseccomp/syscall.c @@ -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 #include 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; +} -- cgit v1.2.3-54-g00ecf