Files
quixos-nix-helpers/restricted-execution/identity-launcher.c
T

89 lines
3.7 KiB
C

#define _GNU_SOURCE
#include <dirent.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
/* Installed only as a narrowly executable NixOS wrapper. This trusted helper
* accepts no executable, environment, filesystem root or arbitrary UID. The
* caller is platform authority, never a workspace guest or authored Worker. */
static void fail(const char *message) {
fprintf(stderr, "RESTRICTED_IDENTITY: %s\n", message);
exit(125);
}
int main(int argc, char **argv) {
const char *root = "/run/quixos-restricted-engines/";
uid_t caller = getuid();
gid_t group = getgid();
struct passwd *owner = getpwnam("quixos-graph");
if (geteuid() != 0) fail("privileged wrapper unavailable");
if (!owner) fail("authority identity unavailable");
if (caller != owner->pw_uid) fail("caller identity mismatch");
if (argc != 3) fail("invalid arguments");
char *end;
unsigned long number = strtoul(argv[2], &end, 10);
if (!*argv[2] || *end || number < 610000 || number >= 610064)
fail("invalid engine identity");
uid_t identity = (uid_t)number;
if (strlen(argv[1]) != strlen(root) + 16 || strncmp(argv[1], root, strlen(root)))
fail("invalid engine directory");
const char *name = argv[1] + strlen(root);
if (strncmp(name, "qx-engine-", 10) ||
strlen(name) != 16 || strspn(name + 10, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") != 6)
fail("invalid engine directory");
int parent = open("/run/quixos-restricted-engines", O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
struct stat info;
if (parent < 0 || fstat(parent, &info) || info.st_uid != caller || (info.st_mode & 066))
fail("invalid engine root");
int directory = openat(parent, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (directory < 0 || fstat(directory, &info) || info.st_uid != caller || (info.st_mode & 077))
fail("invalid engine owner");
DIR *entries = fdopendir(dup(directory));
if (!entries) fail("engine entries");
struct dirent *entry;
unsigned int files = 0;
while ((entry = readdir(entries))) {
const char *file = entry->d_name;
if (!strcmp(file, ".") || !strcmp(file, "..")) continue;
int socket = !strcmp(file, "broker.sock");
if ((!socket && strcmp(file, "config.capnp") && strcmp(file, "parent.mjs")) ||
fstatat(directory, file, &info, AT_SYMLINK_NOFOLLOW) || info.st_uid != caller ||
info.st_nlink != 1 || (socket ? !S_ISSOCK(info.st_mode) : !S_ISREG(info.st_mode)) ||
info.st_size > 4 * 1024 * 1024)
fail("unexpected engine file");
files++;
}
closedir(entries);
if (files != 3) fail("incomplete engine configuration");
const char *files_to_transfer[] = {"config.capnp", "parent.mjs", "broker.sock"};
for (size_t i = 0; i < 3; i++) {
if (fchownat(directory, files_to_transfer[i], identity, group, AT_SYMLINK_NOFOLLOW) ||
fchmodat(directory, files_to_transfer[i], 0660, 0))
fail("engine file ownership");
}
if (fchown(directory, identity, group) || fchmod(directory, 0770))
fail("engine directory ownership");
close(directory);
close(parent);
struct rlimit core = {0, 0};
if (setrlimit(RLIMIT_CORE, &core) || setgroups(0, NULL) ||
setresgid(identity, identity, identity) || setresuid(identity, identity, identity) ||
prctl(PR_SET_DUMPABLE, 0) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
fail("identity confinement");
/* The containing directory grants only its owner and authority access. A
* socket created by workerd must also admit authority's different UID/GID. */
umask(0000);
char *args[] = {SANDBOX_EXECUTABLE, argv[1], NULL};
clearenv();
execv(SANDBOX_EXECUTABLE, args);
fail("sandbox launch");
}