/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2023 Red Hat GmbH * Authors: Alice Frosi * Stefano Brivio */ #ifndef COMMON_GLUTEN_H #define COMMON_GLUTEN_H #include #include #include #include #include #include #include #include #include "util.h" extern struct seccomp_data anonymous_seccomp_data; #define HEADER_SIZE (1 << 20) #define INST_SIZE (1 << 20) #define RO_DATA_SIZE (1 << 20) #define DATA_SIZE (1 << 20) #define INST_MAX 256 #define OFFSET_MAX \ MAX(MAX(MAX(DATA_SIZE, RO_DATA_SIZE), INST_MAX), \ ARRAY_SIZE(anonymous_seccomp_data.args)) #define OP_EMPTY { .block = { -1 } } #define NO_FIELD block #define GET_BIT(x, i) (((x) & (1UL << (i))) != 0) enum gluten_offset_type { OFFSET_NULL = 0, OFFSET_RO_DATA = 1, OFFSET_DATA = 2, OFFSET_SECCOMP_DATA = 3, OFFSET_INSTRUCTION = 4, OFFSET_METADATA = 5, OFFSET_TYPE_MAX = OFFSET_METADATA, }; #define NULL_OFFSET ((struct gluten_offset){ .type = OFFSET_NULL }) extern const char *gluten_offset_name[OFFSET_TYPE_MAX + 1]; struct gluten_offset { #ifdef __GNUC__ enum gluten_offset_type type :BITS_PER_NUM(OFFSET_TYPE_MAX); #else uint32_t type :BITS_PER_NUM(OFFSET_TYPE_MAX); #endif uint32_t offset :BITS_PER_NUM(OFFSET_MAX); }; BUILD_BUG_ON(BITS_PER_NUM(OFFSET_TYPE_MAX) + BITS_PER_NUM(OFFSET_MAX) > 32) enum op_type { OP_END = 0, OP_NR, OP_CALL, OP_COPY, OP_FD, OP_FDGET, OP_RETURN, OP_LOAD, OP_STORE, OP_IOVLOAD, OP_BITWISE, OP_CMP, OP_RESOLVEFD, }; /** * enum context_spec_type - Type of reference to namespace, directory, UID/GID */ enum context_spec_type { CONTEXT_SPEC_NONE = 0, /* PID from seccomp_data, UID/GID resolved via procfs in seitan */ CONTEXT_SPEC_CALLER = 1, /* UID/GID, or PID resolved in seitan */ CONTEXT_SPEC_NUM = 2, /* User/group names or namespace path, resolved in seitan */ CONTEXT_SPEC_NAME = 3, CONTEXT_SPEC_TYPE_MAX = CONTEXT_SPEC_NAME, }; /** * enum context_type - Directory, namespaces (see ), UID/GID */ 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, 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, 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__ 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 type :BITS_PER_NUM(CONTEXT_TYPE_MAX); uint8_t spec :BITS_PER_NUM(CONTEXT_SPEC_TYPE_MAX); #endif union { pid_t pid; uid_t uid; gid_t gid; char path[PATH_MAX]; char name[LOGIN_NAME_MAX]; } target; }; BUILD_BUG_ON(BITS_PER_NUM(CONTEXT_TYPE_MAX) + \ BITS_PER_NUM(CONTEXT_SPEC_TYPE_MAX) > 8) enum metadata_type { UID_TARGET = 0, GID_TARGET = 1, PID_TARGET = 2, METADATA_MAX = PID_TARGET, }; extern const char *metadata_type_str[METADATA_MAX + 1]; struct syscall_desc { uint32_t nr :9; uint32_t arg_count :3; uint32_t has_ret :1; uint32_t arg_deref :6; struct gluten_offset context; /* struct context_desc [] */ struct gluten_offset args[]; }; struct fd_desc { struct gluten_offset srcfd; struct gluten_offset newfd; uint8_t setfd :1; uint8_t cloexec :1; uint8_t do_return :1; }; struct op_call { struct gluten_offset desc; }; struct op_nr { struct gluten_offset nr; struct gluten_offset no_match; }; struct op_block { int32_t error; }; struct return_desc { struct gluten_offset val; struct gluten_offset error; bool cont; }; struct op_return { struct gluten_offset desc; }; struct op_fd { struct gluten_offset desc; /* struct fd_desc */ }; struct op_fdget { struct gluten_offset src; struct gluten_offset dst; }; struct op_load { struct gluten_offset src; struct gluten_offset dst; size_t size; }; struct op_store { struct gluten_offset src; struct gluten_offset dst; struct gluten_offset count; }; struct op_iovload { struct gluten_offset iov; struct gluten_offset iovlen; struct gluten_offset dst; size_t size; }; enum op_cmp_type { CMP_EQ, CMP_NE, CMP_GT, CMP_GE, CMP_LT, CMP_LE, CMP_MAX = CMP_LE, }; extern const char *cmp_type_str[CMP_MAX + 1]; struct cmp_desc { enum op_cmp_type cmp; size_t size; struct gluten_offset x; struct gluten_offset y; struct gluten_offset jmp; }; struct op_cmp { struct gluten_offset desc; /* struct cmp_desc */ }; enum bitwise_type { BITWISE_AND, BITWISE_OR, BITWISE_MAX = BITWISE_OR, }; extern const char *bitwise_type_str[BITWISE_MAX + 1]; struct bitwise_desc { size_t size; enum bitwise_type type; struct gluten_offset dst; struct gluten_offset x; struct gluten_offset y; }; struct op_bitwise { struct gluten_offset desc; /* struct bitwise_desc */ }; enum resolvefd_type { RESOLVEFD_PATH, RESOLVEFD_MOUNT, RESOLVEFD_MAX = RESOLVEFD_MOUNT, }; extern const char *resolvefd_type_str[RESOLVEFD_MAX + 1]; struct resolvefd_desc { struct gluten_offset fd; struct gluten_offset path; enum resolvefd_type type; size_t path_max; }; struct op_resolvefd { struct gluten_offset desc; /* struct resolvefd_desc */ }; struct op_copy { struct gluten_offset src; struct gluten_offset dst; size_t size; }; struct op { enum op_type type; union { struct op_nr nr; struct op_call call; struct op_return ret; struct op_fd fd; struct op_fdget fdget; struct op_load load; struct op_store store; struct op_iovload iovload; struct op_bitwise bitwise; struct op_cmp cmp; struct op_resolvefd resfd; struct op_copy copy; } op; }; #if defined(COOKER) || defined(SEITAN_TEST) # define GLUTEN_CONST #else # define GLUTEN_CONST const #endif struct gluten_header { struct gluten_offset relocation[256]; }; struct gluten { GLUTEN_CONST struct gluten_header header; GLUTEN_CONST char inst[INST_SIZE]; GLUTEN_CONST char ro_data[RO_DATA_SIZE]; GLUTEN_CONST enum metadata_type metadata; char data[DATA_SIZE]; } __attribute__((packed)); BUILD_BUG_ON(INST_SIZE < INST_MAX * sizeof(struct op)) static inline bool is_offset_valid(const struct gluten_offset x) { switch (x.type) { case OFFSET_NULL: return true; case OFFSET_DATA: return x.offset < DATA_SIZE; case OFFSET_RO_DATA: return x.offset < RO_DATA_SIZE; case OFFSET_INSTRUCTION: return x.offset < INST_SIZE; case OFFSET_SECCOMP_DATA: return x.offset < 6; default: debug("unknown offset in range check"); return false; } } #ifdef COOKER static inline void *gluten_ptr(struct gluten *g, const struct gluten_offset x) #else static inline void *gluten_write_ptr(struct gluten *g, const struct gluten_offset x) #endif { if (!is_offset_valid(x)) return NULL; switch (x.type) { case OFFSET_DATA: return (char *)g->data + x.offset; #ifdef COOKER case OFFSET_RO_DATA: return (char *)g->ro_data + x.offset; case OFFSET_INSTRUCTION: return (struct op *)(g->inst) + x.offset; #endif default: return NULL; } } #ifndef COOKER static inline const void *gluten_ptr(const struct seccomp_data *s, struct gluten *g, const struct gluten_offset x) { if (!is_offset_valid(x)) { err(" offset limits are invalid"); return NULL; } if (x.type == OFFSET_SECCOMP_DATA && s == NULL) { err(" seccomp data is empty"); return NULL; } switch (x.type) { case OFFSET_DATA: return g->data + x.offset; case OFFSET_RO_DATA: return g->ro_data + x.offset; case OFFSET_SECCOMP_DATA: return (const uint64_t *)s->args + x.offset; case OFFSET_INSTRUCTION: return (const struct op *)(g->inst) + x.offset; default: return NULL; } } static inline bool check_gluten_limits(struct gluten_offset v, size_t size) { struct gluten_offset off = { v.type, v.offset + size }; if (v.type == OFFSET_SECCOMP_DATA || is_offset_valid(off)) return true; err(" offset limits are invalid"); return false; } static inline int gluten_write(struct gluten *g, struct gluten_offset dst, const void *src, size_t size) { void *p = gluten_write_ptr(g, dst); if (p == NULL || !check_gluten_limits(dst, size)) return -1; memcpy(p, src, size); return 0; } static inline int gluten_read(const struct seccomp_data *s, struct gluten *g, void *dst, const struct gluten_offset src, size_t size) { const void *p = gluten_ptr(s, g, src); if (p == NULL || !check_gluten_limits(src, size)) return -1; memcpy(dst, p, size); return 0; } #endif #endif /* COMMON_GLUTEN_H */