aboutgitcodelistschat:MatrixIRC
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2023-12-21 12:06:05 +0100
committerStefano Brivio <sbrivio@redhat.com>2023-12-21 12:45:36 +0100
commitbdbec30a849807fb5e6841a38cfe0d168e5962b9 (patch)
tree210949d96b4d764235c1c5b81ad2eebb61681f95
parentc72c2493de8990c3a3b4780ec1429a3c359c121e (diff)
downloadseitan-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--Makefile4
-rw-r--r--common/gluten.h18
-rw-r--r--common/util.c20
-rw-r--r--common/util.h1
-rw-r--r--cooker/call.c61
-rw-r--r--cooker/calls/net.c259
-rw-r--r--cooker/cooker.h6
-rw-r--r--cooker/emit.c38
-rw-r--r--cooker/emit.h4
-rw-r--r--cooker/gluten.c16
-rw-r--r--cooker/gluten.h4
-rw-r--r--cooker/match.c56
-rw-r--r--cooker/parse.c6
-rw-r--r--demo/routes.hjson34
-rw-r--r--operations.c73
-rw-r--r--seitan.c21
16 files changed, 597 insertions, 24 deletions
diff --git a/Makefile b/Makefile
index 00406bc..8f537a4 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
diff --git a/seitan.c b/seitan.c
index fbfeb42..17ec63e 100644
--- a/seitan.c
+++ b/seitan.c
@@ -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");