aboutgitcodelistschat:MatrixIRC
path: root/operations.c
diff options
context:
space:
mode:
authorAlice Frosi <afrosi@redhat.com>2023-03-24 16:08:46 +0100
committerAlice Frosi <afrosi@redhat.com>2023-03-24 16:08:46 +0100
commite6562db0423e6019154baf05beb02ff3da02f3bb (patch)
tree07c17ea7d3c193c7cdf6b591422a1e6b7ba22a99 /operations.c
parent2e4cab1ff7c9d8db278427d9dca6ccb62619ca18 (diff)
downloadseitan-e6562db0423e6019154baf05beb02ff3da02f3bb.tar
seitan-e6562db0423e6019154baf05beb02ff3da02f3bb.tar.gz
seitan-e6562db0423e6019154baf05beb02ff3da02f3bb.tar.bz2
seitan-e6562db0423e6019154baf05beb02ff3da02f3bb.tar.lz
seitan-e6562db0423e6019154baf05beb02ff3da02f3bb.tar.xz
seitan-e6562db0423e6019154baf05beb02ff3da02f3bb.tar.zst
seitan-e6562db0423e6019154baf05beb02ff3da02f3bb.zip
Re-arrange repository structure
Diffstat (limited to 'operations.c')
-rw-r--r--operations.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/operations.c b/operations.c
new file mode 100644
index 0000000..0327e57
--- /dev/null
+++ b/operations.c
@@ -0,0 +1,361 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright 2023 Red Hat GmbH
+ * Author: Alice Frosi <afrosi@redhat.com>
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <linux/seccomp.h>
+#include <linux/filter.h>
+#include <linux/audit.h>
+#include <errno.h>
+
+#include "gluten.h"
+#include "operations.h"
+
+static bool is_cookie_valid(int notifyFd, uint64_t id)
+{
+ return ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) == 0;
+}
+
+static int send_target(const struct seccomp_notif_resp *resp, int notifyfd)
+{
+ if (!is_cookie_valid(notifyfd, resp->id)) {
+ fprintf(stderr,
+ "the response id isn't valid\ncheck if the targets has already terminated\n");
+ return -1;
+ }
+ if (ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) {
+ if (errno != EINPROGRESS) {
+ perror("sending the response");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int send_inject_target(const struct seccomp_notif_addfd *resp,
+ int notifyfd)
+{
+ if (!is_cookie_valid(notifyfd, resp->id)) {
+ fprintf(stderr,
+ "the response id isn't valid\ncheck if the targets has already terminated\n");
+ return -1;
+ }
+ if (ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_ADDFD, resp) < 0) {
+ if (errno != EINPROGRESS) {
+ perror("sending the response");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void proc_ns_name(unsigned i, char *ns)
+{
+ switch (i) {
+ case NS_CGROUP:
+ snprintf(ns, PATH_MAX + 1, "cgroup");
+ break;
+ case NS_IPC:
+ snprintf(ns, PATH_MAX + 1, "ipc");
+ break;
+ case NS_NET:
+ snprintf(ns, PATH_MAX + 1, "net");
+ break;
+ case NS_MOUNT:
+ snprintf(ns, PATH_MAX + 1, "mnt");
+ break;
+ case NS_PID:
+ snprintf(ns, PATH_MAX + 1, "pid");
+ break;
+ case NS_USER:
+ snprintf(ns, PATH_MAX + 1, "user");
+ break;
+ case NS_UTS:
+ snprintf(ns, PATH_MAX + 1, "uts");
+ break;
+ case NS_TIME:
+ snprintf(ns, PATH_MAX + 1, "time");
+ break;
+ default:
+ fprintf(stderr, "unrecognized namespace index %d\n", i);
+ }
+}
+
+static int set_namespaces(const struct op_call *a, int tpid)
+{
+ char path[PATH_MAX + 1];
+ char ns_name[PATH_MAX / 2];
+ struct ns_spec ns;
+ int fd;
+ unsigned int i;
+
+ for (i = 0, ns = (a->context).ns[i]; i < sizeof(enum ns_type);
+ i++, ns = (a->context).ns[i]) {
+ proc_ns_name(i, ns_name);
+ switch (ns.type) {
+ case NS_NONE:
+ continue;
+ case NS_SPEC_TARGET:
+ snprintf(path, sizeof(path), "/proc/%d/ns/%s", tpid,
+ ns_name);
+ break;
+ case NS_SPEC_PID:
+ snprintf(path, sizeof(path), "/proc/%d/ns/%s", ns.pid,
+ ns_name);
+ break;
+ case NS_SPEC_PATH:
+ snprintf(path, sizeof(path), "%s", ns.path);
+ break;
+ }
+
+ if ((fd = open(path, O_CLOEXEC)) < 0) {
+ fprintf(stderr, "open for file %s: %s", path,
+ strerror(errno));
+ return -1;
+ }
+
+ if (setns(fd, 0) != 0) {
+ perror("setns");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int execute_syscall(void *args)
+{
+ struct arg_clone *a = (struct arg_clone *)args;
+ const struct op_call *c = a->args;
+
+ if (set_namespaces(a->args, a->pid) < 0) {
+ exit(EXIT_FAILURE);
+ }
+ /* execute syscall */
+ a->ret = syscall(c->nr, c->args[0], c->args[1], c->args[2], c->args[3],
+ c->args[4], c->args[5]);
+ a->err = errno;
+ if (a->ret < 0) {
+ perror("syscall");
+ exit(EXIT_FAILURE);
+ }
+ exit(0);
+}
+
+int copy_args(struct seccomp_notif *req, struct op_copy_args *copy, void *data,
+ int notifier)
+{
+ char path[PATH_MAX];
+ unsigned int i;
+ ssize_t nread;
+ void *dest;
+ int fd;
+
+ snprintf(path, sizeof(path), "/proc/%d/mem", req->pid);
+ if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+ perror("open mem");
+ return -1;
+ }
+
+ /*
+ * Avoid the TOCTOU and check if the read mappings are still valid
+ */
+ if (!is_cookie_valid(notifier, req->id)) {
+ fprintf(stderr, "the seccomp request isn't valid anymore\n");
+ return -1;
+ }
+ for (i = 0; i < 6; i++) {
+ if (copy->args[i].type == REFERENCE) {
+ dest = (uint16_t *)data + copy->args[i].args_off;
+ nread = pread(fd, dest, copy->args[i].size,
+ req->data.args[i]);
+ if (nread < 0) {
+ perror("pread");
+ return -1;
+ }
+ } else {
+ memcpy((uint16_t *)data + copy->args[i].args_off,
+ &req->data.args[i], copy->args[i].size);
+ }
+ }
+ close(fd);
+ return 0;
+}
+
+static int resolve_fd(void *data, struct op_resolvedfd *resfd, pid_t pid)
+{
+ char fdpath[PATH_MAX], buf[PATH_MAX];
+ char *path = (char *)((uint16_t *)data + resfd->path_off);
+ int *fd = (int *)((uint16_t *)data + resfd->fd_off);
+ ssize_t nbytes;
+
+ snprintf(fdpath, PATH_MAX, "/proc/%d/fd/%d", pid, *fd);
+ if ((nbytes = readlink(fdpath, buf, resfd->path_size)) < 0) {
+ fprintf(stderr, "error reading %s\n", fdpath);
+ perror("readlink");
+ return -1;
+ }
+ if (strcmp(path, buf) == 0)
+ return 0;
+ else
+ return 1;
+}
+
+int do_call(struct arg_clone *c)
+{
+ char stack[STACK_SIZE];
+ pid_t child;
+
+ c->ret = -1;
+ c->err = 0;
+
+ /* Create a process that will be moved to the namespace */
+ child = clone(execute_syscall, stack + sizeof(stack),
+ CLONE_FILES | CLONE_VM | SIGCHLD, (void *)c);
+ if (child == -1) {
+ perror("clone");
+ return -1;
+ }
+ wait(NULL);
+ return 0;
+}
+
+static void set_inject_fields(uint64_t id, void *data, const struct op *a,
+ struct seccomp_notif_addfd *resp)
+{
+ const struct fd_type *new = &(a->inj).newfd;
+ const struct fd_type *old = &(a->inj).oldfd;
+
+ resp->flags = SECCOMP_ADDFD_FLAG_SETFD;
+ resp->id = id;
+ if (new->type == IMMEDIATE)
+ resp->newfd = new->fd;
+ else
+ memcpy(&resp->srcfd, (uint16_t *)data + old->fd_off,
+ sizeof(resp->srcfd));
+ if (old->type == IMMEDIATE)
+ resp->srcfd = old->fd;
+ else
+ memcpy(&resp->srcfd, (uint16_t *)data + old->fd_off,
+ sizeof(resp->srcfd));
+ resp->newfd_flags = 0;
+}
+
+int do_operations(void *data, struct op operations[], struct seccomp_notif *req,
+ unsigned int n_operations, int pid, int notifyfd, uint64_t id)
+{
+ struct seccomp_notif_addfd resp_fd;
+ struct seccomp_notif_resp resp;
+ struct arg_clone c;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < n_operations; i++) {
+ switch (operations[i].type) {
+ case OP_CALL:
+ resp.id = id;
+ resp.val = 0;
+ resp.flags = 0;
+ c.args = &operations[i].call;
+ c.pid = pid;
+ if (do_call(&c) == -1) {
+ resp.error = -1;
+ if (send_target(&resp, notifyfd) == -1)
+ return -1;
+ }
+ if (c.err != 0) {
+ resp.error = -1;
+ if (send_target(&resp, notifyfd) == -1)
+ return c.err;
+ }
+ /*
+ * The result of the call needs to be save as
+ * reference
+ */
+ if (operations[i].call.has_ret) {
+ memcpy((uint16_t *)data +
+ operations[i].call.ret_off,
+ &c.ret, sizeof(c.ret));
+ }
+ break;
+ case OP_BLOCK:
+ resp.id = id;
+ resp.val = 0;
+ resp.flags = 0;
+ resp.error = operations[i].block.error;
+ if (send_target(&resp, notifyfd) == -1)
+ return -1;
+ break;
+ case OP_RETURN:
+ resp.id = id;
+ resp.flags = 0;
+ resp.error = 0;
+ if (operations[i].ret.type == IMMEDIATE)
+ resp.val = operations[i].ret.value;
+ else
+ memcpy(&resp.val,
+ (uint16_t *)data +
+ operations[i].ret.value_off,
+ sizeof(resp.val));
+
+ if (send_target(&resp, notifyfd) == -1)
+ return -1;
+ break;
+
+ case OP_CONT:
+ resp.id = id;
+ resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+ resp.error = 0;
+ resp.val = 0;
+ if (send_target(&resp, notifyfd) == -1)
+ return -1;
+ break;
+ case OP_INJECT_A:
+ set_inject_fields(id, data, &operations[i], &resp_fd);
+ resp_fd.flags |= SECCOMP_ADDFD_FLAG_SEND;
+ if (send_inject_target(&resp_fd, notifyfd) == -1)
+ return -1;
+ break;
+ case OP_INJECT:
+ set_inject_fields(id, data, &operations[i], &resp_fd);
+ if (send_inject_target(&resp_fd, notifyfd) == -1)
+ return -1;
+ break;
+ case OP_COPY_ARGS:
+ if (copy_args(req, &operations[i].copy, data,
+ notifyfd) < 0)
+ return -1;
+ break;
+ case OP_END:
+ return 0;
+ case OP_CMP:
+ if (memcmp((uint16_t *)data + operations[i].cmp.s1_off,
+ (uint16_t *)data + operations[i].cmp.s2_off,
+ operations[i].cmp.size) != 0) {
+ i = operations[i].cmp.jmp;
+ }
+ break;
+ case OP_RESOLVEDFD:
+ ret = resolve_fd(data, &operations[i].resfd, pid);
+ if (ret == -1)
+ return -1;
+ else if (ret == 1)
+ i = operations[i].resfd.jmp;
+ break;
+ default:
+ fprintf(stderr, "unknow operation %d \n",
+ operations[i].type);
+ }
+ }
+ return 0;
+}