From 87afef810c2dfbf67420dc76a67c707fbb7353db Mon Sep 17 00:00:00 2001 From: smitsohu Date: Tue, 19 Jul 2022 15:19:24 +0200 Subject: introduce new option restrict-namespaces --- src/fseccomp/fseccomp.h | 4 + src/fseccomp/main.c | 6 ++ src/fseccomp/namespaces.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 src/fseccomp/namespaces.c (limited to 'src/fseccomp') diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h index 65337da2a..5911b5156 100644 --- a/src/fseccomp/fseccomp.h +++ b/src/fseccomp/fseccomp.h @@ -61,6 +61,10 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list, bool nativ void memory_deny_write_execute(const char *fname); void memory_deny_write_execute_32(const char *fname); +// namespaces.c +void deny_ns(const char *fname, const char *list); +void deny_ns_32(const char *fname, const char *list); + // seccomp_print void filter_print(const char *fname); diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index 48665ab71..01d7dd8cf 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -48,6 +48,8 @@ static void usage(void) { printf("\tfseccomp keep32 file1 file2 list\n"); printf("\tfseccomp memory-deny-write-execute file\n"); printf("\tfseccomp memory-deny-write-execute.32 file\n"); + printf("\tfseccomp restrict-namespaces file list\n"); + printf("\tfseccomp restrict-namespaces.32 file list\n"); } int main(int argc, char **argv) { @@ -135,6 +137,10 @@ printf("\n"); memory_deny_write_execute(argv[2]); else if (argc == 3 && strcmp(argv[1], "memory-deny-write-execute.32") == 0) memory_deny_write_execute_32(argv[2]); + else if (argc == 4 && strcmp(argv[1], "restrict-namespaces") == 0) + deny_ns(argv[2], argv[3]); + else if (argc == 4 && strcmp(argv[1], "restrict-namespaces.32") == 0) + deny_ns_32(argv[2], argv[3]); else { fprintf(stderr, "Error fseccomp: invalid arguments\n"); return 1; diff --git a/src/fseccomp/namespaces.c b/src/fseccomp/namespaces.c new file mode 100644 index 000000000..3df23dcff --- /dev/null +++ b/src/fseccomp/namespaces.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2014-2022 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. + */ +#define _GNU_SOURCE +#include "fseccomp.h" +#include "../include/seccomp.h" +#include + +#include +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0x02000000 +#endif +#ifndef CLONE_NEWTIME +#define CLONE_NEWTIME 0x00000080 +#endif + +// 64-bit architectures +#if INTPTR_MAX == INT64_MAX +#if defined __x86_64__ +// i386 syscalls +#define clone_32 120 +#define clone3_32 435 +#define unshare_32 310 +#define setns_32 346 +#else +#warning 32 bit namespaces filter not implemented yet for your architecture +#endif +#endif + + +static int build_ns_mask(const char *list) { + int mask = 0; + + char *dup = strdup(list); + if (!dup) + errExit("strdup"); + + char *token = strtok(dup, ","); + while (token) { + if (strcmp(token, "cgroup") == 0) + mask |= CLONE_NEWCGROUP; + else if (strcmp(token, "ipc") == 0) + mask |= CLONE_NEWIPC; + else if (strcmp(token, "net") == 0) + mask |= CLONE_NEWNET; + else if (strcmp(token, "mnt") == 0) + mask |= CLONE_NEWNS; + else if (strcmp(token, "pid") == 0) + mask |= CLONE_NEWPID; + else if (strcmp(token, "time") == 0) + mask |= CLONE_NEWTIME; + else if (strcmp(token, "user") == 0) + mask |= CLONE_NEWUSER; + else if (strcmp(token, "uts") == 0) + mask |= CLONE_NEWUTS; + else { + fprintf(stderr, "Error fseccomp: %s is not a valid namespace\n", token); + exit(1); + } + + token = strtok(NULL, ","); + } + + free(dup); + return mask; +} + +void deny_ns(const char *fname, const char *list) { + int mask = build_ns_mask(list); + // CLONE_NEWTIME means something different for clone + // create a second mask without it + int clone_mask = mask & ~CLONE_NEWTIME; + + // 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); + } + + filter_init(fd, true); + + // build filter + struct sock_filter filter[] = { +#ifdef SYS_clone + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_clone, 0, 4), + // s390 has first and second argument flipped +#if defined __s390__ + EXAMINE_ARGUMENT(1), +#else + EXAMINE_ARGUMENT(0), +#endif + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, clone_mask, 0, 1), + KILL_OR_RETURN_ERRNO, + RETURN_ALLOW, +#endif +#ifdef SYS_clone3 + // cannot inspect clone3 argument because + // seccomp does not dereference pointers + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_clone3, 0, 1), + RETURN_ERRNO(ENOSYS), // hint to use clone instead +#endif +#ifdef SYS_unshare + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_unshare, 0, 4), + EXAMINE_ARGUMENT(0), + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1), + KILL_OR_RETURN_ERRNO, + RETURN_ALLOW, +#endif +#ifdef SYS_setns + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_setns, 0, 4), + EXAMINE_ARGUMENT(1), + // always fail if argument is zero + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1), + KILL_OR_RETURN_ERRNO, + RETURN_ALLOW +#endif + }; + write_to_file(fd, filter, sizeof(filter)); + + filter_end_blacklist(fd); + + // close file + close(fd); +} + +void deny_ns_32(const char *fname, const char *list) { + int mask = build_ns_mask(list); + // CLONE_NEWTIME means something different for clone + // create a second mask without it + int clone_mask = mask & ~CLONE_NEWTIME; + + // 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); + } + + filter_init(fd, false); + + // build filter + struct sock_filter filter[] = { +#ifdef clone_32 + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, clone_32, 0, 4), + EXAMINE_ARGUMENT(0), + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, clone_mask, 0, 1), + KILL_OR_RETURN_ERRNO, + RETURN_ALLOW, +#endif +#ifdef clone3_32 + // cannot inspect clone3 argument because + // seccomp does not dereference pointers + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, clone3_32, 0, 1), + RETURN_ERRNO(ENOSYS), // hint to use clone instead +#endif +#ifdef unshare_32 + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, unshare_32, 0, 4), + EXAMINE_ARGUMENT(0), + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1), + KILL_OR_RETURN_ERRNO, + RETURN_ALLOW, +#endif +#ifdef setns_32 + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, setns_32, 0, 4), + EXAMINE_ARGUMENT(1), + // always fail if argument is zero + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1), + KILL_OR_RETURN_ERRNO, + RETURN_ALLOW +#endif + }; + write_to_file(fd, filter, sizeof(filter)); + + filter_end_blacklist(fd); + + // close file + close(fd); +} -- cgit v1.2.3-54-g00ecf