aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlns <matzeton@googlemail.com>2019-02-04 01:54:57 +0100
committerlns <matzeton@googlemail.com>2019-02-04 01:54:57 +0100
commit4f74e5f5c4ba15f1ea02e7425c18cbd06642ce2e (patch)
tree18ebe7301a7f4b1c25f516b71722d7e49e87c182
parente21e46d83d3b4292ad6de84b26902246ee398bcb (diff)
parentf68988980d72d837f49451068b98bc96d1a548fc (diff)
Merge branch 'feature/jail_packet'
-rw-r--r--src/Makefile.am7
-rw-r--r--src/jail.c114
-rw-r--r--src/jail.h7
-rw-r--r--src/jail_packet.c527
-rw-r--r--src/jail_packet.h130
-rw-r--r--src/log.h6
-rw-r--r--src/log_colored.c6
-rw-r--r--src/log_file.c6
-rw-r--r--src/main.c2
-rw-r--r--src/pevent.c124
-rw-r--r--src/pevent.h58
-rw-r--r--src/protocol_ssh.c96
-rw-r--r--src/redirector.c23
13 files changed, 959 insertions, 147 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index dad4dfa..6e2b390 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,8 +1,5 @@
-sbin_PROGRAMS = potd potd-init
-potd_SOURCES = compat.c utils.c options.c log.c log_colored.c log_file.c socket.c pevent.c capabilities.c filesystem.c jail.c forward.c redirector.c protocol.c protocol_ssh.c main.c
-potd_LDADD = @potd_LIBS@
-potd_init_SOURCES = potd-init.c
-potd_init_LDFLAGS = -static
+sbin_PROGRAMS = potd
+potd_SOURCES = compat.c utils.c options.c log.c log_colored.c log_file.c socket.c pevent.c capabilities.c filesystem.c jail_packet.c jail.c forward.c redirector.c protocol.c protocol_ssh.c main.c
if HAVE_SECCOMP
potd_SOURCES += pseccomp.c
endif
diff --git a/src/jail.c b/src/jail.c
index 63ff5b6..feb3bc2 100644
--- a/src/jail.c
+++ b/src/jail.c
@@ -52,6 +52,7 @@
#include <assert.h>
#include "jail.h"
+#include "jail_packet.h"
#include "socket.h"
#ifdef HAVE_SECCOMP
#include "pseccomp.h"
@@ -74,10 +75,9 @@ typedef struct server_event {
} server_event;
typedef struct client_event {
- psocket *client_sock;
+ jail_con connection;
char *host_buf;
char *service_buf;
- int tty_fd;
int signal_fd;
char tty_logbuf[BUFSIZ];
size_t off_logbuf;
@@ -87,15 +87,11 @@ typedef struct client_event {
static int jail_mainloop(event_ctx **ev_ctx, const jail_ctx *ctx[], size_t siz)
__attribute__((noreturn));
-static int jail_accept_client(event_ctx *ev_ctx, event_buf *buf,
+static int jail_accept_client(event_ctx *ev_ctx, int src_fd,
void *user_data);
static int jail_childfn(prisoner_process *ctx)
__attribute__((noreturn));
static int jail_socket_tty(prisoner_process *ctx, int tty_fd);
-static int jail_socket_tty_io(event_ctx *ev_ctx, event_buf *buf,
- void *user_data);
-static int jail_log_input(event_ctx *ev_ctx, int src_fd, int dst_fd,
- char *buf, size_t siz, void *user_data);
void jail_init_ctx(jail_ctx **ctx, size_t stacksize)
@@ -161,7 +157,7 @@ int jail_setup_event(jail_ctx *ctx[], size_t siz, event_ctx **ev_ctx)
return 1;
for (size_t i = 0; i < siz; ++i) {
- if (event_add_sock(*ev_ctx, &ctx[i]->fwd_ctx.sock)) {
+ if (event_add_sock(*ev_ctx, &ctx[i]->fwd_ctx.sock, NULL)) {
return 1;
}
@@ -235,24 +231,23 @@ static int jail_mainloop(event_ctx **ev_ctx, const jail_ctx *ctx[], size_t siz)
exit(rc);
}
-static int jail_accept_client(event_ctx *ev_ctx, event_buf *buf,
+static int jail_accept_client(event_ctx *ev_ctx, int src_fd,
void *user_data)
{
size_t i, rc = 0;
- int s, fd;
+ int s;
pid_t prisoner_pid;
server_event *ev_jail;
static prisoner_process *args;
const jail_ctx *jail_ctx;
(void) ev_ctx;
- assert(ev_ctx && buf && user_data);
+ assert(ev_ctx && user_data);
ev_jail = (server_event *) user_data;
- fd = buf->fd;
for (i = 0; i < ev_jail->siz; ++i) {
jail_ctx = ev_jail->jail_ctx[i];
- if (jail_ctx->fwd_ctx.sock.fd == fd) {
+ if (jail_ctx->fwd_ctx.sock.fd == src_fd) {
args = (prisoner_process *) calloc(1, sizeof(*args));
assert(args);
args->newroot = jail_ctx->newroot;
@@ -440,6 +435,21 @@ static int jail_childfn(prisoner_process *ctx)
if (prctl(PR_SET_PDEATHSIG, SIGKILL) != 0)
exit(EXIT_FAILURE);
+#ifdef HAVE_SECCOMP
+ pseccomp_set_immutable();
+ pseccomp_init(&psc,
+ (getopt_used(OPT_SECCOMP_MINIMAL) ? PS_MINIMUM : 0));
+ if (pseccomp_jail_rules(psc))
+ FATAL("%s", "SECCOMP: adding jail rules");
+ pseccomp_free(&psc);
+#else
+ /* libseccomp is not available, so drop at least all caps */
+ W2("%s", "Compiled without libseccomp, dropping ALL capabilities");
+ caps_drop_all();
+#endif
+
+ if (sethostname("openwrt", SIZEOF("openwrt")))
+ exit(EXIT_FAILURE);
printf("%s",
" _______ ________ __\n"
" | |.-----.-----.-----.| | | |.----.| |_\n"
@@ -458,22 +468,6 @@ static int jail_childfn(prisoner_process *ctx)
" * 1 splash Cranberry juice\n"
" -----------------------------------------------------\n"
);
-
-#ifdef HAVE_SECCOMP
- pseccomp_set_immutable();
- pseccomp_init(&psc,
- (getopt_used(OPT_SECCOMP_MINIMAL) ? PS_MINIMUM : 0));
- if (pseccomp_jail_rules(psc))
- FATAL("%s", "SECCOMP: adding jail rules");
- pseccomp_free(&psc);
-#else
- /* libseccomp is not available, so drop at least all caps */
- W2("%s", "Compiled without libseccomp, dropping ALL capabilities");
- caps_drop_all();
-#endif
-
- if (sethostname("openwrt", SIZEOF("openwrt")))
- exit(EXIT_FAILURE);
/* Flawfinder: ignore */
if (execl(path_shell, path_shell, (char *) NULL))
exit(EXIT_FAILURE);
@@ -511,13 +505,14 @@ finalise:
static int jail_socket_tty(prisoner_process *ctx, int tty_fd)
{
- static client_event ev_cli = {NULL, NULL, NULL, -1, -1, {0}, 0, 0, 0};
+ static client_event ev_cli = {{-1,-1}, NULL, NULL, -1, {0}, 0, NULL, 0};
+ static jail_packet_ctx pkt_ctx =
+ {0, 0, 1, EMPTY_JAILCON, EMPTY_BUF, JC_SERVER, JP_NONE, NULL, NULL};
int s, rc = 1;
event_ctx *ev_ctx = NULL;
sigset_t mask;
assert(ctx);
- ev_cli.tty_fd = tty_fd;
event_init(&ev_ctx);
if (event_setup(ev_ctx)) {
@@ -531,12 +526,12 @@ static int jail_socket_tty(prisoner_process *ctx, int tty_fd)
ctx->host_buf, ctx->service_buf, ctx->client_psock.fd);
goto finish;
}
- if (event_add_sock(ev_ctx, &ctx->client_psock)) {
+ if (event_add_sock(ev_ctx, &ctx->client_psock, NULL)) {
E_STRERR("Jail event context for socket %s:%s",
ctx->host_buf, ctx->service_buf);
goto finish;
}
- if (event_add_fd(ev_ctx, tty_fd)) {
+ if (event_add_fd(ev_ctx, tty_fd, NULL)) {
E_STRERR("Jail event context for tty fd %d",
tty_fd);
goto finish;
@@ -554,25 +549,34 @@ static int jail_socket_tty(prisoner_process *ctx, int tty_fd)
E_STRERR("%s", "SIGNAL fd");
goto finish;
}
- if (event_add_fd(ev_ctx, ev_cli.signal_fd)) {
+ if (event_add_fd(ev_ctx, ev_cli.signal_fd, NULL)) {
E_STRERR("Jail SIGNAL fd %d", ev_cli.signal_fd);
goto finish;
}
- ev_cli.client_sock = &ctx->client_psock;
+ pkt_ctx.connection.client_fd = ev_cli.connection.client_fd = ctx->client_psock.fd;
+ pkt_ctx.connection.jail_fd = ev_cli.connection.jail_fd = tty_fd;
ev_cli.host_buf = &ctx->host_buf[0];
ev_cli.service_buf = &ctx->service_buf[0];
- rc = event_loop(ev_ctx, jail_socket_tty_io, &ev_cli);
+
+ if (!jail_server_handshake(ev_ctx, &pkt_ctx) && pkt_ctx.is_valid) {
+ N("Using Jail protocol for %s:%s",
+ ctx->host_buf, ctx->service_buf);
+ rc = jail_server_loop(ev_ctx, &pkt_ctx);
+ } else {
+ E("Jail protocol handshake failed for %s:%s",
+ ctx->host_buf, ctx->service_buf);
+ }
finish:
- close(ev_cli.signal_fd);
event_free(&ev_ctx);
return rc;
}
+#if 0
static int
-jail_socket_tty_io(event_ctx *ev_ctx, event_buf *buf, void *user_data)
+jail_socket_tty_io(event_ctx *ev_ctx, int src_fd, void *user_data)
{
- int dest_fd, src_fd = buf->fd;
+ int dest_fd;
client_event *ev_cli = (client_event *) user_data;
forward_state fwd_state;
@@ -580,10 +584,10 @@ jail_socket_tty_io(event_ctx *ev_ctx, event_buf *buf, void *user_data)
(void) src_fd;
(void) ev_cli;
- if (src_fd == ev_cli->client_sock->fd) {
- dest_fd = ev_cli->tty_fd;
- } else if (src_fd == ev_cli->tty_fd) {
- dest_fd = ev_cli->client_sock->fd;
+ if (src_fd == ev_cli->connection.client_fd) {
+ dest_fd = ev_cli->connection.jail_fd;
+ } else if (src_fd == ev_cli->connection.jail_fd) {
+ dest_fd = ev_cli->connection.client_fd;
} else if (src_fd == ev_cli->signal_fd) {
ev_ctx->active = 0;
return 0;
@@ -607,18 +611,17 @@ jail_socket_tty_io(event_ctx *ev_ctx, event_buf *buf, void *user_data)
return 1;
}
-static int jail_log_input(event_ctx *ev_ctx, int src_fd, int dst_fd,
- char *buf, size_t siz, void *user_data)
+static int jail_log_input(event_ctx *ev_ctx, event_buf *read_buf,
+ event_buf *write_buf, void *user_data)
{
- size_t idx = 0, slen, ssiz = siz;
+ size_t idx = 0, slen, read_siz = read_buf->buf_used;
client_event *ev_cli = (client_event *) user_data;
(void) ev_ctx;
- (void) src_fd;
- if (ev_cli->tty_fd == dst_fd) {
- while (ssiz > 0) {
- slen = MIN(sizeof(ev_cli->tty_logbuf) - ev_cli->off_logbuf, ssiz);
+ if (ev_cli->connection.jail_fd == write_buf->fd) {
+ while (read_siz > 0) {
+ slen = MIN(sizeof(ev_cli->tty_logbuf) - ev_cli->off_logbuf, read_siz);
if (slen == 0) {
escape_ascii_string(ev_cli->tty_logbuf, ev_cli->off_logbuf,
&ev_cli->tty_logbuf_escaped, &ev_cli->tty_logbuf_size);
@@ -628,12 +631,14 @@ static int jail_log_input(event_ctx *ev_ctx, int src_fd, int dst_fd,
ev_cli->tty_logbuf[0] = 0;
continue;
}
- strncat(ev_cli->tty_logbuf, buf+idx, slen);
- ssiz -= slen;
+ strncat(ev_cli->tty_logbuf, read_buf->buf + idx, slen);
+ read_siz -= slen;
idx += slen;
ev_cli->off_logbuf += slen;
}
- if (buf[siz-1] == '\r' || buf[siz-1] == '\n') {
+ if (read_buf->buf[read_buf->buf_used-1] == '\r' ||
+ read_buf->buf[read_buf->buf_used-1] == '\n')
+ {
escape_ascii_string(ev_cli->tty_logbuf, ev_cli->off_logbuf,
&ev_cli->tty_logbuf_escaped, &ev_cli->tty_logbuf_size);
C("[%s:%s] %s", ev_cli->host_buf, ev_cli->service_buf,
@@ -643,5 +648,8 @@ static int jail_log_input(event_ctx *ev_ctx, int src_fd, int dst_fd,
}
}
+ event_buf_dup(read_buf, write_buf);
+
return 0;
}
+#endif
diff --git a/src/jail.h b/src/jail.h
index 7c3a07b..5ddbb79 100644
--- a/src/jail.h
+++ b/src/jail.h
@@ -43,6 +43,8 @@
#define MIN_STACKSIZE 2048
#define MAX_STACKSIZE BUFSIZ
+#define EMPTY_JAILCON { -1, -1 }
+
typedef struct jail_ctx {
forward_ctx fwd_ctx;
char host_buf[NI_MAXHOST], service_buf[NI_MAXSERV];
@@ -52,6 +54,11 @@ typedef struct jail_ctx {
char *newroot;
} jail_ctx;
+typedef struct jail_con {
+ int client_fd;
+ int jail_fd;
+} jail_con;
+
void jail_init_ctx(jail_ctx **ctx, size_t stacksize);
diff --git a/src/jail_packet.c b/src/jail_packet.c
new file mode 100644
index 0000000..f12a7d2
--- /dev/null
+++ b/src/jail_packet.c
@@ -0,0 +1,527 @@
+#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;
+} 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 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;
+ packet_callback pc;
+} jail_packet_callback;
+
+static ssize_t pkt_header_read(unsigned char *buf, size_t siz);
+static int pkt_handshake(jail_packet_ctx *ctx, jail_packet *pkt,
+ event_buf *write_buf);
+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_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 0;
+ pkt = (jail_packet *) buf;
+
+ if (pkt->type >= SIZEOF(jpc))
+ return -1;
+
+ pkt_size = ntohs(pkt->size);
+ if (pkt_size > PKT_MAXSIZ - sizeof(*pkt))
+ return -1;
+ if (siz < pkt_size)
+ return 0;
+
+ pkt->size = pkt_size;
+ return pkt_size + sizeof(*pkt);
+}
+
+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)
+{
+ 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 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;
+ 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) {
+ /* FIXME: not optimal for preventing buffer bloats */
+ if (event_buf_avail(write_buf) < PKT_MAXSIZ)
+ break;
+
+ 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, pkt, write_buf))
+ {
+ pkt_ctx->pstate = JP_INVALID;
+ break;
+ }
+
+ pkt_off += pkt_siz;
+ read_buf->buf_used -= pkt_siz;
+ }
+
+ if (pkt_off)
+ 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;
+ }
+
+ 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_client_data(event_ctx *ctx, event_buf *in, event_buf *out,
+ jail_packet_ctx *pkt_ctx)
+{
+ 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 rc;
+}
diff --git a/src/jail_packet.h b/src/jail_packet.h
new file mode 100644
index 0000000..274916d
--- /dev/null
+++ b/src/jail_packet.h
@@ -0,0 +1,130 @@
+/*
+ * jail_packet.h
+ * potd is licensed under the BSD license:
+ *
+ * Copyright (c) 2018 Toni Uhlig <matzeton@googlemail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - The names of its contributors may not be used to endorse or promote
+ * products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef POTD_JAIL_PACKET_H
+#define POTD_JAIL_PACKET_H 1
+
+#include <stdint.h>
+
+#include "pevent.h"
+#include "jail.h"
+
+#define INIT_PKTCTX(callback, user_data) \
+ { 0, 0, 1, EMPTY_JAILCON, EMPTY_BUF, JC_CLIENT, JP_NONE, NULL, NULL }
+
+/* Remember: PKT_MAXSIZ should always be less or equal then BUFSIZ/2 - sizeof(pkt) */
+#define PKT_MAXSIZ 1500 /* pkt->size should not exceed this value */
+
+#define PKT_INVALID 0x0 /* should not happen, otherwise error */
+
+/* Client: jail_packet(PKT_HELLO)) + jail_packet_hello
+ * Server: jail_packet(RESP_*)
+ */
+#define PKT_HANDSHAKE 0x1
+
+/* Client: jail_packet(PKT_USER)) + user
+ * Server: -
+ */
+#define PKT_USER 0x2
+
+/* Client: jail_packet(PKT_PASS) + pass
+ * Server: -
+ */
+#define PKT_PASS 0x3
+
+/* Client: jail_packet(PKT_HANDSHAKE_END)
+ * Server: -
+ */
+#define PKT_HANDSHAKE_END 0x4
+
+/* Client: jail_packet(PKT_START)
+ * Server: jail_packet(RESP_*)
+ */
+#define PKT_START 0x5
+
+/* Client: jail_packet(PKT_DATA)
+ * Server: - or jail_packet(PKT_DATA)
+ */
+#define PKT_DATA 0x6
+
+/* Client: -
+ * Server: -
+ */
+#define PKT_RESPOK 0x7
+
+/* Client: -
+ * Server: -
+ */
+#define PKT_RESPERR 0x8
+
+typedef enum jail_packet_state {
+ JP_INVALID = 0, JP_NONE, JP_HANDSHAKE,
+ JP_HANDSHAKE_END, JP_START, JP_DATA
+} jail_packet_state;
+
+typedef enum jail_ctx_type {
+ JC_INVALID = 0, JC_CLIENT /* protocol handler */,
+ JC_SERVER /* jail service */
+} jail_ctx_type;
+
+#define USER_LEN 255
+#define PASS_LEN 255
+
+typedef struct jail_packet_ctx {
+ int is_valid; /* only used during/after jail handshake */
+ int ev_active; /* only used in jail_packet_pkt and pkt callbacks */
+ int ev_readloop; /* only used in jail_client_data */
+ jail_con connection; /* jail/sandbox fd's */
+ event_buf writeback_buf; /* protocol write back buffer */
+
+ jail_ctx_type ctype; /* Client or Server? */
+ jail_packet_state pstate; /* packet state */
+
+ char *user; /* username used by sandbox */
+ char *pass; /* password used by sandbox */
+} jail_packet_ctx;
+
+
+int jail_client_send(jail_packet_ctx *pkt_ctx, unsigned char *buf,
+ size_t siz);
+
+int jail_client_data(event_ctx *ctx, event_buf *in, event_buf *out,
+ jail_packet_ctx *pkt_ctx);
+
+int jail_server_loop(event_ctx *ctx, jail_packet_ctx *pkt_ctx);
+
+event_ctx *jail_client_handshake(int server_fd, jail_packet_ctx *pkt_ctx);
+
+int jail_server_handshake(event_ctx *ctx, jail_packet_ctx *pkt_ctx);
+
+#endif
diff --git a/src/log.h b/src/log.h
index f6094bf..fab6b6e 100644
--- a/src/log.h
+++ b/src/log.h
@@ -46,11 +46,11 @@
log_fmtexerr = fmtexerr_cb; \
}
#define LOG_SET_FUNCS_VA(...) LOG_SET_FUNCS(__VA_ARGS__)
-#define D(fmt, ...) log_fmt(DEBUG, fmt, __VA_ARGS__)
+#define D(fmt, ...) log_fmt(LP_DEBUG, fmt, __VA_ARGS__)
#define N(fmt, ...) log_fmt(NOTICE, fmt, __VA_ARGS__)
#define W(fmt, ...) log_fmt(WARNING, fmt, __VA_ARGS__)
#define E(fmt, ...) log_fmt(ERROR, fmt, __VA_ARGS__)
-#define D2(fmt, ...) log_fmtex(DEBUG, __FILE__, __LINE__, fmt, __VA_ARGS__)
+#define D2(fmt, ...) log_fmtex(LP_DEBUG, __FILE__, __LINE__, fmt, __VA_ARGS__)
#define N2(fmt, ...) log_fmtex(NOTICE, __FILE__, __LINE__, fmt, __VA_ARGS__)
#define W2(fmt, ...) log_fmtex(WARNING, __FILE__, __LINE__, fmt, __VA_ARGS__)
#define E2(fmt, ...) log_fmtex(ERROR, __FILE__, __LINE__, fmt, __VA_ARGS__)
@@ -76,7 +76,7 @@
#define P(fmt, ...) log_fmt(PROTOCOL, fmt, __VA_ARGS__)
typedef enum log_priority {
- DEBUG = 0, PROTOCOL, NOTICE, WARNING, ERROR, CMD
+ LP_DEBUG = 0, PROTOCOL, NOTICE, WARNING, ERROR, CMD
} log_priority;
typedef int (*log_open_cb) (void);
diff --git a/src/log_colored.c b/src/log_colored.c
index 86aeab6..44e462e 100644
--- a/src/log_colored.c
+++ b/src/log_colored.c
@@ -100,7 +100,7 @@ void log_fmt_colored(log_priority prio, const char *fmt, ...)
my_pid = getpid();
curtime_str(time, sizeof time);
switch (prio) {
- case DEBUG:
+ case LP_DEBUG:
LOG(time, "[DEBUG] ", my_pid, out);
break;
case NOTICE:
@@ -140,7 +140,7 @@ void log_fmtex_colored(log_priority prio, const char *srcfile,
my_pid = getpid();
curtime_str(time, sizeof time);
switch (prio) {
- case DEBUG:
+ case LP_DEBUG:
LOGEX(time, "[DEBUG] ", my_pid, srcfile, line, out);
break;
case NOTICE:
@@ -178,7 +178,7 @@ void log_fmtexerr_colored(log_priority prio, const char *srcfile,
my_pid = getpid();
curtime_str(time, sizeof time);
switch (prio) {
- case DEBUG:
+ case LP_DEBUG:
LOGEXERR(time, "[DEBUG] ", my_pid, srcfile, line, out, saved_errno);
break;
case NOTICE:
diff --git a/src/log_file.c b/src/log_file.c
index 5164aed..833b2ad 100644
--- a/src/log_file.c
+++ b/src/log_file.c
@@ -108,7 +108,7 @@ void log_fmt_file(log_priority prio, const char *fmt, ...)
my_pid = getpid();
curtime_str(time, sizeof time);
switch (prio) {
- case DEBUG:
+ case LP_DEBUG:
LOG(flog, time, "[DEBUG] ", my_pid, out);
break;
case NOTICE:
@@ -148,7 +148,7 @@ void log_fmtex_file(log_priority prio, const char *srcfile,
my_pid = getpid();
curtime_str(time, sizeof time);
switch (prio) {
- case DEBUG:
+ case LP_DEBUG:
LOGEX(flog, time, "[DEBUG] ", my_pid, srcfile, line, out);
break;
case NOTICE:
@@ -186,7 +186,7 @@ void log_fmtexerr_file(log_priority prio, const char *srcfile,
my_pid = getpid();
curtime_str(time, sizeof time);
switch (prio) {
- case DEBUG:
+ case LP_DEBUG:
LOGEXERR(flog, time, "[DEBUG] ", my_pid, srcfile, line, out,
saved_errno);
break;
diff --git a/src/main.c b/src/main.c
index 0ea7845..31e4330 100644
--- a/src/main.c
+++ b/src/main.c
@@ -354,7 +354,7 @@ int main(int argc, char *argv[])
if (getopt_used(OPT_LOGLEVEL)) {
value = getopt_str(OPT_LOGLEVEL);
if (!strcasecmp(value, "debug"))
- log_prio = DEBUG;
+ log_prio = LP_DEBUG;
else if (!strcasecmp(value, "protocol"))
log_prio = PROTOCOL;
else if (!strcasecmp(value, "notice"))
diff --git a/src/pevent.c b/src/pevent.c
index edcc16f..bdfed24 100644
--- a/src/pevent.c
+++ b/src/pevent.c
@@ -121,7 +121,7 @@ add_eventbuf(event_ctx *ctx)
if (siz < ctx->buffer_used + 1) {
siz += POTD_EVENTBUF_REALLOCSIZ;
- ctx->buffer_array = realloc(ctx->buffer_array,
+ ctx->buffer_array = (event_buf *) realloc(ctx->buffer_array,
sizeof(*ctx->buffer_array) * siz);
assert(ctx->buffer_array);
@@ -140,7 +140,7 @@ add_eventbuf(event_ctx *ctx)
return &ctx->buffer_array[ctx->buffer_used - 1];
}
-int event_add_sock(event_ctx *ctx, psocket *sock)
+int event_add_sock(event_ctx *ctx, psocket *sock, void *buf_user_data)
{
int s;
struct epoll_event ev = {0,{0}};
@@ -150,6 +150,7 @@ int event_add_sock(event_ctx *ctx, psocket *sock)
eb = add_eventbuf(ctx);
eb->fd = sock->fd;
+ eb->buf_user_data = buf_user_data;
assert(eb->buf_used == 0);
ev.data.ptr = eb;
@@ -161,7 +162,7 @@ int event_add_sock(event_ctx *ctx, psocket *sock)
return 0;
}
-int event_add_fd(event_ctx *ctx, int fd)
+int event_add_fd(event_ctx *ctx, int fd, void *buf_user_data)
{
int s;
struct epoll_event ev = {0,{0}};
@@ -171,6 +172,7 @@ int event_add_fd(event_ctx *ctx, int fd)
eb = add_eventbuf(ctx);
eb->fd = fd;
+ eb->buf_user_data = buf_user_data;
assert(eb->buf_used == 0);
ev.data.ptr = eb;
@@ -224,9 +226,12 @@ int event_loop(event_ctx *ctx, on_event_cb on_event, void *user_data)
ctx->has_error = 1;
} else {
- if (!on_event(ctx, ctx->events[i].data.ptr, user_data) && !ctx->has_error)
+ if (!on_event(ctx, buf->fd,
+ user_data) && !ctx->has_error)
+ {
W2("Event callback failed: [fd: %d , npoll: %d]",
buf->fd, n);
+ }
}
if (!ctx->active || ctx->has_error)
@@ -245,21 +250,21 @@ event_forward_connection(event_ctx *ctx, int dest_fd, on_data_cb on_data,
forward_state rc = CON_OK;
ssize_t siz;
struct epoll_event *ev;
- struct event_buf *ev_buf;
+ struct event_buf *read_buf, write_buf = WRITE_BUF(dest_fd);
+ assert(dest_fd >= 0);
assert(ctx->current_event >= 0 &&
ctx->current_event < POTD_MAXEVENTS);
ev = &ctx->events[ctx->current_event];
- ev_buf = (event_buf *) ev->data.ptr;
+ read_buf = (event_buf *) ev->data.ptr;
- while (1) {
+ while (data_avail && ctx->active && !ctx->has_error) {
saved_errno = 0;
siz = -1;
if (ev->events & EPOLLIN) {
errno = 0;
- ev_buf->buf_used = 0;
- siz = read(ev_buf->fd, ev_buf->buf, sizeof(ev_buf->buf));
+ siz = event_buf_read(read_buf);
saved_errno = errno;
} else break;
if (saved_errno == EAGAIN)
@@ -267,16 +272,16 @@ event_forward_connection(event_ctx *ctx, int dest_fd, on_data_cb on_data,
switch (siz) {
case -1:
- E_STRERR("Client read from fd %d", ev_buf->fd);
+ E_STRERR("Client read from fd %d", read_buf->fd);
ctx->has_error = 1;
rc = CON_IN_ERROR;
break;
case 0:
+ ctx->active = 0;
rc = CON_IN_TERMINATED;
break;
default:
- D2("Read %zu bytes from fd %d", siz, ev_buf->fd);
- ev_buf->buf_used = siz;
+ D2("Read %zu bytes from fd %d", siz, read_buf->fd);
break;
}
@@ -284,35 +289,47 @@ event_forward_connection(event_ctx *ctx, int dest_fd, on_data_cb on_data,
break;
if (on_data &&
- on_data(ctx, ev_buf->fd, dest_fd, ev_buf->buf, ev_buf->buf_used,
- user_data))
+ on_data(ctx, read_buf, &write_buf, user_data))
{
W2("On data callback failed, not forwarding from %d to %d",
- ev_buf->fd, dest_fd);
+ read_buf->fd, dest_fd);
continue;
+ } else if (!on_data) {
+ if (event_buf_fill(&write_buf, read_buf->buf,
+ read_buf->buf_used))
+ {
+ W2("Data copy failed, not forwarding from %d to %d",
+ read_buf->fd, dest_fd);
+ continue;
+ } else {
+ event_buf_discardall(read_buf);
+ }
}
- errno = 0;
- siz = write(dest_fd, ev_buf->buf, ev_buf->buf_used);
- saved_errno = errno;
- // FIXME: Add EPOLLOUT to epoll fd and wait until the buffer is drained
-/*
- if (saved_errno == EAGAIN)
- break;
-*/
-
- switch (siz) {
- case -1:
- ctx->has_error = 1;
- rc = CON_OUT_ERROR;
- break;
- case 0:
- rc = CON_OUT_TERMINATED;
- break;
- default:
- D2("Written %zu bytes from fd %d to fd %d",
- siz, ev_buf->fd, dest_fd);
- break;
+ if (write_buf.buf_used) {
+ errno = 0;
+ siz = event_buf_drain(&write_buf);
+
+ switch (siz) {
+ case -1:
+ ctx->has_error = 1;
+ rc = CON_OUT_ERROR;
+ break;
+ case 0:
+ ctx->active = 0;
+ rc = CON_OUT_TERMINATED;
+ break;
+ default:
+ if (write_buf.buf_used) {
+ W2("Written only %zd bytes (remaining %zu bytes) "
+ "from %d to %d", siz, write_buf.buf_used,
+ read_buf->fd, write_buf.fd);
+ } else {
+ D2("Written %zd bytes from fd %d to fd %d",
+ siz, read_buf->fd, dest_fd);
+ }
+ break;
+ }
}
if (rc != CON_OK)
@@ -321,10 +338,43 @@ event_forward_connection(event_ctx *ctx, int dest_fd, on_data_cb on_data,
D2("Connection state: %d", rc);
if (rc != CON_OK) {
- shutdown(ev_buf->fd, SHUT_RDWR);
+ shutdown(read_buf->fd, SHUT_RDWR);
shutdown(dest_fd, SHUT_RDWR);
}
errno = saved_errno;
return rc;
}
+
+int event_buf_fill(event_buf *buf, char *data, size_t size)
+{
+ if (size > event_buf_avail(buf) &&
+ event_buf_drain(buf) < 0)
+ {
+ return 1;
+ }
+ memcpy(buf->buf + buf->buf_used, data, size);
+ buf->buf_used += size;
+
+ return 0;
+}
+
+ssize_t event_buf_drain(event_buf *buf)
+{
+ ssize_t written;
+
+ if (!buf->buf_used || buf->fd < 0)
+ return 0;
+
+ written = write(buf->fd, buf->buf, buf->buf_used);
+ switch (written) {
+ case 0:
+ case -1:
+ return written;
+ default:
+ break;
+ }
+
+ event_buf_discard(buf, written);
+ return written;
+}
diff --git a/src/pevent.h b/src/pevent.h
index 0f387aa..9384405 100644
--- a/src/pevent.h
+++ b/src/pevent.h
@@ -34,13 +34,18 @@
#ifndef POTD_EVENT_H
#define POTD_EVENT_H 1
+#include <stdio.h>
+#include <unistd.h>
#include <sys/epoll.h>
+#include <string.h>
#include "socket.h"
#define POTD_MAXFD 32
#define POTD_MAXEVENTS 64
#define POTD_EVENTBUF_REALLOCSIZ 5
+#define WRITE_BUF(fd) { fd, {0}, 0, NULL }
+#define EMPTY_BUF { -1, {0}, 0, NULL }
typedef enum forward_state {
CON_OK, CON_IN_TERMINATED, CON_OUT_TERMINATED,
@@ -52,6 +57,8 @@ typedef struct event_buf {
char buf[BUFSIZ];
size_t buf_used;
+
+ void *buf_user_data;
} event_buf;
typedef struct event_ctx {
@@ -67,10 +74,10 @@ typedef struct event_ctx {
size_t buffer_used;
} event_ctx;
-typedef int (*on_event_cb) (event_ctx *ev_ctx, event_buf *buf,
+typedef int (*on_event_cb) (event_ctx *ev_ctx, int src_fd,
void *user_data);
-typedef int (*on_data_cb) (event_ctx *ev_ctx, int src_fd, int dst_fd,
- char *buf, size_t siz, void *user_data);
+typedef int (*on_data_cb) (event_ctx *ev_ctx, event_buf *read_buf,
+ event_buf *write_buf, void *user_data);
void event_init(event_ctx **ctx);
@@ -81,9 +88,9 @@ int event_setup(event_ctx *ctx);
int event_validate_ctx(event_ctx *ctx);
-int event_add_sock(event_ctx *ctx, psocket *sock);
+int event_add_sock(event_ctx *ctx, psocket *sock, void *buf_user_data);
-int event_add_fd(event_ctx *ctx, int fd);
+int event_add_fd(event_ctx *ctx, int fd, void *buf_user_data);
int event_loop(event_ctx *ctx, on_event_cb on_event, void *user_data);
@@ -91,4 +98,45 @@ forward_state
event_forward_connection(event_ctx *ctx, int dest_fd, on_data_cb on_data,
void *user_data);
+int event_buf_fill(event_buf *buf, char *data, size_t size);
+
+ssize_t event_buf_drain(event_buf *write_buf);
+
+static inline size_t event_buf_avail(event_buf *buf)
+{
+ return sizeof(buf->buf) - buf->buf_used;
+}
+
+static inline ssize_t event_buf_read(event_buf *read_buf)
+{
+ ssize_t siz;
+
+ siz = read(read_buf->fd, read_buf->buf + read_buf->buf_used,
+ event_buf_avail(read_buf));
+ if (siz > 0)
+ read_buf->buf_used += siz;
+ return siz;
+}
+
+static inline void event_buf_discard(event_buf *input, size_t siz)
+{
+ if (siz <= input->buf_used) {
+ memmove(input->buf + siz, input->buf, input->buf_used - siz);
+ input->buf_used -= siz;
+ }
+}
+
+static inline void event_buf_discardall(event_buf *input)
+{
+ event_buf_discard(input, input->buf_used);
+}
+
+static inline int event_buf_dup(event_buf *input, event_buf *output)
+{
+ int rc = event_buf_fill(output, input->buf, input->buf_used);
+ if (!rc)
+ event_buf_discardall(input);
+ return rc;
+}
+
#endif
diff --git a/src/protocol_ssh.c b/src/protocol_ssh.c
index 0af4986..9d2be46 100644
--- a/src/protocol_ssh.c
+++ b/src/protocol_ssh.c
@@ -53,6 +53,7 @@
#include "protocol_ssh.h"
#include "protocol.h"
+#include "jail_packet.h"
#ifdef HAVE_SECCOMP
#include "pseccomp.h"
#endif
@@ -65,8 +66,6 @@
LIBSSH_VERSION_MICRO < 3
#pragma message "Unsupported libssh version < 0.7.3"
#endif
-#define USER_LEN LOGIN_NAME_MAX
-#define PASS_LEN 80
#define CACHE_MAX 32
#define CACHE_TIME (60 * 20) /* max cache time 20 minutes */
#define LOGIN_SUCCESS_PROB ((double)1/4) /* successful login probability */
@@ -86,6 +85,15 @@ typedef struct ssh_client {
forward_ctx dst;
} ssh_client;
+typedef struct ssh_userdata {
+ jail_packet_ctx *pkt_ctx;
+ ssh_client *client;
+ event_ctx *ev_ctx;
+ event_buf proto_read;
+ event_buf proto_fd;
+ event_buf proto_chan;
+} ssh_userdata;
+
struct protocol_cbs potd_ssh_callbacks = {
.on_listen = ssh_on_listen,
.on_shutdown = ssh_on_shutdown
@@ -108,10 +116,11 @@ static void ssh_log_cb(int priority, const char *function, const char *buffer,
void *userdata);
static void ssh_mainloop(ssh_data *arg)
__attribute__((noreturn));
-static int authenticate(ssh_session session, ssh_login_cache *cache);
+static int authenticate(ssh_session session, ssh_login_cache *cache,
+ jail_packet_ctx *pkt_ctx);
static int auth_password(const char *user, const char *pass,
ssh_login_cache *cache);
-static int client_mainloop(ssh_client *arg);
+static int client_mainloop(ssh_client *arg, jail_packet_ctx *ctx);
static int copy_fd_to_chan(socket_t fd, int revents, void *userdata);
static int copy_chan_to_fd(ssh_session session, ssh_channel channel, void *data,
uint32_t len, int is_stderr, void *userdata);
@@ -432,6 +441,7 @@ static void ssh_mainloop(ssh_data *arg)
{
pthread_mutexattr_t shared;
ssh_login_cache *cache = NULL;
+ jail_packet_ctx pkt_ctx = INIT_PKTCTX(NULL,NULL);
size_t i;
int s, auth = 0, shell = 0, is_child;
ssh_session ses;
@@ -488,7 +498,7 @@ static void ssh_mainloop(ssh_data *arg)
}
/* proceed to authentication */
- auth = authenticate(ses, cache);
+ auth = authenticate(ses, cache, &pkt_ctx);
if (!auth) {
W("SSH authentication error: %s", ssh_get_error(ses));
goto failed;
@@ -551,7 +561,7 @@ static void ssh_mainloop(ssh_data *arg)
data.chan = chan;
data.dst = arg->ctx->dst;
- if (client_mainloop(&data))
+ if (client_mainloop(&data, &pkt_ctx))
W2("Client mainloop for fd %d failed",
ssh_bind_get_fd(arg->sshbind));
@@ -563,7 +573,8 @@ failed:
}
}
-static int authenticate(ssh_session session, ssh_login_cache *cache)
+static int authenticate(ssh_session session, ssh_login_cache *cache,
+ jail_packet_ctx *pkt_ctx)
{
ssh_message message;
ssh_key pubkey;
@@ -588,6 +599,8 @@ static int authenticate(ssh_session session, ssh_login_cache *cache)
if (auth_password(ssh_message_auth_user(message),
ssh_message_auth_password(message), cache))
{
+ pkt_ctx->user = strdup(ssh_message_auth_user(message));
+ pkt_ctx->pass = strdup(ssh_message_auth_password(message));
ssh_message_auth_reply_success(message,0);
ssh_message_free(message);
return 1;
@@ -737,13 +750,16 @@ static int auth_password(const char *user, const char *pass,
return got_auth;
}
-static int client_mainloop(ssh_client *data)
+static int client_mainloop(ssh_client *data, jail_packet_ctx *pkt_ctx)
{
ssh_channel chan = data->chan;
ssh_session session = ssh_channel_get_session(chan);
+ ssh_userdata userdata = { pkt_ctx, data, NULL,
+ EMPTY_BUF, EMPTY_BUF, EMPTY_BUF };
ssh_event event;
short events;
forward_ctx *ctx = &data->dst;
+ struct event_ctx *ev_ctx;
if (fwd_connect_sock(ctx, NULL)) {
E_STRERR("Connection to %s:%s",
@@ -752,7 +768,25 @@ static int client_mainloop(ssh_client *data)
return 1;
}
- ssh_channel_cb.userdata = &ctx->sock.fd;
+ ev_ctx = jail_client_handshake(ctx->sock.fd, pkt_ctx);
+ if (!ev_ctx) {
+ ssh_channel_close(chan);
+ return 1;
+ }
+
+ pkt_ctx->connection.client_fd = pkt_ctx->writeback_buf.fd =
+ userdata.proto_read.fd = ctx->sock.fd;
+ if (jail_client_data(ev_ctx, &userdata.proto_read,
+ &userdata.proto_chan, pkt_ctx))
+ {
+ ssh_channel_close(chan);
+ event_free(&ev_ctx);
+ return 1;
+ }
+ pkt_ctx->ev_readloop = 0;
+
+ userdata.ev_ctx = ev_ctx;
+ ssh_channel_cb.userdata = &userdata;
ssh_callbacks_init(&ssh_channel_cb);
ssh_set_channel_callbacks(chan, &ssh_channel_cb);
@@ -761,14 +795,19 @@ static int client_mainloop(ssh_client *data)
if (event == NULL) {
E2("%s", "Couldn't get a event");
+ event_free(&ev_ctx);
return 1;
}
- if (ssh_event_add_fd(event, ctx->sock.fd, events, copy_fd_to_chan, chan) != SSH_OK) {
+ if (ssh_event_add_fd(event, ctx->sock.fd, events, copy_fd_to_chan,
+ &userdata) != SSH_OK)
+ {
E2("Couldn't add fd %d to the event queue", ctx->sock.fd);
+ event_free(&ev_ctx);
return 1;
}
if (ssh_event_add_session(event, session) != SSH_OK) {
E2("%s", "Couldn't add the session to the event");
+ event_free(&ev_ctx);
return 1;
}
@@ -780,26 +819,34 @@ static int client_mainloop(ssh_client *data)
ssh_event_remove_fd(event, ctx->sock.fd);
ssh_event_remove_session(event, session);
ssh_event_free(event);
+ event_free(&ev_ctx);
+
return 0;
}
static int copy_fd_to_chan(socket_t fd, int revents, void *userdata)
{
- ssh_channel chan = (ssh_channel)userdata;
- char buf[BUFSIZ];
- int sz = 0;
+ ssh_userdata *sudata = (ssh_userdata *) userdata;
+ ssh_channel chan = sudata->client->chan;
+ ssize_t sz = 0;
+ int written;
- if(!chan) {
+ if (!chan) {
close(fd);
return -1;
}
- if(revents & POLLIN) {
- sz = read(fd, buf, BUFSIZ);
- if(sz > 0) {
- ssh_channel_write(chan, buf, sz);
+ if (revents & POLLIN) {
+ sz = event_buf_read(&sudata->proto_read);
+ if (sz > 0 &&
+ !jail_client_data(sudata->ev_ctx, &sudata->proto_read,
+ &sudata->proto_chan, sudata->pkt_ctx))
+ {
+ written = ssh_channel_write(chan, sudata->proto_chan.buf,
+ sudata->proto_chan.buf_used);
+ event_buf_discard(&sudata->proto_chan, written);
}
}
- if(revents & POLLHUP || sz <= 0) {
+ if (revents & POLLHUP || sz <= 0) {
ssh_channel_close(chan);
sz = -1;
}
@@ -814,23 +861,22 @@ static int copy_chan_to_fd(ssh_session session,
int is_stderr,
void *userdata)
{
- int fd = *(int*) userdata;
- int sz;
+ ssh_userdata *sudata = (ssh_userdata *) userdata;
(void) session;
(void) is_stderr;
- sz = write(fd, data, len);
- if (sz <= 0)
+ if (jail_client_send(sudata->pkt_ctx, data, len))
ssh_channel_close(channel);
- return sz;
+ return len;
}
static void chan_close(ssh_session session, ssh_channel channel,
void *userdata)
{
- int fd = *(int*) userdata;
+ ssh_userdata *sudata = (ssh_userdata *) userdata;
+ int fd = sudata->proto_read.fd;
(void) session;
(void) channel;
diff --git a/src/redirector.c b/src/redirector.c
index e6ddda5..c2be5bc 100644
--- a/src/redirector.c
+++ b/src/redirector.c
@@ -76,11 +76,11 @@ fwd_state_string(const forward_state c_state, const client_thread *args,
static int
redirector_mainloop(event_ctx **ev_ctx, redirector_ctx *rdr_ctx[], size_t siz)
__attribute__((noreturn));
-static int redirector_accept_client(event_ctx *ev_ctx, event_buf *buf,
+static int redirector_accept_client(event_ctx *ev_ctx, int src_fd,
void *user_data);
static void *
client_mainloop(void *arg);
-static int client_io(event_ctx *ev_ctx, event_buf *buf, void *user_data);
+static int client_io(event_ctx *ev_ctx, int src_fd, void *user_data);
static pthread_attr_t pattr;
@@ -177,7 +177,7 @@ int redirector_setup_event(redirector_ctx *rdr_ctx[], size_t siz, event_ctx **ev
return 1;
for (size_t i = 0; i < siz; ++i) {
- if (event_add_sock(*ev_ctx, &rdr_ctx[i]->sock)) {
+ if (event_add_sock(*ev_ctx, &rdr_ctx[i]->sock, NULL)) {
return 1;
}
@@ -294,25 +294,24 @@ static int redirector_mainloop(event_ctx **ev_ctx, redirector_ctx *rdr_ctx[], si
exit(rc);
}
-static int redirector_accept_client(event_ctx *ev_ctx, event_buf *buf,
+static int redirector_accept_client(event_ctx *ev_ctx, int src_fd,
void *user_data)
{
size_t i;
double d;
- int s, fd;
+ int s;
time_t t;
server_event *ev_srv;
client_thread *args;
redirector_ctx *rdr_ctx;
(void) ev_ctx;
- assert(ev_ctx && buf && user_data);
+ assert(ev_ctx && user_data);
ev_srv = (server_event *) user_data;
- fd = buf->fd;
for (i = 0; i < ev_srv->siz; ++i) {
rdr_ctx = ev_srv->rdr_ctx[i];
- if (rdr_ctx->sock.fd == fd) {
+ if (rdr_ctx->sock.fd == src_fd) {
args = (client_thread *) calloc(1, sizeof(*args));
assert(args);
@@ -412,7 +411,7 @@ client_mainloop(void *arg)
args->rdr_ctx->fwd_ctx.host_buf,
args->rdr_ctx->fwd_ctx.service_buf, fwd.fd);
- if (event_add_sock(ev_ctx, &fwd)) {
+ if (event_add_sock(ev_ctx, &fwd, NULL)) {
E_STRERR("Forward event context add to %s:%s forward fd %d",
args->rdr_ctx->fwd_ctx.host_buf,
args->rdr_ctx->fwd_ctx.service_buf, fwd.fd);
@@ -430,7 +429,7 @@ client_mainloop(void *arg)
args->rdr_ctx->fwd_ctx.service_buf, fwd.fd);
goto finish;
}
- if (event_add_sock(ev_ctx, &args->client_sock)) {
+ if (event_add_sock(ev_ctx, &args->client_sock, NULL)) {
E_STRERR("Forward event context add to %s:%s forward fd %d",
args->rdr_ctx->fwd_ctx.host_buf,
args->rdr_ctx->fwd_ctx.service_buf, fwd.fd);
@@ -455,9 +454,9 @@ finish:
}
static int
-client_io(event_ctx *ev_ctx, event_buf *buf, void *user_data)
+client_io(event_ctx *ev_ctx, int src_fd, void *user_data)
{
- int dest_fd, src_fd = buf->fd, saved_errno = 0;
+ int dest_fd;
client_event *ev_cli = (client_event *) user_data;
const psocket *client_sock = &ev_cli->client_args->client_sock;
forward_state fwd_state;