diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | common/gluten.h | 42 | ||||
-rw-r--r-- | common/util.c | 10 | ||||
-rw-r--r-- | cooker/call.c | 20 | ||||
-rw-r--r-- | cooker/calls/fs.c | 190 | ||||
-rw-r--r-- | cooker/calls/net.c | 92 | ||||
-rw-r--r-- | cooker/cooker.h | 5 | ||||
-rw-r--r-- | cooker/emit.c | 101 | ||||
-rw-r--r-- | cooker/emit.h | 15 | ||||
-rw-r--r-- | cooker/filter.c | 12 | ||||
-rw-r--r-- | cooker/gluten.c | 2 | ||||
-rw-r--r-- | cooker/match.c | 172 | ||||
-rw-r--r-- | demo/Makefile | 9 | ||||
-rw-r--r-- | demo/fremovexattr.c | 11 | ||||
-rw-r--r-- | demo/fsetxattr.c | 11 | ||||
-rw-r--r-- | demo/iptables.hjson | 214 | ||||
-rw-r--r-- | demo/nft.hjson | 221 | ||||
-rw-r--r-- | demo/open_by_handle_at.c | 22 | ||||
-rw-r--r-- | demo/virtiofsd.hjson | 93 | ||||
-rw-r--r-- | operations.c | 232 | ||||
-rw-r--r-- | operations.h | 2 |
21 files changed, 1360 insertions, 123 deletions
@@ -19,7 +19,7 @@ HEADERS := $(COMMON_DIR)/common.h $(COMMON_DIR)/gluten.h \ CFLAGS += -DTMP_DATA_SIZE=1000 CFLAGS += -Wall -Wextra -pedantic -std=c99 -I$(COMMON_DIR) -g -all: cooker eater seitan +all: cooker eater seitan demo .PHONY: cooker cooker: @@ -37,12 +37,17 @@ seitan: $(SRCS) $(HEADERS) debug: $(MAKE) -C debug +.PHONY: demo +demo: + $(MAKE) -C demo + .PHONY: clean clean: rm -f $(BIN) $(MAKE) -C cooker clean $(MAKE) -C eater clean $(MAKE) -C debug clean + $(MAKE) -C demo clean numbers.h: ./scripts/nr_syscalls.sh diff --git a/common/gluten.h b/common/gluten.h index 1414d9e..4e1c249 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -21,10 +21,10 @@ extern struct seccomp_data anonymous_seccomp_data; -#define HEADER_SIZE 65536 -#define INST_SIZE 65536 -#define RO_DATA_SIZE 65536 -#define DATA_SIZE 65536 +#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 \ @@ -67,13 +67,14 @@ enum op_type { OP_CALL, OP_COPY, OP_FD, + OP_FDGET, OP_RETURN, OP_LOAD, OP_STORE, OP_IOVLOAD, OP_BITWISE, OP_CMP, - OP_RESOLVEDFD, + OP_RESOLVEFD, }; /** @@ -199,6 +200,11 @@ 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; @@ -216,6 +222,12 @@ struct op_iovload { struct gluten_offset iovlen; struct gluten_offset dst; size_t size; + size_t zero_fill; +}; + +struct vec_desc { + struct gluten_offset start; + off_t len_offset; }; enum op_cmp_type { @@ -232,6 +244,7 @@ extern const char *cmp_type_str[CMP_MAX + 1]; struct cmp_desc { enum op_cmp_type cmp; size_t size; + struct vec_desc vec; struct gluten_offset x; struct gluten_offset y; struct gluten_offset jmp; @@ -252,6 +265,7 @@ extern const char *bitwise_type_str[BITWISE_MAX + 1]; struct bitwise_desc { size_t size; enum bitwise_type type; + struct vec_desc vec; struct gluten_offset dst; struct gluten_offset x; struct gluten_offset y; @@ -261,14 +275,23 @@ 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; - size_t path_max; + struct gluten_offset fd; + struct gluten_offset path; + enum resolvefd_type type; + size_t path_max; }; struct op_resolvefd { - struct gluten_offset desc; + struct gluten_offset desc; /* struct resolvefd_desc */ }; struct op_copy { @@ -284,6 +307,7 @@ struct op { 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; diff --git a/common/util.c b/common/util.c index 94aeea3..f0a1df4 100644 --- a/common/util.c +++ b/common/util.c @@ -54,11 +54,13 @@ const char *context_spec_type_name[CONTEXT_SPEC_TYPE_MAX + 1] = { const char *bitwise_type_str[BITWISE_MAX + 1] = { "&", "|" }; const char *cmp_type_str[CMP_MAX + 1] = { - "EQ", "NE", "GT", "GE", "LT", "LE", + "EQ", "NE", "GT", "GE", "LT", "LE", }; const char *metadata_type_str[METADATA_MAX + 1] = { "uid", "gid", "pid" }; +const char *resolvefd_type_str[RESOLVEFD_MAX + 1] = { "path", "mount" }; + const char *syscall_name(long nr) { struct call **set, *call; @@ -66,14 +68,16 @@ const char *syscall_name(long nr) { if (!call->name) { set++; call = set[0]; - continue; + + if (!*set) + return "unknown"; } if (nr == call->number) break; } - return call ? call->name : "unknown"; + return call->name; } const char *syscall_name_str[N_SYSCALL + 1] = { diff --git a/cooker/call.c b/cooker/call.c index dd37fe9..19a7b7e 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -72,11 +72,13 @@ static union value parse_metadata(struct gluten_ctx *g, struct field *f, if (tag_offset.type == OFFSET_NULL) die(" tag not found"); - if ((*base_offset)->type == OFFSET_NULL || (f->flags & WBUF)) { + if (f->flags & FD) { + emit_fdget(g, tag_offset, offset); + } else if ((*base_offset)->type == OFFSET_NULL || (f->flags & WBUF)) { **base_offset = tag_offset; } else if (f->flags & MASK || add) { - emit_bitwise(g, f->type, BITWISE_OR, offset, offset, - tag_offset); + emit_bitwise(g, f->type, NULL, BITWISE_OR, + offset, offset, tag_offset); } else { emit_copy_field(g, f, offset, tag_offset); } @@ -335,6 +337,9 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, if (json_object_get_string(tmp, "set")) return true; + if (json_object_get_string(tmp, "get") && f->flags & FD) + return true; + if (level) return true; @@ -374,6 +379,7 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, return false; case FDPATH: + case FDMOUNT: case STRING: return false; case STRUCT: @@ -433,7 +439,8 @@ static void parse_call(struct gluten_ctx *g, struct context_desc *cdesc, struct gluten_offset offset[6] = { 0 }, ret_offset = { 0 }; bool is_ptr[6] = { false }; /* Minimum requirements for argument specification: - * - if argument can be FDPATH, exactly one value for that position + * - if argument can be FDPATH/FDMOUNT, exactly one value for that + * position * - if argument is a size field, value is optional * - otherwise, every argument needs to be specified */ @@ -459,7 +466,7 @@ static void parse_call(struct gluten_ctx *g, struct context_desc *cdesc, arg_check[a->pos].multi_field = true; arg_check[a->pos].found = true; - if (a->f.type == FDPATH) + if (a->f.type == FDPATH || a->f.type == FDMOUNT) arg_check[a->pos].needs_fd = true; if (a->f.size) @@ -599,6 +606,9 @@ void handle_calls(struct gluten_ctx *g, JSON_Value *value) } name = json_object_get_name(obj, n); + if (!name) + continue; + value = json_object_get_value_at(obj, n); args = json_object_get_object(obj, name); diff --git a/cooker/calls/fs.c b/cooker/calls/fs.c index 4135be9..29c653a 100644 --- a/cooker/calls/fs.c +++ b/cooker/calls/fs.c @@ -60,6 +60,7 @@ swapoff #define _GNU_SOURCE #include <sys/stat.h> +#include <sys/xattr.h> #include <fcntl.h> #include <linux/limits.h> @@ -200,10 +201,199 @@ static struct arg chown_args[] = { { 0 } }; +static struct num xattr_flags[] = { + { "create", XATTR_CREATE }, + { "replace", XATTR_REPLACE }, + { 0 }, +}; + +static struct arg fsetxattr_args[] = { + { 0, + { + "fd", INT, FD, 0, 0, + { 0 } + } + }, + { 0, + { + "path", FDPATH, FD, 0, 0, + { 0 } + } + }, + { 1, + { + "name", STRING, 0, 0, + XATTR_NAME_MAX, + { 0 } + } + }, + { 2, + { + "value", STRING, WBUF, 0, + XATTR_SIZE_MAX, + { 0 } + } + }, + { 3, + { + "size", LONG, 0, 0, 0, + { 0 } + } + }, + { 4, + { + "flags", INT, FLAGS, 0, 0, + { .d_num = xattr_flags } + } + }, + { 0 }, +}; + +static struct arg fremovexattr_args[] = { + { 0, + { + "fd", INT, FD, 0, 0, + { 0 } + } + }, + { 0, + { + "path", FDPATH, FD, 0, 0, + { 0 } + } + }, + { 1, + { + "name", STRING, 0, 0, + XATTR_NAME_MAX, + { 0 } + } + }, + { 0 }, +}; + +/* enum fid_type copied (without comments) from include/linux/exportfs.h of + * Linux 6.9. Just copied and pasted like that? Is that an acceptable practice? + * Well then, let's say I _vendored_ it. + */ +enum fid_type { + FILEID_ROOT = 0, + FILEID_INO32_GEN = 1, + FILEID_INO32_GEN_PARENT = 2, + FILEID_BTRFS_WITHOUT_PARENT = 0x4d, + FILEID_BTRFS_WITH_PARENT = 0x4e, + FILEID_BTRFS_WITH_PARENT_ROOT = 0x4f, + FILEID_UDF_WITHOUT_PARENT = 0x51, + FILEID_UDF_WITH_PARENT = 0x52, + FILEID_NILFS_WITHOUT_PARENT = 0x61, + FILEID_NILFS_WITH_PARENT = 0x62, + FILEID_FAT_WITHOUT_PARENT = 0x71, + FILEID_FAT_WITH_PARENT = 0x72, + FILEID_INO64_GEN = 0x81, + FILEID_INO64_GEN_PARENT = 0x82, + FILEID_LUSTRE = 0x97, + FILEID_BCACHEFS_WITHOUT_PARENT = 0xb1, + FILEID_BCACHEFS_WITH_PARENT = 0xb2, + FILEID_KERNFS = 0xfe, + FILEID_INVALID = 0xff, +}; + +static struct num handle_types[] = { + { "root", FILEID_ROOT }, + { "ino32_gen", FILEID_INO32_GEN }, + { "ino32_gen_parent", FILEID_INO32_GEN_PARENT }, + { "btrfs_with_parent", FILEID_BTRFS_WITH_PARENT }, + { "btrfs_without_parent", FILEID_BTRFS_WITHOUT_PARENT }, + { "udf_with_parent", FILEID_UDF_WITH_PARENT }, + { "udf_without_parent", FILEID_UDF_WITHOUT_PARENT }, + { "fat_with_parent", FILEID_FAT_WITH_PARENT }, + { "fat_without_parent", FILEID_FAT_WITHOUT_PARENT }, + { "ino64_gen", FILEID_INO64_GEN }, + { "ino64_gen_parent", FILEID_INO64_GEN_PARENT }, + { "lustre", FILEID_LUSTRE }, + { "bcachefs_with_parent", FILEID_BCACHEFS_WITH_PARENT }, + { "bcachefs_without_parent", FILEID_BCACHEFS_WITHOUT_PARENT }, + { "kernfs", FILEID_KERNFS }, + { "invalid", FILEID_INVALID }, + { 0 }, +}; + +static struct field file_handle[] = { + { + "handle_bytes", LONG, SIZE, + offsetof(struct file_handle, handle_bytes), + 0, + { .d_size = (intptr_t)&file_handle[2] }, + }, + { + "handle_type", INT, FLAGS, + offsetof(struct file_handle, handle_type), + 0, + { .d_num = handle_types }, + }, + + { + "f_handle", STRING, 0, + offsetof(struct file_handle, f_handle), + MAX_HANDLE_SZ, + { 0 }, + }, + { 0 } +}; + +static struct num open_flags[] = { + { "rdonly", O_RDONLY }, + { "wronly", O_WRONLY }, + { "rdwr", O_RDWR }, + { 0 }, +}; + +static struct arg open_by_handle_at_args[] = { + { 0, + { + "mount", FDMOUNT, 0, 0, + 0, + { 0 } + } + }, + { 0, + { + "mount_fd", INT, 0, 0, + 0, + { 0 } + } + }, + { 1, + { + "handle", STRUCT, 0, 0, + sizeof(struct file_handle) + MAX_HANDLE_SZ, + { .d_struct = file_handle } + } + }, + { 2, + { + "flags", INT, MASK | FLAGS, 0, + 0, + { .d_num = open_flags } + } + }, + { 0 }, +}; + struct call syscalls_fs[] = { { __NR_mknod, "mknod", mknod_args }, { __NR_mknodat, "mknodat", mknodat_args }, + { __NR_chown, "chown", chown_args }, { __NR_lchown, "lchown", chown_args }, + + { __NR_fsetxattr, "fsetxattr", fsetxattr_args }, + /* fd, name, value, size, flags */ + + { __NR_fremovexattr, "fremovexattr", fremovexattr_args }, + /* fd, name */ + + { __NR_open_by_handle_at, "open_by_handle_at", open_by_handle_at_args }, + { 0 }, }; diff --git a/cooker/calls/net.c b/cooker/calls/net.c index 94b13cd..0688467 100644 --- a/cooker/calls/net.c +++ b/cooker/calls/net.c @@ -65,22 +65,23 @@ static struct num socket_flags[] = { }; static struct num protocols[] = { - { "ip", IPPROTO_IP }, - { "icmp", IPPROTO_ICMP }, - { "igmp", IPPROTO_IGMP }, - { "tcp", IPPROTO_TCP }, - { "udp", IPPROTO_UDP }, - { "ipv6", IPPROTO_IPV6 }, - { "gre", IPPROTO_GRE }, - { "esp", IPPROTO_ESP }, - { "ah", IPPROTO_AH }, - { "sctp", IPPROTO_SCTP }, - { "udplite", IPPROTO_UDPLITE }, - { "mpls", IPPROTO_MPLS }, - { "raw", IPPROTO_RAW }, - { "mptcp", IPPROTO_MPTCP }, + { "ip", IPPROTO_IP }, + { "icmp", IPPROTO_ICMP }, + { "igmp", IPPROTO_IGMP }, + { "tcp", IPPROTO_TCP }, + { "udp", IPPROTO_UDP }, + { "ipv6", IPPROTO_IPV6 }, + { "gre", IPPROTO_GRE }, + { "esp", IPPROTO_ESP }, + { "ah", IPPROTO_AH }, + { "sctp", IPPROTO_SCTP }, + { "udplite", IPPROTO_UDPLITE }, + { "mpls", IPPROTO_MPLS }, + { "raw", IPPROTO_RAW }, + { "mptcp", IPPROTO_MPTCP }, - { "nl_route", NETLINK_ROUTE }, + { "nl_route", NETLINK_ROUTE }, + { "nl_netfilter", NETLINK_NETFILTER }, { 0 }, }; @@ -261,7 +262,7 @@ static struct num send_flags[] = { static struct arg send_args[] = { { 0, { - "fd", INT, 0, + "fd", INT, FD, 0, 0, { 0 }, @@ -297,7 +298,7 @@ static struct arg send_args[] = { static struct arg sendto_args[] = { { 0, { - "fd", INT, 0, + "fd", INT, FD, 0, 0, { 0 }, @@ -346,7 +347,7 @@ static struct arg sendto_args[] = { { 0 } }; -static struct select sendmsg_name_select = { +static struct select msg_name_select = { &connect_family, { .d_num = connect_addr_select_family } }; @@ -355,13 +356,13 @@ static struct field sendmsg_msghdr[] = { "name", SELECT, 0, offsetof(struct msghdr, msg_name), sizeof(struct sockaddr_storage), - { .d_select = &sendmsg_name_select }, + { .d_select = &msg_name_select }, }, { "namelen", LONG, SIZE, offsetof(struct msghdr, msg_namelen), 0, - { .d_size = (intptr_t)&sendmsg_name_select }, + { .d_size = (intptr_t)&msg_name_select }, }, { "iov", STRING, WBUF | IOV, @@ -400,7 +401,7 @@ static struct field sendmsg_msghdr[] = { static struct arg sendmsg_args[] = { { 0, { - "fd", INT, 0, + "fd", INT, FD, 0, 0, { 0 }, @@ -425,6 +426,53 @@ static struct arg sendmsg_args[] = { { 0 } }; +static struct field recvmsg_msghdr[] = { + { + "name", SELECT, 0, + offsetof(struct msghdr, msg_name), + sizeof(struct sockaddr_storage), + { .d_select = &msg_name_select }, + }, + { + "namelen", LONG, SIZE, + offsetof(struct msghdr, msg_namelen), + 0, + { .d_size = (intptr_t)&msg_name_select }, + }, + { + "iov", STRING, RBUF | IOV, + offsetof(struct msghdr, msg_iov), + BUFSIZ, + { .d_iovlen = offsetof(struct msghdr, msg_iovlen) - + offsetof(struct msghdr, msg_iov) }, + }, + { + "iovlen", LONG, 0, + offsetof(struct msghdr, msg_iovlen), + 0, + { 0 }, + }, + { + "control", STRING, 0, + offsetof(struct msghdr, msg_control), + BUFSIZ, + { 0 }, + }, + { + "controllen", LONG, SIZE, + offsetof(struct msghdr, msg_controllen), + 0, + { 0 }, + }, + { + "flags", INT, 0, + offsetof(struct msghdr, msg_flags), + 0, + { 0 }, + }, + { 0 } +}; + static struct arg recvmsg_args[] = { { 0, { @@ -439,7 +487,7 @@ static struct arg recvmsg_args[] = { "msg", STRUCT, 0, 0, sizeof(struct msghdr), - { .d_struct = sendmsg_msghdr }, + { .d_struct = recvmsg_msghdr }, }, }, { 2, diff --git a/cooker/cooker.h b/cooker/cooker.h index 05a1f1b..3fc714a 100644 --- a/cooker/cooker.h +++ b/cooker/cooker.h @@ -92,6 +92,8 @@ enum type { GNU_DEV_MINOR, FDPATH, + FDMOUNT, + UID_T, GID_T, @@ -114,6 +116,9 @@ enum flags { WBUF = BIT(6), IOV = BIT(7), + + /* Copy file descriptor from target with pidfd_getfd() */ + FD = BIT(8), }; #define TYPE_COUNT (TYPE_END - 1) diff --git a/cooker/emit.c b/cooker/emit.c index 7899fa5..7d13a02 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -24,7 +24,8 @@ static const char *type_str[] = { "PID", "PORT", "IPV4", "IPV6", "GNU_DEV_MAJOR", "GNU_DEV_MINOR", - "FDPATH", + "FDPATH", "FDMOUNT", + "UID", "GID", NULL }; @@ -77,6 +78,37 @@ void emit_fd(struct gluten_ctx *g, struct fd_desc *desc) } /** + * emit_fdget() - Emit OP_FDGET instruction: copy file descriptor from target + * @g: gluten context + * @src: Pointer to existing file descriptor number in target process + * @dst: Pointer to new descriptor number in seitan, can be OFFSET_NULL + * + * Return: offset to destination operand, allocated here if not given + */ +struct gluten_offset emit_fdget(struct gluten_ctx *g, + struct gluten_offset src, + struct gluten_offset dst) +{ + struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); + struct op_fdget *fdget = &op->op.fdget; + + op->type = OP_FDGET; + + if (dst.type == OFFSET_NULL) + dst = gluten_rw_alloc(g, sizeof(int)); + + fdget->src = src; + fdget->dst = dst; + + debug(" %i: OP_FDGET: ...", g->ip.offset); + + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); + + return dst; +} + +/** * emit_call() - Emit OP_CALL instruction: execute a system call * @g: gluten context * @context: CONTEXT_SPEC_NONE-terminated array of context references @@ -185,18 +217,19 @@ void emit_store(struct gluten_ctx *g, struct gluten_offset dst, * @iov: Pointer to msg_iov, already stored in gluten * @iovlen: Pointer to msg_iovlen, already stored in gluten * @dst: gluten destination to copy dereferenced data + * @alloc: Allocation size (alloc - len bytes filled with zeroes) * @len: Maximum length of data to copy altogether */ struct gluten_offset emit_iovload(struct gluten_ctx *g, struct gluten_offset iov, struct gluten_offset iovlen, - size_t len) + size_t alloc, size_t len) { struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); struct op_iovload *load = &op->op.iovload; struct gluten_offset dst; - dst = gluten_rw_alloc(g, len); + dst = gluten_rw_alloc(g, alloc); op->type = OP_IOVLOAD; @@ -205,6 +238,7 @@ struct gluten_offset emit_iovload(struct gluten_ctx *g, load->dst = dst; load->size = len; + load->zero_fill = alloc - len; debug(" %i: OP_IOVLOAD: #%i < (#%i) as iovec (size: %lu)", g->ip.offset, dst.offset, iov.offset, len); @@ -218,19 +252,21 @@ struct gluten_offset emit_iovload(struct gluten_ctx *g, /** * emit_resolved() - Emit OP_RESOLVEFD instruction: resolve file descriptor with path * @g: gluten context + * @type: Type of field (FDPATH or FDMOUNT) * @fd: offset of the file descriptor value * @path: offset of the path * @path_size: size of the path */ -void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd, - struct gluten_offset path, size_t path_size) +void emit_resolvefd(struct gluten_ctx *g, enum type type, + struct gluten_offset fd, + struct gluten_offset path, size_t path_size) { struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); struct op_resolvefd *resfd = &op->op.resfd; struct gluten_offset o; struct resolvefd_desc *desc; - op->type = OP_RESOLVEDFD; + op->type = OP_RESOLVEFD; o = gluten_ro_alloc(g, sizeof(struct resolvefd_desc)); desc = (struct resolvefd_desc *)gluten_ptr(&g->g, o); @@ -238,6 +274,13 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd, desc->path = path; desc->path_max = path_size; + if (type == FDPATH) + desc->type = RESOLVEFD_PATH; + else if (type == FDMOUNT) + desc->type = RESOLVEFD_MOUNT; + else + die("Invalid field type for resolvefd"); + resfd->desc = o; debug(" %i: OP_RESOLVEDFD:", g->ip.offset); @@ -253,6 +296,7 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd, * emit_bitwise(): Emit OP_BITWISE instruction: bitwise operation and store * @g: gluten context * @type: Type of operands + * @vec: Description of vector structure, NULL for scalar operation * @op_type: Type of bitwise operation * @dst: gluten pointer to destination operand, can be OFFSET_NULL * @x: gluten pointer to first source operand @@ -261,6 +305,7 @@ void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd, * Return: offset to destination operand, allocated here if not given */ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, + struct vec_desc *vec, enum bitwise_type op_type, struct gluten_offset dst, struct gluten_offset x, @@ -270,6 +315,7 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, struct op_bitwise *op_bitwise = &op->op.bitwise; struct gluten_offset o; struct bitwise_desc *desc; + char ip_str[BUFSIZ]; op->type = OP_BITWISE; @@ -278,21 +324,42 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, desc->size = gluten_size[type]; desc->type = op_type; - if (dst.type == OFFSET_NULL) - desc->dst = gluten_rw_alloc(g, desc->size); - else + + if (vec) + desc->vec = *vec; + + if (dst.type == OFFSET_NULL) { + size_t dst_size; + if (vec) /* FIXME: UIO_MAXIOV (1024) is a dubious choice */ + dst_size = (desc->size + sizeof(uint32_t)) * 1024; + else + dst_size = desc->size; + + desc->dst = gluten_rw_alloc(g, dst_size); + } else { desc->dst = dst; + } + desc->x = x; desc->y = y; op_bitwise->desc = o; - debug(" %i: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu", - g->ip.offset, + snprintf(ip_str, BUFSIZ, "%i", g->ip.offset); + + debug(" %s: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu", + ip_str, gluten_offset_name[desc->dst.type], desc->dst.offset, desc->size, gluten_offset_name[desc->x.type], desc->x.offset, bitwise_type_str[op_type], gluten_offset_name[desc->y.type], desc->y.offset); + if (vec) { + memset(ip_str, ' ', strlen(ip_str)); + debug(" %s vector start: %s: #%lu, length descriptor at %lu", + ip_str, + gluten_offset_name[vec->start.type], vec->start.offset, + vec->len_offset); + } if (++g->ip.offset > INST_MAX) die("Too many instructions"); @@ -304,12 +371,14 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, * emit_cmp(): Emit OP_CMP instruction: compare data from two offsets * @g: gluten context * @cmp_type: Type of comparison + * @vec: Description of vector structure, NULL for scalar comparison * @x: gluten pointer to first operand of comparison * @y: gluten pointer to second operand of comparison * @size: Size of comparison * @jmp: Jump direction if comparison is true */ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, + struct vec_desc *vec, struct gluten_offset x, struct gluten_offset y, size_t size, enum jump_type jmp) { @@ -326,6 +395,11 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, desc->x = x; desc->y = y; desc->size = size; + if (vec) + desc->vec = *vec; + else + desc->vec.start.type = OFFSET_NULL; + desc->cmp = cmp_type; desc->jmp.type = OFFSET_INSTRUCTION; desc->jmp.offset = jmp; @@ -347,17 +421,18 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, * emit_cmp_field() - Emit OP_CMP for a given field type * @g: gluten context * @cmp: Type of comparison + * @vec: Description of vector structure, NULL for scalar comparison * @field: Description of field from system call model * @x: gluten pointer to first operand of comparison * @y: gluten pointer to second operand of comparison * @jmp: Jump direction if comparison is true */ void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, - struct field *field, + struct vec_desc *vec, struct field *field, struct gluten_offset x, struct gluten_offset y, enum jump_type jmp) { - emit_cmp(g, cmp, x, y, + emit_cmp(g, cmp, vec, x, y, field->size ? field->size : gluten_size[field->type], jmp); } diff --git a/cooker/emit.h b/cooker/emit.h index 5557187..7948071 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -8,6 +8,9 @@ void emit_nr(struct gluten_ctx *g, struct gluten_offset number); void emit_fd(struct gluten_ctx *g, struct fd_desc *desc); +struct gluten_offset emit_fdget(struct gluten_ctx *g, + struct gluten_offset src, + struct gluten_offset dst); 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); @@ -16,20 +19,21 @@ void emit_load(struct gluten_ctx *g, struct gluten_offset dst, struct gluten_offset emit_iovload(struct gluten_ctx *g, struct gluten_offset iov, struct gluten_offset iovlen, - size_t len); + size_t alloc, size_t len); void emit_store(struct gluten_ctx *g, struct gluten_offset dst, struct gluten_offset src, struct gluten_offset count); struct gluten_offset emit_seccomp_data(int index); struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, + struct vec_desc *desc, enum bitwise_type op_type, struct gluten_offset dst, struct gluten_offset x, struct gluten_offset y); -void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, +void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, struct vec_desc *vec, struct gluten_offset x, struct gluten_offset y, size_t size, enum jump_type jmp); void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, - struct field *field, + struct vec_desc *vec, struct field *field, struct gluten_offset base, struct gluten_offset match, enum jump_type jmp); void emit_return(struct gluten_ctx *g, struct gluten_offset v, @@ -38,8 +42,9 @@ void emit_copy(struct gluten_ctx *g, struct gluten_offset dst, struct gluten_offset src, size_t size); void emit_copy_field(struct gluten_ctx *g, struct field *field, struct gluten_offset dst, struct gluten_offset src); -void emit_resolvefd(struct gluten_ctx *g, struct gluten_offset fd, - struct gluten_offset path, size_t path_size); +void emit_resolvefd(struct gluten_ctx *g, enum type type, + struct gluten_offset fd, + struct gluten_offset path, size_t path_size); void emit_end(struct gluten_ctx *g); struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, size_t str_len, union value *value); diff --git a/cooker/filter.c b/cooker/filter.c index e0c8081..765fa25 100644 --- a/cooker/filter.c +++ b/cooker/filter.c @@ -540,6 +540,11 @@ unsigned int filter_build(struct sock_filter filter[], unsigned n) /* pre-check instruction + load syscall number (4 instructions) */ accept = size + n_nodes + n; + + /* FIXME: See below */ + if (n > 1 && n % 2) + accept++; + notify = accept + 1; /* Insert nodes */ @@ -570,6 +575,13 @@ unsigned int filter_build(struct sock_filter filter[], unsigned n) */ next_offset += get_n_args_syscall_instr(nr) - 1; } + + /* FIXME: If we have an odd number of leaves, duplicate the last one, + * otherwise, left_child() will miscalculate terminal jump offsets. + */ + if (n > 1 && n % 2) + filter[size++] = EQ(nr, offset - 1, accept - size); + /* Seccomp accept and notify instruction */ filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW); filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF); diff --git a/cooker/gluten.c b/cooker/gluten.c index 69c9384..ac5a4bd 100644 --- a/cooker/gluten.c +++ b/cooker/gluten.c @@ -26,6 +26,8 @@ size_t gluten_size[TYPE_COUNT] = { [GNU_DEV_MAJOR] = sizeof(unsigned long long int), [GNU_DEV_MINOR] = sizeof(unsigned long long int), + + [FDMOUNT] = sizeof(int), }; const char *jump_name[JUMP_COUNT] = { "next block", "next match", "next action", diff --git a/cooker/match.c b/cooker/match.c index c56d9e5..f65ac5c 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -22,9 +22,70 @@ #include <linux/netlink.h> #include <linux/rtnetlink.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_tables_compat.h> static struct num netlink_types[] = { - { "newroute", RTM_NEWROUTE }, + { "newroute", RTM_NEWROUTE }, + +#define SUBSYS_NFCOMPAT(x) (NFNL_SUBSYS_NFT_COMPAT << 8 | (x)) + { "nf_compat_get", (SUBSYS_NFCOMPAT(NFNL_MSG_COMPAT_GET))}, + +#define SUBSYS_NFT(x) (NFNL_SUBSYS_NFTABLES << 8 | (x)) + { "nf_get_any", + SUBSYS_NFT(NFT_MSG_GETTABLE | NFT_MSG_GETCHAIN | NFT_MSG_GETRULE | + NFT_MSG_GETSET | NFT_MSG_GETSETELEM | NFT_MSG_GETGEN | + NFT_MSG_GETFLOWTABLE | NFT_MSG_GETOBJ) | + SUBSYS_NFCOMPAT(NFNL_MSG_COMPAT_GET) }, + +#undef SUBSYS_NFCOMPAT + + { "nf_newtable", SUBSYS_NFT(NFT_MSG_NEWTABLE) }, + { "nf_gettable", SUBSYS_NFT(NFT_MSG_GETTABLE) }, + { "nf_deltable", SUBSYS_NFT(NFT_MSG_DELTABLE) }, + { "nf_destroytable", SUBSYS_NFT(NFT_MSG_DESTROYTABLE) }, + /* ignores ENOENT */ + + { "nf_newchain", SUBSYS_NFT(NFT_MSG_NEWCHAIN) }, + { "nf_getchain", SUBSYS_NFT(NFT_MSG_GETCHAIN) }, + { "nf_delchain", SUBSYS_NFT(NFT_MSG_DELCHAIN) }, + { "nf_destroychain", SUBSYS_NFT(NFT_MSG_DESTROYCHAIN) }, + + { "nf_newrule", SUBSYS_NFT(NFT_MSG_NEWRULE) }, + { "nf_getrule", SUBSYS_NFT(NFT_MSG_GETRULE) }, + { "nf_getrule_reset", SUBSYS_NFT(NFT_MSG_GETRULE_RESET) }, + { "nf_delrule", SUBSYS_NFT(NFT_MSG_DELRULE) }, + { "nf_destroyrule", SUBSYS_NFT(NFT_MSG_DESTROYRULE) }, + + { "nf_newset", SUBSYS_NFT(NFT_MSG_NEWSET) }, + { "nf_getset", SUBSYS_NFT(NFT_MSG_GETSET) }, + { "nf_delset", SUBSYS_NFT(NFT_MSG_DELSET) }, + { "nf_destroyset", SUBSYS_NFT(NFT_MSG_DESTROYSET) }, + + { "nf_newsetelem", SUBSYS_NFT(NFT_MSG_NEWSETELEM) }, + { "nf_getsetelem", SUBSYS_NFT(NFT_MSG_GETSETELEM) }, + { "nf_getsetelem_reset", SUBSYS_NFT(NFT_MSG_GETSETELEM_RESET) }, + { "nf_delsetelem", SUBSYS_NFT(NFT_MSG_DELSETELEM) }, + { "nf_destroysetelem", SUBSYS_NFT(NFT_MSG_DESTROYSETELEM) }, + + { "nf_newgen", SUBSYS_NFT(NFT_MSG_NEWGEN) }, + { "nf_getgen", SUBSYS_NFT(NFT_MSG_GETGEN) }, + + { "nf_trace", SUBSYS_NFT(NFT_MSG_TRACE) }, + + { "nf_newobj", SUBSYS_NFT(NFT_MSG_NEWOBJ) }, + { "nf_getobj", SUBSYS_NFT(NFT_MSG_GETOBJ) }, + { "nf_getobj_reset", SUBSYS_NFT(NFT_MSG_GETOBJ_RESET) }, + { "nf_delobj", SUBSYS_NFT(NFT_MSG_DELOBJ) }, + { "nf_destroyobj", SUBSYS_NFT(NFT_MSG_DESTROYOBJ) }, + + { "nf_newflowtable", SUBSYS_NFT(NFT_MSG_NEWFLOWTABLE) }, + { "nf_getflowtable", SUBSYS_NFT(NFT_MSG_GETFLOWTABLE) }, + { "nf_delflowtable", SUBSYS_NFT(NFT_MSG_DELFLOWTABLE) }, + { "nf_destroyflowtable", SUBSYS_NFT(NFT_MSG_DESTROYFLOWTABLE) }, +#undef SUBSYS_NFT + { 0 }, }; @@ -109,6 +170,7 @@ static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a) */ static union value parse_field(struct gluten_ctx *g, struct gluten_offset offset, + struct vec_desc *vec, enum op_cmp_type cmp, enum jump_type jump, int index, struct field *f, JSON_Value *jvalue) { @@ -134,7 +196,7 @@ ______________________ _____________ */ v.v_num = ((long long)0xfff << 44) | (0xfff << 8); mask_offset = emit_data(g, U64, 0, &v); - offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET, + offset = emit_bitwise(g, U64, NULL, BITWISE_AND, NULL_OFFSET, offset, mask_offset); break; case GNU_DEV_MINOR: @@ -144,7 +206,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ v.v_num = 0xff | ((long long)0xfff << 20); mask_offset = emit_data(g, U64, 0, &v); - offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET, + offset = emit_bitwise(g, U64, NULL, BITWISE_AND, NULL_OFFSET, offset, mask_offset); break; default: @@ -155,9 +217,19 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx if (f->flags & IOV) { struct gluten_offset iovlen = offset; + size_t alloc; + + /* vectorised ops need a zero length descriptor at the end */ + if (json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue)) && + json_object_get_value(tmp, "netlink")) + alloc = f->size + + sizeof(((struct nlmsghdr *)NULL)->nlmsg_len); + else + alloc = f->size; iovlen.offset += f->desc.d_iovlen; - offset = emit_iovload(g, offset, iovlen, f->size); + offset = emit_iovload(g, offset, iovlen, alloc, f->size); } if (json_value_get_type(jvalue) == JSONObject && @@ -188,7 +260,16 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx } jvalue = json_array_get_value(set, i); - parse_field(g, offset, cmp, jump, index, f, jvalue); + + /* FIXME: ugly. Otherwise nested parse_field() will + * increment twice. + */ + offset.offset -= f->offset; + + parse_field(g, offset, vec, cmp, jump, index, f, + jvalue); + + offset.offset += f->offset; } return v; /* No SELECT based on sets... of course */ @@ -215,12 +296,12 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx &set, &cmp, &cmpterm); set_offset = emit_data(g, f->type, 0, &set); - masked = emit_bitwise(g, f->type, BITWISE_AND, + masked = emit_bitwise(g, f->type, vec, BITWISE_AND, NULL_OFFSET, offset, set_offset); cmp_offset = emit_data(g, f->type, 0, &cmpterm); - emit_cmp(g, cmp, masked, cmp_offset, + emit_cmp(g, cmp, vec, masked, cmp_offset, gluten_size[f->type], jump); emit_bpf_arg(index, f->type, cmpterm, set, cmp, g->mode); @@ -241,9 +322,13 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx } else if (f->flags & MASK) { mask.v_num = value_get_mask(f->desc.d_num); mask_offset = emit_data(g, f->type, 0, &mask); - data_offset = emit_bitwise(g, f->type, BITWISE_AND, + + data_offset = emit_bitwise(g, f->type, vec, BITWISE_AND, NULL_OFFSET, offset, mask_offset); + if (vec) + vec->len_offset = 0; + v.v_num = value_get_num(f->desc.d_num, jvalue); } else { v.v_num = value_get_num(f->desc.d_num, jvalue); @@ -251,7 +336,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx const_offset = emit_data(g, f->type, 0, &v); - emit_cmp(g, cmp, data_offset, const_offset, + emit_cmp(g, cmp, vec, data_offset, const_offset, gluten_size[f->type], jump); emit_bpf_arg(index, f->type, v, mask, cmp, g->mode); @@ -266,7 +351,7 @@ ______________________ _____________ v.v_num = (v.v_num & 0xfff) << 8 | (v.v_num & ~0xfff) << 32; const_offset = emit_data(g, U64, 0, &v); - emit_cmp_field(g, cmp, f, offset, const_offset, jump); + emit_cmp_field(g, cmp, vec, f, offset, const_offset, jump); filter_needs_deref(); /* No shifts in BPF */ break; @@ -278,7 +363,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx v.v_num = value_get_num(f->desc.d_num, jvalue); v.v_num = (v.v_num & 0xff) | (v.v_num & ~0xfff) << 12; const_offset = emit_data(g, U64, 0, &v); - emit_cmp_field(g, cmp, f, offset, const_offset, jump); + emit_cmp_field(g, cmp, vec, f, offset, const_offset, jump); filter_needs_deref(); /* No shifts in BPF */ break; @@ -292,17 +377,43 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx sel = jvalue; } - v = parse_field(g, offset, cmp, jump, index, f_inner, sel); + v = parse_field(g, offset, vec, cmp, jump, index, f_inner, sel); f = select_field(g, index, f->desc.d_select, v); - if (f) - parse_field(g, offset, cmp, jump, index, f, jvalue); + if (f) { + parse_field(g, offset, vec, cmp, jump, index, f, + jvalue); + } break; case STRING: if (json_value_get_type(jvalue) == JSONObject && (tmp = json_value_get_object(jvalue))) { if ((jvalue = json_object_get_value(tmp, "netlink"))) { - parse_field(g, offset, cmp, jump, index, + struct vec_desc v_nl, *vecptr; + + /* FIXME: even send()/sendto() might need + * vectorised operations + */ + if (f->flags & IOV) { + v_nl.start = offset; + v_nl.len_offset = offsetof(struct nlmsghdr, + nlmsg_len); + vecptr = &v_nl; + } else { + vecptr = NULL; + } + + /* TODO: mark as CMPVEC, with: + * - offset of length (offsetof nlmsghdr) + * - vector pointer: that's already offset? + * - offset of comparison in vector (0 atm) + * - second term of comparison (from jvalue) + * - length (still needed?) + * + * plus BITWISEVEC? is it even needed? + */ + + parse_field(g, offset, vecptr, cmp, jump, index, &netlink_header, jvalue); } else { die(" unrecognised blob type"); @@ -317,18 +428,19 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx die(" string %s too long for field", v.v_str); const_offset = emit_data(g, STRING, strlen(v.v_str) + 1, &v); - emit_cmp(g, CMP_NE, offset, const_offset, strlen(v.v_str) + 1, - JUMP_NEXT_BLOCK); + emit_cmp(g, CMP_NE, vec, offset, const_offset, + strlen(v.v_str) + 1, JUMP_NEXT_BLOCK); break; case FDPATH: + case FDMOUNT: v.v_str = json_value_get_string(jvalue); size = strlen(v.v_str) + 1; offset = gluten_rw_alloc(g, size); const_offset = emit_data(g, STRING, size, &v); seccomp_offset = emit_seccomp_data(index); - emit_resolvefd(g, seccomp_offset, offset, size); - emit_cmp(g, CMP_NE, offset, const_offset, size, - JUMP_NEXT_BLOCK); + emit_resolvefd(g, f->type, seccomp_offset, offset, size); + emit_cmp(g, CMP_NE, vec, offset, const_offset, + size, JUMP_NEXT_BLOCK); break; case STRUCT: for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { @@ -339,7 +451,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx if (!field_value) continue; - parse_field(g, offset, cmp, jump, index, f_inner, + parse_field(g, offset, vec, cmp, jump, index, f_inner, field_value); } break; @@ -364,7 +476,8 @@ static void parse_arg(struct gluten_ctx *g, JSON_Value *jvalue, struct arg *a) offset = arg_load(g, a); - parse_field(g, offset, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f, jvalue); + parse_field(g, offset, NULL, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f, + jvalue); } /** @@ -406,16 +519,25 @@ static void parse_match(struct gluten_ctx *g, JSON_Object *obj, void handle_matches(struct gluten_ctx *g, JSON_Value *value) { JSON_Array *matches = json_value_get_array(value); - unsigned i; + unsigned i, count; - for (i = 0; i < json_array_get_count(matches); i++) { + if (matches) + count = json_array_get_count(matches); + else + count = 1; + + for (i = 0; i < count; i++) { JSON_Object *match, *args; struct call **set, *call; const char *name; + if (matches) + match = json_array_get_object(matches, i); + else + match = json_value_get_object(value); + g->mr = g->ip; - match = json_array_get_object(matches, i); name = json_object_get_name(match, 0); args = json_object_get_object(match, name); debug(" Parsing match %i: %s", i, name); diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 0000000..6b8558f --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,9 @@ +SRCS=$(wildcard *.c) + +all: $(SRCS:%.c=%) + +clean: + $(RM) $(SRCS:%.c=%) + +%: %.c + $(CC) $(CFLAGS) -o $@ $<
\ No newline at end of file diff --git a/demo/fremovexattr.c b/demo/fremovexattr.c new file mode 100644 index 0000000..1483975 --- /dev/null +++ b/demo/fremovexattr.c @@ -0,0 +1,11 @@ +#include <sys/stat.h> +#include <sys/xattr.h> +#include <fcntl.h> +#include <string.h> + +int main(int argc, char **argv) +{ + int fd = open(argv[1], O_RDONLY); + + return fremovexattr(fd, argv[2]); +} diff --git a/demo/fsetxattr.c b/demo/fsetxattr.c new file mode 100644 index 0000000..6ee423a --- /dev/null +++ b/demo/fsetxattr.c @@ -0,0 +1,11 @@ +#include <sys/stat.h> +#include <sys/xattr.h> +#include <fcntl.h> +#include <string.h> + +int main(int argc, char **argv) +{ + int fd = open(argv[1], O_RDONLY); + + return fsetxattr(fd, argv[2], argv[3], strlen(argv[3]) + 1, 0); +} diff --git a/demo/iptables.hjson b/demo/iptables.hjson new file mode 100644 index 0000000..d24f476 --- /dev/null +++ b/demo/iptables.hjson @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* seitan - Syscall Expressive Interpreter, Transformer and Notifier + * + * demo/iptables.hjson - Example for iptables-nft: fetch ruleset, add rules + * + * Copyright (c) 2024 Red Hat GmbH + * Author: Stefano Brivio <sbrivio@redhat.com> + * + * Example of stand-alone usage: + * + * ./seitan-cooker \ + * -i demo/iptables.hjson -g demo/iptables.gluten -f demo/iptables.bpf + * ./seitan-eater \ + * -i demo/iptables.bpf -- /sbin/iptables -t mangle -A FORWARD -j LOG + * # blocks + * + * ./seitan -p $(pgrep seitan-eater) -i demo/iptables.gluten + * # as root or with CAP_NET_ADMIN: adds rule on behalf of caller + * + * ./seitan-eater \ + * -i demo/iptables.bpf -- /sbin/iptables -t mangle -D FORWARD -j LOG + * # blocks + * + * ./seitan -p $(pgrep seitan-eater) -i demo/iptables.gluten + * # as root or with CAP_NET_ADMIN: deletes rule on behalf of caller + */ + +[ + /* When the target process tries to open a netlink socket for netfilter, open + * one on behalf of the caller, owned by us, and replace it in the caller. + * + * For netlink operations, there's always a double permission check: + * both opener of the socket and sender of the messages need to have matching + * capabilities, see netlink_ns_capable() in net/netlink/af_netlink.c. + * + * This block takes care of the first part. + */ + { + "match": { + "socket": { + "family" : "netlink", + "type" : "raw", + "protocol" : "nl_netfilter" + } + /* socket(2) doesn't point to memory, so we can safely let any unrelated + * system calls proceed, directly in the caller, without replaying it + * ourselves. + */ + }, + "call": { + "socket": { + "family" : "netlink", + "type" : "raw", + "flags" : 0, + "protocol" : "nl_netfilter" + }, + "ret": "fd" + }, + "fd": { + "src": { "get": "fd" }, + "close_on_exec": true, + "return": true + } + }, + + /* Second part: send messages on behalf of the target process. + * + * First, the ones iptables needs to check for nftables compatibility, and to + * fetch tables, chains, rules and sets, including their generation (version) + * identifier. + * + * These are simple messages, without batches, using sendto(). + */ + { + "match": { + "sendto": { + "fd": { + "set": "fd" + }, + "buf": { + "set": "buf", + "value": { + "netlink": { + "type": { + "in": [ + "nf_compat_get", + "nf_getgen", + "nf_gettable", + "nf_getchain", + "nf_getrule", + "nf_getset" + ] + } + } + } + }, + "len" : { "set": "len" }, + "addr": { "set": "addr" } + } + }, + "call": { + "sendto": { + "fd": { "get": "fd" }, + "buf": { "get": "buf" }, + "len": { "get": "len" }, + "addr": { "get": "addr" }, + "flags": 0 + }, + "ret": "rc" + }, + "return": { "value": "rc", "error": "rc" } + }, + + /* sendto(2) points to memory, so we need to match on any unrelated sendto() + * call and replay it ourselves, but pretending we're the original process + * (see "context" below). Otherwise, a thread of the target process can fool + * us into sending other messages with our capability set. + */ + { + "match": { + "sendto": { + "fd": { "set": "fd" }, + "buf": { "set": "buf" }, + "len": { "set": "len" }, + "addr": { "set": "addr" }, + "flags": { "set": "flags" } + } + }, + "call": { + "sendto": { + "fd": { "get": "fd" }, + "buf": { "get": "buf" }, + "len": { "get": "len" }, + "addr": { "get": "addr" }, + "flags": { "get": "flags" } + }, + "context": { "uid": "caller", "gid": "caller" } + } + }, + + /* Now deal with the actual rule insertion or deletion: those are batched + * messages, using sendmsg(). Replay the message and relay return code and any + * error back. + */ + { + "match": { + "sendmsg": { + "fd": { "set": "fd" }, + "msg": { + "iov": { + "set": "buf", + "value": { + "netlink": { + "type": { "in": [ "nf_newrule", "nf_delrule" ] } + } + } + } + } + } + }, + "call": { + "sendmsg": { + "fd": { "get": "fd" }, + "msg": { + "name": { + "family": "netlink", + "pid": 0, + "groups": 0 + }, + "iovlen": 1, + "iov": { "get": "buf" } + }, + "flags": 0 + }, + "ret": "rc" + }, + "return": { "value": "rc", "error": "rc" } + }, + + /* Same as sendto(2): sendmsg(2) points to memory. Replay any unrelated system + * call with the credentials from the target process. + */ + { + "match": { + "sendmsg": { + "fd": { "set": "fd" }, + "msg": { + "name": { "set": "name" }, + "namelen": { "set": "namelen" }, + "iov": { "set": "buf" }, + "control": { "set": "control" }, + "controllen": { "set": "controllen" } + }, + "flags": { "set": "flags" } + } + }, + "call": { + "sendmsg": { + "fd": { "get": "fd" }, + "msg": { + "name": { "get": "name" }, + "namelen": { "get": "namelen" }, + "iov": { "get": "buf" }, + "iovlen": 1, + "control": { "get": "control" }, + "controllen": { "get": "controllen" } + }, + "flags": { "get": "flags" } + }, + "context": { "uid": "caller", "gid": "caller" } + } + } +] diff --git a/demo/nft.hjson b/demo/nft.hjson new file mode 100644 index 0000000..4ad7f1d --- /dev/null +++ b/demo/nft.hjson @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* seitan - Syscall Expressive Interpreter, Transformer and Notifier + * + * demo/nft.hjson - Example for nft(8): fetch ruleset, add/delete set elements + * + * Copyright (c) 2024 Red Hat GmbH + * Author: Stefano Brivio <sbrivio@redhat.com> + * + * Example of stand-alone usage. Prepare a table and a set for testing, first: + * + * nft add table ip test_t + * nft add set ip test_t test_s '{ type ipv4_addr ; }' + * + * Now add the set element: + * + * ./seitan-cooker \ + * -i demo/nft.hjson -g demo/nft.gluten -f demo/nft.bpf + * ./seitan-eater \ + * -i demo/nft.bpf -- /sbin/nft add element ip test_t test_s { 1.2.3.4 } + * # blocks + * + * ./seitan -p $(pgrep seitan-eater) -i demo/nft.gluten + * # as root or with CAP_NET_ADMIN: add element on behalf of caller + * + * nft list ruleset # Check that the new element is there + * + * ./seitan-eater \ + * -i demo/nft.bpf -- /sbin/nft delete element ip test_t test_s { 1.2.3.4 } + * # blocks + * + * ./seitan -p $(pgrep seitan-eater) -i demo/nft.gluten + * # as root or with CAP_NET_ADMIN: deletes element on behalf of caller + * + */ + +[ + /* Open netlink socket on behalf of the caller, owned by us, and replace in + * the caller. + * + * For netlink operations, there's always a double permission check: + * both opener of the socket and sender of the messages need to have matching + * capabilities, see netlink_ns_capable() in net/netlink/af_netlink.c. + * + * This block takes care of the first part. + */ + { + "match": { + "socket": { + "family" : "netlink", + "type" : "raw", + "protocol" : "nl_netfilter" + } + /* socket(2) doesn't point to memory, so we can safely let any unrelated + * system calls proceed, directly in the caller, without replaying it + * ourselves. + */ + }, + "call": { + "socket": { + "family" : "netlink", + "type" : "raw", + "flags" : 0, + "protocol" : "nl_netfilter" + }, + "ret": "fd" + }, + "fd": { + "src": { "get": "fd" }, + "close_on_exec": true, + "return": true + } + }, + + /* Second part: send messages on behalf of the target process. + * + * First, the ones to fetch tables, chains, sets, flow tables, and objects, + * including their generation (version) identifier. + * + * These are simple messages, without batches, using sendto(). + */ + { + "match": { + "sendto": { + "fd": { + "set": "fd" + }, + "buf": { + "set": "buf", + "value": { + "netlink": { + "type": { + "in": [ + "nf_getgen", + "nf_gettable", + "nf_getchain", + "nf_getobj", + "nf_getflowtable", + "nf_getset" + ] + } + } + } + }, + "len" : { "set": "len" }, + "addr": { "set": "addr" } + } + }, + "call": { + "sendto": { + "fd": { "get": "fd" }, + "buf": { "get": "buf" }, + "len": { "get": "len" }, + "addr": { "get": "addr" }, + "flags": 0 + }, + "ret": "rc" + }, + "return": { "value": "rc", "error": "rc" } + }, + + /* sendto(2) points to memory, so we need to match on any unrelated sendto() + * call and replay it ourselves, but pretending we're the original process + * (see "context" below). Otherwise, a thread of the target process can fool + * us into sending other messages with our capability set. + */ + { + "match": { + "sendto": { + "fd": { "set": "fd" }, + "buf": { "set": "buf" }, + "len": { "set": "len" }, + "addr": { "set": "addr" }, + "flags": { "set": "flags" } + } + }, + "call": { + "sendto": { + "fd": { "get": "fd" }, + "buf": { "get": "buf" }, + "len": { "get": "len" }, + "addr": { "get": "addr" }, + "flags": { "get": "flags" } + }, + "context": { "uid": "caller", "gid": "caller" } + } + }, + + /* Now deal with the actual element insertion or deletion: those are batched + * messages, using sendmsg(). Replay the message and relay return code and any + * error back. + */ + { + "match": { + "sendmsg": { + "fd": { "set": "fd" }, + "msg": { + "iov": { + "set": "buf", + "value": { + "netlink": { + "type": { "in": [ "nf_newsetelem", "nf_delsetelem" ] } + } + } + } + } + } + }, + "call": { + "sendmsg": { + "fd": { "get": "fd" }, + "msg": { + "name": { + "family": "netlink", + "pid": 0, + "groups": 0 + }, + "iovlen": 1, + "iov": { "get": "buf" } + }, + "flags": 0 + }, + "ret": "rc" + }, + "return": { "value": "rc", "error": "rc" } + }, + + /* Same as sendto(2): sendmsg(2) points to memory. Replay any unrelated system + * call with the credentials from the target process. + */ + { + "match": { + "sendmsg": { + "fd": { "set": "fd" }, + "msg": { + "name": { "set": "name" }, + "namelen": { "set": "namelen" }, + "iov": { "set": "buf" }, + "control": { "set": "control" }, + "controllen": { "set": "controllen" } + }, + "flags": { "set": "flags" } + } + }, + "call": { + "sendmsg": { + "fd": { "get": "fd" }, + "msg": { + "name": { "get": "name" }, + "namelen": { "get": "namelen" }, + "iov": { "get": "buf" }, + "iovlen": 1, + "control": { "get": "control" }, + "controllen": { "get": "controllen" } + }, + "flags": { "get": "flags" } + }, + "context": { "uid": "caller", "gid": "caller" } + } + } +] diff --git a/demo/open_by_handle_at.c b/demo/open_by_handle_at.c new file mode 100644 index 0000000..1f48eca --- /dev/null +++ b/demo/open_by_handle_at.c @@ -0,0 +1,22 @@ +#define _GNU_SOURCE +#include <sys/stat.h> +#include <sys/xattr.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +int main(int argc, char **argv) +{ + struct { + struct file_handle f; + unsigned char h[MAX_HANDLE_SZ]; + } handle = { .f.handle_bytes = MAX_HANDLE_SZ }; + int mount_fd, fd; + char buf[BUFSIZ]; + + name_to_handle_at(AT_FDCWD, argv[1], &handle.f, &mount_fd, 0); + fd = open_by_handle_at(mount_fd, &handle.f, 0); + read(fd, buf, BUFSIZ); + fprintf(stdout, "%s", buf); +} diff --git a/demo/virtiofsd.hjson b/demo/virtiofsd.hjson new file mode 100644 index 0000000..8dfde03 --- /dev/null +++ b/demo/virtiofsd.hjson @@ -0,0 +1,93 @@ +[ + { + "match": [ + { + "fsetxattr": { + "fd": { "set": "fd" }, + "name": "security.overrated", + "value": { "set": "label" }, + "size": { "set": "size" }, + "flags": { "set": "flags" } + } + } + ], + + "call": [ + { + "fsetxattr": { + "fd": { "get": "fd" }, + "name": "security.overrated", + "value": { "get": "label" }, + "size": { "get": "size" }, + "flags": { "get": "flags" } + }, + "ret": "rc" + } + ], + + "return": { "value": "rc", "error": "rc" } + }, + + { + "match": [ + { + "fremovexattr": { + "fd": { "set": "fd" }, + "name": "security.overrated" + } + } + ], + + "call": [ + { + "fremovexattr": { + "fd": { "get": "fd" }, + "name": "security.overrated" + }, + "ret": "rc" + } + ], + + "return": { "value": "rc", "error": "rc" } + }, + + { + "match": [ + { + "open_by_handle_at": { + "mount": "/", + "handle": { + "handle_bytes": { "set": "len" }, + "f_handle": { "set": "handle" } + }, + "flags": { "set": "flags" } + } + } + ], + + "call": [ + { + "open": { + "path": "/", + "flags": "rdonly", + "mode": "S_IRUSR" + }, + "ret": "fdmount" + }, + { + "open_by_handle_at": { + "mount": { "get": "fdmount" }, + "handle": { + "handle_type": "ino32_gen", + "handle_bytes": { "get": "len" }, + "f_handle": { "get": "handle" } + }, + "flags": { "get": "flags" } + }, + "ret": "new_fd" + } + ], + + "fd": { "src": { "get": "new_fd" }, "close_on_exec": false, "return": true } + } +] diff --git a/operations.c b/operations.c index 507a2f7..4438879 100644 --- a/operations.c +++ b/operations.c @@ -18,6 +18,7 @@ #include <unistd.h> #include <sys/ioctl.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/uio.h> @@ -160,7 +161,7 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, * value. */ if (GET_BIT(s->arg_deref, i) == 1) { - c->args[i] = gluten_ptr(NULL, g, s->args[i]); + c->args[i] = gluten_ptr(&req->data, g, s->args[i]); debug(" read pointer arg%d at offset %d", i, s->args[i].offset); } else if (s->args[i].type == OFFSET_METADATA) { @@ -169,7 +170,7 @@ static int prepare_arg_clone(const struct seccomp_notif *req, struct gluten *g, debug(" read metadata value %s: %d", metadata_type_str[s->args[i].offset], (int *)c->args[i]); } else { - if (gluten_read(NULL, g, &arg, s->args[i], + if (gluten_read(&req->data, g, &arg, s->args[i], sizeof(arg)) == -1) ret_err(-1, " failed reading arg %d", i); debug(" read arg%d at offset %d v=%ld", i, @@ -458,6 +459,8 @@ int op_iovload(const struct seccomp_notif *req, int notifier, struct gluten *g, close(fd); + memset(dst + count, 0, load->size - count + load->zero_fill); + return 0; } @@ -566,12 +569,36 @@ int op_fd(const struct seccomp_notif *req, int notifier, return 0; } +int op_fdget(const struct seccomp_notif *req, int notifier, + struct gluten *g, struct op_fdget *op) +{ + int pidfd, fd, n; + + (void)notifier; + + if (gluten_read(&req->data, g, &n, op->src, sizeof(n)) == -1) + return -1; + + if ((pidfd = syscall(SYS_pidfd_open, req->pid, 0)) < 0) + die(" pidfd_open"); + + if ((fd = syscall(SYS_pidfd_getfd, pidfd, n, 0)) < 0) + die(" pidfd_getfd"); + + close(pidfd); + + return gluten_write(g, op->dst, &fd, sizeof(fd)); +} + int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_bitwise *op) { const struct bitwise_desc *desc = gluten_ptr(&req->data, g, op->desc); - const unsigned char *x, *y; - unsigned char *dst; + const struct vec_desc vec = desc->vec; + const unsigned char *x_ptr, *y_ptr; + struct gluten_offset x, dst; + unsigned char *dst_ptr; + uint32_t *vlen; unsigned i; (void)notifier; @@ -579,10 +606,6 @@ int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g, if (!desc) return -1; - dst = gluten_write_ptr( g, desc->dst); - x = gluten_ptr(&req->data, g, desc->x); - y = gluten_ptr(&req->data, g, desc->y); - /* if (!dst || !src || !mask || !check_gluten_limits(desc->dst, desc->size) || @@ -590,6 +613,7 @@ int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g, !check_gluten_limits(desc->mask, desc->size)) return -1; */ + debug(" op_bitwise: dst=(%s %d) := x=(%s %d) %s y=(%s %d) size=%d", gluten_offset_name[desc->dst.type], desc->dst.offset, gluten_offset_name[desc->x.type], desc->x.offset, @@ -597,13 +621,37 @@ int op_bitwise(const struct seccomp_notif *req, int notifier, struct gluten *g, gluten_offset_name[desc->y.type], desc->y.offset, desc->size); - for (i = 0; i < desc->size; i++) { - if (desc->type == BITWISE_AND) - dst[i] = x[i] & y[i]; - else if (desc->type == BITWISE_OR) - dst[i] = x[i] | y[i]; - else - return -1; + if (vec.start.type != OFFSET_NULL) { + debug(" vector start=(%s %d), length offset: %i", + gluten_offset_name[vec.start.type], vec.start.offset, + vec.len_offset); + } + + if (vec.start.type == OFFSET_NULL) + vlen = &((uint32_t){ 1 }); + else + vlen = (uint32_t *)gluten_ptr(&req->data, g, vec.start); + + y_ptr = gluten_ptr(&req->data, g, desc->y); + + for (x = desc->x, dst = desc->dst; + *vlen; + x.offset += *vlen, vlen += *vlen, dst.offset += desc->size) { + + x_ptr = gluten_ptr(&req->data, g, x); + dst_ptr = gluten_write_ptr( g, dst); + + for (i = 0; i < desc->size; i++) { + if (desc->type == BITWISE_AND) + dst_ptr[i] = x_ptr[i] & y_ptr[i]; + else if (desc->type == BITWISE_OR) + dst_ptr[i] = x_ptr[i] | y_ptr[i]; + else + return -1; + } + + if (vec.start.type == OFFSET_NULL) + break; } return 0; @@ -661,9 +709,13 @@ int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_cmp *op) { const struct cmp_desc *desc = gluten_ptr(&req->data, g, op->desc); + bool some = false, all = true, verdict; + const struct vec_desc vec = desc->vec; char str_x[PATH_MAX], str_y[PATH_MAX]; + const void *x_ptr, *y_ptr; + struct gluten_offset x; enum op_cmp_type cmp; - const void *px, *py; + uint32_t *vlen; int res; (void)notifier; @@ -671,29 +723,88 @@ int op_cmp(const struct seccomp_notif *req, int notifier, struct gluten *g, if (!desc) return -1; - px = gluten_ptr(&req->data, g, desc->x); - py = gluten_ptr(&req->data, g, desc->y); - cmp = desc->cmp; - - if (!px || !py || - !check_gluten_limits(desc->x, desc->size) || - !check_gluten_limits(desc->y, desc->size)) - return -1; str_offset_value(req, g, &desc->x, desc->size, str_x, PATH_MAX); str_offset_value(req, g, &desc->y, desc->size, str_y, PATH_MAX); debug(" op_cmp: operands x=%s y=%s", str_x, str_y); - res = memcmp(px, py, desc->size); + if (vec.start.type != OFFSET_NULL) { + debug(" vector start=(%s %d), length offset: %i", + gluten_offset_name[vec.start.type], vec.start.offset, + vec.len_offset); + } + + if (vec.start.type == OFFSET_NULL) + vlen = &((uint32_t){ 1 }); + else + vlen = (uint32_t *)gluten_ptr(&req->data, g, vec.start); + + y_ptr = gluten_ptr(&req->data, g, desc->y); + + if (!y_ptr || !check_gluten_limits(desc->y, desc->size)) + return -1; + + cmp = desc->cmp; + + for (x = desc->x; + *vlen; + x.offset += *vlen, vlen = (uint32_t *)((uint8_t *)vlen + *vlen)) { + unsigned char *c; + int __i; + + debug(" in loop, vlen: %u", *vlen); + + x_ptr = gluten_ptr(&req->data, g, x); + + if (!x_ptr || !check_gluten_limits(x, desc->size)) + return -1; + + debug(" in loop #2"); + + res = memcmp(x_ptr, y_ptr, desc->size); + + debug("=== x: %04x, y: %04x", *(uint16_t *)x_ptr, *(uint16_t *)y_ptr); - if ((res == 0 && (cmp == CMP_EQ || cmp == CMP_LE || cmp == CMP_GE)) || - (res < 0 && (cmp == CMP_LT || cmp == CMP_LE)) || - (res > 0 && (cmp == CMP_GT || cmp == CMP_GE)) || - (res != 0 && (cmp == CMP_NE))) { - debug(" op_cmp: successful comparison, jump to %d", - desc->jmp.offset); + c = (unsigned char *)x_ptr; + for (__i = 0; __i < 32; __i += 4) + debug("%02x %02x %02x %02x", c[__i], c[__i + 1], c[__i + 2], c[__i + 3]); + + if ((res == 0 && + (cmp == CMP_EQ || cmp == CMP_LE || cmp == CMP_GE)) || + (res < 0 && + (cmp == CMP_LT || cmp == CMP_LE)) || + (res > 0 && + (cmp == CMP_GT || cmp == CMP_GE)) || + (res != 0 && + (cmp == CMP_NE))) + some = true; + else + all = false; + + if (vec.start.type == OFFSET_NULL) + break; + + debug(" in loop #3"); + } + + /* FIXME: vectors always imply the existential quantifier for now: + * CMP_NE and all non-equal: jump + * CMP_EQ and some equal: jump + */ + if (vec.start.type == OFFSET_NULL) + verdict = some; + else if (cmp == CMP_NE) + verdict = all; + else + verdict = some; + + debug(" op_cmp: comparison: %s for some, %s for all, verdict: %s", + some ? "true" : "false", all ? "true" : "false", + verdict ? "true" : "false"); + + if (verdict) { + debug(" -> jump to %d", desc->jmp.offset); return desc->jmp.offset; } - debug(" op_cmp: comparison is false"); return 0; } @@ -702,13 +813,14 @@ int op_resolve_fd(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_resolvefd *op) { const struct resolvefd_desc *desc = gluten_ptr(&req->data, g, op->desc); - char fdpath[PATH_MAX], buf[PATH_MAX]; + char fdpath[PATH_MAX], buf[BUFSIZ], *p = NULL; ssize_t nbytes; int fd; (void)notifier; - debug(" op_resolvefd: fd=(%s %d) path=(%s %d) path_max=%d", + debug(" op_resolvefd: %s fd=(%s %d) path=(%s %d) path_max=%d", + resolvefd_type_str[desc->type], gluten_offset_name[desc->fd.type], desc->fd.offset, gluten_offset_name[desc->path.type], desc->path.offset, desc->path_max); @@ -716,12 +828,51 @@ int op_resolve_fd(const struct seccomp_notif *req, int notifier, if (gluten_read(&req->data, g, &fd, desc->fd, sizeof(fd)) == -1) return -1; - snprintf(fdpath, PATH_MAX, "/proc/%d/fd/%d", req->pid, fd); - if ((nbytes = readlink(fdpath, buf, desc->path_max)) < 0) - ret_err(-1, "error reading %s", buf); + if (desc->type == RESOLVEFD_PATH) { + snprintf(fdpath, PATH_MAX, "/proc/%d/fd/%d", req->pid, fd); + if ((nbytes = readlink(fdpath, buf, desc->path_max)) < 0) + ret_err(-1, "error reading %s", buf); + + p = buf; + } else if (desc->type == RESOLVEFD_MOUNT) { + FILE *fp; + + snprintf(fdpath, PATH_MAX, "/proc/%d/mountinfo", req->pid); + if (!(fp = fopen(fdpath, "r"))) { + err("Couldn't open %s", fdpath); + return -1; + } + + while (fgets(buf, BUFSIZ, fp)) { + long n; + + errno = 0; + n = strtoul(buf, NULL, 0); + if (errno) { + fclose(fp); + return -errno; + } + + if (n == fd) { + int i; + + for (i = 0; i < 5; i++) + p = strtok(i ? NULL : buf, " "); + + break; + } + } + + fclose(fp); + + if (!p) { + err("No mountpoint for mount_fd %i", fd); + return -1; + } + } - debug(" op_resolvefd: fd %d -> path: %s", fd, buf); - gluten_write(g, desc->path, &buf, desc->path_max); + debug(" op_resolvefd: fd %d -> path: %s", fd, p); + gluten_write(g, desc->path, p, desc->path_max); return 0; } @@ -764,12 +915,13 @@ int eval(struct gluten *g, const struct seccomp_notif *req, HANDLE_OP(OP_CALL, op_call, call, g); HANDLE_OP(OP_RETURN, op_return, ret, g); HANDLE_OP(OP_FD, op_fd, fd, g); + HANDLE_OP(OP_FDGET, op_fdget, fdget, g); HANDLE_OP(OP_LOAD, op_load, load, g); HANDLE_OP(OP_STORE, op_store, store, g); HANDLE_OP(OP_IOVLOAD, op_iovload, iovload, g); HANDLE_OP(OP_BITWISE, op_bitwise, bitwise, g); HANDLE_OP(OP_CMP, op_cmp, cmp, g); - HANDLE_OP(OP_RESOLVEDFD, op_resolve_fd, resfd, g); + HANDLE_OP(OP_RESOLVEFD, op_resolve_fd, resfd, g); HANDLE_OP(OP_NR, op_nr, nr, g); HANDLE_OP(OP_COPY, op_copy, copy, g); default: diff --git a/operations.h b/operations.h index 6cff7a8..4c827f9 100644 --- a/operations.h +++ b/operations.h @@ -61,4 +61,6 @@ int op_copy(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_copy *op); int op_fd(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_fd *op); +int op_fdget(const struct seccomp_notif *req, int notifier, struct gluten *g, + struct op_fdget *op); #endif /* ACTIONS_H */ |