From 88eadbf31fe25dcd7c224a5d92f71c79ccf6c9d3 Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Sat, 14 Mar 2020 00:07:06 +0200 Subject: seccomp: allow defining separate filters for 32-bit arch System calls (names and numbers) are not exactly the same for 32 bit and 64 bit architectures. Let's allow defining separate filters for 32-bit arch using seccomp.32, seccomp.32.drop, seccomp.32.keep. This is useful for mixed 64/32 bit application environments like Steam and Wine. Implement protocol and mdwx filtering also for 32 bit arch. It's still better to block secondary archs completely if not needed. Lists of supported system calls are also updated. Warn if preload libraries would be needed due to trace, tracelog or postexecseccomp (seccomp.drop=execve etc), because a 32-bit dynamic linker does not understand the 64 bit preload libraries. Closes #3267. Signed-off-by: Topi Miettinen --- src/fseccomp/seccomp.c | 143 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 28 deletions(-) (limited to 'src/fseccomp/seccomp.c') diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c index 29aa2f2f5..0db7b5954 100644 --- a/src/fseccomp/seccomp.c +++ b/src/fseccomp/seccomp.c @@ -24,12 +24,12 @@ #include #include -static void add_default_list(int fd, int allow_debuggers) { +static void add_default_list(int fd, int allow_debuggers, bool native) { int r; if (!allow_debuggers) - r = syscall_check_list("@default-nodebuggers", filter_add_blacklist, fd, 0, NULL); + r = syscall_check_list("@default-nodebuggers", filter_add_blacklist, fd, 0, NULL, native); else - r = syscall_check_list("@default", filter_add_blacklist, fd, 0, NULL); + r = syscall_check_list("@default", filter_add_blacklist, fd, 0, NULL, native); assert(r == 0); //#ifdef SYS_mknod - emoved in 0.9.29 - it breaks Zotero extension @@ -46,7 +46,7 @@ static void add_default_list(int fd, int allow_debuggers) { } // default list -void seccomp_default(const char *fname, int allow_debuggers) { +void seccomp_default(const char *fname, int allow_debuggers, bool native) { assert(fname); // open file @@ -57,8 +57,8 @@ void seccomp_default(const char *fname, int allow_debuggers) { } // build filter (no post-exec filter needed because default list is fine for us) - filter_init(fd); - add_default_list(fd, allow_debuggers); + filter_init(fd, native); + add_default_list(fd, allow_debuggers, native); filter_end_blacklist(fd); // close file @@ -66,7 +66,7 @@ void seccomp_default(const char *fname, int allow_debuggers) { } // drop list -void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers) { +void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers, bool native) { assert(fname1); assert(fname2); (void) allow_debuggers; // todo: to implemnet it @@ -79,15 +79,15 @@ void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_ } // build pre-exec filter: don't blacklist any syscalls in @default-keep - filter_init(fd); + filter_init(fd, native); // allow exceptions in form of !syscall - syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL); + syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL, native); char *prelist, *postlist; - syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist); + syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist, native); if (prelist) - if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL)) { + if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL, native)) { fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); exit(1); } @@ -106,8 +106,8 @@ void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_ } // build post-exec filter: blacklist remaining syscalls - filter_init(fd); - if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL)) { + filter_init(fd, native); + if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL, native)) { fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); exit(1); } @@ -118,7 +118,7 @@ void seccomp_drop(const char *fname1, const char *fname2, char *list, int allow_ } // default+drop -void seccomp_default_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers) { +void seccomp_default_drop(const char *fname1, const char *fname2, char *list, int allow_debuggers, bool native) { assert(fname1); assert(fname2); @@ -131,16 +131,16 @@ void seccomp_default_drop(const char *fname1, const char *fname2, char *list, in // build pre-exec filter: blacklist @default, don't blacklist // any listed syscalls in @default-keep - filter_init(fd); + filter_init(fd, native); // allow exceptions in form of !syscall - syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL); + syscall_check_list(list, filter_add_whitelist_for_excluded, fd, 0, NULL, native); - add_default_list(fd, allow_debuggers); + add_default_list(fd, allow_debuggers, native); char *prelist, *postlist; - syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist); + syscalls_in_list(list, "@default-keep", fd, &prelist, &postlist, native); if (prelist) - if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL)) { + if (syscall_check_list(prelist, filter_add_blacklist, fd, 0, NULL, native)) { fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); exit(1); } @@ -160,8 +160,8 @@ void seccomp_default_drop(const char *fname1, const char *fname2, char *list, in } // build post-exec filter: blacklist remaining syscalls - filter_init(fd); - if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL)) { + filter_init(fd, native); + if (syscall_check_list(postlist, filter_add_blacklist, fd, 0, NULL, native)) { fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); exit(1); } @@ -171,7 +171,7 @@ void seccomp_default_drop(const char *fname1, const char *fname2, char *list, in close(fd); } -void seccomp_keep(const char *fname1, const char *fname2, char *list) { +void seccomp_keep(const char *fname1, const char *fname2, char *list, bool native) { (void) fname2; // open file for pre-exec filter @@ -182,17 +182,17 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list) { } // build pre-exec filter: whitelist also @default-keep - filter_init(fd); + filter_init(fd, native); // allow exceptions in form of !syscall - syscall_check_list(list, filter_add_blacklist_for_excluded, fd, 0, NULL); + syscall_check_list(list, filter_add_blacklist_for_excluded, fd, 0, NULL, native); // 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, NULL); + r = syscall_check_list("@default-keep", filter_add_whitelist, fd, 0, NULL, native); assert(r == 0); - if (syscall_check_list(list, filter_add_whitelist, fd, 0, NULL)) { + if (syscall_check_list(list, filter_add_whitelist, fd, 0, NULL, native)) { fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); exit(1); } @@ -206,6 +206,15 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list) { #if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) # define filter_syscall SYS_mmap # undef block_syscall +#if defined(__x86_64__) +// i386 syscalls +# define filter_syscall_32 192 +# define block_syscall_32 90 +# define mprotect_32 125 +# define pkey_mprotect_32 380 +# define shmat_32 397 +# define memfd_create_32 356 +#endif #elif defined(__i386__) # define filter_syscall SYS_mmap2 # define block_syscall SYS_mmap @@ -216,6 +225,12 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list) { # warning "Platform does not support seccomp memory-deny-write-execute filter yet" # undef filter_syscall # undef block_syscall +# undef filter_syscall_32 +# undef block_syscall_32 +# undef mprotect_32 +# undef pkey_mprotect_32 +# undef shmat_32 +# undef memfd_create_32 #endif void memory_deny_write_execute(const char *fname) { @@ -226,10 +241,10 @@ void memory_deny_write_execute(const char *fname) { exit(1); } - filter_init(fd); + filter_init(fd, true); // build filter - static const struct sock_filter filter[] = { + struct sock_filter filter[] = { #ifdef block_syscall // block old multiplexing mmap syscall for i386 BLACKLIST(block_syscall), @@ -288,3 +303,75 @@ void memory_deny_write_execute(const char *fname) { // close file close(fd); } + +void memory_deny_write_execute_32(const char *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); + } + + filter_init(fd, false); + + // build filter + struct sock_filter filter[] = { +#if defined(__x86_64__) +#ifdef block_syscall_32 + // block old multiplexing mmap syscall for i386 + BLACKLIST(block_syscall_32), +#endif +#ifdef filter_syscall_32 + // block mmap(,,x|PROT_WRITE|PROT_EXEC) so W&X memory can't be created + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, filter_syscall_32, 0, 5), + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW, +#endif +#ifdef mprotect_32 + // block mprotect(,,PROT_EXEC) so writable memory can't be turned into executable + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, mprotect_32, 0, 5), + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW, +#endif +#ifdef pkey_mprotect_32 + // same for pkey_mprotect(,,PROT_EXEC), where available + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, pkey_mprotect_32, 0, 5), + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW, +#endif + +#ifdef shmat_32 + // block shmat(,,x|SHM_EXEC) so W&X shared memory can't be created + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, shmat_32, 0, 5), + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW, +#endif +#ifdef memfd_create_32 + // block memfd_create as it can be used to create + // arbitrary memory contents which can be later mapped + // as executable + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, memfd_create_32, 0, 1), + KILL_PROCESS, +#endif +#endif + RETURN_ALLOW + }; + write_to_file(fd, filter, sizeof(filter)); + + filter_end_blacklist(fd); + + // close file + close(fd); +} -- cgit v1.2.3-54-g00ecf