From f1f136577a52b1588da5f74683f41d14df410300 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Thu, 8 Jun 2023 17:43:56 +0200 Subject: seitan,cooker: add wd to change work directory and mknod ./seitan-cooker demo/mknod.hjson demo/mknod.gluten demo/mknod.bpf Start seitan with the socket option: ./seitan -s /tmp/seitan.sock -i demo/mknod.gluten Start the container: sudo rm -f /dev/lol sudo chown $USER:$USER /tmp/seitan.sock podman run -ti --runtime /usr/bin/crun \ --security-opt label=disable \ -v $(pwd)/test:/test \ --annotation run.oci.seccomp_bpf_data="$(base64 -w0 demo/mknod.bpf)" \ --annotation run.oci.seccomp.receiver=/tmp/seitan.sock fedora \ sh -c 'mknod /dev/lol c 1 7 && ls -l /dev/lol' --- common/gluten.h | 72 +++++++++++++++++++++++---------------------------- common/util.c | 8 ++++-- cooker/call.c | 47 ++++++++++++++++----------------- cooker/emit.c | 18 ++++++------- cooker/emit.h | 2 +- demo/mknod.hjson | 5 ++-- operations.c | 79 +++++++++++++++++++++++++++++++++++++++----------------- operations.h | 1 + seitan.c | 2 +- 9 files changed, 132 insertions(+), 102 deletions(-) diff --git a/common/gluten.h b/common/gluten.h index a90cf4a..794b50f 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -74,52 +74,55 @@ enum op_type { }; /** - * enum ns_spec_type - Type of reference to target namespace + * enum context_spec_type - Type of reference to target namespace and directory */ -enum ns_spec_type { - NS_SPEC_NONE = 0, +enum context_spec_type { + CONTEXT_SPEC_NONE = 0, /* PID from seccomp_data */ - NS_SPEC_CALLER = 1, + CONTEXT_SPEC_CALLER = 1, /* PID/path from gluten, resolved in seitan */ - NS_SPEC_PID = 2, - NS_SPEC_PATH = 3, + CONTEXT_SPEC_PID = 2, + CONTEXT_SPEC_PATH = 3, - NS_SPEC_TYPE_MAX = NS_SPEC_PATH, + CONTEXT_SPEC_TYPE_MAX = CONTEXT_SPEC_PATH, }; /** - * enum ns_type - Namespace types: see + * enum context_type - Working directory, and namespaces (see ) */ -enum ns_type { - NS_MOUNT = 0, - NS_CGROUP = 1, - NS_UTS = 2, - NS_IPC = 3, - NS_USER = 4, - NS_PID = 5, - NS_NET = 6, - NS_TIME = 7, - NS_TYPE_MAX = NS_TIME, +enum context_type { + NS_MOUNT = 0, + NS_CGROUP = 1, + NS_UTS = 2, + NS_IPC = 3, + NS_USER = 4, + NS_PID = 5, + NS_NET = 6, + NS_TIME = 7, + NS_TYPE_MAX = NS_TIME, + CWD = 8, + CONTEXT_TYPE_MAX = CWD, }; -extern const char *ns_type_name[NS_TYPE_MAX + 1]; +extern const char *context_type_name[CONTEXT_TYPE_MAX + 1]; +extern const char *context_spec_type_name[CONTEXT_SPEC_TYPE_MAX + 1]; /** - * struct ns_spec - Identification of one type of target namespace - * @ns: Namespace type + * struct context_desc - Identification of one type of context information + * @context: Type of context (namespace types, or working directory) * @spec: Reference type * @target.pid: PID in procfs reference * @target.path: Filesystem-bound (nsfs) reference */ -struct ns_spec { +struct context_desc { #ifdef __GNUC__ - enum ns_type ns :BITS_PER_NUM(NS_TYPE_MAX); - enum ns_spec_type spec :BITS_PER_NUM(NS_SPEC_TYPE_MAX); + enum context_type type :BITS_PER_NUM(CONTEXT_TYPE_MAX); + enum context_spec_type spec :BITS_PER_NUM(CONTEXT_SPEC_TYPE_MAX); #else - uint8_t ns :BITS_PER_NUM(NS_TYPE_MAX); - uint8_t spec :BITS_PER_NUM(NS_SPEC_TYPE_MAX); + uint8_t type :BITS_PER_NUM(CONTEXT_TYPE_MAX); + uint8_t spec :BITS_PER_NUM(CONTEXT_SPEC_TYPE_MAX); #endif union { pid_t pid; @@ -127,17 +130,8 @@ struct ns_spec { } target; }; -BUILD_BUG_ON(BITS_PER_NUM(NS_TYPE_MAX) + BITS_PER_NUM(NS_SPEC_TYPE_MAX) > 8) - -/** - * struct context_desc - Description of context where the call is executed - * @count: Number of namespace specifications - * @ns: Namespace specifications - */ -struct context_desc { - uint8_t count; - struct ns_spec ns[]; -}; +BUILD_BUG_ON(BITS_PER_NUM(CONTEXT_TYPE_MAX) + \ + BITS_PER_NUM(CONTEXT_SPEC_TYPE_MAX) > 8) struct syscall_desc { uint32_t nr :9; @@ -145,8 +139,8 @@ struct syscall_desc { uint32_t has_ret :1; uint32_t arg_deref :6; - struct gluten_offset context; /* struct ns_spec [] */ - struct gluten_offset args[]; + struct gluten_offset context; /* struct context_desc [] */ + struct gluten_offset args[]; }; struct fd_desc { diff --git a/common/util.c b/common/util.c index a03b73e..f7fc288 100644 --- a/common/util.c +++ b/common/util.c @@ -34,8 +34,12 @@ const char *gluten_offset_name[OFFSET_TYPE_MAX + 1] = { "read-only data", "temporary data", "seccomp data", "instruction area", }; -const char *ns_type_name[NS_TYPE_MAX + 1] = { - "mnt", "cgroup", "uts", "ipc", "user", "pid", "net", "time", +const char *context_type_name[CONTEXT_TYPE_MAX + 1] = { + "mnt", "cgroup", "uts", "ipc", "user", "pid", "net", "time", "cwd", +}; + +const char *context_spec_type_name[CONTEXT_SPEC_TYPE_MAX + 1] = { + "none", "caller", "pid", "path", }; const char *bitwise_type_str[BITWISE_MAX + 1] = { "&", "|" }; diff --git a/cooker/call.c b/cooker/call.c index e2a5777..4e9cb9c 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -340,8 +340,9 @@ static struct gluten_offset parse_arg(struct gluten_ctx *g, struct arg *args, return offset; } -static void parse_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, - JSON_Object *obj, const char *ret, struct arg *args) +static void parse_call(struct gluten_ctx *g, struct context_desc *cdesc, + long nr, JSON_Object *obj, const char *ret, + struct arg *args) { struct gluten_offset offset[6] = { 0 }, ret_offset = { 0 }; bool is_ptr[6] = { false }; @@ -421,49 +422,49 @@ static void parse_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, if (count != json_object_get_count(obj)) die(" Stray elements in call"); - emit_call(g, ns, nr, arg_max_pos + 1, is_ptr, offset, ret_offset); + emit_call(g, cdesc, nr, arg_max_pos + 1, is_ptr, offset, ret_offset); } -static void parse_context(struct ns_spec *ns, JSON_Object *obj) +static void parse_context(struct context_desc *cdesc, JSON_Object *obj) { unsigned i, n = 0; /* Key order gives setns() order */ for (i = 0; i < json_object_get_count(obj); i++) { const char *name = json_object_get_name(obj, i); - const char **ns_name = ns_type_name, *str; - enum ns_type type; + const char **ctx_name, *str; + enum context_type type; double num; - for (ns_name = ns_type_name; *ns_name; ns_name++) { - if (!strcmp(name, *ns_name)) + for (ctx_name = context_type_name; *ctx_name; ctx_name++) { + if (!strcmp(name, *ctx_name)) break; } - if (!*ns_name) - die("invalid namespace type \"%s\"", name); + if (!*ctx_name) + die("invalid context type \"%s\"", name); - type = ns_name - ns_type_name; - ns[n].ns = type; + type = ctx_name - context_type_name; + cdesc[n].type = type; if ((str = json_object_get_string(obj, name))) { if (!strcmp(str, "init")) continue; - debug(" '%s' namespace: %s", name, str); + debug(" '%s' context: %s", name, str); if (!strcmp(str, "caller")) { - ns[n].spec = NS_SPEC_CALLER; + cdesc[n].spec = CONTEXT_SPEC_CALLER; } else { - ns[n].spec = NS_SPEC_PATH; - strncpy(ns[n].target.path, str, PATH_MAX); + cdesc[n].spec = CONTEXT_SPEC_PATH; + strncpy(cdesc[n].target.path, str, PATH_MAX); } } else if ((num = json_object_get_number(obj, name))) { - debug(" '%s' namespace: %lli", name, num); + debug(" '%s' context: %lli", name, num); - ns[n].spec = NS_SPEC_PID; - ns[n].target.pid = num; + cdesc[n].spec = CONTEXT_SPEC_PID; + cdesc[n].target.pid = num; } else { - die("invalid namespace specification"); + die("invalid context specification"); } n++; } @@ -481,7 +482,7 @@ void handle_calls(struct gluten_ctx *g, JSON_Value *value) count = 1; for (i = 0; i < count; i++) { - struct ns_spec ns[NS_TYPE_MAX + 1] = { 0 }; + struct context_desc cdesc[CONTEXT_TYPE_MAX + 1] = { 0 }; JSON_Object *obj, *args, *ctx; struct call **set, *call; const char *name, *ret; @@ -509,7 +510,7 @@ void handle_calls(struct gluten_ctx *g, JSON_Value *value) ret = json_object_get_string(obj, "ret"); ctx = json_object_get_object(obj, "context"); - parse_context(ns, ctx); + parse_context(cdesc, ctx); /* TODO: Factor this out into a function in calls.c */ for (set = call_sets, call = set[0]; *set; ) { @@ -522,7 +523,7 @@ void handle_calls(struct gluten_ctx *g, JSON_Value *value) if (!strcmp(name, call->name)) { debug(" Found description for %s", name); - parse_call(g, ns, call->number, + parse_call(g, cdesc, call->number, args, ret, call->args); break; } diff --git a/cooker/emit.c b/cooker/emit.c index d4ca97b..41b64a6 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -81,31 +81,31 @@ void emit_fd(struct gluten_ctx *g, struct fd_desc *desc) /** * emit_call() - Emit OP_CALL instruction: execute a system call * @g: gluten context - * @ns: NS_SPEC_NONE-terminated array of namespaces references + * @context: CONTEXT_SPEC_NONE-terminated array of context references * @nr: System call number * @count: Argument count * @is_ptr: Array indicating whether arguments need to be dereferenced * @args: Offsets of arguments * @ret_offset: Offset where return value must be saved, can be OFFSET_NULL */ -void emit_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, +void emit_call(struct gluten_ctx *g, struct context_desc *cdesc, long nr, unsigned count, bool is_ptr[6], struct gluten_offset offset[6], struct gluten_offset ret_offset) { struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); struct gluten_offset o1 = { 0 }, o2 = { 0 }; struct op_call *call = &op->op.call; + struct context_desc *c = cdesc; struct syscall_desc *desc; - unsigned ns_count, i; - struct ns_spec *ctx; + unsigned i; op->type = OP_CALL; - for (ns_count = 0; ns[ns_count].spec != NS_SPEC_NONE; ns_count++); - if (ns_count) { - o1 = gluten_ro_alloc(g, sizeof(struct ns_spec) * ns_count); - ctx = (struct ns_spec *)gluten_ptr(&g->g, o1); - memcpy(ctx, ns, sizeof(struct ns_spec) * ns_count); + for (i = 0; c[i].spec != CONTEXT_SPEC_NONE; i++); + if (i) { + o1 = gluten_ro_alloc(g, sizeof(struct context_desc) * i); + c = (struct context_desc *)gluten_ptr(&g->g, o1); + memcpy(c, cdesc, sizeof(struct context_desc) * i); } o2 = gluten_ro_alloc(g, sizeof(struct syscall_desc) + diff --git a/cooker/emit.h b/cooker/emit.h index 978c9e0..b9d326f 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -8,7 +8,7 @@ void emit_nr(struct gluten_ctx *g, struct gluten_offset number); void emit_fd(struct gluten_ctx *g, struct fd_desc *desc); -void emit_call(struct gluten_ctx *g, struct ns_spec *ns, long nr, +void emit_call(struct gluten_ctx *g, struct context_desc *cdesc, long nr, unsigned count, bool is_ptr[6], struct gluten_offset offset[6], struct gluten_offset ret_offset); void emit_load(struct gluten_ctx *g, struct gluten_offset dst, diff --git a/demo/mknod.hjson b/demo/mknod.hjson index 9660e0d..01f3c07 100644 --- a/demo/mknod.hjson +++ b/demo/mknod.hjson @@ -26,10 +26,9 @@ "major": 1, "minor": { "tag": { "get": "minor" } } }, - "ret": "x"/*, - "context": { "user": "init", "mnt": "caller" }*/ + "context": { "cwd": "caller" } }, - "return": { "tag": "x" } + "return": { "value": 0 } } ] diff --git a/operations.c b/operations.c index b08d837..e1132d5 100644 --- a/operations.c +++ b/operations.c @@ -80,10 +80,11 @@ static int write_syscall_ret(struct gluten *g, struct syscall_desc *s, } static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, - struct syscall_desc *s, struct ns_spec *ctx, + struct syscall_desc *s, struct context_desc *cdesc, struct arg_clone *c) { - unsigned int i, n = 0; + char (*dst)[PATH_MAX]; + unsigned int i; long arg; c->err = 0; @@ -109,32 +110,56 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, } /* TODO: add proper check when there is no context */ - if (ctx == NULL) + if (cdesc == NULL) { + debug(" op_call: no context provided"); return 0; + } + + for (dst = c->ns_path; cdesc->spec != CONTEXT_SPEC_NONE; cdesc++) { + enum context_spec_type spec = cdesc->spec; + enum context_type type = cdesc->type; - for (; ctx->spec != NS_SPEC_NONE; ctx++) { - enum ns_spec_type spec = ctx->spec; - enum ns_type ns = ctx->ns; + debug(" op_call: adding context for %s, type: %s", + context_type_name[type], context_spec_type_name[spec]); + + if (spec == CONTEXT_SPEC_NONE) + break; switch (spec) { - case NS_SPEC_NONE: + case CONTEXT_SPEC_CALLER: + if (type == CWD) { + snprintf(c->cwd, PATH_MAX, "/proc/%d/root", + req->pid); + } else { + snprintf(c->cwd, PATH_MAX, "/proc/%d/ns/%s", + req->pid, context_type_name[type]); + } break; - case NS_SPEC_CALLER: - snprintf(c->ns_path[n++], PATH_MAX, "/proc/%d/ns/%s", - req->pid, ns_type_name[ns]); + case CONTEXT_SPEC_PID: + if (type == CWD) { + snprintf(c->cwd, PATH_MAX, "/proc/%d/root", + cdesc->target.pid); + } else { + snprintf(*dst, PATH_MAX, "/proc/%d/ns/%s", + cdesc->target.pid, + context_type_name[type]); + } break; - case NS_SPEC_PID: - snprintf(c->ns_path[n++], PATH_MAX, "/proc/%d/ns/%s", - ctx->target.pid, ns_type_name[ns]); + case CONTEXT_SPEC_PATH: + if (type == CWD) + strncpy(c->cwd, cdesc->target.path, PATH_MAX); + else + strncpy(*dst, cdesc->target.path, PATH_MAX); break; - case NS_SPEC_PATH: - strncpy(c->ns_path[n++], ctx->target.path, - PATH_MAX); + default: break; } + + if (type != CWD) + dst++; } - *c->ns_path[n] = 0; + **dst = 0; return 0; } @@ -146,10 +171,10 @@ static int set_namespaces(struct arg_clone *c) for (path = c->ns_path; **path; *path++) { if ((fd = open(*path, O_CLOEXEC)) < 0) - ;//ret_err(-1, "open for file %s", *path); + ret_err(-1, "open for file %s", *path); if (setns(fd, 0) != 0) - ;//ret_err(-1, "setns"); + ret_err(-1, "setns"); } return 0; } @@ -158,13 +183,19 @@ static int execute_syscall(void *args) { struct arg_clone *c = (struct arg_clone *)args; - if (set_namespaces(c) < 0) { + if (*c->cwd && chdir(c->cwd) < 0) exit(EXIT_FAILURE); - } + + if (set_namespaces(c) < 0) + exit(EXIT_FAILURE); + + errno = 0; /* execute syscall */ c->ret = syscall(c->nr, c->args[0], c->args[1], c->args[2], c->args[3], c->args[4], c->args[5]); c->err = errno; + debug(" execute syscall: ret=%ld errno=%d%s%s", c->ret, c->err, + *c->cwd ? " cwd=" : "", *c->cwd ? c->cwd : ""); if (c->ret < 0) { perror(" syscall"); exit(EXIT_FAILURE); @@ -191,7 +222,7 @@ int op_call(const struct seccomp_notif *req, int notifier, struct gluten *g, struct seccomp_notif_resp resp; struct arg_clone c = { 0 }; struct syscall_desc *s; - struct ns_spec *ctx; + struct context_desc *cdesc; resp.id = req->id; resp.val = 0; @@ -199,9 +230,9 @@ int op_call(const struct seccomp_notif *req, int notifier, struct gluten *g, resp.error = 0; s = (struct syscall_desc *)gluten_ptr(NULL, g, op->desc); - ctx = (struct ns_spec *)gluten_ptr(NULL, g, s->context); + cdesc = (struct context_desc *)gluten_ptr(NULL, g, s->context); - if (prepare_arg_clone(req, g, s, ctx, &c) == -1) + if (prepare_arg_clone(req, g, s, cdesc, &c) == -1) return -1; debug(" op_call: execute syscall nr=%ld", c.nr); diff --git a/operations.h b/operations.h index ec06a15..9f2e2d6 100644 --- a/operations.h +++ b/operations.h @@ -36,6 +36,7 @@ struct arg_clone { long nr; const void *args[6]; char ns_path[NS_TYPE_MAX + 1][PATH_MAX]; + char cwd[PATH_MAX]; long ret; int err; }; diff --git a/seitan.c b/seitan.c index 4005a9b..a18e3ea 100644 --- a/seitan.c +++ b/seitan.c @@ -184,7 +184,7 @@ int main(int argc, char **argv) char req_b[BUFSIZ]; struct epoll_event ev, events[EPOLL_EVENTS]; struct seccomp_notif *req = (struct seccomp_notif *)req_b; - struct arguments arguments; + struct arguments arguments = { 0 }; char path[PATH_MAX + 1]; int fd = -1, epollfd; int pidfd, notifier; -- cgit v1.2.3