From b88cef5db1132c1e6e94ae282f94d980a959bb15 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Thu, 13 Oct 2016 16:45:28 -0400 Subject: testing --- test/apps-x11-xorg/apps-x11-xorg.sh | 35 +++++++++++++ test/apps-x11-xorg/firefox.exp | 90 +++++++++++++++++++++++++++++++++ test/apps-x11-xorg/icedove.exp | 85 +++++++++++++++++++++++++++++++ test/apps-x11-xorg/transmission-gtk.exp | 85 +++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100755 test/apps-x11-xorg/apps-x11-xorg.sh create mode 100755 test/apps-x11-xorg/firefox.exp create mode 100755 test/apps-x11-xorg/icedove.exp create mode 100755 test/apps-x11-xorg/transmission-gtk.exp (limited to 'test') diff --git a/test/apps-x11-xorg/apps-x11-xorg.sh b/test/apps-x11-xorg/apps-x11-xorg.sh new file mode 100755 index 000000000..b05914b52 --- /dev/null +++ b/test/apps-x11-xorg/apps-x11-xorg.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +export MALLOC_CHECK_=3 +export MALLOC_PERTURB_=$(($RANDOM % 255 + 1)) + +which firefox +if [ "$?" -eq 0 ]; +then + echo "TESTING: firefox x11 xorg" + ./firefox.exp +else + echo "TESTING SKIP: firefox not found" +fi + +which transmission-gtk +if [ "$?" -eq 0 ]; +then + echo "TESTING: transmission-gtk x11 xorg" + ./transmission-gtk.exp +else + echo "TESTING SKIP: transmission-gtk not found" +fi + +which icedove +if [ "$?" -eq 0 ]; +then + echo "TESTING: icedove x11 xorg" + ./icedove.exp +else + echo "TESTING SKIP: icedove not found" +fi + diff --git a/test/apps-x11-xorg/firefox.exp b/test/apps-x11-xorg/firefox.exp new file mode 100755 index 000000000..5231bf8ed --- /dev/null +++ b/test/apps-x11-xorg/firefox.exp @@ -0,0 +1,90 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail --name=test --x11=xorg firefox -no-remote www.gentoo.org\r" +sleep 10 + +spawn $env(SHELL) +send -- "firejail --list\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 3.1\n";exit} + "firefox" {puts "firefox detected\n";} + "iceweasel" {puts "iceweasel detected\n";} +} +expect { + timeout {puts "TESTING ERROR 3.2\n";exit} + "no-remote" +} +sleep 1 +# grsecurity exit +send -- "file /proc/sys/kernel/grsecurity\r" +expect { + timeout {puts "TESTING ERROR - grsecurity detection\n";exit} + "grsecurity: directory" {puts "grsecurity present, exiting...\n";exit} + "cannot open" {puts "grsecurity not present\n"} +} +send -- "firejail --name=blablabla\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 2 + +spawn $env(SHELL) +send -- "firemon --seccomp\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + " firefox" {puts "firefox detected\n";} + " iceweasel" {puts "iceweasel detected\n";} +} +expect { + timeout {puts "TESTING ERROR 5.0\n";exit} + "no-remote" +} +expect { + timeout {puts "TESTING ERROR 5.1 (seccomp)\n";exit} + "Seccomp: 2" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firemon --caps\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + " firefox" {puts "firefox detected\n";} + " iceweasel" {puts "iceweasel detected\n";} +} +expect { + timeout {puts "TESTING ERROR 6.0\n";exit} + "no-remote" +} +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "CapBnd:" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "0000000000000000" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firejail --shutdown=test\r" +sleep 3 + +puts "\nall done\n" + diff --git a/test/apps-x11-xorg/icedove.exp b/test/apps-x11-xorg/icedove.exp new file mode 100755 index 000000000..f676264ed --- /dev/null +++ b/test/apps-x11-xorg/icedove.exp @@ -0,0 +1,85 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail --name=test --x11=xorg icedove\r" +sleep 10 + +spawn $env(SHELL) +send -- "firejail --list\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 3.1\n";exit} + "icedove" +} +sleep 1 + +# grsecurity exit +send -- "file /proc/sys/kernel/grsecurity\r" +expect { + timeout {puts "TESTING ERROR - grsecurity detection\n";exit} + "grsecurity: directory" {puts "grsecurity present, exiting...\n";exit} + "cannot open" {puts "grsecurity not present\n"} +} + +send -- "firejail --name=blablabla\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 2 + +spawn $env(SHELL) +send -- "firemon --seccomp\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 5.0\n";exit} + "icedove" +} +expect { + timeout {puts "TESTING ERROR 5.1 (seccomp)\n";exit} + "Seccomp: 2" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "name=blablabla" +} +sleep 2 +send -- "firemon --caps\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 6.0\n";exit} + "icedove" +} +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "CapBnd" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "0000000000000000" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firejail --shutdown=test\r" +sleep 3 + +puts "\nall done\n" + diff --git a/test/apps-x11-xorg/transmission-gtk.exp b/test/apps-x11-xorg/transmission-gtk.exp new file mode 100755 index 000000000..a91a1be08 --- /dev/null +++ b/test/apps-x11-xorg/transmission-gtk.exp @@ -0,0 +1,85 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail --name=test --x11=xorg transmission-gtk\r" +sleep 10 + +spawn $env(SHELL) +send -- "firejail --list\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 3.1\n";exit} + "transmission-gtk" +} +sleep 1 + +# grsecurity exit +send -- "file /proc/sys/kernel/grsecurity\r" +expect { + timeout {puts "TESTING ERROR - grsecurity detection\n";exit} + "grsecurity: directory" {puts "grsecurity present, exiting...\n";exit} + "cannot open" {puts "grsecurity not present\n"} +} + +send -- "firejail --name=blablabla\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Child process initialized" +} +sleep 2 + +spawn $env(SHELL) +send -- "firemon --seccomp\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 5.0\n";exit} + "transmission-gtk" +} +expect { + timeout {puts "TESTING ERROR 5.1 (seccomp)\n";exit} + "Seccomp: 2" +} +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firemon --caps\r" +expect { + timeout {puts "TESTING ERROR 6\n";exit} + ":firejail" +} +expect { + timeout {puts "TESTING ERROR 6.0\n";exit} + "transmission-gtk" +} +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "CapBnd" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "0000000000000000" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "name=blablabla" +} +sleep 1 +send -- "firejail --shutdown=test\r" +sleep 3 + +puts "\nall done\n" + -- cgit v1.2.3-70-g09d2 From 82c6ce3168de710dc41ae4065610257c911848bb Mon Sep 17 00:00:00 2001 From: netblue30 Date: Fri, 14 Oct 2016 08:34:04 -0400 Subject: 0.9.44~rc1 testing --- Makefile.in | 2 +- test/environment/allow-debuggers.exp | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/Makefile.in b/Makefile.in index ad81c8851..9574c74bc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -216,5 +216,5 @@ test-network: test-fs: cd test/fs; ./fs.sh | grep TESTING -test: test-profiles test-fs test-utils test-environment test-apps test-apps-x11 test-apps-xorg-x11 test-filters +test: test-profiles test-fs test-utils test-environment test-apps test-apps-x11 test-apps-x11-xorg test-filters echo "TEST COMPLETE" diff --git a/test/environment/allow-debuggers.exp b/test/environment/allow-debuggers.exp index dde9c4cc1..8a404decb 100755 --- a/test/environment/allow-debuggers.exp +++ b/test/environment/allow-debuggers.exp @@ -11,19 +11,27 @@ expect { "Child process initialized" } expect { - timeout {puts "TESTING ERROR 1\n";exit} - "exited with 0" + timeout {puts "TESTING ERROR 1\n";exit} + "ioctl" +} +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "exit_group" } after 100 send -- "firejail --allow-debuggers --profile=/etc/firejail/firefox.profile strace ls\r" expect { - timeout {puts "TESTING ERROR 2\n";exit} + timeout {puts "TESTING ERROR 3\n";exit} "Child process initialized" } expect { - timeout {puts "TESTING ERROR 3\n";exit} - "exited with 0" + timeout {puts "TESTING ERROR 4\n";exit} + "ioctl" +} +expect { + timeout {puts "TESTING ERROR 5\n";exit} + "exit_group" } after 100 -- cgit v1.2.3-70-g09d2 From bb6c744fd4f59d0f407c37955ba36f8d40cc60cf Mon Sep 17 00:00:00 2001 From: netblue30 Date: Mon, 17 Oct 2016 08:41:39 -0400 Subject: allow user access to /sys/fs (--noblacklist=/sys/fs) --- RELNOTES | 1 + configure | 18 +++++++++--------- configure.ac | 2 +- src/firejail/fs.c | 6 +++++- test/fs/fs.sh | 3 +++ test/fs/sys_fs.exp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 11 deletions(-) create mode 100755 test/fs/sys_fs.exp (limited to 'test') diff --git a/RELNOTES b/RELNOTES index 0206e5433..23e44a14f 100644 --- a/RELNOTES +++ b/RELNOTES @@ -5,6 +5,7 @@ firejail (0.9.44~rc1) baseline; urgency=low * modifs: --private-tmp whitelists /tmp/.X11-unix directory * modifs: Nvidia drivers added to --private-dev * modifs: /srv supported by --whitelist + * feature: allow user access to /sys/fs (--noblacklist=/sys/fs) * feature: support starting/joining sandbox is a single command (--join-or-start) * feature: X11 detection support for --audit diff --git a/configure b/configure index 48b891c40..9a33f0401 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for firejail 0.9.44~rc1. +# Generated by GNU Autoconf 2.69 for firejail 0.9.44~rc2. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='firejail' PACKAGE_TARNAME='firejail' -PACKAGE_VERSION='0.9.44~rc1' -PACKAGE_STRING='firejail 0.9.44~rc1' +PACKAGE_VERSION='0.9.44~rc2' +PACKAGE_STRING='firejail 0.9.44~rc2' PACKAGE_BUGREPORT='netblue30@yahoo.com' PACKAGE_URL='http://firejail.wordpress.com' @@ -1259,7 +1259,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures firejail 0.9.44~rc1 to adapt to many kinds of systems. +\`configure' configures firejail 0.9.44~rc2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1320,7 +1320,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of firejail 0.9.44~rc1:";; + short | recursive ) echo "Configuration of firejail 0.9.44~rc2:";; esac cat <<\_ACEOF @@ -1424,7 +1424,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -firejail configure 0.9.44~rc1 +firejail configure 0.9.44~rc2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1726,7 +1726,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by firejail $as_me 0.9.44~rc1, which was +It was created by firejail $as_me 0.9.44~rc2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -4303,7 +4303,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by firejail $as_me 0.9.44~rc1, which was +This file was extended by firejail $as_me 0.9.44~rc2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4357,7 +4357,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -firejail config.status 0.9.44~rc1 +firejail config.status 0.9.44~rc2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 108b558d4..4496550fd 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.68]) -AC_INIT(firejail, 0.9.44~rc1, netblue30@yahoo.com, , http://firejail.wordpress.com) +AC_INIT(firejail, 0.9.44~rc2, netblue30@yahoo.com, , http://firejail.wordpress.com) AC_CONFIG_SRCDIR([src/firejail/main.c]) #AC_CONFIG_HEADERS([config.h]) diff --git a/src/firejail/fs.c b/src/firejail/fs.c index a5f12c7df..6c566bd90 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -649,7 +649,11 @@ void fs_proc_sys_dev_boot(void) { disable_file(BLACKLIST_FILE, "/sys/firmware"); disable_file(BLACKLIST_FILE, "/sys/hypervisor"); - disable_file(BLACKLIST_FILE, "/sys/fs"); + { // allow user access to /sys/fs if "--noblacklist=/sys/fs" is present on the command line + EUID_USER(); + profile_add("blacklist /sys/fs"); + EUID_ROOT(); + } disable_file(BLACKLIST_FILE, "/sys/module"); disable_file(BLACKLIST_FILE, "/sys/power"); disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); diff --git a/test/fs/fs.sh b/test/fs/fs.sh index d45ef48bd..3139b8eae 100755 --- a/test/fs/fs.sh +++ b/test/fs/fs.sh @@ -6,6 +6,9 @@ export MALLOC_CHECK_=3 export MALLOC_PERTURB_=$(($RANDOM % 255 + 1)) +echo "TESTING: /sys/fs access (test/fs/sys_fs.exp)" +./sys_fs.exp + echo "TESTING: kmsg access (test/fs/kmsg.exp)" ./kmsg.exp diff --git a/test/fs/sys_fs.exp b/test/fs/sys_fs.exp new file mode 100755 index 000000000..f512776d9 --- /dev/null +++ b/test/fs/sys_fs.exp @@ -0,0 +1,44 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "ls /sys/fs\r" +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "Permission denied" +} +after 100 + +send -- "exit\r" +sleep 1 + +send -- "firejail --noblacklist=/sys/fs\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "ls /sys/fs\r" +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "cgroup" +} +after 100 +send -- "exit\r" +after 100 + +puts "\nall done\n" + -- cgit v1.2.3-70-g09d2 From 08106743010d1b005e8d6cd377d79fdc8fb0af96 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Fri, 21 Oct 2016 10:14:08 -0400 Subject: 0.9.44 testing --- test/filters/noroot.exp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'test') diff --git a/test/filters/noroot.exp b/test/filters/noroot.exp index 2a7cb7975..b011f2bf9 100755 --- a/test/filters/noroot.exp +++ b/test/filters/noroot.exp @@ -46,20 +46,20 @@ expect { } send -- "sudo -s\r" expect { - timeout {puts "TESTING ERROR 8\n";exit} + timeout {puts "TESTING ERROR 7\n";exit} "effective uid is not 0, is sudo installed setuid root?" { puts "OK\n";} "sudo must be owned by uid 0 and have the setuid bit set" { puts "OK\n";} "Bad system call" { puts "OK\n";} } send -- "cat /proc/self/uid_map | wc -l\r" expect { - timeout {puts "TESTING ERROR 7\n";exit} + timeout {puts "TESTING ERROR 8\n";exit} "1" } send -- "cat /proc/self/gid_map | wc -l\r" expect { - timeout {puts "TESTING ERROR 8\n";exit} - "3" + timeout {puts "TESTING ERROR 9\n";exit} + "5" } puts "\n" @@ -70,59 +70,59 @@ sleep 2 send -- "firejail --name=test --noroot --noprofile\r" expect { - timeout {puts "TESTING ERROR 9\n";exit} + timeout {puts "TESTING ERROR 10\n";exit} "Child process initialized" } sleep 1 send -- "cat /proc/self/status\r" expect { - timeout {puts "TESTING ERROR 10\n";exit} + timeout {puts "TESTING ERROR 11\n";exit} "CapBnd:" } expect { - timeout {puts "TESTING ERROR 11\n";exit} + timeout {puts "TESTING ERROR 12\n";exit} "ffffffff" } expect { - timeout {puts "TESTING ERROR 12\n";exit} + timeout {puts "TESTING ERROR 13\n";exit} "Seccomp:" } expect { - timeout {puts "TESTING ERROR 13\n";exit} + timeout {puts "TESTING ERROR 14\n";exit} "0" } expect { - timeout {puts "TESTING ERROR 14\n";exit} + timeout {puts "TESTING ERROR 15\n";exit} "Cpus_allowed:" } puts "\n" send -- "whoami\r" expect { - timeout {puts "TESTING ERROR 15\n";exit} + timeout {puts "TESTING ERROR 16\n";exit} $env(USER) } send -- "sudo -s\r" expect { - timeout {puts "TESTING ERROR 16\n";exit} + timeout {puts "TESTING ERROR 17\n";exit} "effective uid is not 0, is sudo installed setuid root?" { puts "OK\n";} "sudo must be owned by uid 0 and have the setuid bit set" { puts "OK\n";} } send -- "ping 0\r" expect { - timeout {puts "TESTING ERROR 17\n";exit} + timeout {puts "TESTING ERROR 18\n";exit} "Operation not permitted" } send -- "cat /proc/self/uid_map | wc -l\r" expect { - timeout {puts "TESTING ERROR 18\n";exit} + timeout {puts "TESTING ERROR 19\n";exit} "1" } send -- "cat /proc/self/gid_map | wc -l\r" expect { - timeout {puts "TESTING ERROR 19\n";exit} - "3" + timeout {puts "TESTING ERROR 20\n";exit} + "5" } @@ -130,31 +130,31 @@ expect { spawn $env(SHELL) send -- "firejail --debug --join=test\r" expect { - timeout {puts "TESTING ERROR 20\n";exit} + timeout {puts "TESTING ERROR 21\n";exit} "User namespace detected" } expect { - timeout {puts "TESTING ERROR 21\n";exit} + timeout {puts "TESTING ERROR 22\n";exit} "Joining user namespace" } sleep 1 send -- "sudo -s\r" expect { - timeout {puts "TESTING ERROR 22\n";exit} + timeout {puts "TESTING ERROR 23\n";exit} "effective uid is not 0, is sudo installed setuid root?" { puts "OK\n";} "sudo must be owned by uid 0 and have the setuid bit set" { puts "OK\n";} "Permission denied" { puts "OK\n";} } send -- "cat /proc/self/uid_map | wc -l\r" expect { - timeout {puts "TESTING ERROR 23\n";exit} + timeout {puts "TESTING ERROR 24\n";exit} "1" } send -- "cat /proc/self/gid_map | wc -l\r" expect { - timeout {puts "TESTING ERROR 24\n";exit} - "3" + timeout {puts "TESTING ERROR 25\n";exit} + "5" } after 100 puts "\nall done\n" -- cgit v1.2.3-70-g09d2 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 ++++ test/filters/filters.sh | 15 +- test/filters/fseccomp.exp | 138 +++++++ 21 files changed, 1281 insertions(+), 929 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 create mode 100755 test/filters/fseccomp.exp (limited to 'test') 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; +} diff --git a/test/filters/filters.sh b/test/filters/filters.sh index 5093c8614..5c7c98b3e 100755 --- a/test/filters/filters.sh +++ b/test/filters/filters.sh @@ -12,11 +12,21 @@ echo "TESTING: noroot (test/filters/noroot.exp)" echo "TESTING: capabilities (test/filters/caps.exp)" ./caps.exp +rm -f seccomp-test-file +if [ "$(uname -m)" = "x86_64" ]; then + echo "TESTING: fseccomp (test/filters/fseccomp.exp)" + ./fseccomp.exp +else + echo "TESTING SKIP: fseccomp test implemented only for x86_64" +fi +rm -f seccomp-test-file + + if [ "$(uname -m)" = "x86_64" ]; then echo "TESTING: protocol (test/filters/protocol.exp)" ./protocol.exp else - echo "TESTING SKIP: protocol, not running on x86_64" + echo "TESTING SKIP: protocol, running only on x86_64" fi echo "TESTING: seccomp bad empty (test/filters/seccomp-bad-empty.exp)" @@ -50,9 +60,6 @@ echo "TESTING: seccomp chmod profile - seccomp lists (test/filters/seccomp-chmod echo "TESTING: seccomp empty (test/filters/seccomp-empty.exp)" ./seccomp-empty.exp -echo "TESTING: seccomp bad empty (test/filters/seccomp-bad-empty.exp)" -./seccomp-bad-empty.exp - if [ "$(uname -m)" = "x86_64" ]; then echo "TESTING: seccomp dual filter (test/filters/seccomp-dualfilter.exp)" ./seccomp-dualfilter.exp diff --git a/test/filters/fseccomp.exp b/test/filters/fseccomp.exp new file mode 100755 index 000000000..8a9a8f9dc --- /dev/null +++ b/test/filters/fseccomp.exp @@ -0,0 +1,138 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2016 Firejail Authors +# License GPL v2 + +set timeout 10 +spawn $env(SHELL) +match_max 100000 + +after 100 +send -- "/usr/lib/firejail/fseccomp debug-syscalls\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "1 - write" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp debug-errnos\r" +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "1 - EPERM" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp debug-protocols\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "unix, inet, inet6, netlink, packet," +} + +after 100 +send -- "/usr/lib/firejail/fseccomp protocol build unix,inet seccomp-test-file\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 4.1\n";exit} + "WHITELIST 41 socket" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp secondary 64 seccomp-test-file\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 5.1\n";exit} + "BLACKLIST 165 mount" +} +expect { + timeout {puts "TESTING ERROR 5.2\n";exit} + "BLACKLIST 166 umount2" +} +expect { + timeout {puts "TESTING ERROR 5.3\n";exit} + "RETURN_ALLOW" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp default seccomp-test-file\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 6.1\n";exit} + "BLACKLIST 165 mount" +} +expect { + timeout {puts "TESTING ERROR 6.2\n";exit} + "BLACKLIST 166 umount2" +} +expect { + timeout {puts "TESTING ERROR 6.3\n";exit} + "RETURN_ALLOW" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp drop seccomp-test-file chmod,chown\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 7.1\n";exit} + "BLACKLIST 165 mount" {puts "TESTING ERROR 7.2\n";exit} + "BLACKLIST 166 umount2" {puts "TESTING ERROR 7.3\n";exit} + "BLACKLIST 90 chmod" +} +expect { + timeout {puts "TESTING ERROR 7.4\n";exit} + "BLACKLIST 92 chown" +} +expect { + timeout {puts "TESTING ERROR 7.5\n";exit} + "RETURN_ALLOW" +} + +after 100 +send -- "/usr/lib/firejail/fseccomp default drop seccomp-test-file chmod,chown\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 8.1\n";exit} + "BLACKLIST 165 mount" +} +expect { + timeout {puts "TESTING ERROR 8.2\n";exit} + "BLACKLIST 166 umount2" +} +expect { + timeout {puts "TESTING ERROR 8.3\n";exit} + "BLACKLIST 90 chmod" +} +expect { + timeout {puts "TESTING ERROR 8.4\n";exit} + "BLACKLIST 92 chown" +} +expect { + timeout {puts "TESTING ERROR 8.5\n";exit} + "RETURN_ALLOW" +} +after 100 +send -- "/usr/lib/firejail/fseccomp keep seccomp-test-file chmod,chown\r" +after 100 +send -- "/usr/lib/firejail/fseccomp print seccomp-test-file\r" +expect { + timeout {puts "TESTING ERROR 9.1\n";exit} + "WHITELIST 90 chmod" +} +expect { + timeout {puts "TESTING ERROR 9.2\n";exit} + "WHITELIST 92 chown" +} +expect { + timeout {puts "TESTING ERROR 9.3\n";exit} + "KILL_PROCESS" +} + + + +after 100 +puts "\nall done\n" + -- cgit v1.2.3-70-g09d2