seccomp rework

This commit is contained in:
netblue30 2016-11-06 13:14:53 -05:00
parent 646d046c4c
commit 322ce2cdc9
10 changed files with 106 additions and 385 deletions

View file

@ -1,207 +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 <errno.h>
//#include <attr/xattr.h>
typedef struct {
char *name;
int nr;
} ErrnoEntry;
static ErrnoEntry errnolist[] = {
//
// code generated using tools/extract-errnos
//
{"EPERM", EPERM},
{"ENOENT", ENOENT},
{"ESRCH", ESRCH},
{"EINTR", EINTR},
{"EIO", EIO},
{"ENXIO", ENXIO},
{"E2BIG", E2BIG},
{"ENOEXEC", ENOEXEC},
{"EBADF", EBADF},
{"ECHILD", ECHILD},
{"EAGAIN", EAGAIN},
{"ENOMEM", ENOMEM},
{"EACCES", EACCES},
{"EFAULT", EFAULT},
{"ENOTBLK", ENOTBLK},
{"EBUSY", EBUSY},
{"EEXIST", EEXIST},
{"EXDEV", EXDEV},
{"ENODEV", ENODEV},
{"ENOTDIR", ENOTDIR},
{"EISDIR", EISDIR},
{"EINVAL", EINVAL},
{"ENFILE", ENFILE},
{"EMFILE", EMFILE},
{"ENOTTY", ENOTTY},
{"ETXTBSY", ETXTBSY},
{"EFBIG", EFBIG},
{"ENOSPC", ENOSPC},
{"ESPIPE", ESPIPE},
{"EROFS", EROFS},
{"EMLINK", EMLINK},
{"EPIPE", EPIPE},
{"EDOM", EDOM},
{"ERANGE", ERANGE},
{"EDEADLK", EDEADLK},
{"ENAMETOOLONG", ENAMETOOLONG},
{"ENOLCK", ENOLCK},
{"ENOSYS", ENOSYS},
{"ENOTEMPTY", ENOTEMPTY},
{"ELOOP", ELOOP},
{"EWOULDBLOCK", EWOULDBLOCK},
{"ENOMSG", ENOMSG},
{"EIDRM", EIDRM},
{"ECHRNG", ECHRNG},
{"EL2NSYNC", EL2NSYNC},
{"EL3HLT", EL3HLT},
{"EL3RST", EL3RST},
{"ELNRNG", ELNRNG},
{"EUNATCH", EUNATCH},
{"ENOCSI", ENOCSI},
{"EL2HLT", EL2HLT},
{"EBADE", EBADE},
{"EBADR", EBADR},
{"EXFULL", EXFULL},
{"ENOANO", ENOANO},
{"EBADRQC", EBADRQC},
{"EBADSLT", EBADSLT},
{"EDEADLOCK", EDEADLOCK},
{"EBFONT", EBFONT},
{"ENOSTR", ENOSTR},
{"ENODATA", ENODATA},
{"ETIME", ETIME},
{"ENOSR", ENOSR},
{"ENONET", ENONET},
{"ENOPKG", ENOPKG},
{"EREMOTE", EREMOTE},
{"ENOLINK", ENOLINK},
{"EADV", EADV},
{"ESRMNT", ESRMNT},
{"ECOMM", ECOMM},
{"EPROTO", EPROTO},
{"EMULTIHOP", EMULTIHOP},
{"EDOTDOT", EDOTDOT},
{"EBADMSG", EBADMSG},
{"EOVERFLOW", EOVERFLOW},
{"ENOTUNIQ", ENOTUNIQ},
{"EBADFD", EBADFD},
{"EREMCHG", EREMCHG},
{"ELIBACC", ELIBACC},
{"ELIBBAD", ELIBBAD},
{"ELIBSCN", ELIBSCN},
{"ELIBMAX", ELIBMAX},
{"ELIBEXEC", ELIBEXEC},
{"EILSEQ", EILSEQ},
{"ERESTART", ERESTART},
{"ESTRPIPE", ESTRPIPE},
{"EUSERS", EUSERS},
{"ENOTSOCK", ENOTSOCK},
{"EDESTADDRREQ", EDESTADDRREQ},
{"EMSGSIZE", EMSGSIZE},
{"EPROTOTYPE", EPROTOTYPE},
{"ENOPROTOOPT", ENOPROTOOPT},
{"EPROTONOSUPPORT", EPROTONOSUPPORT},
{"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT},
{"EOPNOTSUPP", EOPNOTSUPP},
{"EPFNOSUPPORT", EPFNOSUPPORT},
{"EAFNOSUPPORT", EAFNOSUPPORT},
{"EADDRINUSE", EADDRINUSE},
{"EADDRNOTAVAIL", EADDRNOTAVAIL},
{"ENETDOWN", ENETDOWN},
{"ENETUNREACH", ENETUNREACH},
{"ENETRESET", ENETRESET},
{"ECONNABORTED", ECONNABORTED},
{"ECONNRESET", ECONNRESET},
{"ENOBUFS", ENOBUFS},
{"EISCONN", EISCONN},
{"ENOTCONN", ENOTCONN},
{"ESHUTDOWN", ESHUTDOWN},
{"ETOOMANYREFS", ETOOMANYREFS},
{"ETIMEDOUT", ETIMEDOUT},
{"ECONNREFUSED", ECONNREFUSED},
{"EHOSTDOWN", EHOSTDOWN},
{"EHOSTUNREACH", EHOSTUNREACH},
{"EALREADY", EALREADY},
{"EINPROGRESS", EINPROGRESS},
{"ESTALE", ESTALE},
{"EUCLEAN", EUCLEAN},
{"ENOTNAM", ENOTNAM},
{"ENAVAIL", ENAVAIL},
{"EISNAM", EISNAM},
{"EREMOTEIO", EREMOTEIO},
{"EDQUOT", EDQUOT},
{"ENOMEDIUM", ENOMEDIUM},
{"EMEDIUMTYPE", EMEDIUMTYPE},
{"ECANCELED", ECANCELED},
{"ENOKEY", ENOKEY},
{"EKEYEXPIRED", EKEYEXPIRED},
{"EKEYREVOKED", EKEYREVOKED},
{"EKEYREJECTED", EKEYREJECTED},
{"EOWNERDEAD", EOWNERDEAD},
{"ENOTRECOVERABLE", ENOTRECOVERABLE},
{"ERFKILL", ERFKILL},
{"EHWPOISON", EHWPOISON},
{"ENOTSUP", ENOTSUP},
#ifdef ENOATTR
{"ENOATTR", ENOATTR},
#endif
};
int errno_highest_nr(void) {
int i, max = 0;
int elems = sizeof(errnolist) / sizeof(errnolist[0]);
for (i = 0; i < elems; i++) {
if (errnolist[i].nr > max)
max = errnolist[i].nr;
}
return max;
}
int errno_find_name(const char *name) {
EUID_ASSERT();
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";
}
#endif // HAVE_SECCOMP

View file

@ -222,7 +222,6 @@ typedef struct config_t {
char *seccomp_list;// optional seccomp list on top of default filter
char *seccomp_list_drop; // seccomp drop list
char *seccomp_list_keep; // seccomp keep list
char **seccomp_list_errno; // seccomp errno[nr] lists
char *protocol; // protocol list
// rlimits
@ -496,12 +495,12 @@ void fs_private_home_list(void);
// seccomp.c
char *seccomp_check_list(const char *str);
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);
int seccomp_filter_errno(void);
void seccomp_print_filter_name(const char *name);
void seccomp_print_filter(pid_t pid);

View file

@ -851,9 +851,6 @@ int main(int argc, char **argv) {
int custom_profile = 0; // custom profile loaded
char *custom_profile_dir = NULL; // custom profile directory
int arg_noprofile = 0; // use default.profile if none other found/specified
#ifdef HAVE_SECCOMP
int highest_errno = errno_highest_nr();
#endif
// build /run/firejail directory structure
preproc_build_firejail_dir();
@ -1155,9 +1152,7 @@ int main(int argc, char **argv) {
exit(1);
}
arg_seccomp = 1;
cfg.seccomp_list = strdup(argv[i] + 10);
if (!cfg.seccomp_list)
errExit("strdup");
cfg.seccomp_list = seccomp_check_list(argv[i] + 10);
}
else {
fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n");
@ -1171,9 +1166,7 @@ int main(int argc, char **argv) {
exit(1);
}
arg_seccomp = 1;
cfg.seccomp_list_drop = strdup(argv[i] + 15);
if (!cfg.seccomp_list_drop)
errExit("strdup");
cfg.seccomp_list_drop = seccomp_check_list(argv[i] + 15);
}
else {
fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n");
@ -1187,43 +1180,7 @@ int main(int argc, char **argv) {
exit(1);
}
arg_seccomp = 1;
cfg.seccomp_list_keep = strdup(argv[i] + 15);
if (!cfg.seccomp_list_keep)
errExit("strdup");
}
else {
fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n");
exit(1);
}
}
else if (strncmp(argv[i], "--seccomp.e", 11) == 0 && strchr(argv[i], '=')) {
if (checkcfg(CFG_SECCOMP)) {
if (arg_seccomp && !cfg.seccomp_list_errno) {
fprintf(stderr, "Error: seccomp already enabled\n");
exit(1);
}
char *eq = strchr(argv[i], '=');
char *errnoname = strndup(argv[i] + 10, eq - (argv[i] + 10));
int nr = errno_find_name(errnoname);
if (nr == -1) {
fprintf(stderr, "Error: unknown errno %s\n", errnoname);
free(errnoname);
exit(1);
}
if (!cfg.seccomp_list_errno)
cfg.seccomp_list_errno = calloc(highest_errno+1, sizeof(cfg.seccomp_list_errno[0]));
if (cfg.seccomp_list_errno[nr]) {
fprintf(stderr, "Error: errno %s already configured\n", errnoname);
free(errnoname);
exit(1);
}
arg_seccomp = 1;
cfg.seccomp_list_errno[nr] = strdup(eq+1);
if (!cfg.seccomp_list_errno[nr])
errExit("strdup");
free(errnoname);
cfg.seccomp_list_keep = seccomp_check_list(argv[i] + 15);
}
else {
fprintf(stderr, "Error: seccomp feature is disabled in Firejail configuration file\n");
@ -2606,13 +2563,6 @@ int main(int argc, char **argv) {
waitpid(child, &status, 0);
// free globals
#ifdef HAVE_SECCOMP
if (cfg.seccomp_list_errno) {
for (i = 0; i < highest_errno; i++)
free(cfg.seccomp_list_errno[i]);
free(cfg.seccomp_list_errno);
}
#endif
if (cfg.profile) {
ProfileEntry *prf = cfg.profile;
while (prf != NULL) {

View file

@ -529,9 +529,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
#ifdef HAVE_SECCOMP
if (checkcfg(CFG_SECCOMP)) {
arg_seccomp = 1;
cfg.seccomp_list = strdup(ptr + 8);
if (!cfg.seccomp_list)
errExit("strdup");
cfg.seccomp_list = seccomp_check_list(ptr + 8);
}
else
fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
@ -545,9 +543,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
#ifdef HAVE_SECCOMP
if (checkcfg(CFG_SECCOMP)) {
arg_seccomp = 1;
cfg.seccomp_list_drop = strdup(ptr + 13);
if (!cfg.seccomp_list_drop)
errExit("strdup");
cfg.seccomp_list_drop = seccomp_check_list(ptr + 13);
}
else
fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
@ -560,9 +556,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
#ifdef HAVE_SECCOMP
if (checkcfg(CFG_SECCOMP)) {
arg_seccomp = 1;
cfg.seccomp_list_keep= strdup(ptr + 13);
if (!cfg.seccomp_list_keep)
errExit("strdup");
cfg.seccomp_list_keep= seccomp_check_list(ptr + 13);
}
else
fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n");
@ -576,7 +570,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
arg_caps_list = strdup(ptr + 10);
if (!arg_caps_list)
errExit("strdup");
// verify seccomp list and exit if problems
// verify caps list and exit if problems
if (caps_check_list(arg_caps_list, NULL))
exit(1);
return 0;
@ -588,7 +582,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
arg_caps_list = strdup(ptr + 10);
if (!arg_caps_list)
errExit("strdup");
// verify seccomp list and exit if problems
// verify caps list and exit if problems
if (caps_check_list(arg_caps_list, NULL))
exit(1);
return 0;

View file

@ -847,8 +847,6 @@ assert(0);
if (arg_seccomp == 1) {
if (cfg.seccomp_list_keep)
seccomp_filter_keep();
else if (cfg.seccomp_list_errno)
seccomp_filter_errno();
else
seccomp_filter_drop(enforce_seccomp);
}

View file

@ -22,6 +22,34 @@
#include "firejail.h"
#include "../include/seccomp.h"
char *seccomp_check_list(const char *str) {
assert(str);
if (strlen(str) == 0) {
fprintf(stderr, "Error: empty syscall lists are not allowed\n");
exit(1);
}
int len = strlen(str) + 1;
char *rv = malloc(len);
if (!rv)
errExit("malloc");
memset(rv, 0, len);
const char *ptr1 = str;
char *ptr2 = rv;
while (*ptr1 != '\0') {
if (isalnum(*ptr1) || *ptr1 == '_' || *ptr1 == ',' || *ptr1 == ':')
*ptr2++ = *ptr1++;
else {
fprintf(stderr, "Error: invalid syscall list\n");
exit(1);
}
}
return rv;
}
int seccomp_load(const char *fname) {
assert(fname);
@ -136,10 +164,6 @@ int seccomp_filter_drop(int enforce_seccomp) {
#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;
@ -157,10 +181,6 @@ int seccomp_filter_drop(int enforce_seccomp) {
else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) {
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);
}
// build the seccomp filter as a regular user
int rv;
@ -199,10 +219,6 @@ int seccomp_filter_drop(int enforce_seccomp) {
int seccomp_filter_keep(void) {
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);
}
// build the seccomp filter as a regular user
int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
@ -216,38 +232,6 @@ int seccomp_filter_keep(void) {
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();
// apply errno list
for (i = 0; i < higest_errno; i++) {
if (cfg.seccomp_list_errno[i]) {
if (syscall_check_list(cfg.seccomp_list_errno[i], filter_add_errno, i)) {
fprintf(stderr, "Error: cannot load seccomp filter\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();
return seccomp_load(RUN_SECCOMP_CFG);
#else
printf("*** --seccomp.<errno> is temporarily disabled, it will be brought back soon ***\n");
return 0;
#endif
}
void seccomp_print_filter_name(const char *name) {
EUID_ASSERT();
if (!name || strlen(name) == 0) {

View file

@ -40,7 +40,7 @@ static void usage(void) {
int main(int argc, char **argv) {
#if 0
{
system("cat /proc/self/status");
//system("cat /proc/self/status");
int i;
for (i = 0; i < argc; i++)
printf("*%s* ", argv[i]);

View file

@ -107,7 +107,7 @@ void protocol_build_filter(const char *prlist, const char *fname) {
assert(fname);
#ifndef SYS_socket
fprintf(stderr, "Warning: --protocol not supported on this platform\n");
fprintf(stderr, "Warning fseccomp: --protocol not supported on this platform\n");
return;
#else
// build the filter

View file

@ -67,12 +67,52 @@ void syscall_print(void) {
printf("\n");
}
// allowed input:
// - syscall
// - syscall(error)
static void syscall_process_name(const char *name, int *syscall_nr, int *error_nr) {
assert(name);
if (strlen(name) == 0)
goto error;
*error_nr = -1;
// syntax check
char *str = strdup(name);
if (!str)
errExit("strdup");
char *syscall_name = str;
char *error_name = strchr(str, ':');
if (error_name) {
*error_name = '\0';
error_name++;
}
if (strlen(syscall_name) == 0) {
free(str);
goto error;
}
*syscall_nr = syscall_find_name(syscall_name);
if (error_name) {
*error_nr = errno_find_name(error_name);
if (*error_nr == -1)
*syscall_nr = -1;
}
free(str);
return;
error:
fprintf(stderr, "Error fseccomp: invalid syscall list entry %s\n", name);
exit(1);
}
// return 1 if error, 0 if OK
int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg) {
// don't allow empty lists
if (slist == NULL || *slist == '\0') {
fprintf(stderr, "Error: empty syscall lists are not allowed\n");
return -1;
fprintf(stderr, "Error fseccomp: empty syscall lists are not allowed\n");
exit(1);
}
// work on a copy of the string
@ -80,29 +120,28 @@ int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall,
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++;
char *ptr =strtok(str, ",");
if (ptr == NULL) {
fprintf(stderr, "Error fseccomp: empty syscall lists are not allowed\n");
exit(1);
}
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);
while (ptr) {
printf("ptr %s\n", ptr);
int syscall_nr;
int error_nr;
syscall_process_name(ptr, &syscall_nr, &error_nr);
printf("%d, %d\n", syscall_nr, error_nr);
if (syscall_nr == -1)
fprintf(stderr, "Warning fseccomp: syscall %s not found\n", ptr);
else if (callback != NULL) {
if (error_nr != -1)
filter_add_errno(fd, syscall_nr, error_nr);
else
callback(fd, syscall_nr, arg);
}
ptr = strtok(NULL, ",");
}
free(str);

View file

@ -8,23 +8,23 @@ spawn $env(SHELL)
match_max 100000
send -- "touch seccomp-test-file\r"
sleep 1
after 100
send -- "firejail --seccomp.enoent=unlinkat rm seccomp-test-file\r"
send -- "firejail --seccomp=unlinkat:ENOENT rm seccomp-test-file\r"
expect {
timeout {puts "TESTING ERROR 0\n";exit}
"No such file or directory"
}
sleep 1
send -- "firejail --seccomp.enoent=unlinkat --debug rm seccomp-test-file\r"
send -- "firejail --seccomp=unlinkat:ENOENT --debug rm seccomp-test-file\r"
expect {
timeout {puts "TESTING ERROR 1\n";exit}
"unlinkat 2 ENOENT"
}
sleep 1
send -- "firejail --seccomp.enoent=unlinkat,mkdir\r"
send -- "firejail --seccomp=unlinkat:ENOENT,mkdir:ENOENT\r"
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"Child process initialized"
@ -49,42 +49,6 @@ puts "\n"
send -- "exit\r"
sleep 1
send -- "firejail --seccomp.enoent=unlinkat --seccomp.enoent=mkdir\r"
expect {
timeout {puts "TESTING ERROR 5\n";exit}
"errno enoent already configured"
}
sleep 1
send -- "firejail --seccomp.enoent=unlinkat --seccomp.eperm=mkdir\r"
expect {
timeout {puts "TESTING ERROR 6\n";exit}
"Child process initialized"
}
sleep 1
send -- "rm seccomp-test-file\r"
expect {
timeout {puts "TESTING ERROR 7\n";exit}
"No such file or directory"
}
after 100
puts "\n"
send -- "mkdir seccomp-test-dir\r"
expect {
timeout {puts "TESTING ERROR 8\n";exit}
"Operation not permitted"
}
after 100
puts "\n"
send -- "exit\r"
sleep 1
send -- "rm seccomp-test-file\r"
after 100
puts "all done\n"