0
0
mirror of https://github.com/cjdelisle/cjdns synced 2025-10-06 00:32:50 +02:00

Get rid of Seccomp because the whitelist strategy doesn't work with Rust libraries and it is not maintained anymore

This commit is contained in:
Caleb James DeLisle
2024-09-09 20:17:26 +00:00
parent e60257daa0
commit 887ee6e184
25 changed files with 17 additions and 949 deletions

View File

@@ -540,7 +540,6 @@ static void ethInterface(Dict* config, struct Context* ctx)
static void security(struct Allocator* tempAlloc, List* conf, struct Log* log, struct Context* ctx)
{
int seccomp = 1;
int nofiles = 0;
int noforks = 1;
int chroot = 1;
@@ -601,10 +600,6 @@ static void security(struct Allocator* tempAlloc, List* conf, struct Log* log, s
if (!*x) { setuser = 0; }
continue;
}
if (elem && (x = Dict_getIntC(elem, "seccomp"))) {
if (!*x) { seccomp = 0; }
continue;
}
if (elem && (x = Dict_getIntC(elem, "noforks"))) {
if (!*x) { noforks = 0; }
continue;
@@ -652,11 +647,6 @@ static void security(struct Allocator* tempAlloc, List* conf, struct Log* log, s
Dict* d = Dict_new(tempAlloc);
rpcCall(String_CONST("Security_nofiles"), d, ctx, tempAlloc);
}
if (seccomp) {
Log_debug(log, "Security_seccomp()");
Dict* d = Dict_new(tempAlloc);
rpcCall(String_CONST("Security_seccomp"), d, ctx, tempAlloc);
}
if (setupComplete) {
Log_debug(log, "Security_setupComplete()");
Dict* d = Dict_new(tempAlloc);

View File

@@ -385,20 +385,7 @@ static int genconf(struct Allocator* alloc, struct Random* rand, bool eth, bool
" // Noforks will prevent cjdns from spawning any new processes or threads,\n"
" // this prevents many types of exploits from attacking the wider system.\n"
" // Default: enabled\n"
" { \"noforks\": 1 },\n"
"\n"
" // Seccomp is the most advanced sandboxing feature in cjdns, it uses\n"
" // SECCOMP_BPF to filter the system calls which cjdns is able to make on a\n"
" // linux system, strictly limiting it's access to the outside world\n"
" // This will fail quietly on any non-linux system\n");
if (Defined(Cjdns_android)) {
printf(" // Default: disabled\n"
" { \"seccomp\": 0 },\n");
}
else {
printf(" // Default: enabled\n"
" { \"seccomp\": 1 },\n");
}
" { \"noforks\": 1 },\n");
printf("\n"
" // The client sets up the core using a sequence of RPC calls, the responses\n"
" // to these calls are verified but in the event that the client crashes\n"
@@ -786,8 +773,7 @@ int cjdroute2_main(int argc, char** argv)
break;
}
// sleep 50ms
struct timespec timeout = { 0, 1000000 * 50 };
nanosleep(&timeout, NULL);
Rffi_sleep_ms_sync(50);
}
if (!exists) {
Except_throw(eh, "Core did not setup pipe file [%s] within 60 seconds",

1
debian/docs vendored
View File

@@ -1,7 +1,6 @@
README.md
doc/configure.md
doc/Whitepaper.md
doc/Seccomp.md
doc/nat-gateway.md
doc/non-root-user.md
tunnel/README.md

View File

@@ -1,43 +0,0 @@
# Seccomp
SECCOMP (secure computing) is a way for programs to declare to the Linux kernel
that they will never make certain system calls, thus any attempt to make one of
these calls is interpreted as a security penetration and the kernel can forcibly
kill off the program, preventing harm to the computer.
## Seccomp failures in cjdns
If you are reading this because cjdns is halting on you, you are probably getting
a log message like the following:
Attempted banned syscall number [232] see docs/Seccomp.md for more information
This number (`232` in the example) is specific to your system and you need to
run a command to convert it to a syscall name.
```bash
echo '#include <sys/syscall.h>' | cpp -dM | grep '#define __NR_.* 232'
```
Obviously you'll be replacing `232` with the actual syscall number which your system
printed. The Result might look something like the following:
```c
#define __NR_epoll_wait 232
```
Which would tell you (for example) that the `epoll_wait` syscall was disallowed on
your system. In this case you'd need to go to `util/Seccomp.c` and inside of the
`mkfilter()` function where the actual SECCOMP rules are set up, you'll see a set
of entries such as the following.
```c
#ifdef __NR_mmap2
IFEQ(__NR_mmap2, success),
#endif
```
Add a similar entry for the syscall (make sure you put it with the others and not)
below the `RET(SECCOMP_RET_TRAP),` line which triggers the failure). When you have
finished adding your system call rebuild and re-test cjdns. If it works well then
please make a Pull Request :-) If not then open a bug report and explain the problem.

View File

@@ -52,7 +52,7 @@ Achievements expire, so if at any given time you don't qualify, you lose those p
40. Configure an authorizedPassword without restarting cjdns.
41. Find out whether your home router can run cjdns: [OpenWrt table of hardware](http://wiki.openwrt.org/toh/start)
42. Try out the [Meshbox firmware](https://github.com/seattlemeshnet/meshbox) on your home router.
43. Harden your cjdns OpenWrt router by building OpenWrt with cjdns from source and make sure [seccomp](http://lwn.net/Articles/475043/), [SSP](http://lwn.net/Articles/584225/) and [RELRO](http://tk-blog.blogspot.de/2009/02/relro-not-so-well-known-memory.html) are enabled. Using [musl](http://www.musl-libc.org/) instead of [uClibc](http://www.uclibc.org/) may make you sleep even better. See [buildsdk.sh](https://github.com/SeattleMeshnet/meshbox/blob/master/buildsdk.sh) to see how this can work.
43. Harden your cjdns OpenWrt router by building OpenWrt with cjdns from source and make sure [SSP](http://lwn.net/Articles/584225/) and [RELRO](http://tk-blog.blogspot.de/2009/02/relro-not-so-well-known-memory.html) are enabled. Using [musl](http://www.musl-libc.org/) instead of [uClibc](http://www.uclibc.org/) may make you sleep even better. See [buildsdk.sh](https://github.com/SeattleMeshnet/meshbox/blob/master/buildsdk.sh) to see how this can work.
44. Monitor your nodes' cjdns preformance and make pretty graphs with munin (hint: [here's a nice munin plugin to help](https://github.com/thefinn93/munin-plugins/blob/master/cjdns/cjdns_bandwidth.py))
## Penalties

View File

@@ -71,7 +71,6 @@ You can contribute to its documentaion: https://github.com/hyperboria/docs
- Troubleshooting
- [Read this first](bugs/policy.md)
- [Memory leaks](debugging_memory_leaks.md)
- [SecComp](Seccomp.md)
- [Analyzing network IO](TrafficAnalisys.md)
- Known issues
- [Black Hole](bugs/black-hole.md)

View File

@@ -22,7 +22,7 @@ apt install -y nodejs
cd /opt
git clone https://github.com/cjdelisle/cjdns.git
cd cjdns
NO_TEST=1 Seccomp_NO=1 ./do
NO_TEST=1 ./do
ln -s /opt/cjdns/cjdroute /usr/bin
# Generate a config file

View File

@@ -27,7 +27,7 @@ If you see something listed here that has been taken care of, please knock it of
- allows obfsproxy deployment
+ in datacenters (private networking)
+ over iodine
* Security mechanisms (seccomp, angel/core, etc.)
* Security mechanisms (angel/core, etc.)
* WiFi Recommendations
* cjdns/contrib/ scripts
+ dumpTable

View File

@@ -28,7 +28,7 @@ var LDFLAGS = process.env['LDFLAGS'];
// case clang doesn't reliably support march except on x86/amd64.
var NO_MARCH_FLAG = ['arm', 'ppc', 'ppc64', 'arm64'];
if (process.version.replace('v','').split('.').map(Number)[0] >= 18) {
if (process.version.replace('v','').split('.').map(Number)[0] >= 12) {
// OK
} else if ('OLD_NODE_VERSION_I_EXPECT_ERRORS' in process.env) {
console.log('OLD_NODE_VERSION_I_EXPECT_ERRORS is set, ignoring old version');

View File

@@ -206,6 +206,8 @@ uint64_t Rffi_hrtime(void);
*/
uint64_t Rffi_now_ms(void);
void Rffi_sleep_ms_sync(uint64_t ms);
RTypes_IfWrapper_t Rffi_testwrapper_create(Allocator_t *a);
RTypes_IfWrapper_t Rffi_android_create(Allocator_t *a);

View File

@@ -129,10 +129,10 @@ impl <const COUNT: usize> IoContext<COUNT> {
assert!((d as usize) >= bottom && (d as usize + std::mem::size_of::<i32>()) <= top);
*d = send_fd;
let len = libc::CMSG_LEN(std::mem::size_of::<i32>() as _);
(*cmsg).cmsg_len = len;
(*cmsg).cmsg_len = len as _;
(*cmsg).cmsg_type = libc::SCM_RIGHTS;
(*cmsg).cmsg_level = libc::SOL_SOCKET;
hdr.msg_hdr.msg_controllen = len;
hdr.msg_hdr.msg_controllen = len as _;
}
} else {
hdr.msg_hdr.msg_control = std::ptr::null_mut();

View File

@@ -22,3 +22,8 @@ pub extern "C" fn Rffi_now_ms() -> u64 {
(Instant::now() - *BASE_INSTANT).as_millis() as u64 + *INSTANT_OFFSET
}
#[no_mangle]
pub extern "C" fn Rffi_sleep_ms_sync(ms: u64) {
std::thread::sleep(Duration::from_millis(ms));
}

View File

@@ -1,18 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "util/Seccomp.h"
#include "util/Js.h"
Js({ require("../util/Seccomp.js").detect(js, builder); })

View File

@@ -1,22 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef Seccomp_H
#define Seccomp_H
#include "util/Seccomp_impl.h"
#include "util/Linker.h"
Linker_require("util/Seccomp.c")
#endif

View File

@@ -1,101 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
'use strict';
var Os = require('os');
var TEST_PROGRAM = [
"#include <sys/resource.h>",
"#include <sys/prctl.h>",
"#include <linux/filter.h>",
"#include <linux/seccomp.h>",
"#include <linux/audit.h>",
"#include <sys/syscall.h>",
"int main() {",
" return __NR_read",
" | PR_SET_NO_NEW_PRIVS | PR_SET_SECCOMP | AUDIT_ARCH_X86_64",
" | BPF_K | SECCOMP_MODE_FILTER;",
"}"
].join('\n');
// Turns a version string into an array of integers
// 1.2.3-4-generic-x-5 -> [1, 2, 3, 4, 5]
// 1.2.3-xx-14.2 -> [1, 2, 3, 14, 2]
// 3.2.0-23-generic-pae -> [3, 2, 0, 23]
var version_to_array = function (version) {
var ver_list =
version.replace(/[^0-9]/g, '.').replace(/\.+/g, '.').replace(/\.$/, '').split('.');
for (var i = 0; i < ver_list.length; i++) {
ver_list[i] = Number(ver_list[i]);
}
return ver_list;
};
// Compares two arrays of integers
// Returns
// -1 for version1 < version2
// 0 for version1 == version2
// 1 for version1 > version2
var compare_versions = function (version1, version2) {
if (version1.length === 0 && version2.length === 0) {
return 0;
} else if (version1.length === 0) {
return (version2[0] === 0) ? 0 : 1;
} else if (version2.length === 0) {
return (version1[0] === 0) ? 0 : -1;
} else if (version1[0] === version2[0]) {
return compare_versions(version1.splice(1), version2.splice(1));
} else {
return (version1[0] < version2[0]) ? -1 : 1;
}
};
var seccomp_version_check = function (version) {
var ver_list = version_to_array(version);
return compare_versions(ver_list, [3, 5, 0]);
};
module.exports.detect = function (js, builder)
{
console.log("Searching for SECCOMP");
var osversion = Os.release();
if (builder.config.systemName !== 'linux') {
console.log("SECCOMP is only available on linux");
} else if (process.env['Seccomp_NO']) {
console.log("SECCOMP disabled");
} else if (!builder.config.crossCompiling && (seccomp_version_check(osversion) === -1)) {
console.log("SECCOMP filtering is only available in Linux 3.5+");
} else {
var done = js.async();
var CanCompile = require('../node_build/CanCompile');
var cflags = [ builder.config.cflags, '-x', 'c' ];
CanCompile.check(builder, TEST_PROGRAM, cflags, function (err, can) {
if (can) {
console.log("SECCOMP enabled");
js.linkerDependency("util/Seccomp_linux.c");
} else {
console.log("Failed to get SECCOMP, compile failure: [" + err + "]");
js.linkerDependency("util/Seccomp_dummy.c");
}
done();
});
return;
}
js.linkerDependency("util/Seccomp_dummy.c");
};

View File

@@ -1,32 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "util/Seccomp_impl.h"
// This file is for machines which do not support seccomp.
Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger))
{
Er_ret();
}
int Seccomp_isWorking()
{
return 0;
}
int Seccomp_exists()
{
return 0;
}

View File

@@ -1,28 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef Seccomp_impl_H
#define Seccomp_impl_H
#include "exception/Er.h"
#include "memory/Allocator.h"
#include "util/log/Log.h"
Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger));
int Seccomp_isWorking(void);
int Seccomp_exists(void);
#endif

View File

@@ -1,467 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
// sigaction() siginfo_t SIG_UNBLOCK
#define _POSIX_C_SOURCE 199309L
#include "util/Seccomp_impl.h"
#include "util/Bits.h"
#include "util/ArchInfo.h"
#include "util/Defined.h"
// getpriority()
#include <sys/resource.h>
#include <signal.h>
#include <sys/prctl.h>
#include <errno.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#include <linux/netlink.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
/**
* A unique number which is returned as errno by getpriority(), a syscall we never use
* this will be used by Seccomp_isWorking() to detect that the filter has been properly installed.
*/
#define IS_WORKING_ERRNO 3333
/**
* Accessing the SIGSYS siginfo depends on the fields being defined by the libc.
* Older libc do not yet include the needed definitions and accessor macros.
* Work around that by falling back to si_value.sival_int which works on some
* but not all architectures.
*/
#if defined(si_syscall)
# define GET_SYSCALL_NUM(si) ((si)->si_syscall)
#else
#pragma message "your libc doesn't define SIGSYS signal info! \
info about syscall number in case of SECCOMP crash can be invalid"
# define GET_SYSCALL_NUM(si) ((si)->si_value.sival_int)
#endif
static void catchViolation(int sig, siginfo_t* si, void* threadContext)
{
printf("Attempted banned syscall number [%d] see doc/Seccomp.md for more information\n",
GET_SYSCALL_NUM(si));
if (Defined(si_syscall)) {
printf("Your libc doesn't define SIGSYS signal info. "
"Above information about syscall number can be invalid.\n");
}
Assert_failure("Disallowed Syscall");
}
struct Filter {
int label;
int jt;
int jf;
struct sock_filter sf;
};
static struct sock_fprog* compile(struct Filter* input, int inputLen, struct Allocator* alloc)
{
// compute gotos
int totalOut = 0;
for (int i = inputLen-1; i >= 0; i--) {
struct Filter* a = &input[i];
if (a->label == 0) {
// check for unresolved gotos...
Assert_true(a->jt == 0 && a->jf == 0);
totalOut++;
continue;
}
int diff = 0;
for (int j = i-1; j >= 0; j--) {
struct Filter* b = &input[j];
if (b->label != 0) { continue; }
if (b->jt == a->label) {
b->sf.jt = diff;
b->jt = 0;
}
if (b->jf == a->label) {
b->sf.jf = diff;
b->jf = 0;
}
diff++;
}
}
// copy into output filter array...
struct sock_filter* sf = Allocator_calloc(alloc, sizeof(struct sock_filter), totalOut);
int outI = 0;
for (int i = 0; i < inputLen; i++) {
if (input[i].label == 0) {
Bits_memcpy(&sf[outI++], &input[i].sf, sizeof(struct sock_filter));
}
Assert_true(outI <= totalOut);
Assert_true(i != inputLen-1 || outI == totalOut);
}
struct sock_fprog* out = Allocator_malloc(alloc, sizeof(struct sock_fprog));
out->len = (unsigned short) totalOut;
out->filter = sf;
return out;
}
#define RET_TRAP 0x00030000u
#define RET_ERRNO(x) (0x00050000u | ((x) & 0x0000ffffu))
#define RET_SUCCESS 0x7fff0000u
static Er_DEFUN(struct sock_fprog* mkFilter(struct Allocator* alloc))
{
// Adding exceptions to the syscall filter:
//
// echo '#include <sys/syscall.h>' | gcc -E -dM - | grep 'define __NR_' | sort
// for the full list of system calls with syscall numbers (different per ABI)
//
// If gdb traps out it will look like this:
//
// Program received signal SIGSYS, Bad system call.
// [Switching to Thread 0x7ffff7fdd740 (LWP 14673)]
// 0x00007ffff74d1caa in mmap64 () at ../sysdeps/unix/syscall-template.S:81
// 81 ../sysdeps/unix/syscall-template.S: No such file or directory.
//
// %eax should contain the system call number (on different ABIs YMMV)
//
// (gdb) print $eax
// $1 = 9
// (gdb)
//
// Consult your syscall table from the above gcc command...
//
// #define __NR_mmap 9
//
// Then add:
//
// IFEQ(__NR_mmap, success),
//
// And add a comment documenting where you needed that syscall :)
#define STMT(code, val) { .sf = BPF_STMT(code, val) }
#define JMPK(type, not, input, label) { \
.sf = BPF_JUMP(BPF_JMP+(type)+BPF_K, (input), 0, 0), \
.jt = (!(not) ? (label) : 0), \
.jf = ((not) ? (label) : 0) \
}
// Create a label for jumps, the label must be represented by a non-zero integer.
#define LABEL(lbl) { .label = (lbl) }
// Load offset into the register
#define LOAD(offset) STMT(BPF_LD+BPF_W+BPF_ABS, (offset))
// Return constant value
#define RET(val) STMT(BPF_RET+BPF_K, (val))
// If-equal if the currently loaded value equals input, jump to label.
#define IFEQ(input, label) JMPK(BPF_JEQ, 0, (input), (label))
// If-not-equal if the currently loaded value is not equal to input, jump to label.
#define IFNE(input, label) JMPK(BPF_JEQ, 1, (input), (label))
// If-greater-than
#define IFGT(input, label) JMPK(BPF_JGT, 0, (input), (label))
// If-greater-than-or-equal-to
#define IFGE(input, label) JMPK(BPF_JGE, 0, (input), (label))
// If-less-than
#define IFLT(input, label) JMPK(BPF_JGE, 1, (input), (label))
// If-less-than-or-equal-to
#define IFLE(input, label) JMPK(BPF_JGT, 1, (input), (label))
// labels are integers so they must be predefined
int success = 1;
int fail = 2;
int unmaskOnly = 3;
int isworking = 4;
int ioctl_setip = 5;
int bind_netlink = 6;
uint32_t auditArch = ArchInfo_getAuditArch();
struct Filter seccompFilter[] = {
LOAD(offsetof(struct seccomp_data, arch)),
IFNE(auditArch, fail),
// Get the syscall num.
LOAD(offsetof(struct seccomp_data, nr)),
// rust/threading
#ifdef __NR_futex
IFEQ(__NR_futex, success),
#endif
// udp
#ifdef __NR_sendmsg
IFEQ(__NR_sendmsg, success),
#endif
#ifdef __NR_recvmsg
IFEQ(__NR_recvmsg, success),
#endif
// ETHInterface
#ifdef __NR_sendto
IFEQ(__NR_sendto, success),
#endif
#ifdef __NR_recvfrom
IFEQ(__NR_recvfrom, success),
#endif
#ifdef __NR_socketcall
// 32-bit: recvmsg is a socketcall
IFEQ(__NR_socketcall, success),
#endif
// libuv
IFEQ(__NR_epoll_ctl, success),
#ifdef __NR_epoll_wait
IFEQ(__NR_epoll_wait, success),
#endif
#ifdef __NR_epoll_pwait
IFEQ(__NR_epoll_pwait, success),
#endif
// gettimeofday is required on some architectures
#ifdef __NR_gettimeofday
IFEQ(__NR_gettimeofday, success),
#endif
// TUN (and logging)
IFEQ(__NR_write, success),
IFEQ(__NR_read, success),
// readv and writev are used by some libc (musl)
#ifdef __NR_readv
IFEQ(__NR_readv, success),
#endif
#ifdef __NR_writev
IFEQ(__NR_writev, success),
#endif
// modern librt reads a read-only mapped section of kernel space which contains the time
// older versions need system calls for getting the time.
// i686 glibc-2.18's time() uses __NR_time
// Raspberry Pi and BeagleBone Black don't provide __NR_time
// 32-bit systems with 64-bit time_t use __NR_clock_gettime64
#ifdef __NR_clock_gettime64
IFEQ(__NR_clock_gettime64, success),
#endif
#ifdef __NR_clock_gettime
IFEQ(__NR_clock_gettime, success),
#endif
#ifdef __NR_time
IFEQ(__NR_time, success),
#endif
// NetPlatform_linux.c send recv
#ifdef __NR_send
IFEQ(__NR_send, success),
#endif
#ifdef __NR_recv
IFEQ(__NR_recv, success),
#endif
// malloc()
IFEQ(__NR_brk, success),
// abort()
IFEQ(__NR_gettid, success),
IFEQ(__NR_tgkill, success),
IFEQ(__NR_rt_sigprocmask, unmaskOnly),
// exit()
IFEQ(__NR_exit_group, success),
// Seccomp_isWorking()
IFEQ(__NR_getpriority, isworking),
// Securiy_checkPermissions() -> canOpenFiles()
IFEQ(__NR_dup, success),
IFEQ(__NR_close, success),
// Security_checkPermissions() -> getMaxMem()
// x86/ARM use ugetrlimit and mmap2
// ARM does not even have __NR_getrlimit or __NR_mmap defined
// and AMD64 does not have __NR_ugetrlimit or __NR_mmap2 defined
#ifdef __NR_getrlimit
IFEQ(__NR_getrlimit, success),
#endif
#ifdef __NR_ugetrlimit
IFEQ(__NR_ugetrlimit, success),
#endif
#ifdef __NR_mmap
IFEQ(__NR_mmap, success),
#endif
#ifdef __NR_mmap2
IFEQ(__NR_mmap2, success),
#endif
IFEQ(__NR_munmap, success),
// printf()
IFEQ(__NR_fstat, success),
#ifdef __NR_fstat64
IFEQ(__NR_fstat64, success),
#endif
// for setting IP addresses
// socketForIfName()
// and ETHInterface_listDevices
#ifdef __NR_socket
IFEQ(__NR_socket, success),
#endif
IFEQ(__NR_ioctl, ioctl_setip),
// Security_checkPermissions
IFEQ(__NR_getuid, success),
// Security_nofiles
IFEQ(__NR_setrlimit, success),
// for ETHInterface_listDevices (netlinkk)
#ifdef __NR_bind
IFEQ(__NR_bind, bind_netlink),
#endif
#ifdef __NR_getsockname
IFEQ(__NR_getsockname, success),
#endif
// musl free() calls madvise()
#ifdef __NR_madvise
IFEQ(__NR_madvise, success),
#endif
// accept() for PipeServer
#ifdef __NR_accept4
IFEQ(__NR_accept4, success),
#endif
#ifdef Cjdns_android
#ifdef __NR_rt_sigprocmask
IFEQ(__NR_rt_sigprocmask, success),
#endif
#endif
// rust/wg
#ifdef __NR_getrandom
IFEQ(__NR_getrandom, success),
#endif
// https://github.com/cjdelisle/boringtun/blob/master/src/crypto/x25519/mod.rs#L22
#if defined(__ARM_EABI__) && defined(__NR_fcntl64)
IFEQ(__NR_fcntl64, success),
#endif
// 2024-01-09 by Caleb's advice
// it is used by Seccomp_test
#ifdef __NR_sigaltstack
IFEQ(__NR_sigaltstack, success),
#endif
RET(SECCOMP_RET_TRAP),
LABEL(ioctl_setip),
LOAD(offsetof(struct seccomp_data, args[1])),
IFEQ(SIOCGIFINDEX, success),
IFEQ(SIOCGIFFLAGS, success),
IFEQ(SIOCSIFFLAGS, success),
IFEQ(SIOCSIFADDR, success),
IFEQ(SIOCSIFNETMASK, success),
IFEQ(SIOCSIFMTU, success),
RET(SECCOMP_RET_TRAP),
LABEL(bind_netlink),
LOAD(offsetof(struct seccomp_data, args[2])),
// Filter NETLINK by size of address.
// Most importantly INET and INET6
// are differnt.
IFEQ(sizeof(struct sockaddr_nl), success),
RET(SECCOMP_RET_TRAP),
// We allow sigprocmask to *unmask* signals but we don't allow it to mask them.
LABEL(unmaskOnly),
LOAD(offsetof(struct seccomp_data, args[0])),
IFEQ(SIG_UNBLOCK, success),
RET(SECCOMP_RET_TRAP),
LABEL(isworking),
RET(RET_ERRNO(IS_WORKING_ERRNO)),
LABEL(fail),
RET(SECCOMP_RET_TRAP),
LABEL(success),
RET(SECCOMP_RET_ALLOW),
};
Er_ret(compile(seccompFilter, sizeof(seccompFilter)/sizeof(seccompFilter[0]), alloc));
}
static Er_DEFUN(void installFilter(
struct sock_fprog* filter, struct Log* logger, struct Allocator* alloc))
{
struct sigaction sa = { .sa_sigaction = catchViolation, .sa_flags = SA_SIGINFO };
if (sigaction(SIGSYS, &sa, NULL)) {
Log_warn(logger, "sigaction(SIGSYS) -> [%s]\n", strerror(errno));
}
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
// don't worry about it.
Log_warn(logger, "prctl(PR_SET_NO_NEW_PRIVS) -> [%s]\n", strerror(errno));
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter) == -1) {
Er_raise(alloc, "prctl(PR_SET_SECCOMP) -> [%s]\n", strerror(errno));
}
Er_ret();
}
Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger))
{
struct sock_fprog* filter = Er(mkFilter(tempAlloc));
Er(installFilter(filter, logger, tempAlloc));
if (!Seccomp_isWorking()) {
Er_raise(tempAlloc, "Seccomp filter not installed properly, Seccomp_isWorking() -> false");
}
Er_ret();
}
int Seccomp_isWorking(void)
{
errno = 0;
// If seccomp is not working, this will fail setting errno to EINVAL
long ret = getpriority(1000, 1);
int err = errno;
// Inside of the kernel, it seems to check whether the errno return is sane
// and if it is not, it treates it as a return value, IS_WORKING_ERRNO (3333) is very unique so
// we'll check for either case just in case this changes.
return (ret == -1 && err == IS_WORKING_ERRNO) || (ret == -IS_WORKING_ERRNO && err == 0);
}
int Seccomp_exists(void)
{
return 1;
}

View File

@@ -18,7 +18,6 @@
#include "exception/Except.h"
#include "util/log/Log.h"
#include "util/Security.h"
#include "util/Seccomp.h"
#include "memory/Allocator.h"
#include "util/Bits.h"
#include "util/Setuid.h"
@@ -137,12 +136,6 @@ Er_DEFUN(void Security_chroot(char* root, struct Allocator* errAlloc))
Er_ret();
}
Er_DEFUN(void Security_seccomp(struct Allocator* tempAlloc, struct Log* logger))
{
Er(Seccomp_dropPermissions(tempAlloc, logger));
Er_ret();
}
struct Security_pvt
{
struct Security pub;
@@ -181,8 +174,6 @@ Er_DEFUN(struct Security_Permissions* Security_checkPermissions(struct Allocator
Allocator_calloc(alloc, sizeof(struct Security_Permissions), 1);
out->noOpenFiles = !canOpenFiles();
out->seccompExists = Seccomp_exists();
out->seccompEnforcing = Seccomp_isWorking();
out->uid = getuid();
Er_ret(out);

View File

@@ -33,8 +33,6 @@
struct Security_Permissions
{
int noOpenFiles;
int seccompExists;
int seccompEnforcing;
int uid;
};
@@ -55,8 +53,6 @@ Er_DEFUN(void Security_noforks(struct Allocator* errAlloc));
Er_DEFUN(void Security_chroot(char* root, struct Allocator* errAlloc));
Er_DEFUN(void Security_seccomp(struct Allocator* tempAlloc, struct Log* logger));
void Security_setupComplete(struct Security* security);
struct Security* Security_new(struct Allocator* alloc, struct Log* log, EventBase_t* base);

View File

@@ -62,8 +62,6 @@ static void checkPermissions(Dict* args, void* vctx, String* txid, struct Alloca
}
Dict* out = Dict_new(requestAlloc);
Dict_putIntC(out, "noOpenFiles", sp->noOpenFiles, requestAlloc);
Dict_putIntC(out, "seccompExists", sp->seccompExists, requestAlloc);
Dict_putIntC(out, "seccompEnforcing", sp->seccompEnforcing, requestAlloc);
Dict_putIntC(out, "userId", sp->uid, requestAlloc);
Dict_putStringCC(out, "error", "none", requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
@@ -105,27 +103,6 @@ static void chroot(Dict* args, void* vctx, String* txid, struct Allocator* reque
sendError("none", txid, ctx->admin);
}
static void seccomp(Dict* args, void* vctx, String* txid, struct Allocator* requestAlloc)
{
struct Context* const ctx = Identity_check((struct Context*) vctx);
struct Er_Ret* er = NULL;
struct Security_Permissions* sp = Er_check(&er, Security_checkPermissions(requestAlloc));
if (er) {
sendError(er->message, txid, ctx->admin);
return;
}
if (sp->seccompEnforcing) {
sendError("seccomp is already enabled", txid, ctx->admin);
return;
}
Er_check(&er, Security_seccomp(requestAlloc, ctx->logger));
if (er) {
sendError(er->message, txid, ctx->admin);
return;
}
sendError("none", txid, ctx->admin);
}
static void setupComplete(Dict* args, void* vctx, String* txid, struct Allocator* requestAlloc)
{
struct Context* const ctx = Identity_check((struct Context*) vctx);
@@ -166,7 +143,6 @@ void Security_admin_register(struct Allocator* alloc,
Admin_registerFunction("Security_getUser", getUser, ctx, true, ((struct Admin_FunctionArg[]) {
{ .name = "user", .required = 0, .type = "String" }
}), admin);
Admin_registerFunction("Security_seccomp", seccomp, ctx, true, NULL, admin);
Admin_registerFunction("Security_setupComplete", setupComplete, ctx, true, NULL, admin);
Admin_registerFunction("Security_checkPermissions", checkPermissions, ctx, true, NULL, admin);
}

View File

@@ -57,11 +57,6 @@ Er_DEFUN(void Security_chroot(char* root, struct Allocator* errAlloc))
Er_ret();
}
Er_DEFUN(void Security_seccomp(struct Allocator* tempAlloc, struct Log* logger))
{
Er_ret();
}
struct Security_pvt
{
struct Security pub;

View File

@@ -14,7 +14,6 @@
*/
#include "memory/Allocator.h"
#include "util/SysInfo.h"
#include "util/Seccomp.h"
#include "util/CString.h"
#include "util/Bits.h"
#include "util/Defined.h"
@@ -43,8 +42,6 @@ struct SysInfo SysInfo_detect(void)
out.os = SysInfo_Os_UNKNOWN;
}
out.seccomp = Seccomp_exists();
return out;
}
@@ -64,9 +61,8 @@ static char* getName(enum SysInfo_Os os)
char* SysInfo_describe(struct SysInfo si, struct Allocator* alloc)
{
uint8_t buff[BUFF_SZ];
snprintf(buff, BUFF_SZ, "%s%s",
getName(si.os),
(si.seccomp) ? " +seccomp" : "");
snprintf(buff, BUFF_SZ, "%s",
getName(si.os));
int len = CString_strlen(buff)+1;
char* out = Allocator_malloc(alloc, len);

View File

@@ -35,7 +35,6 @@ enum SysInfo_Os
struct SysInfo
{
int seccomp : 1;
int os : 4;
};
Assert_compileTime(sizeof(struct SysInfo) == 4);

View File

@@ -1,155 +0,0 @@
/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "benc/String.h"
#include "util/Identity.h"
#include "util/log/FileWriterLog.h"
#include "memory/Allocator.h"
#include "util/Seccomp.h"
#include "util/events/EventBase.h"
#include "util/events/Process.h"
#include "util/events/Pipe.h"
#include "util/events/Socket.h"
#include "util/events/Timeout.h"
#include "util/CString.h"
#include "crypto/random/Random.h"
#include "interface/addressable/AddrIface.h"
#include <unistd.h>
struct Context
{
struct Iface iface;
struct Allocator* alloc;
EventBase_t* eventBase;
Identity
};
struct ChildCtx
{
EventBase_t* base;
struct Log* log;
Iface_t* socket;
struct Allocator* alloc;
Identity
};
static void childMessageSent(struct Allocator_OnFreeJob* j) {
struct ChildCtx* child = Identity_check((struct ChildCtx*) j->userData);
printf("Child shutting down\n");
EventBase_endLoop(child->base);
}
static void timeout(void* vNULL)
{
Assert_true(!"timed out");
}
static void onConnectionChild(struct ChildCtx* child)
{
Er_assert(Seccomp_dropPermissions(child->alloc, child->log));
Assert_true(Seccomp_isWorking());
Allocator_t* alloc = Allocator_child(child->alloc);
Allocator_t* alloc1 = Allocator_child(alloc);
Message_t* ok = Message_new(0, 512, alloc1);
Er_assert(Message_epush(ok, "OK", 3));
struct Iface iface = { .send = NULL };
Iface_plumb(child->socket, &iface);
Iface_send(&iface, ok);
Allocator_onFree(alloc1, childMessageSent, child);
Allocator_free(alloc);
// just set a timeout long enough that we're pretty sure the parent will get the message
// before we quit.
// Timeout_setInterval(childComplete, child->base, 10, child->base, child->alloc);
Timeout_setTimeout(timeout, child->base, 2000, child->base, alloc);
EventBase_beginLoop(child->base);
}
static void timeout2(void* vNULL)
{
Assert_true(!"time out 2");
}
static int child(char* pipeName, struct Allocator* alloc, struct Log* logger)
{
struct ChildCtx* ctx = Allocator_calloc(alloc, sizeof(struct ChildCtx), 1);
ctx->base = EventBase_new(alloc);
ctx->alloc = alloc;
ctx->log = logger;
ctx->socket = Er_assert(Socket_connect(pipeName, alloc));
Identity_set(ctx);
onConnectionChild(ctx);
return 0;
}
static Iface_DEFUN receiveMessageParent(Message_t* msg, struct Iface* iface)
{
struct Context* ctx = Identity_check((struct Context*) iface);
// PipeServer pushes a uint32 identifier of the client who sent the message
Er_assert(AddrIface_popAddr(msg));
Assert_true(Message_getLength(msg) == 3);
Assert_true(!Bits_memcmp(Message_bytes(msg), "OK", 3));
printf("Parent got reply\n");
EventBase_endLoop(ctx->eventBase);
return NULL;
}
int main(int argc, char** argv)
{
struct Allocator* alloc = Allocator_new(20000);
struct Log* logger = FileWriterLog_new(stdout, alloc);
if (!Seccomp_exists()) {
Log_debug(logger, "Seccomp not supported on this system");
return 0;
}
if (argc > 3 && !CString_strcmp("Seccomp_test", argv[1]) && !CString_strcmp("child", argv[2])) {
child(argv[3], alloc, logger);
// Allocator_free(alloc);
// TODO: Freeing the allocator causes a Identity_check() assertion crash in Pipe.c, replace after async allocator free is abolished.
return 0;
}
EventBase_t* eb = EventBase_new(alloc);
struct Random* rand = Random_new(alloc, logger, NULL);
char randName[32] = {0};
Random_base32(rand, (uint8_t*)randName, 31);
String* name = String_printf(alloc, "%s%scjdns-test-%s", Pipe_PATH, Pipe_PATH_SEP, randName);
struct Context* ctx = Allocator_calloc(alloc, sizeof(struct Context), 1);
Identity_set(ctx);
ctx->alloc = alloc;
ctx->iface.send = receiveMessageParent;
ctx->eventBase = eb;
Socket_Server_t* ss = Er_assert(Socket_server(name->bytes, alloc));
Iface_plumb(&ctx->iface, ss->iface);
const char* path = Process_getPath(alloc);
const char* args[] = { "Seccomp_test", "child", name->bytes, NULL };
Assert_true(!Process_spawn(path, args, alloc, NULL));
Timeout_setTimeout(timeout2, NULL, 2000, eb, alloc);
EventBase_beginLoop(eb);
unlink(name->bytes);
return 0;
}