From beeefb214a2dc8917b5a31945e740ecce4536764 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Wed, 28 Jun 2023 17:45:36 +0200 Subject: cooker, seitan: Add support for GID/UID in context Similarly to namespace specifications, the special value "caller", as well as login/group names and numeric UID/GIDs are supported. Example of usage in demo/mknod.hjson. Light on checks and with some TODOs left behind at the moment. Signed-off-by: Stefano Brivio --- common/gluten.h | 29 ++++++++++++++++++++--------- common/util.c | 7 ++++++- cooker/call.c | 19 +++++++++++++++---- demo/mknod.hjson | 2 +- operations.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++------ operations.h | 2 ++ 6 files changed, 93 insertions(+), 21 deletions(-) diff --git a/common/gluten.h b/common/gluten.h index 185927a..f3ef47d 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -75,23 +76,25 @@ enum op_type { }; /** - * enum context_spec_type - Type of reference to target namespace and directory + * enum context_spec_type - Type of reference to namespace, directory, UID/GID */ enum context_spec_type { CONTEXT_SPEC_NONE = 0, - /* PID from seccomp_data */ + /* PID from seccomp_data, UID/GID resolved via procfs in seitan */ CONTEXT_SPEC_CALLER = 1, - /* PID/path from gluten, resolved in seitan */ - CONTEXT_SPEC_PID = 2, - CONTEXT_SPEC_PATH = 3, + /* UID/GID, or PID resolved in seitan */ + CONTEXT_SPEC_NUM = 2, - CONTEXT_SPEC_TYPE_MAX = CONTEXT_SPEC_PATH, + /* User/group names or namespace path, resolved in seitan */ + CONTEXT_SPEC_NAME = 3, + + CONTEXT_SPEC_TYPE_MAX = CONTEXT_SPEC_NAME, }; /** - * enum context_type - Working directory, and namespaces (see ) + * enum context_type - Directory, namespaces (see ), UID/GID */ enum context_type { NS_MOUNT = 0, @@ -104,17 +107,22 @@ enum context_type { NS_TIME = 7, NS_TYPE_MAX = NS_TIME, CWD = 8, - CONTEXT_TYPE_MAX = CWD, + UID = 9, + GID = 10, + CONTEXT_TYPE_MAX = GID, }; extern const char *context_type_name[CONTEXT_TYPE_MAX + 1]; extern const char *context_spec_type_name[CONTEXT_SPEC_TYPE_MAX + 1]; /** * struct context_desc - Identification of one type of context information - * @context: Type of context (namespace types, or working directory) + * @context: Type of context (namespace, working directory, UID/GID) * @spec: Reference type * @target.pid: PID in procfs reference + * @target.uid: UID to switch to + * @target.gid: GID to switch to * @target.path: Filesystem-bound (nsfs) reference + * @target.name: Username or group name */ struct context_desc { #ifdef __GNUC__ @@ -126,7 +134,10 @@ struct context_desc { #endif union { pid_t pid; + uid_t uid; + gid_t gid; char path[PATH_MAX]; + char name[LOGIN_NAME_MAX]; } target; }; diff --git a/common/util.c b/common/util.c index 21676b0..d74e199 100644 --- a/common/util.c +++ b/common/util.c @@ -8,6 +8,9 @@ * Author: Stefano Brivio */ +#include /* TODO: Why isn't __USE_POSIX with limits.h + * enough for LOGIN_NAME_MAX here? + */ #include #include #include @@ -35,7 +38,9 @@ const char *gluten_offset_name[OFFSET_TYPE_MAX + 1] = { }; const char *context_type_name[CONTEXT_TYPE_MAX + 1] = { - "mnt", "cgroup", "uts", "ipc", "user", "pid", "net", "time", "cwd", + "mnt", "cgroup", "uts", "ipc", "user", "pid", "net", "time", + "cwd", + "uid", "gid", }; const char *context_spec_type_name[CONTEXT_SPEC_TYPE_MAX + 1] = { diff --git a/cooker/call.c b/cooker/call.c index c3f290c..6dbfd29 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -458,14 +458,25 @@ static void parse_context(struct context_desc *cdesc, JSON_Object *obj) if (!strcmp(str, "caller")) { cdesc[n].spec = CONTEXT_SPEC_CALLER; } else { - cdesc[n].spec = CONTEXT_SPEC_PATH; - strncpy(cdesc[n].target.path, str, PATH_MAX); + cdesc[n].spec = CONTEXT_SPEC_NAME; + if (type == UID || type == GID) { + strncpy(cdesc[n].target.name, str, + LOGIN_NAME_MAX); + } else { + strncpy(cdesc[n].target.path, str, + PATH_MAX); + } } } else if ((num = json_object_get_number(obj, name))) { debug(" '%s' context: %lli", name, num); - cdesc[n].spec = CONTEXT_SPEC_PID; - cdesc[n].target.pid = num; + cdesc[n].spec = CONTEXT_SPEC_NUM; + if (type == UID) + cdesc[n].target.uid = num; + else if (type == GID) + cdesc[n].target.gid = num; + else + cdesc[n].target.pid = num; } else { die("invalid context specification"); } diff --git a/demo/mknod.hjson b/demo/mknod.hjson index 9124a8a..1936eb8 100644 --- a/demo/mknod.hjson +++ b/demo/mknod.hjson @@ -26,7 +26,7 @@ "major": 1, "minor": { "tag": { "get": "minor" } } }, - "context": { "mnt": "caller" } + "context": { "mnt": "caller", "uid": "caller", "gid": "caller" } }, "return": { "value": 0, "error": 0 } } diff --git a/operations.c b/operations.c index 97c4730..5a882d9 100644 --- a/operations.c +++ b/operations.c @@ -14,11 +14,13 @@ #include #include #include -#include #include #include #include +#include +#include #include +#include #include #include #include @@ -79,6 +81,7 @@ static int write_syscall_ret(struct gluten *g, struct syscall_desc *s, return 0; } +/* TODO: Move all "context" stuff to separate file */ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, struct syscall_desc *s, struct context_desc *cdesc, struct arg_clone *c) @@ -130,26 +133,57 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, if (type == CWD) { snprintf(c->cwd, PATH_MAX, "/proc/%d/root", req->pid); + } else if (type == UID || type == GID) { + /* TODO: Move into its own function */ + struct stat st = { 0 }; + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "/proc/%d", req->pid); + if (stat(path, &st)) + return errno; + + if (type == UID) + c->uid = st.st_uid; + else if (type == GID) + c->gid = st.st_gid; } else { snprintf(*dst, PATH_MAX, "/proc/%d/ns/%s", - req->pid, context_type_name[type]); + req->pid, context_type_name[type]); } + break; - case CONTEXT_SPEC_PID: + case CONTEXT_SPEC_NUM: if (type == CWD) { snprintf(c->cwd, PATH_MAX, "/proc/%d/root", cdesc->target.pid); + } else if (type == UID) { + c->uid = cdesc->target.uid; + } else if (type == GID) { + c->gid = cdesc->target.gid; } else { snprintf(*dst, PATH_MAX, "/proc/%d/ns/%s", cdesc->target.pid, context_type_name[type]); } + break; - case CONTEXT_SPEC_PATH: - if (type == CWD) + case CONTEXT_SPEC_NAME: + if (type == CWD) { strncpy(c->cwd, cdesc->target.path, PATH_MAX); - else + } else if (type == UID || type == GID) { + struct passwd *pw; + + if (!(pw = getpwnam(cdesc->target.name))) + return errno; + + if (type == UID) + c->uid = pw->pw_uid; + else if (type == GID) + c->gid = pw->pw_gid; + } else { strncpy(*dst, cdesc->target.path, PATH_MAX); + } + break; default: break; @@ -183,6 +217,15 @@ static int execute_syscall(void *args) { struct arg_clone *c = (struct arg_clone *)args; + /* We can use 0 as "unspecified" value because we can't switch from a + * non-zero UID/GID to zero. + */ + if (c->uid && setuid(c->uid)) + exit(EXIT_FAILURE); + + if (c->gid && setgid(c->gid)) + exit(EXIT_FAILURE); + if (*c->cwd && chdir(c->cwd) < 0) exit(EXIT_FAILURE); diff --git a/operations.h b/operations.h index 9f2e2d6..6cff7a8 100644 --- a/operations.h +++ b/operations.h @@ -37,6 +37,8 @@ struct arg_clone { const void *args[6]; char ns_path[NS_TYPE_MAX + 1][PATH_MAX]; char cwd[PATH_MAX]; + uid_t uid; + gid_t gid; long ret; int err; }; -- cgit v1.2.3