diff options
author | Stefano Brivio <sbrivio@redhat.com> | 2023-12-21 12:06:05 +0100 |
---|---|---|
committer | Stefano Brivio <sbrivio@redhat.com> | 2023-12-21 12:45:36 +0100 |
commit | bdbec30a849807fb5e6841a38cfe0d168e5962b9 (patch) | |
tree | 210949d96b4d764235c1c5b81ad2eebb61681f95 | |
parent | c72c2493de8990c3a3b4780ec1429a3c359c121e (diff) | |
download | seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.tar seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.tar.gz seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.tar.bz2 seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.tar.lz seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.tar.xz seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.tar.zst seitan-bdbec30a849807fb5e6841a38cfe0d168e5962b9.zip |
seitan: Add netlink, sendto()/sendmsg(), iovec handling, demo with routes
A bit rough at the moment, but it does the trick. Bonus: setsockopt()
(with magic values only, not used in any demo yet).
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | common/gluten.h | 18 | ||||
-rw-r--r-- | common/util.c | 20 | ||||
-rw-r--r-- | common/util.h | 1 | ||||
-rw-r--r-- | cooker/call.c | 61 | ||||
-rw-r--r-- | cooker/calls/net.c | 259 | ||||
-rw-r--r-- | cooker/cooker.h | 6 | ||||
-rw-r--r-- | cooker/emit.c | 38 | ||||
-rw-r--r-- | cooker/emit.h | 4 | ||||
-rw-r--r-- | cooker/gluten.c | 16 | ||||
-rw-r--r-- | cooker/gluten.h | 4 | ||||
-rw-r--r-- | cooker/match.c | 56 | ||||
-rw-r--r-- | cooker/parse.c | 6 | ||||
-rw-r--r-- | demo/routes.hjson | 34 | ||||
-rw-r--r-- | operations.c | 73 | ||||
-rw-r--r-- | seitan.c | 21 |
16 files changed, 597 insertions, 24 deletions
@@ -12,12 +12,12 @@ export OUTDIR COMMON_DIR := $(DIR)/common BIN := $(OUTDIR)/seitan -SRCS := seitan.c $(COMMON_DIR)/common.c $(COMMON_DIR)/util.c operations.c +SRCS := seitan.c $(COMMON_DIR)/common.c $(COMMON_DIR)/util.c operations.c cooker/calls.c $(wildcard cooker/calls/*.c) HEADERS := $(COMMON_DIR)/common.h $(COMMON_DIR)/gluten.h \ $(COMMON_DIR)/util.h operations.h CFLAGS += -DTMP_DATA_SIZE=1000 -CFLAGS += -Wall -Wextra -pedantic -std=c99 -I$(COMMON_DIR) +CFLAGS += -Wall -Wextra -pedantic -std=c99 -I$(COMMON_DIR) -g all: cooker eater seitan diff --git a/common/gluten.h b/common/gluten.h index e80916a..1414d9e 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -70,6 +70,7 @@ enum op_type { OP_RETURN, OP_LOAD, OP_STORE, + OP_IOVLOAD, OP_BITWISE, OP_CMP, OP_RESOLVEDFD, @@ -210,6 +211,13 @@ struct op_store { 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, @@ -278,6 +286,7 @@ struct op { struct op_fd fd; struct op_load load; struct op_store store; + struct op_iovload iovload; struct op_bitwise bitwise; struct op_cmp cmp; struct op_resolvefd resfd; @@ -291,8 +300,12 @@ struct op { # define GLUTEN_CONST const #endif +struct gluten_header { + struct gluten_offset relocation[256]; +}; + struct gluten { - GLUTEN_CONST char header[HEADER_SIZE]; + GLUTEN_CONST struct gluten_header header; GLUTEN_CONST char inst[INST_SIZE]; @@ -309,7 +322,7 @@ static inline bool is_offset_valid(const struct gluten_offset x) { switch (x.type) { case OFFSET_NULL: - return false; + return true; case OFFSET_DATA: return x.offset < DATA_SIZE; case OFFSET_RO_DATA: @@ -319,6 +332,7 @@ static inline bool is_offset_valid(const struct gluten_offset x) case OFFSET_SECCOMP_DATA: return x.offset < 6; default: + debug("unknown offset in range check"); return false; } } diff --git a/common/util.c b/common/util.c index 3e81a1c..94aeea3 100644 --- a/common/util.c +++ b/common/util.c @@ -18,6 +18,8 @@ #include "gluten.h" +#include "../cooker/calls.h" + #define logfn(name) \ void name(const char *format, ...) { \ va_list args; \ @@ -56,6 +58,24 @@ const char *cmp_type_str[CMP_MAX + 1] = { }; const char *metadata_type_str[METADATA_MAX + 1] = { "uid", "gid", "pid" }; + +const char *syscall_name(long nr) { + struct call **set, *call; + + for (set = call_sets, call = set[0]; *set; call++) { + if (!call->name) { + set++; + call = set[0]; + continue; + } + + if (nr == call->number) + break; + } + + return call ? call->name : "unknown"; +} + const char *syscall_name_str[N_SYSCALL + 1] = { [__NR_chown] = "chown", [__NR_connect] = "connect", diff --git a/common/util.h b/common/util.h index 1367c0d..61fb3fe 100644 --- a/common/util.h +++ b/common/util.h @@ -130,6 +130,7 @@ void debug(const char *format, ...); #define BITS_PER_NUM(n) (const_ilog2(n) + 1) #define N_SYSCALL 512 extern const char *syscall_name_str[N_SYSCALL + 1]; +const char *syscall_name(long nr); #define ret_clone_err(c, ...) \ do { \ diff --git a/cooker/call.c b/cooker/call.c index bb53829..dd37fe9 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -72,7 +72,7 @@ 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) { + 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, @@ -133,6 +133,10 @@ Examples of arguments: - STRING: abcd write abcd to ro_data (using size), and pointer to it parse_arg() passes ro_data offset +- IOV: <tag> write (one!) struct iovec with iov_base pointer to tag + parse_arg() passes null offset + parse_field() gives back data offset + - STRUCT: 1, 2 write struct to ro_data, and pointer to it parse_arg() passes ro_data offset @@ -169,12 +173,38 @@ static union value parse_field(struct gluten_ctx *g, struct arg *args, (tmp1 = json_value_get_object(jvalue)) && is_metadata_obj(tmp1)) { v = parse_metadata(g, f, &base_offset, offset, tmp1, dry_run, add); - if (v.v_num == 0) + if (!(f->flags & IOV) && v.v_num == 0) + return v; + } + + if (f->flags & IOV) { + struct gluten_offset iov; + struct iovec *iovp; + intptr_t *msg_iov; + + if (dry_run) return v; + + /* iov_base: v */ + /* iov_len: at the moment from field */ + msg_iov = (intptr_t *)gluten_ptr(&g->g, offset); + + iov = gluten_rw_alloc(g, sizeof(struct iovec)); + iovp = (struct iovec *)gluten_ptr(&g->g, iov); + iovp->iov_base = (void *)(intptr_t)base_offset->offset; + gluten_relocation_add(g, iov); + + iovp->iov_len = f->size; + + *msg_iov = iov.offset; + gluten_relocation_add(g, offset); + + return v; } if (!jvalue && !(f->flags & SIZE)) return v; + switch (f->type) { case USHORT: case INT: @@ -249,12 +279,28 @@ static union value parse_field(struct gluten_ctx *g, struct arg *args, tmp1 = json_value_get_object(jvalue); f_value = json_object_get_value(tmp1, f_inner->name); - debug(" parse struct internal value:%s", + debug(" parse struct, field %s, internal value:%s", + f_inner->name, json_serialize_to_string(f_value)); - if (!f_value) + if (!f_value && !(f_inner->flags & SIZE)) continue; - parse_field(g, args, &struct_start, index, f_inner, - f_value, false, add); + + if (f_inner->size && f_inner->type == SELECT) { + struct gluten_offset ptr_offset; + intptr_t *link; + + ptr_offset = gluten_rw_alloc(g, f_inner->size); + parse_field(g, args, &ptr_offset, index, f_inner, + f_value, false, add); + + link = (intptr_t *)gluten_ptr(&g->g, offset); + *link = ptr_offset.offset; + gluten_relocation_add(g, offset); + } else { + parse_field(g, args, &struct_start, index, f_inner, + f_value, false, add); + } + if (base_offset->type == OFFSET_NULL) *base_offset = struct_start; } @@ -278,6 +324,9 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, if (f->flags & COPY_ON_CALL) return true; + if (f->flags & IOV) + return true; + if (f->flags & SIZE) return false; diff --git a/cooker/calls/net.c b/cooker/calls/net.c index a348037..94b13cd 100644 --- a/cooker/calls/net.c +++ b/cooker/calls/net.c @@ -79,6 +79,9 @@ static struct num protocols[] = { { "mpls", IPPROTO_MPLS }, { "raw", IPPROTO_RAW }, { "mptcp", IPPROTO_MPTCP }, + + { "nl_route", NETLINK_ROUTE }, + { 0 }, }; @@ -243,9 +246,265 @@ static struct arg connect_args[] = { { 0 } }; +static struct num send_flags[] = { + { "confirm", MSG_CONFIRM }, + { "dontroute", MSG_DONTROUTE }, + { "dontwait", MSG_DONTWAIT }, + { "eor", MSG_EOR }, + { "more", MSG_MORE }, + { "nosignal", MSG_NOSIGNAL }, + { "oob", MSG_OOB }, + { "fastopen", MSG_FASTOPEN }, + { 0 }, +}; + +static struct arg send_args[] = { + { 0, + { + "fd", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 1, + { + "buf", STRING, WBUF, + 0, + BUFSIZ, + { 0 }, + }, + }, + { 2, + { + "len", LONG, SIZE, + 0, + 0, + { .d_size = (intptr_t)&send_args[1] }, + }, + }, + { 3, + { + "flags", INT, FLAGS, + 0, + 0, + { .d_num = send_flags } + } + }, + { 0 } +}; + +static struct arg sendto_args[] = { + { 0, + { + "fd", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 1, + { + "buf", STRING, WBUF, + 0, + BUFSIZ, + { 0 }, + }, + }, + { 2, + { + "len", LONG, SIZE, + 0, + 0, + { .d_size = (intptr_t)&sendto_args[1] }, + }, + }, + { 3, + { + "flags", INT, FLAGS, + 0, + 0, + { .d_num = send_flags } + } + }, + { 4, + { + "addr", SELECT, 0, + 0, + sizeof(struct sockaddr_storage), + { .d_select = &connect_addr_select }, + }, + }, + { 5, + { + "addrlen", LONG, SIZE, + 0, + 0, + { .d_size = (intptr_t)&connect_addr_select }, + }, + }, + { 0 } +}; + +static struct select sendmsg_name_select = { + &connect_family, { .d_num = connect_addr_select_family } +}; + +static struct field sendmsg_msghdr[] = { + { + "name", SELECT, 0, + offsetof(struct msghdr, msg_name), + sizeof(struct sockaddr_storage), + { .d_select = &sendmsg_name_select }, + }, + { + "namelen", LONG, SIZE, + offsetof(struct msghdr, msg_namelen), + 0, + { .d_size = (intptr_t)&sendmsg_name_select }, + }, + { + "iov", STRING, WBUF | 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 sendmsg_args[] = { + { 0, + { + "fd", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 1, + { + "msg", STRUCT, 0, + 0, + sizeof(struct msghdr), + { .d_struct = sendmsg_msghdr }, + }, + }, + { 2, + { + "flags", INT, FLAGS, + 0, + 0, + { .d_num = send_flags } + } + }, + { 0 } +}; + +static struct arg recvmsg_args[] = { + { 0, + { + "fd", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 1, + { + "msg", STRUCT, 0, + 0, + sizeof(struct msghdr), + { .d_struct = sendmsg_msghdr }, + }, + }, + { 2, + { + "flags", INT, FLAGS, + 0, + 0, + { .d_num = send_flags } + } + }, + { 0 } +}; + +static struct arg setsockopt_args[] = { + { 0, + { + "fd", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 1, + { + "level", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 2, + { + "optname", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 3, + { + "optval", INT, 0, + 0, + BUFSIZ, + { 0 }, + }, + }, + { 4, + { + "optlen", INT, 0, + 0, + 0, + { 0 }, + }, + }, + { 0 } +}; + struct call syscalls_net[] = { { __NR_connect, "connect", connect_args }, { __NR_bind, "bind", connect_args }, { __NR_socket, "socket", socket_args }, + /* { __NR_send, "send", send_args }, */ + { __NR_sendto, "sendto", sendto_args }, + { __NR_sendmsg, "sendmsg", sendmsg_args }, + { __NR_recvmsg, "recvmsg", recvmsg_args }, + { __NR_setsockopt, "setsockopt", setsockopt_args }, { 0 }, }; diff --git a/cooker/cooker.h b/cooker/cooker.h index 9217c40..05a1f1b 100644 --- a/cooker/cooker.h +++ b/cooker/cooker.h @@ -35,13 +35,15 @@ struct size; * @d_num: Pointer to a list of numbers and their labels * @d_struct: Pointer to a struct description * @d_select: Pointer to description of a selector - * @d_arg_size: Position of argument whose pointed length is described + * @d_size: Position of argument whose pointed length is described + * @d_iovlen: Relative offset from pointed iovec field to corresponding iovlen */ union desc { struct num *d_num; struct field *d_struct; struct select *d_select; intptr_t d_size; + ptrdiff_t d_iovlen; }; /** @@ -110,6 +112,8 @@ enum flags { RBUF = BIT(5), /* Copy value from original call, ignore on return */ WBUF = BIT(6), + + IOV = BIT(7), }; #define TYPE_COUNT (TYPE_END - 1) diff --git a/cooker/emit.c b/cooker/emit.c index efe70eb..7899fa5 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -119,7 +119,7 @@ void emit_call(struct gluten_ctx *g, struct context_desc *cdesc, long nr, memcpy(desc->args, offset, sizeof(struct gluten_offset) * count); desc->args[count] = ret_offset; - debug(" %i: OP_CALL: %s, arguments:", g->ip.offset, syscall_name_str[nr]); + debug(" %i: OP_CALL: %s, arguments:", g->ip.offset, syscall_name(nr)); for (i = 0; i < count; i++) { debug("\t%i: %s %s%i", i, gluten_offset_name[offset[i].type], is_ptr[i] ? "*" : "", offset[i].offset); @@ -180,6 +180,42 @@ void emit_store(struct gluten_ctx *g, struct gluten_offset dst, } /** + * emit_iovload() - Emit OP_IOVLOAD instruction: IO vector from loaded pointers + * @g: gluten context + * @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 + * @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) +{ + 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); + + op->type = OP_IOVLOAD; + + load->iov = iov; + load->iovlen = iovlen; + + load->dst = dst; + load->size = len; + + debug(" %i: OP_IOVLOAD: #%i < (#%i) as iovec (size: %lu)", + g->ip.offset, dst.offset, iov.offset, len); + + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); + + return dst; +} + +/** * emit_resolved() - Emit OP_RESOLVEFD instruction: resolve file descriptor with path * @g: gluten context * @fd: offset of the file descriptor value diff --git a/cooker/emit.h b/cooker/emit.h index 835aae7..5557187 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -13,6 +13,10 @@ void emit_call(struct gluten_ctx *g, struct context_desc *cdesc, long nr, struct gluten_offset offset[6], struct gluten_offset ret_offset); void emit_load(struct gluten_ctx *g, struct gluten_offset dst, int index, size_t len); +struct gluten_offset emit_iovload(struct gluten_ctx *g, + struct gluten_offset iov, + struct gluten_offset iovlen, + 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); diff --git a/cooker/gluten.c b/cooker/gluten.c index 4d3aea5..69c9384 100644 --- a/cooker/gluten.c +++ b/cooker/gluten.c @@ -146,6 +146,7 @@ void gluten_add_attr(struct gluten_ctx *g, enum attr_type type, intptr_t id, union value gluten_get_attr(struct gluten_ctx *g, enum attr_type type, intptr_t id) { + union value missing = { 0 }; int i; for (i = 0; i < ATTRS_MAX && g->attrs[i].type; i++) { @@ -153,8 +154,8 @@ union value gluten_get_attr(struct gluten_ctx *g, enum attr_type type, return g->attrs[i].v; } - die(" attribute '%p' not found", id); - return g->attrs[0].v; /* Pro forma, not actually happening */ + debug(" attribute '%p' not found", id); + return missing; } /** @@ -163,8 +164,8 @@ union value gluten_get_attr(struct gluten_ctx *g, enum attr_type type, */ void gluten_init(struct gluten_ctx *g) { - g->ip.type = g->lr.type = g->mr.type = OFFSET_INSTRUCTION; g->ip.offset = g->lr.offset = g->mr.offset = 0; + g->ip.type = g->lr.type = g->mr.type = OFFSET_INSTRUCTION; g->dp.type = OFFSET_DATA; g->cp.type = OFFSET_RO_DATA; } @@ -185,3 +186,12 @@ void gluten_write(struct gluten_ctx *g, const char *path) close(fd); } + +void gluten_relocation_add(struct gluten_ctx *g, struct gluten_offset offset) +{ + g->g.header.relocation[g->rel_count++] = offset; + debug(" Added relocation for %s: %u", gluten_offset_name[offset.type], + offset.offset); + if (g->rel_count >= 256) + die("Too many relocations"); +} diff --git a/cooker/gluten.h b/cooker/gluten.h index 4659583..4a3b3f0 100644 --- a/cooker/gluten.h +++ b/cooker/gluten.h @@ -44,6 +44,8 @@ struct gluten_ctx { struct arg *selected_arg[6]; enum scmp_mode mode; + + size_t rel_count; }; /** @@ -70,8 +72,8 @@ void gluten_add_attr(struct gluten_ctx *g, enum attr_type type, intptr_t id, union value v); union value gluten_get_attr(struct gluten_ctx *g, enum attr_type type, intptr_t id); +void gluten_relocation_add(struct gluten_ctx *g, struct gluten_offset offset); void gluten_init(struct gluten_ctx *g); -void gluten_block_init(struct gluten_ctx *g); void gluten_write(struct gluten_ctx *g, const char *path); extern size_t gluten_size[TYPE_COUNT]; diff --git a/cooker/match.c b/cooker/match.c index 36ac9df..c56d9e5 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -19,6 +19,40 @@ #include "calls/net.h" #include "seccomp_profile.h" + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +static struct num netlink_types[] = { + { "newroute", RTM_NEWROUTE }, + { 0 }, +}; + +static struct num netlink_flags[] = { + { "request", NLM_F_REQUEST }, + { "create", NLM_F_CREATE }, + { 0 }, +}; + +static struct field netlink_header_fields[] = { + { + "type", USHORT, 0, + offsetof(struct nlmsghdr, nlmsg_type), + 0, { .d_num = netlink_types } + }, + { + "flags", USHORT, FLAGS, + offsetof(struct nlmsghdr, nlmsg_flags), + 0, { .d_num = netlink_flags } + }, + { 0 }, +}; + +static struct field netlink_header = { + "netlink", STRUCT, 0, 0, 0, + { .d_struct = netlink_header_fields } +}; + /** * arg_load() - Allocate and build bytecode for one syscall argument * @g: gluten context @@ -117,6 +151,15 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx break; } + offset.offset += f->offset; + + if (f->flags & IOV) { + struct gluten_offset iovlen = offset; + + iovlen.offset += f->desc.d_iovlen; + offset = emit_iovload(g, offset, iovlen, f->size); + } + if (json_value_get_type(jvalue) == JSONObject && (tmp = json_value_get_object(jvalue)) && (tag_name = json_object_get_string(tmp, "set"))) { @@ -155,8 +198,6 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx if (!jvalue || (f->flags & RBUF)) return v; - offset.offset += f->offset; - switch (f->type) { case USHORT: case INT: @@ -258,6 +299,17 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx parse_field(g, offset, 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, + &netlink_header, jvalue); + } else { + die(" unrecognised blob type"); + } + break; + } + if ((v.v_str = json_value_get_string(jvalue)) == NULL) die(" failed parsing field for value:%s", json_serialize_to_string_pretty(jvalue)); diff --git a/cooker/parse.c b/cooker/parse.c index 5d5cab3..8d0c20d 100644 --- a/cooker/parse.c +++ b/cooker/parse.c @@ -201,14 +201,14 @@ struct rule_parser { static union value value_get_set(struct num *desc, JSON_Array *set) { + union value n = { 0 }; struct num *tmp; - union value n; unsigned i; for (i = 0; i < json_array_get_count(set); i++) { for (tmp = desc; tmp->name; tmp++) { if (!strcmp(tmp->name, json_array_get_string(set, i))) { - n.v_num |= desc->value; + n.v_num |= tmp->value; break; } } @@ -364,6 +364,8 @@ static void parse_block(struct gluten_ctx *g, JSON_Object *block) unsigned i; memset(g->selected_arg, 0, sizeof(g->selected_arg)); + memset(g->match_dst, 0, sizeof(g->match_dst)); + memset(g->call_src, 0, sizeof(g->call_src)); memset(g->tags, 0, sizeof(g->tags)); g->lr = g->ip; diff --git a/demo/routes.hjson b/demo/routes.hjson new file mode 100644 index 0000000..5534001 --- /dev/null +++ b/demo/routes.hjson @@ -0,0 +1,34 @@ +[ + { + "match": [ + /* Is somebody trying to create a route? Hang on... */ + { "sendmsg": { "fd": { "set": "fd" }, + "msg": { "iov": { "set": "buf", "value": { "netlink": { "type": "newroute", "flags": { "all": [ "create", "request" ] } } } } } } + } + ], + "call": [ + /* Create a new socket, owned by us */ + { "socket": { "family": "netlink", "type": "raw", "flags": "nonblock", "protocol": "nl_route" }, "ret": "new_fd" }, + + /* Bind it for netlink usage */ + { "bind": { "fd": { "get": "new_fd" }, "addr": { "family": "netlink", "pid": 0, "groups": 0 } }, "ret": "y" }, + + /* And re-send the original message */ + { "sendmsg": { "fd": { "get": "new_fd" }, "msg": { "name": { "family": "netlink", "pid": 0, "groups": 0 }, "iovlen": 1, "iov": { "get": "buf" } }, "flags": 0 }, "ret": "n" } + ], + /* Then, sneak our socket back into the calling process */ + "fd": { "src": { "get": "fd" }, "new": { "set": "new_fd" }, "close_on_exec": false }, + + /* And report success, or failure */ + "return": { "value": "n", "error": "n" } + }, + { + /* FIXME: Unblock subsequent recvmsg(). To keep this clean, we should proxy + * it ourselves. + */ + "match": [ + { "recvmsg": { "fd": 4 /* TODO: get *our* new number */ } } + ], + "return": { "value": 0, "error": 0 } + } +] diff --git a/operations.c b/operations.c index 33596aa..507a2f7 100644 --- a/operations.c +++ b/operations.c @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> +#include <sys/uio.h> #include <pwd.h> #include <linux/seccomp.h> #include <linux/filter.h> @@ -31,6 +32,8 @@ #include "common/util.h" #include "operations.h" +#include "cooker/calls.h" + static bool is_cookie_valid(int notifyFd, uint64_t id) { return ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) == 0; @@ -301,12 +304,13 @@ static int execute_syscall(void *args) 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 %s: ret=%ld errno=%d%s%s", syscall_name_str[c->nr], c->ret, - c->err, *c->cwd ? " cwd=" : "", *c->cwd ? c->cwd : ""); + debug(" execute syscall %s: ret=%ld errno=%d%s%s", syscall_name(c->nr), + c->ret, c->err, *c->cwd ? " cwd=" : "", *c->cwd ? c->cwd : ""); if (c->ret < 0) { perror(" syscall"); exit(EXIT_FAILURE); } + exit(0); } @@ -398,6 +402,65 @@ out: return ret; } +int op_iovload(const struct seccomp_notif *req, int notifier, struct gluten *g, + struct op_iovload *load) +{ + const long unsigned int *iovptr = gluten_ptr(&req->data, g, load->iov); + const size_t *iovlen = gluten_ptr(&req->data, g, load->iovlen); + unsigned char *dst = gluten_write_ptr(g, load->dst); + struct iovec iovbuf[UIO_MAXIOV], *iov = iovbuf; + char path[PATH_MAX]; + char dbuf[BUFSIZ]; + unsigned i, count; + int fd; + + if (!*iovlen || *iovlen >= UIO_MAXIOV) + ret_err(EINVAL, "bad iovlen: %lu", *iovlen); + + debug(" op_iovload: vector at (%s %d) to (%s %d) iovlen=%zu, size=%zu", + gluten_offset_name[load->iov.type], load->iov.offset, + gluten_offset_name[load->dst.type], load->dst.offset, *iovlen, + load->size); + + snprintf(path, sizeof(path), "/proc/%d/mem", req->pid); + if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) + ret_err(-1, "error opening mem for %d", req->pid); + + if (!is_cookie_valid(notifier, req->id)) + ret_err(EIO, "expired seccomp notification"); + + if (!check_gluten_limits(load->dst, load->size)) + ret_err(EINVAL, "destination out of range"); + + if (pread(fd, iov, *iovlen * sizeof(struct iovec), *iovptr) <= 0) + ret_err(EIO, "pread"); + + for (i = 0, count = 0; i < *iovlen && count < load->size; i++, iov++) { + unsigned j, dn = 0; + ssize_t n; + + n = pread(fd, dst + count, + MIN(load->size - count, iov->iov_len), + (intptr_t)iov->iov_base); + + if (n < 0) + ret_err(EIO, "pread"); + + for (j = 0; j < n; j++) { + dn += snprintf(dbuf + dn, BUFSIZ - dn, "%02x ", + dst[count + j]); + } + + count += n; + } + + debug(" %s", dbuf); + + close(fd); + + return 0; +} + int op_store(const struct seccomp_notif *req, int notifier, struct gluten *g, struct op_store *store) { @@ -672,11 +735,12 @@ int op_nr(const struct seccomp_notif *req, int notifier, struct gluten *g, if (gluten_read(NULL, g, &nr, op->nr, sizeof(nr)) == -1) return -1; - debug(" op_nr: checking syscall %s", syscall_name_str[nr]); + + debug(" op_nr: checking syscall %s (%li)", syscall_name(nr), nr); if (nr == req->data.nr) return 0; - debug(" op_nr: jmp to instr %d", op->no_match.offset); + debug(" op_nr: no match: jump to #%d", op->no_match.offset); return op->no_match.offset; } @@ -702,6 +766,7 @@ int eval(struct gluten *g, const struct seccomp_notif *req, HANDLE_OP(OP_FD, op_fd, fd, 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); @@ -195,6 +195,24 @@ static int recvfd(int sockfd) return fd; } +static void gluten_relocate(struct gluten *g) +{ + unsigned i; + + for (i = 0; i < 256; i++) { + void **p; + + switch (g->header.relocation[i].type) { + case OFFSET_DATA: + p = gluten_write_ptr(g, g->header.relocation[i]); + *p = (intptr_t)*p + g->data; + break; + default: + ; + } + } +} + int main(int argc, char **argv) { char req_b[BUFSIZ]; @@ -212,11 +230,14 @@ int main(int argc, char **argv) arguments.uid = getuid(); arguments.gid = getgid(); parse(argc, argv, &arguments); + fd = open(arguments.input_file, O_CLOEXEC | O_RDONLY); if (read(fd, &g, sizeof(g)) != sizeof(g)) die("Failed to read gluten file"); close(fd); + gluten_relocate(&g); + if (arguments.pid > 0) { if ((pidfd = syscall(SYS_pidfd_open, arguments.pid, 0)) < 0) die(" pidfd_open"); |