Commit d01216de authored by Topi Miettinen's avatar Topi Miettinen

Feature: switch/config option to block secondary architectures

Add a feature for a new (opt-in) command line switch and config file
option to block secondary architectures entirely. Also block changing
Linux execution domain with personality() system call for the primary
architecture.

Closes #1479
parent 0b768c04
......@@ -30,4 +30,5 @@ seccomp
seccomp.debug
seccomp.i386
seccomp.amd64
seccomp.block_secondary
seccomp.mdwx
......@@ -2,7 +2,7 @@ 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 src/libpostexecseccomp
MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5
SECCOMP_FILTERS = seccomp seccomp.i386 seccomp.amd64
SECCOMP_FILTERS = seccomp seccomp.debug seccomp.i386 seccomp.amd64 seccomp.block_secondary seccomp.mwdx
prefix=@prefix@
exec_prefix=@exec_prefix@
......@@ -45,6 +45,7 @@ ifeq ($(HAVE_SECCOMP),-DHAVE_SECCOMP)
src/fseccomp/fseccomp default seccomp.debug allow-debuggers
src/fseccomp/fseccomp secondary 32 seccomp.i386
src/fseccomp/fseccomp secondary 64 seccomp.amd64
src/fseccomp/fseccomp secondary block seccomp.block_secondary
src/fseccomp/fseccomp memory-deny-write-execute seccomp.mdwx
endif
......@@ -53,7 +54,7 @@ clean:
$(MAKE) -C $$dir clean; \
done
rm -f $(MANPAGES) $(MANPAGES:%=%.gz) firejail*.rpm
rm -f seccomp seccomp.debug seccomp.i386 seccomp.amd64 seccomp.mdwx
rm -f $(SECCOMP_FILTERS)
rm -f test/utils/index.html*
rm -f test/utils/wget-log
rm -f test/utils/lstesting
......@@ -104,6 +105,7 @@ ifeq ($(HAVE_SECCOMP),-DHAVE_SECCOMP)
install -c -m 0644 seccomp.debug $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0644 seccomp.i386 $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0644 seccomp.amd64 $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0644 seccomp.block_secondary $(DESTDIR)/$(libdir)/firejail/.
install -c -m 0644 seccomp.mdwx $(DESTDIR)/$(libdir)/firejail/.
endif
ifeq ($(HAVE_CONTRIB_INSTALL),yes)
......
......@@ -57,12 +57,14 @@
#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_BLOCK_SECONDARY "/run/firejail/mnt/seccomp.block_secondary" // secondary arch blocking filter
#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
#define PATH_SECCOMP_I386 (LIBDIR "/firejail/seccomp.i386") // i386 filter built during make
#define PATH_SECCOMP_MDWX (LIBDIR "/firejail/seccomp.mdwx") // filter for memory-deny-write-execute built during make
#define PATH_SECCOMP_BLOCK_SECONDARY (LIBDIR "/firejail/seccomp.block_secondary") // secondary arch blocking filter built during make
#define RUN_DEV_DIR "/run/firejail/mnt/dev"
......@@ -307,6 +309,7 @@ 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_seccomp_block_secondary; // block any secondary architectures
extern int arg_caps_default_filter; // enable default capabilities filter
extern int arg_caps_drop; // drop list
......@@ -538,8 +541,6 @@ void fs_private_home_list(void);
char *seccomp_check_list(const char *str);
int seccomp_install_filters(void);
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_print_filter(pid_t pid);
......
......@@ -57,6 +57,7 @@ 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_seccomp_block_secondary = 0; // block any secondary architectures
int arg_caps_default_filter = 0; // enable default capabilities filter
int arg_caps_drop = 0; // drop list
......@@ -1147,6 +1148,13 @@ int main(int argc, char **argv) {
else
exit_err_feature("seccomp");
}
else if (strcmp(argv[i], "--seccomp.block-secondary") == 0) {
if (checkcfg(CFG_SECCOMP)) {
arg_seccomp_block_secondary = 1;
}
else
exit_err_feature("seccomp");
}
else if (strcmp(argv[i], "--memory-deny-write-execute") == 0) {
if (checkcfg(CFG_SECCOMP))
arg_memory_deny_write_execute = 1;
......@@ -2239,6 +2247,10 @@ int main(int argc, char **argv) {
}
}
// enable seccomp if only seccomp.block-secondary was specified
if (arg_seccomp_block_secondary)
arg_seccomp = 1;
// log command
logargs(argc, argv);
if (fullargc) {
......
......@@ -75,9 +75,13 @@ void preproc_mount_mnt_dir(void) {
tmpfs_mounted = 1;
fs_logger2("tmpfs", RUN_MNT_DIR);
//copy defaultl seccomp files
copy_file(PATH_SECCOMP_I386, RUN_SECCOMP_I386, getuid(), getgid(), 0644); // root needed
copy_file(PATH_SECCOMP_AMD64, RUN_SECCOMP_AMD64, getuid(), getgid(), 0644); // root needed
if (arg_seccomp_block_secondary)
copy_file(PATH_SECCOMP_BLOCK_SECONDARY, RUN_SECCOMP_BLOCK_SECONDARY, getuid(), getgid(), 0644); // root needed
else {
//copy default seccomp files
copy_file(PATH_SECCOMP_I386, RUN_SECCOMP_I386, getuid(), getgid(), 0644); // root needed
copy_file(PATH_SECCOMP_AMD64, RUN_SECCOMP_AMD64, getuid(), getgid(), 0644); // root needed
}
if (arg_allow_debuggers)
copy_file(PATH_SECCOMP_DEFAULT_DEBUG, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed
else
......
......@@ -577,6 +577,16 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
return 0;
}
if (strcmp(ptr, "seccomp.block-secondary") == 0) {
#ifdef HAVE_SECCOMP
if (checkcfg(CFG_SECCOMP)) {
arg_seccomp_block_secondary = 1;
}
else
warning_feature_disabled("seccomp");
#endif
return 0;
}
// seccomp drop list without default list
if (strncmp(ptr, "seccomp.drop ", 13) == 0) {
#ifdef HAVE_SECCOMP
......
......@@ -118,7 +118,7 @@ errexit:
}
// i386 filter installed on amd64 architectures
void seccomp_filter_32(void) {
static void seccomp_filter_32(void) {
if (seccomp_load(RUN_SECCOMP_I386) == 0) {
if (arg_debug)
printf("Dual i386/amd64 seccomp filter configured\n");
......@@ -126,13 +126,20 @@ void seccomp_filter_32(void) {
}
// amd64 filter installed on i386 architectures
void seccomp_filter_64(void) {
static void seccomp_filter_64(void) {
if (seccomp_load(RUN_SECCOMP_AMD64) == 0) {
if (arg_debug)
printf("Dual i386/amd64 seccomp filter configured\n");
}
}
static void seccomp_filter_block_secondary(void) {
if (seccomp_load(RUN_SECCOMP_BLOCK_SECONDARY) == 0) {
if (arg_debug)
printf("Secondary arch blocking seccomp filter configured\n");
}
}
// drop filter for seccomp option
int seccomp_filter_drop(int enforce_seccomp) {
// if we have multiple seccomp commands, only one of them is executed
......@@ -143,21 +150,29 @@ int seccomp_filter_drop(int enforce_seccomp) {
if (cfg.seccomp_list_drop == NULL) {
// default seccomp
if (cfg.seccomp_list == NULL) {
if (arg_seccomp_block_secondary)
seccomp_filter_block_secondary();
else {
#if defined(__x86_64__)
seccomp_filter_32();
seccomp_filter_32();
#endif
#if defined(__i386__)
seccomp_filter_64();
seccomp_filter_64();
#endif
}
}
// default seccomp filter with additional drop list
else { // cfg.seccomp_list != NULL
if (arg_seccomp_block_secondary)
seccomp_filter_block_secondary();
else {
#if defined(__x86_64__)
seccomp_filter_32();
seccomp_filter_32();
#endif
#if defined(__i386__)
seccomp_filter_64();
seccomp_filter_64();
#endif
}
if (arg_debug)
printf("Build default+drop seccomp filter\n");
......@@ -175,7 +190,10 @@ int seccomp_filter_drop(int enforce_seccomp) {
}
// drop list without defaults - secondary filters are not installed
// except when secondary architectures are explicitly blocked
else { // cfg.seccomp_list_drop != NULL
if (arg_seccomp_block_secondary)
seccomp_filter_block_secondary();
if (arg_debug)
printf("Build drop seccomp filter\n");
......@@ -216,6 +234,11 @@ int seccomp_filter_drop(int enforce_seccomp) {
// keep filter for seccomp option
int seccomp_filter_keep(void) {
// secondary filters are not installed except when secondary
// architectures are explicitly blocked
if (arg_seccomp_block_secondary)
seccomp_filter_block_secondary();
if (arg_debug)
printf("Build drop seccomp filter\n");
......
......@@ -46,6 +46,7 @@ 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);
void seccomp_secondary_block(const char *fname);
// seccomp_file.c
void write_to_file(int fd, const void *data, int size);
......
......@@ -28,6 +28,7 @@ static void usage(void) {
printf("\tfseccomp protocol build list file\n");
printf("\tfseccomp secondary 64 file\n");
printf("\tfseccomp secondary 32 file\n");
printf("\tfseccomp secondary block file\n");
printf("\tfseccomp default file\n");
printf("\tfseccomp default file allow-debuggers\n");
printf("\tfseccomp drop file1 file2 list\n");
......@@ -74,6 +75,8 @@ printf("\n");
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 == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "block") == 0)
seccomp_secondary_block(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)
......
......@@ -113,6 +113,25 @@ static int detect_filter_type(void) {
printf(" EXAMINE_SYSCALL\n");
return sizeof(start_secondary_32) / sizeof(struct sock_filter);
}
const struct sock_filter start_secondary_block[] = {
VALIDATE_ARCHITECTURE_KILL,
#if defined(__x86_64__)
EXAMINE_SYSCALL,
HANDLE_X32_KILL,
#else
EXAMINE_SYSCALL
#endif
};
if (memcmp(&start_secondary_block[0], filter, sizeof(start_secondary_block)) == 0) {
printf(" VALIDATE_ARCHITECTURE_KILL\n");
printf(" EXAMINE_SYSCALL\n");
#if defined(__x86_64__)
printf(" HANDLE_X32_KILL\n");
#endif
return sizeof(start_secondary_block) / sizeof(struct sock_filter);
}
return 0; // filter unrecognized
}
......
......@@ -19,8 +19,29 @@
*/
#include "fseccomp.h"
#include "../include/seccomp.h"
#include <sys/personality.h>
#include <sys/syscall.h>
static void write_filter(const char *fname, size_t size, const void *filter) {
// 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);
}
size_t written = 0;
while (written < size) {
ssize_t 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);
}
void seccomp_secondary_64(const char *fname) {
// hardcoded syscall values
struct sock_filter filter[] = {
......@@ -84,23 +105,7 @@ void seccomp_secondary_64(const char *fname) {
};
// 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);
write_filter(fname, sizeof(filter), filter);
}
// i386 filter installed on amd64 architectures
......@@ -166,21 +171,47 @@ void seccomp_secondary_32(const char *fname) {
};
// 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);
}
write_filter(fname, sizeof(filter), filter);
}
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);
#define jmp_from_to(from_addr, to_addr) ((to_addr) - (from_addr) - 1)
#if __BYTE_ORDER == __BIG_ENDIAN
#define MSW 0
#define LSW (sizeof(int))
#else
#define MSW (sizeof(int))
#define LSW 0
#endif
void seccomp_secondary_block(const char *fname) {
struct sock_filter filter[] = {
// block other architectures
VALIDATE_ARCHITECTURE_KILL,
EXAMINE_SYSCALL,
#if defined(__x86_64__)
// block x32
HANDLE_X32_KILL,
#endif
// block personality(2) where domain != PER_LINUX or 0xffffffff (query current personality)
// 0: if personality(2), continue to 1, else goto 7 (allow)
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SYS_personality, 0, jmp_from_to(0, 7)),
// 1: get LSW of system call argument 0
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, args[0])) + LSW),
// 2: if LSW(arg0) == PER_LINUX, goto step 4, else continue to 3
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PER_LINUX, jmp_from_to(2, 4), 0),
// 3: if LSW(arg0) == 0xffffffff, continue to 4, else goto 6 (kill)
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xffffffff, 0, jmp_from_to(3, 6)),
// 4: get MSW of system call argument 0
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, args[0])) + MSW),
// 5: if MSW(arg0) == 0, goto 7 (allow) else continue to 6 (kill)
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, jmp_from_to(5, 7), 0),
// 6:
KILL_PROCESS,
// 7:
RETURN_ALLOW
};
// save filter to file
write_filter(fname, sizeof(filter), filter);
}
......@@ -105,6 +105,11 @@ struct seccomp_data {
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
#define VALIDATE_ARCHITECTURE_KILL \
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_KILL)
#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), \
......@@ -122,6 +127,10 @@ struct seccomp_data {
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)
#define HANDLE_X32_KILL \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \
KILL_PROCESS
#endif
#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
......
......@@ -310,6 +310,10 @@ Enable seccomp filter and blacklist the syscalls in the default list. See man 1
\fBseccomp syscall,syscall,syscall
Enable seccomp filter and blacklist the system calls in the list on top of default seccomp filter.
.TP
\fBseccomp.block-secondary
Enable seccomp filter and filter system call architectures
so that only the native architecture is allowed.
.TP
\fBseccomp.drop syscall,syscall,syscall
Enable seccomp filter and blacklist the system calls in the list.
.TP
......
......@@ -1572,9 +1572,10 @@ system call can be specified by its number instead of name with prefix
$, so for example $165 would be equal to mount on i386.
.br
System architecture is not strictly imposed. The filter is applied
at run time only if the correct architecture was detected. For the case of I386 and AMD64
both 32-bit and 64-bit filters are installed.
System architecture is strictly imposed only if flag
\-\-seccomp.block_secondary is used. The filter is applied at run time
only if the correct architecture was detected. For the case of I386
and AMD64 both 32-bit and 64-bit filters are installed.
.br
.br
......@@ -1645,6 +1646,14 @@ $ ls
Bad system call
.br
.TP
\fB\-\-seccomp.block_secondary
Enable seccomp filter and filter system call architectures so that
only the native architecture is allowed. For example, on amd64, i386
and x32 system calls are blocked as well as changing the execution
domain with personality(2) system call.
.br
.TP
\fB\-\-seccomp.drop=syscall,syscall,syscall
Enable seccomp filter, and blacklist the syscalls specified by the command.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment