65 lines
2.7 KiB
C
65 lines
2.7 KiB
C
#define _GNU_SOURCE
|
|||
|
|
#include <errno.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <sched.h>
|
||
|
|
#include <stddef.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <sys/ptrace.h>
|
||
|
|
#include <sys/socket.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <sys/un.h>
|
||
|
|
#include <sys/wait.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
static void check(int condition, const char *label) {
|
||
|
|
if (!condition) {
|
||
|
|
fprintf(stderr, "ESCAPE: %s (%s)\n", label, strerror(errno));
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
int main(void) {
|
||
|
|
for (int fd = 3; fd < 256; fd++)
|
||
|
|
check(fcntl(fd, F_GETFD) < 0 && errno == EBADF, "inherited descriptor");
|
||
|
|
check(!getenv("QUIXOS_VM_SECRET"), "parent environment");
|
||
|
|
check(open("/etc/test-secret", O_RDONLY) < 0, "authority filesystem");
|
||
|
|
check(open("/sys/fs/cgroup/cgroup.procs", O_WRONLY) < 0, "resource controls");
|
||
|
|
check(open("/proc/1/root/etc/test-secret", O_RDONLY) < 0, "proc root escape");
|
||
|
|
check(socket(AF_INET, SOCK_STREAM, 0) < 0 && errno == EPERM, "IPv4 socket");
|
||
|
|
check(socket(AF_INET6, SOCK_STREAM, 0) < 0 && errno == EPERM, "IPv6 socket");
|
||
|
|
check(socket(AF_NETLINK, SOCK_RAW, 0) < 0 && errno == EPERM, "netlink socket");
|
||
|
|
check(ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0 && errno == EPERM, "ptrace");
|
||
|
|
check(unshare(CLONE_NEWUSER) < 0, "nested user namespace");
|
||
|
|
pid_t child = fork();
|
||
|
|
if (child == 0) _exit(2);
|
||
|
|
check(child < 0 && errno == EPERM, "subprocess");
|
||
|
|
check(access("/bin/sh", X_OK) < 0, "ambient executable");
|
||
|
|
check(getppid() <= 1, "host process visibility");
|
||
|
|
struct sockaddr_un address = {.sun_family = AF_UNIX};
|
||
|
|
int outside = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
|
|
check(outside >= 0, "Unix socket support");
|
||
|
|
strcpy(address.sun_path, "/run/quixos-native-authority/test.sock");
|
||
|
|
check(connect(outside, (struct sockaddr *)&address, sizeof(address)) < 0,
|
||
|
|
"authority Unix socket");
|
||
|
|
close(outside);
|
||
|
|
outside = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
|
|
memset(address.sun_path, 0, sizeof(address.sun_path));
|
||
|
|
strcpy(address.sun_path + 1, "quixos-native-authority");
|
||
|
|
check(connect(outside, (struct sockaddr *)&address,
|
||
|
|
offsetof(struct sockaddr_un, sun_path) + 1 + strlen(address.sun_path + 1)) < 0,
|
||
|
|
"authority abstract socket");
|
||
|
|
close(outside);
|
||
|
|
char directory[4096];
|
||
|
|
check(getcwd(directory, sizeof(directory)) != NULL, "engine directory");
|
||
|
|
check(snprintf(address.sun_path, sizeof(address.sun_path), "%s/broker.sock", directory) <
|
||
|
|
(int)sizeof(address.sun_path), "broker path length");
|
||
|
|
int broker = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
|
|
check(broker >= 0 && connect(broker, (struct sockaddr *)&address, sizeof(address)) == 0,
|
||
|
|
"authorized broker");
|
||
|
|
check(write(broker, "sandbox-probe", 13) == 13, "broker write");
|
||
|
|
close(broker);
|
||
|
|
puts("RESTRICTED_NATIVE_SANDBOX_PASSED");
|
||
|
|
return 0;
|
||
|
|
}
|