From 72b93c5761b5e42c5742e192f46bac1696c36f4c Mon Sep 17 00:00:00 2001 From: netblue30 Date: Wed, 2 Nov 2016 07:49:01 -0400 Subject: major cleanup --- src/firejail/errno.c | 2 - src/firejail/firejail.h | 24 +- src/firejail/join.c | 6 +- src/firejail/preproc.c | 31 +- src/firejail/protocol.c | 46 --- src/firejail/sandbox.c | 33 +- src/firejail/seccomp.c | 873 +++++++-------------------------------- src/firejail/syscall.c | 105 ----- src/firejail/util.c | 4 +- src/fnet/Makefile.in | 4 +- src/fseccomp/Makefile.in | 4 +- src/fseccomp/errno.c | 43 ++ src/fseccomp/fseccomp.h | 50 +++ src/fseccomp/main.c | 49 +++ src/fseccomp/seccomp.c | 292 +++++++++++++ src/fseccomp/seccomp_file.c | 108 +++++ src/fseccomp/seccomp_print.c | 116 ++++++ src/fseccomp/seccomp_secondary.c | 183 ++++++++ src/fseccomp/syscall.c | 84 ++++ 19 files changed, 1132 insertions(+), 925 deletions(-) delete mode 100644 src/firejail/syscall.c create mode 100644 src/fseccomp/seccomp.c create mode 100644 src/fseccomp/seccomp_file.c create mode 100644 src/fseccomp/seccomp_print.c create mode 100644 src/fseccomp/seccomp_secondary.c (limited to 'src') diff --git a/src/firejail/errno.c b/src/firejail/errno.c index 03f10bb14..8215c99a1 100644 --- a/src/firejail/errno.c +++ b/src/firejail/errno.c @@ -17,7 +17,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifdef HAVE_SECCOMP #include "firejail.h" #include @@ -205,5 +204,4 @@ char *errno_find_nr(int nr) { return "unknown"; } - #endif // HAVE_SECCOMP diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index dcec160fb..cf540ff91 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -25,7 +25,6 @@ // debug restricted shell //#define DEBUG_RESTRICTED_SHELL - // filesystem #define RUN_FIREJAIL_BASEDIR "/run" #define RUN_FIREJAIL_DIR "/run/firejail" @@ -38,8 +37,6 @@ #define RUN_RO_DIR "/run/firejail/firejail.ro.dir" #define RUN_RO_FILE "/run/firejail/firejail.ro.file" #define RUN_MNT_DIR "/run/firejail/mnt" // a tmpfs is mounted on this directory before any of the files below are created -#define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" -#define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" #define RUN_CGROUP_CFG "/run/firejail/mnt/cgroup" #define RUN_CPU_CFG "/run/firejail/mnt/cpu" #define RUN_GROUPS_CFG "/run/firejail/mnt/groups" @@ -50,6 +47,12 @@ #define RUN_BIN_DIR "/run/firejail/mnt/bin" #define RUN_PULSE_DIR "/run/firejail/mnt/pulse" +#define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter +#define RUN_SECCOMP_PROTOCOL "/run/firejail/mnt/seccomp.protocol" // protocol filter +#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_DEV_DIR "/run/firejail/mnt/dev" #define RUN_DEVLOG_FILE "/run/firejail/mnt/devlog" @@ -76,6 +79,8 @@ #define RUN_GROUP_FILE "/run/firejail/mnt/group" #define RUN_FSLOGGER_FILE "/run/firejail/mnt/fslogger" + + // profiles #define DEFAULT_USER_PROFILE "default" #define DEFAULT_ROOT_PROFILE "server" @@ -491,12 +496,14 @@ void fs_private_home_list(void); // seccomp.c +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_set(void); +int seccomp_filter_errno(void); void seccomp_print_filter_name(const char *name); void seccomp_print_filter(pid_t pid); -int seccomp_filter_errno(void); // caps.c int caps_default_filter(void); @@ -591,13 +598,10 @@ void fs_check_bin_list(void); void fs_private_bin_list(void); // protocol.c -void protocol_list(); -void protocol_print_filter_name(const char *name); -void protocol_print_filter(pid_t pid); -void protocol_store(const char *prlist); -void protocol_filter(const char *fname); void protocol_filter_save(void); void protocol_filter_load(const char *fname); +void protocol_print_filter_name(const char *name); +void protocol_print_filter(pid_t pid); // restrict_users.c void restrict_users(void); diff --git a/src/firejail/join.c b/src/firejail/join.c index 9b5fba24d..6f1e9455c 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c @@ -292,16 +292,16 @@ void join(pid_t pid, int argc, char **argv, int index) { if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP - // set protocol filter + // read cfg.protocol from file if (getuid() != 0) protocol_filter_load(RUN_PROTOCOL_CFG); if (cfg.protocol) { // not available for uid 0 - protocol_filter(RUN_SECCOMP_PROTOCOL); + seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter } // set seccomp filter if (apply_seccomp == 1) // not available for uid 0 - seccomp_set(); + seccomp_load(RUN_SECCOMP_CFG); #endif // fix qt 4.8 diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c index 27e06b556..2873571a9 100644 --- a/src/firejail/preproc.c +++ b/src/firejail/preproc.c @@ -66,8 +66,6 @@ void preproc_build_firejail_dir(void) { // build /run/firejail/mnt directory void preproc_mount_mnt_dir(void) { - struct stat s; - // mount tmpfs on top of /run/firejail/mnt if (!tmpfs_mounted) { if (arg_debug) @@ -76,6 +74,35 @@ void preproc_mount_mnt_dir(void) { errExit("mounting /run/firejail/mnt"); tmpfs_mounted = 1; fs_logger2("tmpfs", RUN_MNT_DIR); + + // create all seccomp files + // as root, create RUN_SECCOMP_I386 file + create_empty_file_as_root(RUN_SECCOMP_I386, 0644); + if (chown(RUN_SECCOMP_I386, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_I386, 0644) == -1) + errExit("chmod"); + + // as root, create RUN_SECCOMP_AMD64 file + create_empty_file_as_root(RUN_SECCOMP_AMD64, 0644); + if (chown(RUN_SECCOMP_AMD64, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_AMD64, 0644) == -1) + errExit("chmod"); + + // as root, create RUN_SECCOMP file + create_empty_file_as_root(RUN_SECCOMP_CFG, 0644); + if (chown(RUN_SECCOMP_CFG, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_CFG, 0644) == -1) + errExit("chmod"); + + // as root, create RUN_SECCOMP_PROTOCOL file + create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); + if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_PROTOCOL, 0644) == -1) + errExit("chmod"); } } diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index db6c6cad9..e8e88aee9 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c @@ -22,52 +22,6 @@ #include "firejail.h" #include "../include/seccomp.h" -// install protocol filter -void protocol_filter(const char *fname) { -#ifndef SYS_socket - if (arg_debug) - printf("No support for --protocol on this platform\n"); - return; -#else - assert(fname); - - // check file - struct stat s; - if (stat(fname, &s) == -1) { - fprintf(stderr, "Error: cannot read protocol filter file\n"); - exit(1); - } - int size = s.st_size; - - // read filter - struct sock_filter filter[32]; // big enough - memset(&filter[0], 0, sizeof(filter)); - int src = open(fname, O_RDONLY); - int rd = 0; - while (rd < size) { - int rv = read(src, (unsigned char *) filter + rd, size - rd); - if (rv == -1) { - fprintf(stderr, "Error: cannot read %s file\n", fname); - exit(1); - } - rd += rv; - } - close(src); - - // install filter - unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); - struct sock_fprog prog = { - .len = entries, - .filter = filter, - }; - - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); - return; - } -#endif -} - void protocol_filter_save(void) { // save protocol filter configuration in PROTOCOL_CFG FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index a15003d03..3942e4da6 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -559,13 +559,6 @@ assert(0); if (cfg.protocol) { if (arg_debug) printf("Build protocol filter: %s\n", cfg.protocol); - // as root, create RUN_SECCOMP_PROTOCOL file - // this is where fseccomp program will store the protocol filter - create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); - if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) - errExit("chown"); - if (chmod(RUN_SECCOMP_PROTOCOL, 0644) == -1) - errExit("chmod"); // build the seccomp filter as a regular user int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, @@ -826,13 +819,23 @@ assert(0); // set rlimits set_rlimits(); - // set seccomp + // set cpu affinity + if (cfg.cpus) { + save_cpu(); // save cpu affinity mask to CPU_CFG file + set_cpu_affinity(); + } + + // save cgroup in CGROUP_CFG file + if (cfg.cgroup) + save_cgroup(); + + // set seccomp //todo: push it down after drop_privs and/or configuring noroot #ifdef HAVE_SECCOMP // install protocol filter if (cfg.protocol) { if (arg_debug) printf("Install protocol filter: %s\n", cfg.protocol); - protocol_filter(RUN_SECCOMP_PROTOCOL); // install filter + seccomp_load(RUN_SECCOMP_PROTOCOL); // install filter protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG } @@ -847,16 +850,6 @@ assert(0); } #endif - // set cpu affinity - if (cfg.cpus) { - save_cpu(); // save cpu affinity mask to CPU_CFG file - set_cpu_affinity(); - } - - // save cgroup in CGROUP_CFG file - if (cfg.cgroup) - save_cgroup(); - //**************************************** // drop privileges or create a new user namespace //**************************************** @@ -929,8 +922,6 @@ assert(0); int status = monitor_application(app_pid); // monitor application flush_stdin(); - - if (WIFEXITED(status)) { // if we had a proper exit, return that exit status return WEXITSTATUS(status); diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 69be04a03..74d29fc9d 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -22,760 +22,203 @@ #include "firejail.h" #include "../include/seccomp.h" -#define SECSIZE 128 // initial filter size -static struct sock_filter *sfilter = NULL; -static int sfilter_alloc_size = 0; -static int sfilter_index = 0; - -// debug filter -void filter_debug(void) { - // start filter - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL - }; +int seccomp_load(const char *fname) { + assert(fname); - // print sizes - printf("SECCOMP Filter:\n"); - if (sfilter == NULL) { - printf("SECCOMP filter not allocated\n"); - return; - } - if (sfilter_index < 4) - return; - - // test the start of the filter - if (memcmp(sfilter, filter, sizeof(filter)) == 0) { - printf(" VALIDATE_ARCHITECTURE\n"); - printf(" EXAMINE_SYSCAL\n"); + // check file + struct stat s; + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error: cannot read protocol filter file\n"); + exit(1); } - - // loop trough blacklists - int i = 4; - while (i < sfilter_index) { - // minimal parsing! - unsigned char *ptr = (unsigned char *) &sfilter[i]; - int *nr = (int *) (ptr + 4); - if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { - printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); - i += 2; - } - else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { - printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); - i += 2; - } - else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { - int err = *(ptr + 13) << 8 | *(ptr + 12); - printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); - i += 2; - } - else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { - printf(" KILL_PROCESS\n"); - i++; - } - else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { - printf(" RETURN_ALLOW\n"); - i++; - } - else { - printf(" UNKNOWN ENTRY!!!\n"); - i++; + int size = s.st_size; + unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); +//printf("size %d, entries %d\n", s.st_size, entries); + + // read filter + struct sock_filter filter[entries]; + memset(&filter[0], 0, sizeof(filter)); + int src = open(fname, O_RDONLY); + int rd = 0; + while (rd < size) { + int rv = read(src, (unsigned char *) filter + rd, size - rd); + if (rv == -1) { + fprintf(stderr, "Error: cannot read %s file\n", fname); + exit(1); } + rd += rv; } -} - -// initialize filter -static void filter_init(void) { - if (sfilter) { - assert(0); - return; - } - -// if (arg_debug) -// printf("Initialize seccomp filter\n"); - // allocate a filter of SECSIZE - sfilter = malloc(sizeof(struct sock_filter) * SECSIZE); - if (!sfilter) - errExit("malloc"); - memset(sfilter, 0, sizeof(struct sock_filter) * SECSIZE); - sfilter_alloc_size = SECSIZE; - - // copy the start entries -#if defined(__x86_64__) -#define X32_SYSCALL_BIT 0x40000000 - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL, - // handle X32 ABI - 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) - }; -#else - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL - }; -#endif - sfilter_index = sizeof(filter) / sizeof(struct sock_filter); - memcpy(sfilter, filter, sizeof(filter)); -} - -static void filter_realloc(void) { - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); - if (arg_debug) - printf("Allocating more seccomp filter entries\n"); - - // allocate the new memory - struct sock_filter *old = sfilter; - sfilter = malloc(sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); - if (!sfilter) - errExit("malloc"); - memset(sfilter, 0, sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE)); - - // copy old filter - memcpy(sfilter, old, sizeof(struct sock_filter) * sfilter_alloc_size); - sfilter_alloc_size += SECSIZE; -} - -static void filter_add_whitelist(int syscall, int arg) { - (void) arg; - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); -// if (arg_debug) -// printf("Whitelisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); - - if ((sfilter_index + 2) > sfilter_alloc_size) - filter_realloc(); - - struct sock_filter filter[] = { - WHITELIST(syscall) - }; -#if 0 -{ - int i; - unsigned char *ptr = (unsigned char *) &filter[0]; - for (i = 0; i < sizeof(filter); i++, ptr++) - printf("%x, ", (*ptr) & 0xff); - printf("\n"); -} -#endif - memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); - sfilter_index += sizeof(filter) / sizeof(struct sock_filter); -} + close(src); -static void filter_add_blacklist(int syscall, int arg) { - (void) arg; - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); -// if (arg_debug) -// printf("Blacklisting syscall %d %s\n", syscall, syscall_find_nr(syscall)); - - if ((sfilter_index + 2) > sfilter_alloc_size) - filter_realloc(); - - struct sock_filter filter[] = { - BLACKLIST(syscall) - }; -#if 0 -{ - int i; - unsigned char *ptr = (unsigned char *) &filter[0]; - for (i = 0; i < sizeof(filter); i++, ptr++) - printf("%x, ", (*ptr) & 0xff); - printf("\n"); -} -#endif - memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); - sfilter_index += sizeof(filter) / sizeof(struct sock_filter); -} - -static void filter_add_errno(int syscall, int arg) { - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); -// if (arg_debug) -// printf("Errno syscall %d %d %s\n", syscall, arg, syscall_find_nr(syscall)); - - if ((sfilter_index + 2) > sfilter_alloc_size) - filter_realloc(); - - struct sock_filter filter[] = { - BLACKLIST_ERRNO(syscall, arg) - }; -#if 0 -{ - int i; - unsigned char *ptr = (unsigned char *) &filter[0]; - for (i = 0; i < sizeof(filter); i++, ptr++) - printf("%x, ", (*ptr) & 0xff); - printf("\n"); -} -#endif - memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); - sfilter_index += sizeof(filter) / sizeof(struct sock_filter); -} - -static void filter_end_blacklist(void) { - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); -// if (arg_debug) -// printf("Ending syscall filter\n"); - - if ((sfilter_index + 2) > sfilter_alloc_size) - filter_realloc(); - - struct sock_filter filter[] = { - RETURN_ALLOW - }; -#if 0 -{ - int i; - unsigned char *ptr = (unsigned char *) &filter[0]; - for (i = 0; i < sizeof(filter); i++, ptr++) - printf("%x, ", (*ptr) & 0xff); - printf("\n"); -} -#endif - memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); - sfilter_index += sizeof(filter) / sizeof(struct sock_filter); -} - -static void filter_end_whitelist(void) { - assert(sfilter); - assert(sfilter_alloc_size); - assert(sfilter_index); - if (arg_debug) - printf("Ending syscall filter\n"); - - if ((sfilter_index + 2) > sfilter_alloc_size) - filter_realloc(); - - struct sock_filter filter[] = { - KILL_PROCESS + // install filter + struct sock_fprog prog = { + .len = entries, + .filter = filter, }; -#if 0 -{ - int i; - unsigned char *ptr = (unsigned char *) &filter[0]; - for (i = 0; i < sizeof(filter); i++, ptr++) - printf("%x, ", (*ptr) & 0xff); - printf("\n"); -} -#endif - memcpy(&sfilter[sfilter_index], filter, sizeof(filter)); - sfilter_index += sizeof(filter) / sizeof(struct sock_filter); -} - - -// save seccomp filter in /run/firejail/mnt/seccomp -static void write_seccomp_file(void) { - assert(sfilter); - - int fd = open(RUN_SECCOMP_CFG, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); - if (fd == -1) - errExit("open"); - if (arg_debug) - printf("Save seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); - errno = 0; - ssize_t sz = write(fd, sfilter, sfilter_index * sizeof(struct sock_filter)); - if (sz != (ssize_t)(sfilter_index * sizeof(struct sock_filter))) { - fprintf(stderr, "Error: cannot save seccomp filter\n"); - exit(1); + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); + return 1; } - SET_PERMS_FD(fd, 0, 0, S_IRUSR | S_IWUSR); - close(fd); + + return 0; } -// read seccomp filter from /run/firejail/mnt/seccomp -static void read_seccomp_file(const char *fname) { - assert(sfilter == NULL && sfilter_index == 0); - // check file - struct stat s; - if (stat(fname, &s) == -1) { - fprintf(stderr, "Warning: seccomp file not found\n"); - return; - } - ssize_t sz = s.st_size; - if (sz == 0 || (sz % sizeof(struct sock_filter)) != 0) { - fprintf(stderr, "Error: invalid seccomp file\n"); - exit(1); - } - sfilter = malloc(sz); - if (!sfilter) - errExit("malloc"); - - // read file - /* coverity[toctou] */ - int fd = open(fname,O_RDONLY); - if (fd == -1) - errExit("open"); - errno = 0; - ssize_t size = read(fd, sfilter, sz); - if (size != sz) { - fprintf(stderr, "Error: invalid seccomp file\n"); - exit(1); - } - sfilter_index = sz / sizeof(struct sock_filter); - if (arg_debug) - printf("Read seccomp filter, size %u bytes\n", (unsigned) (sfilter_index * sizeof(struct sock_filter))); - - close(fd); - - if (arg_debug) - filter_debug(); -} // i386 filter installed on amd64 architectures void seccomp_filter_32(void) { - // hardcoded syscall values - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE_32, - EXAMINE_SYSCALL, - BLACKLIST(21), // mount - BLACKLIST(52), // umount2 -// todo: implement --allow-debuggers - BLACKLIST(26), // ptrace - BLACKLIST(283), // kexec_load - BLACKLIST(341), // name_to_handle_at - BLACKLIST(342), // open_by_handle_at - BLACKLIST(127), // create_module - BLACKLIST(128), // init_module - BLACKLIST(350), // finit_module - BLACKLIST(129), // delete_module - BLACKLIST(110), // iopl - BLACKLIST(101), // ioperm - BLACKLIST(289), // ioprio_set - BLACKLIST(87), // swapon - BLACKLIST(115), // swapoff - BLACKLIST(103), // syslog - BLACKLIST(347), // process_vm_readv - BLACKLIST(348), // process_vm_writev - BLACKLIST(135), // sysfs - BLACKLIST(149), // _sysctl - BLACKLIST(124), // adjtimex - BLACKLIST(343), // clock_adjtime - BLACKLIST(253), // lookup_dcookie - BLACKLIST(336), // perf_event_open - BLACKLIST(338), // fanotify_init - BLACKLIST(349), // kcmp - BLACKLIST(286), // add_key - BLACKLIST(287), // request_key - BLACKLIST(288), // keyctl - BLACKLIST(86), // uselib - BLACKLIST(51), // acct - BLACKLIST(123), // modify_ldt - BLACKLIST(217), // pivot_root - BLACKLIST(245), // io_setup - BLACKLIST(246), // io_destroy - BLACKLIST(247), // io_getevents - BLACKLIST(248), // io_submit - BLACKLIST(249), // io_cancel - BLACKLIST(257), // remap_file_pages - BLACKLIST(274), // mbind - BLACKLIST(275), // get_mempolicy - BLACKLIST(276), // set_mempolicy - BLACKLIST(294), // migrate_pages - BLACKLIST(317), // move_pages - BLACKLIST(316), // vmsplice - BLACKLIST(61), // chroot - BLACKLIST(88), // reboot - BLACKLIST(169), // nfsservctl - BLACKLIST(130), // get_kernel_syms - - RETURN_ALLOW - }; + if (arg_debug) + printf("Build secondary 32-bit filter\n"); - struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), - .filter = filter, - }; + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "secondary", "32", RUN_SECCOMP_I386); + if (rv) + exit(rv); - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - ; - } - else if (arg_debug) { - printf("Dual i386/amd64 seccomp filter configured\n"); + 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) { - // hardcoded syscall values - struct sock_filter filter[] = { - VALIDATE_ARCHITECTURE_64, - EXAMINE_SYSCALL, - BLACKLIST(165), // mount - BLACKLIST(166), // umount2 -// todo: implement --allow-debuggers - BLACKLIST(101), // ptrace - BLACKLIST(246), // kexec_load - BLACKLIST(304), // open_by_handle_at - BLACKLIST(303), // name_to_handle_at - BLACKLIST(174), // create_module - BLACKLIST(175), // init_module - BLACKLIST(313), // finit_module - BLACKLIST(176), // delete_module - BLACKLIST(172), // iopl - BLACKLIST(173), // ioperm - BLACKLIST(251), // ioprio_set - BLACKLIST(167), // swapon - BLACKLIST(168), // swapoff - BLACKLIST(103), // syslog - BLACKLIST(310), // process_vm_readv - BLACKLIST(311), // process_vm_writev - BLACKLIST(139), // sysfs - BLACKLIST(156), // _sysctl - BLACKLIST(159), // adjtimex - BLACKLIST(305), // clock_adjtime - BLACKLIST(212), // lookup_dcookie - BLACKLIST(298), // perf_event_open - BLACKLIST(300), // fanotify_init - BLACKLIST(312), // kcmp - BLACKLIST(248), // add_key - BLACKLIST(249), // request_key - BLACKLIST(250), // keyctl - BLACKLIST(134), // uselib - BLACKLIST(163), // acct - BLACKLIST(154), // modify_ldt - BLACKLIST(155), // pivot_root - BLACKLIST(206), // io_setup - BLACKLIST(207), // io_destroy - BLACKLIST(208), // io_getevents - BLACKLIST(209), // io_submit - BLACKLIST(210), // io_cancel - BLACKLIST(216), // remap_file_pages - BLACKLIST(237), // mbind - BLACKLIST(239), // get_mempolicy - BLACKLIST(238), // set_mempolicy - BLACKLIST(256), // migrate_pages - BLACKLIST(279), // move_pages - BLACKLIST(278), // vmsplice - BLACKLIST(161), // chroot - BLACKLIST(184), // tuxcall - BLACKLIST(169), // reboot - BLACKLIST(180), // nfsservctl - BLACKLIST(177), // get_kernel_syms - - RETURN_ALLOW - }; + if (arg_debug) + printf("Build secondary 64-bit filter\n"); - struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), - .filter = filter, - }; + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "secondary", "64", RUN_SECCOMP_AMD64); + if (rv) + exit(rv); - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - ; - } - else if (arg_debug) { - printf("Dual i386/amd64 seccomp filter configured\n"); + if (seccomp_load(RUN_SECCOMP_AMD64) == 0) { + if (arg_debug) + printf("Dual i386/amd64 seccomp filter configured\n"); } } // drop filter for seccomp option int seccomp_filter_drop(int enforce_seccomp) { - filter_init(); - // default seccomp - if (cfg.seccomp_list_drop == NULL) { + if (cfg.seccomp_list_drop == NULL && cfg.seccomp_list == NULL) { #if defined(__x86_64__) seccomp_filter_32(); #endif #if defined(__i386__) seccomp_filter_64(); #endif - -#ifdef SYS_mount - filter_add_blacklist(SYS_mount, 0); -#endif -#ifdef SYS_umount2 - filter_add_blacklist(SYS_umount2, 0); -#endif - - if (!arg_allow_debuggers) { -#ifdef SYS_ptrace - filter_add_blacklist(SYS_ptrace, 0); -#endif - } - -#ifdef SYS_kexec_load - filter_add_blacklist(SYS_kexec_load, 0); -#endif -#ifdef SYS_kexec_file_load - filter_add_blacklist(SYS_kexec_file_load, 0); -#endif -#ifdef SYS_open_by_handle_at - filter_add_blacklist(SYS_open_by_handle_at, 0); -#endif -#ifdef SYS_name_to_handle_at - filter_add_blacklist(SYS_name_to_handle_at, 0); -#endif -#ifdef SYS_init_module - filter_add_blacklist(SYS_init_module, 0); -#endif -#ifdef SYS_finit_module // introduced in 2013 - filter_add_blacklist(SYS_finit_module, 0); -#endif -#ifdef SYS_create_module - filter_add_blacklist(SYS_create_module, 0); -#endif -#ifdef SYS_delete_module - filter_add_blacklist(SYS_delete_module, 0); -#endif -#ifdef SYS_iopl - filter_add_blacklist(SYS_iopl, 0); -#endif -#ifdef SYS_ioperm - filter_add_blacklist(SYS_ioperm, 0); -#endif -#ifdef SYS_ioprio_set - filter_add_blacklist(SYS_ioprio_set, 0); -#endif -#ifdef SYS_ni_syscall // new io permissions call on arm devices - filter_add_blacklist(SYS_ni_syscall, 0); -#endif -#ifdef SYS_swapon - filter_add_blacklist(SYS_swapon, 0); -#endif -#ifdef SYS_swapoff - filter_add_blacklist(SYS_swapoff, 0); -#endif -#ifdef SYS_syslog - filter_add_blacklist(SYS_syslog, 0); -#endif - if (!arg_allow_debuggers) { -#ifdef SYS_process_vm_readv - filter_add_blacklist(SYS_process_vm_readv, 0); -#endif - } - -#ifdef SYS_process_vm_writev - filter_add_blacklist(SYS_process_vm_writev, 0); -#endif - -// mknod removed in 0.9.29 - it brakes Zotero extension -//#ifdef SYS_mknod -// filter_add_blacklist(SYS_mknod, 0); -//#endif - - // new syscalls in 0.9,23 -#ifdef SYS_sysfs - filter_add_blacklist(SYS_sysfs, 0); -#endif -#ifdef SYS__sysctl - filter_add_blacklist(SYS__sysctl, 0); -#endif -#ifdef SYS_adjtimex - filter_add_blacklist(SYS_adjtimex, 0); -#endif -#ifdef SYS_clock_adjtime - filter_add_blacklist(SYS_clock_adjtime, 0); -#endif -#ifdef SYS_lookup_dcookie - filter_add_blacklist(SYS_lookup_dcookie, 0); -#endif -#ifdef SYS_perf_event_open - filter_add_blacklist(SYS_perf_event_open, 0); -#endif -#ifdef SYS_fanotify_init - filter_add_blacklist(SYS_fanotify_init, 0); -#endif -#ifdef SYS_kcmp - filter_add_blacklist(SYS_kcmp, 0); -#endif - -// 0.9.32 -#ifdef SYS_add_key - filter_add_blacklist(SYS_add_key, 0); -#endif -#ifdef SYS_request_key - filter_add_blacklist(SYS_request_key, 0); -#endif -#ifdef SYS_keyctl - filter_add_blacklist(SYS_keyctl, 0); -#endif -#ifdef SYS_uselib - filter_add_blacklist(SYS_uselib, 0); -#endif -#ifdef SYS_acct - filter_add_blacklist(SYS_acct, 0); -#endif -#ifdef SYS_modify_ldt - filter_add_blacklist(SYS_modify_ldt, 0); -#endif - //#ifdef SYS_unshare - // filter_add_blacklist(SYS_unshare, 0); - //#endif -#ifdef SYS_pivot_root - filter_add_blacklist(SYS_pivot_root, 0); -#endif - //#ifdef SYS_quotactl - // filter_add_blacklist(SYS_quotactl, 0); - //#endif -#ifdef SYS_io_setup - filter_add_blacklist(SYS_io_setup, 0); -#endif -#ifdef SYS_io_destroy - filter_add_blacklist(SYS_io_destroy, 0); -#endif -#ifdef SYS_io_getevents - filter_add_blacklist(SYS_io_getevents, 0); -#endif -#ifdef SYS_io_submit - filter_add_blacklist(SYS_io_submit, 0); -#endif -#ifdef SYS_io_cancel - filter_add_blacklist(SYS_io_cancel, 0); -#endif -#ifdef SYS_remap_file_pages - filter_add_blacklist(SYS_remap_file_pages, 0); -#endif -#ifdef SYS_mbind - filter_add_blacklist(SYS_mbind, 0); -#endif -#ifdef SYS_get_mempolicy - filter_add_blacklist(SYS_get_mempolicy, 0); -#endif -#ifdef SYS_set_mempolicy - filter_add_blacklist(SYS_set_mempolicy, 0); -#endif -#ifdef SYS_migrate_pages - filter_add_blacklist(SYS_migrate_pages, 0); -#endif -#ifdef SYS_move_pages - filter_add_blacklist(SYS_move_pages, 0); -#endif -#ifdef SYS_vmsplice - filter_add_blacklist(SYS_vmsplice, 0); -#endif -#ifdef SYS_chroot - filter_add_blacklist(SYS_chroot, 0); -#endif - //#ifdef SYS_set_robust_list - // filter_add_blacklist(SYS_set_robust_list, 0); - //#endif - //#ifdef SYS_get_robust_list - // filter_add_blacklist(SYS_get_robust_list, 0); - //#endif - - // CHECK_SECCOMP(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(clone), 1, - // SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER))); - -// 0.9.39 -#ifdef SYS_tuxcall - filter_add_blacklist(SYS_tuxcall, 0); -#endif -#ifdef SYS_reboot - filter_add_blacklist(SYS_reboot, 0); -#endif -#ifdef SYS_nfsservctl - filter_add_blacklist(SYS_nfsservctl, 0); -#endif -#ifdef SYS_get_kernel_syms - filter_add_blacklist(SYS_get_kernel_syms, 0); -#endif - + if (arg_debug) + printf("Build default seccomp filter\n"); + // build the seccomp filter as a regular user + int rv; + if (arg_allow_debuggers) + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "default", RUN_SECCOMP_CFG, "allow-debuggers"); + else + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, + PATH_FSECCOMP, "default", RUN_SECCOMP_CFG); + if (rv) + exit(rv); } // default seccomp filter with additional drop list - if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { - if (syscall_check_list(cfg.seccomp_list, filter_add_blacklist, 0)) { - fprintf(stderr, "Error: cannot load seccomp filter\n"); + else if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { +#if defined(__x86_64__) + seccomp_filter_32(); +#endif +#if defined(__i386__) + seccomp_filter_64(); +#endif + if (arg_debug) + printf("Build default+drop seccomp filter\n"); + if (strlen(cfg.seccomp_list) == 0) { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); exit(1); } + + // 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"); + else + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, + PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list); + if (rv) + exit(rv); } - // drop list + + // drop list without defaults - secondary filters are not installed else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { - if (syscall_check_list(cfg.seccomp_list_drop, filter_add_blacklist, 0)) { - fprintf(stderr, "Error: cannot load seccomp filter\n"); + if (arg_debug) + printf("Build drop seccomp filter\n"); + if (strlen(cfg.seccomp_list_drop) == 0) { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); exit(1); } - } - - - filter_end_blacklist(); - if (arg_debug) - filter_debug(); - - // save seccomp filter in /run/firejail/mnt/seccomp - // in order to use it in --join operations - write_seccomp_file(); - - - struct sock_fprog prog = { - .len = sfilter_index, - .filter = sfilter, - }; - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - if (enforce_seccomp) { - fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); - exit(1); - } + // 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"); else - fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop); - return 1; + if (rv) + exit(rv); + } + else { + assert(0); } - return 0; + // load the filter + if (seccomp_load(RUN_SECCOMP_CFG) == 0) { + if (arg_debug) + printf("seccomp filter configured\n"); + } + else if (enforce_seccomp) { + fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); + exit(1); + } + + if (arg_debug) + sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, + PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); + + return seccomp_load(RUN_SECCOMP_CFG); } // keep filter for seccomp option int seccomp_filter_keep(void) { - filter_init(); - - // these 4 syscalls are used by firejail after the seccomp filter is initialized - filter_add_whitelist(SYS_setuid, 0); - filter_add_whitelist(SYS_setgid, 0); - filter_add_whitelist(SYS_setgroups, 0); - filter_add_whitelist(SYS_dup, 0); - - // apply keep list - if (cfg.seccomp_list_keep) { - if (syscall_check_list(cfg.seccomp_list_keep, filter_add_whitelist, 0)) { - fprintf(stderr, "Error: cannot load seccomp filter\n"); - exit(1); - } + if (arg_debug) + printf("Build drop seccomp filter\n"); + if (strlen(cfg.seccomp_list_keep) == 0) { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); + exit(1); } - filter_end_whitelist(); + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, + PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, cfg.seccomp_list_keep); + if (rv) + exit(rv); if (arg_debug) - filter_debug(); - - // save seccomp filter in /run/firejail/mnt/seccomp - // in order to use it in --join operations - write_seccomp_file(); - + printf("seccomp filter configured\n"); - struct sock_fprog prog = { - .len = sfilter_index, - .filter = sfilter, - }; - - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); - return 1; - } - else if (arg_debug) { - printf("seccomp enabled\n"); - } - - return 0; + + return seccomp_load(RUN_SECCOMP_CFG); } // errno filter for seccomp option int seccomp_filter_errno(void) { +#if 0 //todo: disabled temporarely, bring it back int i; int higest_errno = errno_highest_nr(); filter_init(); @@ -798,42 +241,11 @@ int seccomp_filter_errno(void) { // save seccomp filter in /run/firejail/mnt/seccomp // in order to use it in --join operations write_seccomp_file(); - - struct sock_fprog prog = { - .len = sfilter_index, - .filter = sfilter, - }; - - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); - return 1; - } - else if (arg_debug) { - printf("seccomp enabled\n"); - } - + return seccomp_load(RUN_SECCOMP_CFG); +#else +printf("*** --seccomp. is temporarily disabled, it will be brought back soon ***\n"); return 0; -} - - - -void seccomp_set(void) { - // read seccomp filter from /runp/firejail/mnt/seccomp - read_seccomp_file(RUN_SECCOMP_CFG); - - // apply filter - struct sock_fprog prog = { - .len = sfilter_index, - .filter = sfilter, - }; - - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); - return; - } - else if (arg_debug) { - printf("seccomp enabled\n"); - } +#endif } void seccomp_print_filter_name(const char *name) { @@ -890,10 +302,11 @@ void seccomp_print_filter(pid_t pid) { exit(1); } - // read and print the filter - read_seccomp_file(fname); - drop_privs(1); - filter_debug(); + // read and print the filter - run this as root, the user doesn't have access + int rv = sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, + PATH_FSECCOMP, "print", fname); + if (rv) + exit(rv); free(fname); exit(0); diff --git a/src/firejail/syscall.c b/src/firejail/syscall.c deleted file mode 100644 index f405f23c8..000000000 --- a/src/firejail/syscall.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2014-2016 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. -*/ - -#ifdef HAVE_SECCOMP -#include "firejail.h" -#include - -typedef struct { - char *name; - int nr; -} SyscallEntry; - -static SyscallEntry syslist[] = { -// -// code generated using tools/extract-syscall -// -#include "../include/syscall.h" -// -// end of generated code -// -}; // end of syslist - -const char *syscall_find_nr(int nr) { - int i; - int elems = sizeof(syslist) / sizeof(syslist[0]); - for (i = 0; i < elems; i++) { - if (nr == syslist[i].nr) - return syslist[i].name; - } - - return "unknown"; -} - -// return -1 if error, or syscall number -static int syscall_find_name(const char *name) { - int i; - int elems = sizeof(syslist) / sizeof(syslist[0]); - for (i = 0; i < elems; i++) { - if (strcmp(name, syslist[i].name) == 0) - return syslist[i].nr; - } - - return -1; -} - -// return 1 if error, 0 if OK -int syscall_check_list(const char *slist, void (*callback)(int syscall, int arg), int arg) { - // don't allow empty lists - if (slist == NULL || *slist == '\0') { - fprintf(stderr, "Error: empty syscall lists are not allowed\n"); - return -1; - } - - // work on a copy of the string - char *str = strdup(slist); - if (!str) - errExit("strdup"); - - char *ptr = str; - char *start = str; - while (*ptr != '\0') { - if (islower(*ptr) || isdigit(*ptr) || *ptr == '_') - ; - else if (*ptr == ',') { - *ptr = '\0'; - int nr = syscall_find_name(start); - if (nr == -1) - fprintf(stderr, "Warning: syscall %s not found\n", start); - else if (callback != NULL) - callback(nr, arg); - - start = ptr + 1; - } - ptr++; - } - if (*start != '\0') { - int nr = syscall_find_name(start); - if (nr == -1) - fprintf(stderr, "Warning: syscall %s not found\n", start); - else if (callback != NULL) - callback(nr, arg); - } - - free(str); - return 0; -} - -#endif // HAVE_SECCOMP diff --git a/src/firejail/util.c b/src/firejail/util.c index 8d3b9d3cd..9752504e5 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -721,13 +721,13 @@ void create_empty_file_as_root(const char *fname, mode_t mode) { if (arg_debug) printf("Creating empty %s file\n", fname); - /* coverity[toctou] */ FILE *fp = fopen(fname, "w"); if (!fp) errExit("fopen"); - SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); fclose(fp); + if (chmod(fname, mode) == -1) + errExit("chmod"); } } diff --git a/src/fnet/Makefile.in b/src/fnet/Makefile.in index 1bfb4c68d..b515d2333 100644 --- a/src/fnet/Makefile.in +++ b/src/fnet/Makefile.in @@ -30,11 +30,11 @@ BINOBJS = $(foreach file, $(OBJS), $file) CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread -%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/libnetlink.h $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ fnet: $(OBJS) ../lib/libnetlink.o ../lib/common.o - $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o $(LIBS) $(EXTRA_LDFLAGS) clean:; rm -f *.o fnet diff --git a/src/fseccomp/Makefile.in b/src/fseccomp/Makefile.in index e7edd1b8f..110d2c95f 100644 --- a/src/fseccomp/Makefile.in +++ b/src/fseccomp/Makefile.in @@ -30,11 +30,11 @@ BINOBJS = $(foreach file, $(OBJS), $file) CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread -%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/euid_common.h ../include/libnetlink.h ../include/pid.h ../include/syscall.h +%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ fseccomp: $(OBJS) ../lib/libnetlink.o ../lib/common.o - $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) clean:; rm -f *.o fseccomp diff --git a/src/fseccomp/errno.c b/src/fseccomp/errno.c index 625f484bd..dbee916d4 100644 --- a/src/fseccomp/errno.c +++ b/src/fseccomp/errno.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" #include @@ -151,6 +170,30 @@ static ErrnoEntry errnolist[] = { #endif }; +int errno_find_name(const char *name) { + int i; + int elems = sizeof(errnolist) / sizeof(errnolist[0]); + for (i = 0; i < elems; i++) { + if (strcasecmp(name, errnolist[i].name) == 0) + return errnolist[i].nr; + } + + return -1; +} + +char *errno_find_nr(int nr) { + int i; + int elems = sizeof(errnolist) / sizeof(errnolist[0]); + for (i = 0; i < elems; i++) { + if (nr == errnolist[i].nr) + return errnolist[i].name; + } + + return "unknown"; +} + + + void errno_print(void) { int i; int elems = sizeof(errnolist) / sizeof(errnolist[0]); diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h index 57757ea6c..504f1c23f 100644 --- a/src/fseccomp/fseccomp.h +++ b/src/fseccomp/fseccomp.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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. +*/ #ifndef FSECCOMP_H #define FSECCOMP_H #include @@ -8,11 +27,42 @@ // 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_find_name(const char *name); +char *syscall_find_nr(int nr); // errno.c void errno_print(void); +int errno_find_name(const char *name); +char *errno_find_nr(int nr); // protocol.c void protocol_print(void); 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); + +// seccomp_file.c +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_end_blacklist(int fd); +void filter_end_whitelist(int fd); + +// seccomp.c +// default list +void seccomp_default(const char *fname, int allow_debuggers); +// drop list +void seccomp_drop(const char *fname, char *list, int allow_debuggers); +// default+drop list +void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); +// whitelisted filter +void seccomp_keep(const char *fname, char *list); + +// seccomp_print +void filter_print(const char *fname); + #endif diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index f53e2ef8b..22b13bcd9 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" static void usage(void) { @@ -6,6 +25,16 @@ static void usage(void) { printf("\tfseccomp debug-errnos\n"); printf("\tfseccomp debug-protocols\n"); printf("\tfseccomp protocol build list file\n"); + printf("\tfseccomp secondary 64 file\n"); + 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 print file\n"); } int main(int argc, char **argv) { @@ -33,6 +62,26 @@ printf("\n"); protocol_print(); else if (argc == 5 && strcmp(argv[1], "protocol") == 0 && strcmp(argv[2], "build") == 0) protocol_build_filter(argv[3], argv[4]); + else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "64") == 0) + 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 == 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) + 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 == 3 && strcmp(argv[1], "print") == 0) + filter_print(argv[2]); else { fprintf(stderr, "Error fseccomp: invalid arguments\n"); return 1; diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c new file mode 100644 index 000000000..cc6edc8ca --- /dev/null +++ b/src/fseccomp/seccomp.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +static void add_default_list(int fd, int allow_debuggers) { +#ifdef SYS_mount + filter_add_blacklist(fd, SYS_mount, 0); +#endif +#ifdef SYS_umount2 + filter_add_blacklist(fd, SYS_umount2, 0); +#endif + + if (!allow_debuggers) { +#ifdef SYS_ptrace + filter_add_blacklist(fd, SYS_ptrace, 0); +#endif + } + +#ifdef SYS_kexec_load + filter_add_blacklist(fd, SYS_kexec_load, 0); +#endif +#ifdef SYS_kexec_file_load + filter_add_blacklist(fd, SYS_kexec_file_load, 0); +#endif +#ifdef SYS_open_by_handle_at + filter_add_blacklist(fd, SYS_open_by_handle_at, 0); +#endif +#ifdef SYS_name_to_handle_at + filter_add_blacklist(fd, SYS_name_to_handle_at, 0); +#endif +#ifdef SYS_init_module + filter_add_blacklist(fd, SYS_init_module, 0); +#endif +#ifdef SYS_finit_module + filter_add_blacklist(fd, SYS_finit_module, 0); +#endif +#ifdef SYS_create_module + filter_add_blacklist(fd, SYS_create_module, 0); +#endif +#ifdef SYS_delete_module + filter_add_blacklist(fd, SYS_delete_module, 0); +#endif +#ifdef SYS_iopl + filter_add_blacklist(fd, SYS_iopl, 0); +#endif +#ifdef SYS_ioperm + filter_add_blacklist(fd, SYS_ioperm, 0); +#endif +#ifdef SYS_ioprio_set + filter_add_blacklist(fd, SYS_ioprio_set, 0); +#endif +#ifdef SYS_ni_syscall + filter_add_blacklist(fd, SYS_ni_syscall, 0); +#endif +#ifdef SYS_swapon + filter_add_blacklist(fd, SYS_swapon, 0); +#endif +#ifdef SYS_swapoff + filter_add_blacklist(fd, SYS_swapoff, 0); +#endif +#ifdef SYS_syslog + filter_add_blacklist(fd, SYS_syslog, 0); +#endif + + if (!allow_debuggers) { +#ifdef SYS_process_vm_readv + filter_add_blacklist(fd, SYS_process_vm_readv, 0); +#endif + } + +#ifdef SYS_process_vm_writev + filter_add_blacklist(fd, SYS_process_vm_writev, 0); +#endif + + // mknod removed in 0.9.29 - it brakes Zotero extension + //#ifdef SYS_mknod + // filter_add_blacklist(SYS_mknod, 0); + //#endif + +#ifdef SYS_sysfs + filter_add_blacklist(fd, SYS_sysfs, 0); +#endif +#ifdef SYS__sysctl + filter_add_blacklist(fd, SYS__sysctl, 0); +#endif +#ifdef SYS_adjtimex + filter_add_blacklist(fd, SYS_adjtimex, 0); +#endif +#ifdef SYS_clock_adjtime + filter_add_blacklist(fd, SYS_clock_adjtime, 0); +#endif +#ifdef SYS_lookup_dcookie + filter_add_blacklist(fd, SYS_lookup_dcookie, 0); +#endif +#ifdef SYS_perf_event_open + filter_add_blacklist(fd, SYS_perf_event_open, 0); +#endif +#ifdef SYS_fanotify_init + filter_add_blacklist(fd, SYS_fanotify_init, 0); +#endif +#ifdef SYS_kcmp + filter_add_blacklist(fd, SYS_kcmp, 0); +#endif +#ifdef SYS_add_key + filter_add_blacklist(fd, SYS_add_key, 0); +#endif +#ifdef SYS_request_key + filter_add_blacklist(fd, SYS_request_key, 0); +#endif +#ifdef SYS_keyctl + filter_add_blacklist(fd, SYS_keyctl, 0); +#endif +#ifdef SYS_uselib + filter_add_blacklist(fd, SYS_uselib, 0); +#endif +#ifdef SYS_acct + filter_add_blacklist(fd, SYS_acct, 0); +#endif +#ifdef SYS_modify_ldt + filter_add_blacklist(fd, SYS_modify_ldt, 0); +#endif +#ifdef SYS_pivot_root + filter_add_blacklist(fd, SYS_pivot_root, 0); +#endif +#ifdef SYS_io_setup + filter_add_blacklist(fd, SYS_io_setup, 0); +#endif +#ifdef SYS_io_destroy + filter_add_blacklist(fd, SYS_io_destroy, 0); +#endif +#ifdef SYS_io_getevents + filter_add_blacklist(fd, SYS_io_getevents, 0); +#endif +#ifdef SYS_io_submit + filter_add_blacklist(fd, SYS_io_submit, 0); +#endif +#ifdef SYS_io_cancel + filter_add_blacklist(fd, SYS_io_cancel, 0); +#endif +#ifdef SYS_remap_file_pages + filter_add_blacklist(fd, SYS_remap_file_pages, 0); +#endif +#ifdef SYS_mbind + filter_add_blacklist(fd, SYS_mbind, 0); +#endif +#ifdef SYS_get_mempolicy + filter_add_blacklist(fd, SYS_get_mempolicy, 0); +#endif +#ifdef SYS_set_mempolicy + filter_add_blacklist(fd, SYS_set_mempolicy, 0); +#endif +#ifdef SYS_migrate_pages + filter_add_blacklist(fd, SYS_migrate_pages, 0); +#endif +#ifdef SYS_move_pages + filter_add_blacklist(fd, SYS_move_pages, 0); +#endif +#ifdef SYS_vmsplice + filter_add_blacklist(fd, SYS_vmsplice, 0); +#endif +#ifdef SYS_chroot + filter_add_blacklist(fd, SYS_chroot, 0); +#endif +#ifdef SYS_tuxcall + filter_add_blacklist(fd, SYS_tuxcall, 0); +#endif +#ifdef SYS_reboot + filter_add_blacklist(fd, SYS_reboot, 0); +#endif +#ifdef SYS_nfsservctl + filter_add_blacklist(fd, SYS_nfsservctl, 0); +#endif +#ifdef SYS_get_kernel_syms + filter_add_blacklist(fd, SYS_get_kernel_syms, 0); +#endif +} + +// default list +void seccomp_default(const char *fname, int allow_debuggers) { + assert(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); + } + + // build filter + filter_init(fd); + add_default_list(fd, allow_debuggers); + filter_end_blacklist(fd); + + // close file + close(fd); +} + +// drop list +void seccomp_drop(const char *fname, char *list, int allow_debuggers) { + assert(fname); + (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); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + // build filter + filter_init(fd); + if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { + fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); + exit(1); + } + filter_end_blacklist(fd); + + // close file + close(fd); +} + +// default+drop +void seccomp_default_drop(const char *fname, char *list, int allow_debuggers) { + assert(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); + } + + // build filter + filter_init(fd); + add_default_list(fd, allow_debuggers); + if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { + fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); + exit(1); + } + filter_end_blacklist(fd); + + // close file + 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); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + // build filter + filter_init(fd); + // these 4 syscalls are used by firejail after the seccomp filter is initialized + filter_add_whitelist(fd, SYS_setuid, 0); + filter_add_whitelist(fd, SYS_setgid, 0); + filter_add_whitelist(fd, SYS_setgroups, 0); + filter_add_whitelist(fd, SYS_dup, 0); + filter_add_whitelist(fd, SYS_prctl, 0); + + if (syscall_check_list(list, filter_add_whitelist, fd, 0)) { + fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); + exit(1); + } + + filter_end_whitelist(fd); + + // close file + close(fd); +} + diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c new file mode 100644 index 000000000..10ef9dd31 --- /dev/null +++ b/src/fseccomp/seccomp_file.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +static void write_to_file(int fd, void *data, int size) { + assert(data); + assert(size); + + int written = 0; + while (written < size) { + int rv = write(fd, (unsigned char *) data + written, size - written); + if (rv == -1) { + fprintf(stderr, "Error fseccomp: cannot write seccomp file\n"); + exit(1); + } + written += rv; + } +} + +void filter_init(int fd) { +#if defined(__x86_64__) +#define X32_SYSCALL_BIT 0x40000000 + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL, + // handle X32 ABI + 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) + }; +#else + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL + }; +#endif + +#if 0 +{ + int i; + unsigned char *ptr = (unsigned char *) &filter[0]; + for (i = 0; i < sizeof(filter); i++, ptr++) + printf("%x, ", (*ptr) & 0xff); + printf("\n"); +} +#endif + + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_add_whitelist(int fd, int syscall, int arg) { + (void) arg; + + struct sock_filter filter[] = { + WHITELIST(syscall) + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_add_blacklist(int fd, int syscall, int arg) { + (void) arg; + + struct sock_filter filter[] = { + BLACKLIST(syscall) + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_add_errno(int fd, int syscall, int arg) { + struct sock_filter filter[] = { + BLACKLIST_ERRNO(syscall, arg) + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_end_blacklist(int fd) { + struct sock_filter filter[] = { + RETURN_ALLOW + }; + write_to_file(fd, filter, sizeof(filter)); +} + +void filter_end_whitelist(int fd) { + struct sock_filter filter[] = { + KILL_PROCESS + }; + write_to_file(fd, filter, sizeof(filter)); +} + diff --git a/src/fseccomp/seccomp_print.c b/src/fseccomp/seccomp_print.c new file mode 100644 index 000000000..7dc983b12 --- /dev/null +++ b/src/fseccomp/seccomp_print.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +static struct sock_filter *filter = NULL; +static int filter_cnt = 0; + +static void load_seccomp(const char *fname) { + assert(fname); + + // check file + struct stat s; + if (stat(fname, &s) == -1) { + fprintf(stderr, "Error fseccomp: cannot read protocol filter file\n"); + exit(1); + } + int size = s.st_size; + unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); + filter_cnt = entries; +//printf("size %d, entries %d\n", s.st_size, entries); + + filter = malloc(sizeof(struct sock_filter) * entries); + if (!filter) + errExit("malloc"); + + // read filter + memset(filter, 0, sizeof(struct sock_filter) * entries); + int src = open(fname, O_RDONLY); + int rd = 0; + while (rd < size) { + int rv = read(src, (unsigned char *) filter + rd, size - rd); + if (rv == -1) { + fprintf(stderr, "Error fseccomp: cannot read %s file\n", fname); + exit(1); + } + rd += rv; + } + close(src); +} + +// debug filter +void filter_print(const char *fname) { + assert(fname); + load_seccomp(fname); + + // start filter + struct sock_filter start[] = { + VALIDATE_ARCHITECTURE, + EXAMINE_SYSCALL + }; + + // print sizes + printf("SECCOMP Filter:\n"); + + // test the start of the filter + if (memcmp(&start[0], filter, sizeof(start)) == 0) { + printf(" VALIDATE_ARCHITECTURE\n"); + printf(" EXAMINE_SYSCAL\n"); + } + else { + printf("Invalid seccomp filter %s\n", fname); + return; + } + + // loop trough blacklists + int i = 4; + while (i < filter_cnt) { + // minimal parsing! + unsigned char *ptr = (unsigned char *) &filter[i]; + int *nr = (int *) (ptr + 4); + if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { + printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); + i += 2; + } + else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { + printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); + i += 2; + } + else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { + int err = *(ptr + 13) << 8 | *(ptr + 12); + printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); + i += 2; + } + else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { + printf(" KILL_PROCESS\n"); + i++; + } + else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { + printf(" RETURN_ALLOW\n"); + i++; + } + else { + printf(" UNKNOWN ENTRY!!!\n"); + i++; + } + } +} diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c new file mode 100644 index 000000000..a856e5aef --- /dev/null +++ b/src/fseccomp/seccomp_secondary.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" +#include "../include/seccomp.h" +#include + +void seccomp_secondary_64(const char *fname) { + // hardcoded syscall values + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE_64, + EXAMINE_SYSCALL, + BLACKLIST(165), // mount + BLACKLIST(166), // umount2 +// todo: implement --allow-debuggers + BLACKLIST(101), // ptrace + BLACKLIST(246), // kexec_load + BLACKLIST(304), // open_by_handle_at + BLACKLIST(303), // name_to_handle_at + BLACKLIST(174), // create_module + BLACKLIST(175), // init_module + BLACKLIST(313), // finit_module + BLACKLIST(176), // delete_module + BLACKLIST(172), // iopl + BLACKLIST(173), // ioperm + BLACKLIST(251), // ioprio_set + BLACKLIST(167), // swapon + BLACKLIST(168), // swapoff + BLACKLIST(103), // syslog + BLACKLIST(310), // process_vm_readv + BLACKLIST(311), // process_vm_writev + BLACKLIST(139), // sysfs + BLACKLIST(156), // _sysctl + BLACKLIST(159), // adjtimex + BLACKLIST(305), // clock_adjtime + BLACKLIST(212), // lookup_dcookie + BLACKLIST(298), // perf_event_open + BLACKLIST(300), // fanotify_init + BLACKLIST(312), // kcmp + BLACKLIST(248), // add_key + BLACKLIST(249), // request_key + BLACKLIST(250), // keyctl + BLACKLIST(134), // uselib + BLACKLIST(163), // acct + BLACKLIST(154), // modify_ldt + BLACKLIST(155), // pivot_root + BLACKLIST(206), // io_setup + BLACKLIST(207), // io_destroy + BLACKLIST(208), // io_getevents + BLACKLIST(209), // io_submit + BLACKLIST(210), // io_cancel + BLACKLIST(216), // remap_file_pages + BLACKLIST(237), // mbind + BLACKLIST(239), // get_mempolicy + BLACKLIST(238), // set_mempolicy + BLACKLIST(256), // migrate_pages + BLACKLIST(279), // move_pages + BLACKLIST(278), // vmsplice + BLACKLIST(161), // chroot + BLACKLIST(184), // tuxcall + BLACKLIST(169), // reboot + BLACKLIST(180), // nfsservctl + BLACKLIST(177), // get_kernel_syms + + RETURN_ALLOW + }; + + // 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); +} + +// i386 filter installed on amd64 architectures +void seccomp_secondary_32(const char *fname) { + // hardcoded syscall values + struct sock_filter filter[] = { + VALIDATE_ARCHITECTURE_32, + EXAMINE_SYSCALL, + BLACKLIST(21), // mount + BLACKLIST(52), // umount2 +// todo: implement --allow-debuggers + BLACKLIST(26), // ptrace + BLACKLIST(283), // kexec_load + BLACKLIST(341), // name_to_handle_at + BLACKLIST(342), // open_by_handle_at + BLACKLIST(127), // create_module + BLACKLIST(128), // init_module + BLACKLIST(350), // finit_module + BLACKLIST(129), // delete_module + BLACKLIST(110), // iopl + BLACKLIST(101), // ioperm + BLACKLIST(289), // ioprio_set + BLACKLIST(87), // swapon + BLACKLIST(115), // swapoff + BLACKLIST(103), // syslog + BLACKLIST(347), // process_vm_readv + BLACKLIST(348), // process_vm_writev + BLACKLIST(135), // sysfs + BLACKLIST(149), // _sysctl + BLACKLIST(124), // adjtimex + BLACKLIST(343), // clock_adjtime + BLACKLIST(253), // lookup_dcookie + BLACKLIST(336), // perf_event_open + BLACKLIST(338), // fanotify_init + BLACKLIST(349), // kcmp + BLACKLIST(286), // add_key + BLACKLIST(287), // request_key + BLACKLIST(288), // keyctl + BLACKLIST(86), // uselib + BLACKLIST(51), // acct + BLACKLIST(123), // modify_ldt + BLACKLIST(217), // pivot_root + BLACKLIST(245), // io_setup + BLACKLIST(246), // io_destroy + BLACKLIST(247), // io_getevents + BLACKLIST(248), // io_submit + BLACKLIST(249), // io_cancel + BLACKLIST(257), // remap_file_pages + BLACKLIST(274), // mbind + BLACKLIST(275), // get_mempolicy + BLACKLIST(276), // set_mempolicy + BLACKLIST(294), // migrate_pages + BLACKLIST(317), // move_pages + BLACKLIST(316), // vmsplice + BLACKLIST(61), // chroot + BLACKLIST(88), // reboot + BLACKLIST(169), // nfsservctl + BLACKLIST(130), // get_kernel_syms + + RETURN_ALLOW + }; + + // 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); +} + diff --git a/src/fseccomp/syscall.c b/src/fseccomp/syscall.c index c67d45598..e2052efde 100644 --- a/src/fseccomp/syscall.c +++ b/src/fseccomp/syscall.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2014-2016 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 "fseccomp.h" #include @@ -16,6 +35,29 @@ static SyscallEntry syslist[] = { // }; // end of syslist +// return -1 if error, or syscall number +int syscall_find_name(const char *name) { + int i; + int elems = sizeof(syslist) / sizeof(syslist[0]); + for (i = 0; i < elems; i++) { + if (strcmp(name, syslist[i].name) == 0) + return syslist[i].nr; + } + + return -1; +} + +char *syscall_find_nr(int nr) { + int i; + int elems = sizeof(syslist) / sizeof(syslist[0]); + for (i = 0; i < elems; i++) { + if (nr == syslist[i].nr) + return syslist[i].name; + } + + return "unknown"; +} + void syscall_print(void) { int i; int elems = sizeof(syslist) / sizeof(syslist[0]); @@ -24,3 +66,45 @@ void syscall_print(void) { } printf("\n"); } + +// 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) { + // don't allow empty lists + if (slist == NULL || *slist == '\0') { + fprintf(stderr, "Error: empty syscall lists are not allowed\n"); + return -1; + } + + // work on a copy of the string + char *str = strdup(slist); + if (!str) + errExit("strdup"); + + char *ptr = str; + char *start = str; + while (*ptr != '\0') { + if (islower(*ptr) || isdigit(*ptr) || *ptr == '_') + ; + else if (*ptr == ',') { + *ptr = '\0'; + int nr = syscall_find_name(start); + if (nr == -1) + fprintf(stderr, "Warning: syscall %s not found\n", start); + else if (callback != NULL) + callback(fd, nr, arg); + + start = ptr + 1; + } + ptr++; + } + if (*start != '\0') { + int nr = syscall_find_name(start); + if (nr == -1) + fprintf(stderr, "Warning: syscall %s not found\n", start); + else if (callback != NULL) + callback(fd, nr, arg); + } + + free(str); + return 0; +} -- cgit v1.2.3-54-g00ecf