/* * Copyright (C) 2014-2017 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 typedef struct { const char * const name; int nr; } SyscallEntry; typedef struct { const char * const name; const char * const list; } SyscallGroupList; static const SyscallEntry syslist[] = { // // code generated using tools/extract-syscall // #include "../include/syscall.h" // // end of generated code // }; // end of syslist static const SyscallGroupList sysgroups[] = { { .name = "@clock", .list = #ifdef SYS_adjtimex "adjtimex," #endif #ifdef SYS_clock_adjtime "clock_adjtime," #endif #ifdef SYS_clock_settime "clock_settime," #endif #ifdef SYS_settimeofday "settimeofday," #endif #ifdef SYS_stime "stime" #endif }, { .name = "@cpu-emulation", .list = #ifdef SYS_modify_ldt "modify_ldt," #endif #ifdef SYS_subpage_prot "subpage_prot," #endif #ifdef SYS_switch_endian "switch_endian," #endif #ifdef SYS_vm86 "vm86," #endif #ifdef SYS_vm86old "vm86old" #endif }, { .name = "@debug", .list = #ifdef SYS_lookup_dcookie "lookup_dcookie," #endif #ifdef SYS_perf_event_open "perf_event_open," #endif #ifdef SYS_process_vm_writev "process_vm_writev," #endif #ifdef SYS_rtas "rtas," #endif #ifdef SYS_s390_runtime_instr "s390_runtime_instr," #endif #ifdef SYS_sys_debug_setcontext "sys_debug_setcontext," #endif }, { .name = "@default", .list = "@cpu-emulation," "@debug," "@module," "@obsolete," "@privileged," "@resources," #ifdef SYS_open_by_handle_at "open_by_handle_at," #endif #ifdef SYS_name_to_handle_at "name_to_handle_at," #endif #ifdef SYS_ioprio_set "ioprio_set," #endif #ifdef SYS_ni_syscall "ni_syscall," #endif #ifdef SYS_syslog "syslog," #endif #ifdef SYS_fanotify_init "fanotify_init," #endif #ifdef SYS_kcmp "kcmp," #endif #ifdef SYS_add_key "add_key," #endif #ifdef SYS_request_key "request_key," #endif #ifdef SYS_keyctl "keyctl," #endif #ifdef SYS_io_setup "io_setup," #endif #ifdef SYS_io_destroy "io_destroy," #endif #ifdef SYS_io_getevents "io_getevents," #endif #ifdef SYS_io_submit "io_submit," #endif #ifdef SYS_io_cancel "io_cancel," #endif #ifdef SYS_remap_file_pages "remap_file_pages," #endif #ifdef SYS_vmsplice "vmsplice," #endif #ifdef SYS_personality "personality," #endif #ifdef SYS_umount "umount," #endif #ifdef SYS_userfaultfd "userfaultfd" #endif }, { .name = "@default-nodebuggers", .list = "@default," #ifdef SYS_ptrace "ptrace," #endif #ifdef SYS_process_vm_readv "process_vm_readv" #endif }, { .name = "@default-keep", .list = "dup," "prctl," "setgid," "setgroups," "setuid" }, { .name = "@module", .list = #ifdef SYS_delete_module "delete_module," #endif #ifdef SYS_finit_module "finit_module," #endif #ifdef SYS_init_module "init_module" #endif }, { .name = "@obsolete", .list = #ifdef SYS__sysctl "_sysctl," #endif #ifdef SYS_afs_syscall "afs_syscall," #endif #ifdef SYS_bdflush "bdflush," #endif #ifdef SYS_break "break," #endif #ifdef SYS_create_module "create_module," #endif #ifdef SYS_ftime "ftime," #endif #ifdef SYS_get_kernel_syms "get_kernel_syms," #endif #ifdef SYS_getpmsg "getpmsg," #endif #ifdef SYS_gtty "gtty," #endif #ifdef SYS_lock "lock," #endif #ifdef SYS_mpx "mpx," #endif #ifdef SYS_prof "prof," #endif #ifdef SYS_profil "profil," #endif #ifdef SYS_putpmsg "putpmsg," #endif #ifdef SYS_query_module "query_module," #endif #ifdef SYS_security "security," #endif #ifdef SYS_sgetmask "sgetmask," #endif #ifdef SYS_ssetmask "ssetmask," #endif #ifdef SYS_stty "stty," #endif #ifdef SYS_sysfs "sysfs," #endif #ifdef SYS_tuxcall "tuxcall," #endif #ifdef SYS_ulimit "ulimit," #endif #ifdef SYS_uselib "uselib," #endif #ifdef SYS_ustat "ustat," #endif #ifdef SYS_vserver "vserver" #endif }, { .name = "@privileged", .list = "@clock," "@module," "@raw-io," "@reboot," "@swap," #ifdef SYS_acct "acct," #endif #ifdef SYS_bpf "bpf," #endif #ifdef SYS_chroot "chroot," #endif #ifdef SYS_mount "mount," #endif #ifdef SYS_nfsservctl "nfsservctl," #endif #ifdef SYS_pivot_root "pivot_root," #endif #ifdef SYS_setdomainname "setdomainname," #endif #ifdef SYS_sethostname "sethostname," #endif #ifdef SYS_umount2 "umount2," #endif #ifdef SYS_vhangup "vhangup" #endif }, { .name = "@raw-io", .list = #ifdef SYS_ioperm "ioperm," #endif #ifdef SYS_iopl "iopl," #endif #ifdef SYS_pciconfig_iobase "pciconfig_iobase," #endif #ifdef SYS_pciconfig_read "pciconfig_read," #endif #ifdef SYS_pciconfig_write "pciconfig_write," #endif #ifdef SYS_s390_mmio_read "s390_mmio_read," #endif #ifdef SYS_s390_mmio_write "s390_mmio_write" #endif }, { .name = "@reboot", .list = #ifdef SYS_kexec_load "kexec_load," #endif #ifdef SYS_kexec_file_load "kexec_file_load," #endif #ifdef SYS_reboot "reboot," #endif }, { .name = "@resources", .list = #ifdef SYS_set_mempolicy "set_mempolicy," #endif #ifdef SYS_migrate_pages "migrate_pages," #endif #ifdef SYS_move_pages "move_pages," #endif #ifdef SYS_mbind "mbind" #endif }, { .name = "@swap", .list = #ifdef SYS_swapon "swapon," #endif #ifdef SYS_swapoff "swapoff" #endif } }; // 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; } 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"; } void syscall_print(void) { int i; int elems = sizeof(syslist) / sizeof(syslist[0]); for (i = 0; i < elems; i++) { printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); } printf("\n"); } static const char *syscall_find_group(const char *name) { int i; int elems = sizeof(sysgroups) / sizeof(sysgroups[0]); for (i = 0; i < elems; i++) { if (strcmp(name, sysgroups[i].name) == 0) return sysgroups[i].list; } return NULL; } // allowed input: // - syscall // - syscall(error) static void syscall_process_name(const char *name, int *syscall_nr, int *error_nr) { assert(name); if (strlen(name) == 0) goto error; *error_nr = -1; // syntax check char *str = strdup(name); if (!str) errExit("strdup"); char *syscall_name = str; char *error_name = strchr(str, ':'); if (error_name) { *error_name = '\0'; error_name++; } if (strlen(syscall_name) == 0) { free(str); goto error; } if (*syscall_name == '$') *syscall_nr = strtol(syscall_name + 1, NULL, 0); else *syscall_nr = syscall_find_name(syscall_name); if (error_name) { *error_nr = errno_find_name(error_name); if (*error_nr == -1) *syscall_nr = -1; } free(str); return; error: fprintf(stderr, "Error fseccomp: invalid syscall list entry %s\n", name); exit(1); } // 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 fseccomp: empty syscall lists are not allowed\n"); exit(1); } // work on a copy of the string char *str = strdup(slist); if (!str) errExit("strdup"); char *saveptr; char *ptr = strtok_r(str, ",", &saveptr); if (ptr == NULL) { fprintf(stderr, "Error fseccomp: empty syscall lists are not allowed\n"); exit(1); } while (ptr) { int syscall_nr; int error_nr; if (*ptr == '@') { const char *new_list = syscall_find_group(ptr); if (!new_list) { fprintf(stderr, "Error fseccomp: unknown syscall group %s\n", ptr); exit(1); } syscall_check_list(new_list, callback, fd, arg); } else { syscall_process_name(ptr, &syscall_nr, &error_nr); if (syscall_nr == -1) { if (!arg_quiet) fprintf(stderr, "Warning fseccomp: syscall \"%s\" not available on this platform\n", ptr); } else if (callback != NULL) { if (error_nr != -1) filter_add_errno(fd, syscall_nr, error_nr); else callback(fd, syscall_nr, arg); } } ptr = strtok_r(NULL, ",", &saveptr); } free(str); return 0; }