aboutsummaryrefslogtreecommitdiff
path: root/src/jail_packet.c
diff options
context:
space:
mode:
authorlns <matzeton@googlemail.com>2018-08-26 16:01:18 +0200
committerlns <matzeton@googlemail.com>2019-02-04 01:52:06 +0100
commitf68988980d72d837f49451068b98bc96d1a548fc (patch)
tree557f91ecac2d94afa737c25b5cb81dc1eaebfc5e /src/jail_packet.c
parente7b96666c03f1536fa89149204b363951523560e (diff)
Introduced the protocol->jail binary packet.feature/jail_packet
We are using a handler/callback functions to obtain additional information from the protocol handler and transmit it to the sandbox. Signed-off-by: lns <matzeton@googlemail.com>
Diffstat (limited to 'src/jail_packet.c')
-rw-r--r--src/jail_packet.c485
1 files changed, 458 insertions, 27 deletions
diff --git a/src/jail_packet.c b/src/jail_packet.c
index fa19cc5..f12a7d2 100644
--- a/src/jail_packet.c
+++ b/src/jail_packet.c
@@ -1,17 +1,37 @@
+#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <assert.h>
#include "jail_packet.h"
+#include "pevent.h"
+#include "log.h"
#include "utils.h"
+#ifdef gcc_struct
+#define JP_ATTRS __attribute__((packed, aligned(1), gcc_struct))
+#else
+#define JP_ATTRS __attribute__((packed, aligned(1)))
+#endif
+
typedef struct jail_packet {
uint8_t type;
uint16_t size;
-} jail_packet;
+} JP_ATTRS jail_packet;
+
+#define PKT_SIZ(pkt) (sizeof(jail_packet) + sizeof(*pkt))
+#define PKT_SUB(pkt_ptr) ((unsigned char *)pkt_ptr + sizeof(jail_packet))
+
+#define JP_MAGIC1 0xDEADC0DE
+#define JP_MAGIC2 0xDEADBEEF
-typedef ssize_t (*packet_callback)(jail_packet_ctx *ctx, event_buf *read_buf,
- event_buf *write_buf);
+typedef struct jail_packet_handshake {
+ uint32_t magic1;
+ uint32_t magic2;
+} JP_ATTRS jail_packet_handshake;
+
+typedef int (*packet_callback)(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
typedef struct jail_packet_callback {
uint8_t type;
@@ -19,78 +39,489 @@ typedef struct jail_packet_callback {
} jail_packet_callback;
static ssize_t pkt_header_read(unsigned char *buf, size_t siz);
-static ssize_t pkt_hello(jail_packet_ctx *ctx, event_buf *read_buf,
+static int pkt_handshake(jail_packet_ctx *ctx, jail_packet *pkt,
event_buf *write_buf);
-static int jail_event_loop(event_ctx *ctx, event_buf *buf, void *user_data);
+static int pkt_user(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int pkt_pass(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int pkt_handshake_end(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int pkt_start(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int pkt_data(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int pkt_respok(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int pkt_resperr(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+static int jail_packet_io(event_ctx *ctx, int src_fd, void *user_data);
+static int jail_packet_pkt(event_ctx *ev_ctx, event_buf *read_buf,
+ event_buf *write_buf, void *user_data);
#define PKT_CB(type, cb) \
{ type, cb }
static const jail_packet_callback jpc[] = {
PKT_CB(PKT_INVALID, NULL),
- PKT_CB(PKT_HELLO, pkt_hello)
+ PKT_CB(PKT_HANDSHAKE, pkt_handshake),
+ PKT_CB(PKT_USER, pkt_user),
+ PKT_CB(PKT_PASS, pkt_pass),
+ PKT_CB(PKT_HANDSHAKE_END, pkt_handshake_end),
+ PKT_CB(PKT_START, pkt_start),
+ PKT_CB(PKT_DATA, pkt_data),
+ PKT_CB(PKT_RESPOK, pkt_respok),
+ PKT_CB(PKT_RESPERR, pkt_resperr)
};
static ssize_t pkt_header_read(unsigned char *buf, size_t siz)
{
+ uint16_t pkt_size;
jail_packet *pkt;
if (siz < sizeof(*pkt))
- return -1;
+ return 0;
pkt = (jail_packet *) buf;
if (pkt->type >= SIZEOF(jpc))
return -1;
- pkt->size = ntohs(pkt->size);
- if (siz < pkt->size)
+ pkt_size = ntohs(pkt->size);
+ if (pkt_size > PKT_MAXSIZ - sizeof(*pkt))
return -1;
+ if (siz < pkt_size)
+ return 0;
- return pkt->size;
+ pkt->size = pkt_size;
+ return pkt_size + sizeof(*pkt);
}
-static ssize_t pkt_hello(jail_packet_ctx *ctx, event_buf *read_buf,
+static int pkt_write(event_buf *write_buf, uint8_t type, unsigned char *buf,
+ size_t siz)
+{
+ uint16_t pkt_size;
+ jail_packet pkt;
+
+ pkt.type = type;
+ pkt_size = siz;
+
+ do {
+ pkt_size = (siz > PKT_MAXSIZ - sizeof(pkt) ?
+ PKT_MAXSIZ - sizeof(pkt) : pkt_size);
+ pkt.size = htons(pkt_size);
+
+ if (event_buf_fill(write_buf, (char *) &pkt, sizeof pkt) ||
+ (buf && event_buf_fill(write_buf, (char *) buf, pkt_size)))
+ {
+ return 1;
+ }
+ siz -= pkt_size;
+ } while (siz > 0);
+
+ return 0;
+}
+
+static int pkt_handshake(jail_packet_ctx *ctx, jail_packet *pkt,
event_buf *write_buf)
{
- return -1;
+ jail_packet_handshake *pkt_hello;
+
+ if (ctx->ctype != JC_SERVER)
+ return 1;
+
+ if (ctx->pstate != JP_HANDSHAKE)
+ return 1;
+ pkt_hello = (jail_packet_handshake *) PKT_SUB(pkt);
+ pkt_hello->magic1 = ntohl(pkt_hello->magic1);
+ pkt_hello->magic2 = ntohl(pkt_hello->magic2);
+ if (pkt_hello->magic1 != JP_MAGIC1 ||
+ pkt_hello->magic2 != JP_MAGIC2)
+ {
+ return 1;
+ }
+
+ if (pkt_write(&ctx->writeback_buf, PKT_RESPOK, NULL, 0))
+ return 1;
+
+ ctx->pstate = JP_HANDSHAKE_END;
+
+ return 0;
+}
+
+static int pkt_user(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ char *user;
+
+ (void) write_buf;
+
+ if (ctx->ctype != JC_SERVER || ctx->pstate != JP_HANDSHAKE_END ||
+ !pkt->size || pkt->size > USER_LEN)
+ {
+ return 1;
+ }
+ user = (char *) PKT_SUB(pkt);
+ ctx->user = strndup(user, pkt->size);
+
+ return 0;
+}
+
+static int pkt_pass(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ char *pass;
+
+ (void) write_buf;
+
+ if (ctx->ctype != JC_SERVER || ctx->pstate != JP_HANDSHAKE_END ||
+ !pkt->size || pkt->size > PASS_LEN)
+ {
+ return 1;
+ }
+ pass = (char *) PKT_SUB(pkt);
+ ctx->pass = strndup(pass, pkt->size);
+
+ return 0;
+}
+
+static int pkt_handshake_end(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ (void) write_buf;
+
+ if (ctx->ctype != JC_SERVER || ctx->pstate != JP_HANDSHAKE_END ||
+ pkt->size)
+ {
+ return 1;
+ }
+
+ ctx->is_valid = 1;
+ ctx->ev_active = 0;
+
+ return 0;
}
-static int jail_event_loop(event_ctx *ctx, event_buf *buf, void *user_data)
+static int pkt_start(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ if (ctx->ctype != JC_SERVER || ctx->pstate != JP_START ||
+ pkt->size)
+ {
+ return 1;
+ }
+
+ (void) write_buf;
+ ctx->pstate = JP_DATA;
+
+ if (pkt_write(&ctx->writeback_buf, PKT_RESPOK, NULL, 0))
+ return 1;
+
+ return 0;
+}
+
+static int pkt_data(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ unsigned char *data = PKT_SUB(pkt);
+
+ if (ctx->ctype == JC_SERVER || ctx->ctype == JC_CLIENT) {
+ if (event_buf_fill(write_buf, (char *) data, pkt->size))
+ return 1;
+ if (ctx->ctype == JC_CLIENT)
+ ctx->ev_active = 0;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int pkt_respok(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ if (ctx->ctype == JC_CLIENT) {
+ switch (ctx->pstate) {
+ case JP_HANDSHAKE:
+ ctx->pstate = JP_HANDSHAKE_END;
+ ctx->ev_active = 0;
+ break;
+ case JP_START:
+ ctx->pstate = JP_DATA;
+ ctx->ev_active = 0;
+ break;
+ default: return 1;
+ }
+ } else return 1;
+
+ return 0;
+}
+
+static int pkt_resperr(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf)
+{
+ (void) ctx;
+ (void) pkt;
+ (void) write_buf;
+
+ return 1;
+}
+
+static int jail_packet_io(event_ctx *ev_ctx, int src_fd, void *user_data)
+{
+ int dest_fd;
+ jail_packet_ctx *pkt_ctx = (jail_packet_ctx *) user_data;
+ forward_state fwd_state;
+
+ (void) ev_ctx;
+ (void) src_fd;
+ (void) pkt_ctx;
+
+ if (pkt_ctx->ctype == JC_CLIENT) {
+ dest_fd = src_fd;
+ } else if (src_fd == pkt_ctx->connection.client_fd) {
+ dest_fd = pkt_ctx->connection.jail_fd;
+ } else if (src_fd == pkt_ctx->connection.jail_fd) {
+ dest_fd = pkt_ctx->connection.client_fd;
+ } else return 0;
+
+ fwd_state = event_forward_connection(ev_ctx, dest_fd, jail_packet_pkt,
+ user_data);
+
+ switch (fwd_state) {
+ case CON_IN_TERMINATED:
+ case CON_OUT_TERMINATED:
+ ev_ctx->active = 0;
+ case CON_OK:
+ return 1;
+ case CON_IN_ERROR:
+ case CON_OUT_ERROR:
+ ev_ctx->has_error = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int jail_packet_pkt(event_ctx *ev_ctx, event_buf *read_buf,
+ event_buf *write_buf, void *user_data)
{
jail_packet_ctx *pkt_ctx = (jail_packet_ctx *) user_data;
jail_packet *pkt;
- event_buf wbuf = { -1, {0}, 0, user_data };
ssize_t pkt_siz;
off_t pkt_off = 0;
+ if (read_buf->fd == pkt_ctx->connection.jail_fd &&
+ pkt_ctx->ctype == JC_SERVER)
+ {
+ if (pkt_ctx->pstate != JP_DATA)
+ return 0;
+
+ if (pkt_write(&pkt_ctx->writeback_buf, PKT_DATA,
+ (unsigned char *) read_buf->buf,
+ read_buf->buf_used))
+ {
+ return 1;
+ }
+
+ if (event_buf_drain(&pkt_ctx->writeback_buf) < 0)
+ return 1;
+ event_buf_discardall(read_buf);
+
+ return 0;
+ } else
+ if (read_buf->fd != pkt_ctx->connection.client_fd &&
+ pkt_ctx->ctype != JC_CLIENT)
+ {
+ W2("Unknown fd %d for jail packet", read_buf->fd);
+ return 0;
+ }
+
while (1) {
- pkt_siz = pkt_header_read((unsigned char *) buf->buf + pkt_off,
- buf->buf_used);
- if (pkt_siz < 0)
+ /* FIXME: not optimal for preventing buffer bloats */
+ if (event_buf_avail(write_buf) < PKT_MAXSIZ)
break;
- pkt = (jail_packet *)(buf->buf + pkt_off);
+ pkt_siz = pkt_header_read((unsigned char *) read_buf->buf + pkt_off,
+ read_buf->buf_used);
+ if (pkt_siz < 0) {
+ /* invalid jail packet */
+ pkt_ctx->pstate = JP_INVALID;
+ break;
+ } else if (pkt_siz == 0)
+ /* require more data */
+ break;
+
+ pkt = (jail_packet *)(read_buf->buf + pkt_off);
if (jpc[pkt->type].pc &&
- jpc[pkt->type].pc(pkt_ctx, buf, &wbuf) < 0)
+ jpc[pkt->type].pc(pkt_ctx, pkt, write_buf))
{
pkt_ctx->pstate = JP_INVALID;
break;
}
- pkt_off += pkt_siz + sizeof *pkt;
- buf->buf_used -= pkt_off;
+ pkt_off += pkt_siz;
+ read_buf->buf_used -= pkt_siz;
}
if (pkt_off)
- memmove(buf->buf, buf->buf + pkt_off, buf->buf_used);
+ event_buf_discard(read_buf, pkt_off);
+
+ if (event_buf_drain(write_buf) < 0)
+ pkt_ctx->pstate = JP_INVALID;
+ if (event_buf_drain(&pkt_ctx->writeback_buf) < 0)
+ pkt_ctx->pstate = JP_INVALID;
+
+ if (pkt_ctx->pstate == JP_NONE /* default value not allowed */
+ || pkt_ctx->pstate == JP_INVALID) /* an invalid state */
+ {
+ ev_ctx->has_error = 1;
+ return 1;
+ }
- return pkt_ctx->pstate != JP_NONE && pkt_ctx->pstate != JP_INVALID;
+ ev_ctx->active = pkt_ctx->ev_active;
+
+ return 0;
+}
+
+int jail_client_send(jail_packet_ctx *pkt_ctx, unsigned char *buf, size_t siz)
+{
+ if (pkt_write(&pkt_ctx->writeback_buf, PKT_DATA, buf, siz) ||
+ event_buf_drain(&pkt_ctx->writeback_buf) < 0)
+ {
+ return 1;
+ }
+ return 0;
}
-int jail_packet_loop(event_ctx *ctx, jail_packet_ctx *pkt_ctx)
+int jail_client_data(event_ctx *ctx, event_buf *in, event_buf *out,
+ jail_packet_ctx *pkt_ctx)
{
- assert(pkt_ctx->on_data && pkt_ctx->user_data);
- pkt_ctx->pstate = JP_NONE;
+ assert(ctx && pkt_ctx);
+ assert(in->fd >= 0 && out->fd < 0);
+ assert(pkt_ctx->connection.client_fd >= 0 &&
+ pkt_ctx->connection.jail_fd < 0);
+ assert(pkt_ctx->pstate == JP_DATA ||
+ pkt_ctx->pstate == JP_START ||
+ pkt_ctx->pstate == JP_HANDSHAKE_END);
+
+ if (pkt_ctx->pstate == JP_HANDSHAKE_END) {
+ pkt_ctx->pstate = JP_START;
+ if (pkt_write(&pkt_ctx->writeback_buf, PKT_START, NULL, 0) ||
+ event_buf_drain(&pkt_ctx->writeback_buf) < 0)
+ {
+ return 1;
+ }
+ }
+
+ if (pkt_ctx->ev_readloop)
+ return event_loop(ctx, jail_packet_io, pkt_ctx) || ctx->has_error;
+ else
+ return jail_packet_pkt(ctx, in, out, pkt_ctx) || ctx->has_error;
+}
+
+int jail_server_loop(event_ctx *ctx, jail_packet_ctx *pkt_ctx)
+{
+ assert(ctx && pkt_ctx);
+ assert(pkt_ctx->pstate == JP_START && pkt_ctx->ctype == JC_SERVER);
+ assert(pkt_ctx->connection.client_fd >= 0 &&
+ pkt_ctx->connection.jail_fd >= 0);
+ pkt_ctx->ev_active = 1;
+
+ return event_loop(ctx, jail_packet_io, pkt_ctx) || ctx->has_error;
+}
+
+event_ctx *jail_client_handshake(int server_fd, jail_packet_ctx *pkt_ctx)
+{
+ event_ctx *ev_ctx = NULL;
+ event_buf write_buf = WRITE_BUF(server_fd);
+ size_t user_len, pass_len;
+ jail_packet_handshake pkt_hello;
+
+ assert(pkt_ctx);
+ assert(pkt_ctx->pstate == JP_NONE);
+ assert(pkt_ctx->ctype == JC_CLIENT);
+
+ pkt_ctx->pstate = JP_HANDSHAKE;
+ pkt_ctx->ev_active = 1;
+
+ event_init(&ev_ctx);
+ if (event_setup(ev_ctx)) {
+ E_STRERR("Jail protocol event context creation for jail tty fd %d",
+ server_fd);
+ goto finish;
+ }
+ if (set_fd_nonblock(server_fd)) {
+ E_STRERR("Jail protocol nonblock for %d", server_fd);
+ goto finish;
+ }
+ if (event_add_fd(ev_ctx, server_fd, NULL)) {
+ E_STRERR("Jail protocol event context for fd %d", server_fd);
+ goto finish;
+ }
+
+ pkt_hello.magic1 = htonl(JP_MAGIC1);
+ pkt_hello.magic2 = htonl(JP_MAGIC2);
+ if (pkt_write(&write_buf, PKT_HANDSHAKE,
+ (unsigned char *) &pkt_hello, sizeof(pkt_hello)))
+ {
+ goto finish;
+ }
+
+ if (pkt_ctx->user) {
+ user_len = strnlen(pkt_ctx->user, USER_LEN);
+ if (pkt_write(&write_buf, PKT_USER,
+ (unsigned char *) pkt_ctx->user, user_len))
+ {
+ goto finish;
+ }
+ }
+ if (pkt_ctx->pass) {
+ pass_len = strnlen(pkt_ctx->pass, PASS_LEN);
+ if (pkt_write(&write_buf, PKT_PASS,
+ (unsigned char *) pkt_ctx->pass, pass_len))
+ {
+ goto finish;
+ }
+ }
+
+ if (pkt_write(&write_buf, PKT_HANDSHAKE_END, NULL, 0))
+ goto finish;
+
+ if (event_buf_drain(&write_buf) < 0)
+ goto finish;
+
+ pkt_ctx->is_valid = 1;
+ pkt_ctx->writeback_buf = write_buf;
+
+ if (event_loop(ev_ctx, jail_packet_io, pkt_ctx) || ev_ctx->has_error ||
+ pkt_ctx->pstate != JP_HANDSHAKE_END || !pkt_ctx->is_valid)
+ {
+ W_STRERR("Jail protocol handshake for fd %d", server_fd);
+ goto finish;
+ }
+
+ return ev_ctx;
+finish:
+ event_free(&ev_ctx);
+ return NULL;
+}
+
+int jail_server_handshake(event_ctx *ctx, jail_packet_ctx *pkt_ctx)
+{
+ int rc;
+
+ assert(ctx && pkt_ctx);
+ assert(pkt_ctx->pstate == JP_NONE);
+ assert(pkt_ctx->ctype == JC_SERVER);
+
+ struct event_buf write_buf = WRITE_BUF(pkt_ctx->connection.client_fd);
+ pkt_ctx->pstate = JP_HANDSHAKE;
+ pkt_ctx->ev_active = 1;
+ pkt_ctx->writeback_buf = write_buf;
+
+ rc = event_loop(ctx, jail_packet_io, pkt_ctx);
+ if (!rc && pkt_ctx->is_valid)
+ pkt_ctx->pstate = JP_START;
- return event_loop(ctx, jail_event_loop, pkt_ctx);
+ return rc;
}