Skip to content

Commit

Permalink
Allow changing error action in seccomp filters
Browse files Browse the repository at this point in the history
Let user specify the action when seccomp filters trigger:
- errno name like EPERM (default) or ENOSYS: return errno and let the process continue.
- 'kill': kill the process as previous versions

The default action is EPERM, but killing can still be specified with
syscall:kill syntax or globally with seccomp-error-action=kill. The
action can be also overridden /etc/firejail/firejail.config file.

Not killing the process weakens Firejail slightly when trying to
contain intrusion, but it may also allow tighter filters if the
only alternative is to allow a system call.
  • Loading branch information
topimiettinen committed Apr 6, 2020
1 parent 1267eb2 commit 3f27e84
Show file tree
Hide file tree
Showing 20 changed files with 266 additions and 61 deletions.
5 changes: 5 additions & 0 deletions RELNOTES
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
firejail (0.9.63) baseline; urgency=low
* work in progress
* The blocking action of seccomp filters has been changed from
killing the process to returning EPERM to the caller. To get the
previous behaviour, use --seccomp-error-action=kill or
syscall:kill syntax when constructing filters, or override in
/etc/firejail/firejail.config file.
* DHCP client support
* SELinux labeling support
* 32-bit seccomp filter
Expand Down
3 changes: 3 additions & 0 deletions etc/firejail.config
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,6 @@

# Xvfb command extra parameters. None by default; this is an example.
# xvfb-extra-params -pixdepths 8 24 32

# Seccomp error action, kill or errno (EPERM, ENOSYS etc)
# seccomp-error-action EPERM
22 changes: 22 additions & 0 deletions src/firejail/checkcfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "firejail.h"
#include "../include/seccomp.h"
#include "../include/syscall.h"
#include <sys/stat.h>
#include <linux/loop.h>

Expand All @@ -32,6 +34,7 @@ char *xvfb_screen = "800x600x24";
char *xvfb_extra_params = "";
char *netfilter_default = NULL;
unsigned long join_timeout = 5000000; // microseconds
char *config_seccomp_error_action_str = "EPERM";

int checkcfg(int val) {
assert(val < CFG_MAX);
Expand All @@ -51,6 +54,7 @@ int checkcfg(int val) {
cfg_val[CFG_DISABLE_MNT] = 0;
cfg_val[CFG_ARP_PROBES] = DEFAULT_ARP_PROBES;
cfg_val[CFG_XPRA_ATTACH] = 0;
cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1;

// open configuration file
const char *fname = SYSCONFDIR "/firejail.config";
Expand Down Expand Up @@ -219,6 +223,24 @@ int checkcfg(int val) {
else if (strncmp(ptr, "join-timeout ", 13) == 0)
join_timeout = strtoul(ptr + 13, NULL, 10) * 1000000; // seconds to microseconds

// seccomp error action
else if (strncmp(ptr, "seccomp-error-action ", 21) == 0) {
#ifdef HAVE_SECCOMP
if (strcmp(ptr + 21, "kill") == 0)
cfg_val[CFG_SECCOMP_ERROR_ACTION] = SECCOMP_RET_KILL;
else {
cfg_val[CFG_SECCOMP_ERROR_ACTION] = errno_find_name(ptr + 21);
if (cfg_val[CFG_SECCOMP_ERROR_ACTION] == -1)
errExit("seccomp-error-action: unknown errno");
}
config_seccomp_error_action_str = strdup(ptr + 21);
if (!config_seccomp_error_action_str)
errExit("strdup");
#else
warning_feature_disabled("seccomp");
#endif
}

else
goto errout;

Expand Down
5 changes: 5 additions & 0 deletions src/firejail/firejail.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ typedef struct config_t {
char *seccomp_list_drop, *seccomp_list_drop32; // seccomp drop list
char *seccomp_list_keep, *seccomp_list_keep32; // seccomp keep list
char *protocol; // protocol list
char *seccomp_error_action; // error action: kill or errno

// rlimits
long long unsigned rlimit_cpu;
Expand Down Expand Up @@ -572,6 +573,7 @@ int seccomp_install_filters(void);
int seccomp_load(const char *fname);
int seccomp_filter_drop(bool native);
int seccomp_filter_keep(bool native);
int seccomp_filter_mdwx(bool native);
void seccomp_print_filter(pid_t pid);

// caps.c
Expand Down Expand Up @@ -754,6 +756,7 @@ enum {
CFG_PRIVATE_CACHE,
CFG_CGROUP,
CFG_NAME_CHANGE,
CFG_SECCOMP_ERROR_ACTION,
// CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv
CFG_MAX // this should always be the last entry
};
Expand All @@ -764,6 +767,8 @@ extern char *xvfb_screen;
extern char *xvfb_extra_params;
extern char *netfilter_default;
extern unsigned long join_timeout;
extern char *config_seccomp_error_action_str;

int checkcfg(int val);
void print_compiletime_support(void);

Expand Down
32 changes: 32 additions & 0 deletions src/firejail/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "../include/pid.h"
#include "../include/firejail_user.h"
#include "../include/syscall.h"
#include "../include/seccomp.h"
#define _GNU_SOURCE
#include <sys/utsname.h>
#include <sched.h>
Expand Down Expand Up @@ -76,6 +77,7 @@ int arg_seccomp = 0; // enable default seccomp filter
int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch
int arg_seccomp_postexec = 0; // need postexec ld.preload library?
int arg_seccomp_block_secondary = 0; // block any secondary architectures
int arg_seccomp_error_action = 0;

int arg_caps_default_filter = 0; // enable default capabilities filter
int arg_caps_drop = 0; // drop list
Expand Down Expand Up @@ -349,6 +351,9 @@ static void init_cfg(int argc, char **argv) {
sandbox_pid = getpid();
time_t t = time(NULL);
srand(t ^ sandbox_pid);

arg_seccomp_error_action = EPERM;
cfg.seccomp_error_action = "EPERM";
}

static void check_network(Bridge *br) {
Expand Down Expand Up @@ -973,6 +978,13 @@ void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native) {
(void) ptrarg;
(void) native;
}
void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) {
(void) fd;
(void) syscall;
(void) arg;
(void) ptrarg;
(void) native;
}

#ifdef HAVE_SECCOMP
static int check_postexec(const char *list) {
Expand Down Expand Up @@ -1398,6 +1410,26 @@ int main(int argc, char **argv, char **envp) {
else
exit_err_feature("seccomp");
}
else if (strncmp(argv[i], "--seccomp-error-action=", 23) == 0) {
if (checkcfg(CFG_SECCOMP)) {
int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION);
if (config_seccomp_error_action == -1) {
if (strcmp(argv[i] + 23, "kill") == 0)
arg_seccomp_error_action = SECCOMP_RET_KILL;
else {
arg_seccomp_error_action = errno_find_name(argv[i] + 23);
if (arg_seccomp_error_action == -1)
errExit("seccomp-error-action: unknown errno");
}
cfg.seccomp_error_action = strdup(argv[i] + 23);
if (!cfg.seccomp_error_action)
errExit("strdup");
} else
exit_err_feature("seccomp-error-action");

} else
exit_err_feature("seccomp");
}
#endif
else if (strcmp(argv[i], "--caps") == 0) {
arg_caps_default_filter = 1;
Expand Down
29 changes: 29 additions & 0 deletions src/firejail/profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "firejail.h"
#include "../include/seccomp.h"
#include "../include/syscall.h"
#include <dirent.h>
#include <sys/stat.h>
extern char *xephyr_screen;
Expand Down Expand Up @@ -870,6 +872,33 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
return 0;
}

// seccomp error action
if (strncmp(ptr, "seccomp-error-action ", 21) == 0) {
#ifdef HAVE_SECCOMP
if (checkcfg(CFG_SECCOMP)) {
int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION);
if (config_seccomp_error_action == -1) {
if (strcmp(ptr + 21, "kill") == 0)
arg_seccomp_error_action = SECCOMP_RET_KILL;
else {
arg_seccomp_error_action = errno_find_name(ptr + 21);
if (arg_seccomp_error_action == -1)
errExit("seccomp-error-action: unknown errno");
}
cfg.seccomp_error_action = strdup(ptr + 21);
if (!cfg.seccomp_error_action)
errExit("strdup");
} else {
arg_seccomp_error_action = config_seccomp_error_action;
cfg.seccomp_error_action = config_seccomp_error_action_str;
warning_feature_disabled("seccomp-error-action");
}
} else
warning_feature_disabled("seccomp");
#endif
return 0;
}

// caps drop list
if (strncmp(ptr, "caps.drop ", 10) == 0) {
arg_caps_drop = 1;
Expand Down
5 changes: 5 additions & 0 deletions src/firejail/sandbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

#include "firejail.h"
#include "../include/seccomp.h"
#include <sys/mount.h>
#include <sys/wait.h>
#include <sys/stat.h>
Expand Down Expand Up @@ -1124,6 +1125,10 @@ int sandbox(void* sandbox_arg) {
}

if (arg_memory_deny_write_execute) {
if (arg_seccomp_error_action != EPERM) {
seccomp_filter_mdwx(true);
seccomp_filter_mdwx(false);
}
if (arg_debug)
printf("Install memory write&execute filter\n");
seccomp_load(RUN_SECCOMP_MDWX); // install filter
Expand Down
55 changes: 29 additions & 26 deletions src/firejail/sbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,27 @@
#define O_PATH 010000000
#endif

static struct sock_filter filter[] = {
int sbox_run(unsigned filtermask, int num, ...) {
va_list valist;
va_start(valist, num);

// build argument list
char **arg = malloc((num + 1) * sizeof(char *));
int i;
for (i = 0; i < num; i++)
arg[i] = va_arg(valist, char*);
arg[i] = NULL;
va_end(valist);

int status = sbox_run_v(filtermask, arg);

free(arg);

return status;
}

int sbox_run_v(unsigned filtermask, char * const arg[]) {
struct sock_filter filter[] = {
VALIDATE_ARCHITECTURE,
EXAMINE_SYSCALL,

Expand Down Expand Up @@ -105,33 +125,13 @@ static struct sock_filter filter[] = {
BLACKLIST(SYS_syslog), // kernel printk control
#endif
RETURN_ALLOW
};
};

static struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};

int sbox_run(unsigned filtermask, int num, ...) {
va_list valist;
va_start(valist, num);

// build argument list
char **arg = malloc((num + 1) * sizeof(char *));
int i;
for (i = 0; i < num; i++)
arg[i] = va_arg(valist, char*);
arg[i] = NULL;
va_end(valist);

int status = sbox_run_v(filtermask, arg);

free(arg);

return status;
}

int sbox_run_v(unsigned filtermask, char * const arg[]) {
EUID_ROOT();

if (arg_debug) {
Expand Down Expand Up @@ -161,6 +161,9 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
new_environment[env_index++] = "FIREJAIL_QUIET=yes";
if (arg_debug) // --debug is passed as an environment variable
new_environment[env_index++] = "FIREJAIL_DEBUG=yes";
if (cfg.seccomp_error_action)
if (asprintf(&new_environment[env_index++], "FIREJAIL_SECCOMP_ERROR_ACTION=%s", cfg.seccomp_error_action) == -1)
errExit("asprintf");

if (filtermask & SBOX_STDIN_FROM_FILE) {
int fd;
Expand Down
35 changes: 33 additions & 2 deletions src/firejail/seccomp.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ int seccomp_filter_drop(bool native) {
// - seccomp list
// - seccomp
if (cfg.seccomp_list_drop == NULL) {
// default seccomp
if (cfg.seccomp_list == NULL) {
// default seccomp if error action is not changed
if (cfg.seccomp_list == NULL && cfg.seccomp_error_action) {
if (arg_seccomp_block_secondary)
seccomp_filter_block_secondary();
else {
Expand Down Expand Up @@ -243,6 +243,8 @@ int seccomp_filter_drop(bool native) {
list = cfg.seccomp_list32;
}

if (list == NULL)
list = "";
// build the seccomp filter as a regular user
int rv;
if (arg_allow_debuggers)
Expand Down Expand Up @@ -365,6 +367,35 @@ int seccomp_filter_keep(bool native) {
return 0;
}

// create mdwx filter for non-default error action
int seccomp_filter_mdwx(bool native) {
if (arg_debug)
printf("Build memory-deny-write-execute filter\n");

const char *command, *filter, *postexec_filter, *list;
if (native) {
command = "memory-deny-write-execute";
filter = RUN_SECCOMP_MDWX;
} else {
command = "memory-deny-write-execute.32";
filter = RUN_SECCOMP_MDWX_32;
}

// build the seccomp filter as a regular user
int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3,
PATH_FSECCOMP, command, filter);

if (rv) {
fprintf(stderr, "Error: cannot build memory-deny-write-execute filter\n");
exit(rv);
}

if (arg_debug)
printf("Memory-deny-write-execute filter configured\n");

return 0;
}

void seccomp_print_filter(pid_t pid) {
EUID_ASSERT();

Expand Down
1 change: 1 addition & 0 deletions src/firejail/usage.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ static char *usage_str =
" --seccomp.print=name|pid - print the seccomp filter for the sandbox\n"
"\tidentified by name or PID.\n"
" --seccomp.32[.drop,.keep][=syscall] - like above but for 32 bit architecture.\n"
" --seccomp-error-action=errno|kill - change error code or kill process.\n"
#endif
" --shell=none - run the program directly without a user shell.\n"
" --shell=program - set default user shell.\n"
Expand Down
8 changes: 8 additions & 0 deletions src/fsec-print/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native) {
(void) native;
}

void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) {
(void) fd;
(void) syscall;
(void) arg;
(void) ptrarg;
(void) native;
}

int main(int argc, char **argv) {
#if 0
{
Expand Down
Loading

0 comments on commit 3f27e84

Please sign in to comment.