Skip to content

Commit

Permalink
Feature: switch/config option to block secondary architectures
Browse files Browse the repository at this point in the history
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
  • Loading branch information
topimiettinen committed Aug 19, 2017
1 parent 0b768c0 commit d01216d
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ seccomp
seccomp.debug
seccomp.i386
seccomp.amd64
seccomp.block_secondary
seccomp.mdwx
6 changes: 4 additions & 2 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -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@
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions src/firejail/firejail.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/firejail/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 7 additions & 3 deletions src/firejail/preproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/firejail/profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 29 additions & 6 deletions src/firejail/seccomp.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,28 @@ int seccomp_load(const char *fname) {
}

// 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");
}
}

// 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
Expand All @@ -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");

Expand All @@ -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");

Expand Down Expand Up @@ -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");

Expand Down
1 change: 1 addition & 0 deletions src/fseccomp/fseccomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/fseccomp/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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)
Expand Down
19 changes: 19 additions & 0 deletions src/fseccomp/seccomp_print.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
97 changes: 64 additions & 33 deletions src/fseccomp/seccomp_secondary.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Loading

0 comments on commit d01216d

Please sign in to comment.