diff options
author | lns <matzeton@googlemail.com> | 2018-08-26 16:01:18 +0200 |
---|---|---|
committer | lns <matzeton@googlemail.com> | 2019-02-04 01:52:06 +0100 |
commit | f68988980d72d837f49451068b98bc96d1a548fc (patch) | |
tree | 557f91ecac2d94afa737c25b5cb81dc1eaebfc5e /src | |
parent | e7b96666c03f1536fa89149204b363951523560e (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')
-rw-r--r-- | src/jail.c | 106 | ||||
-rw-r--r-- | src/jail.h | 7 | ||||
-rw-r--r-- | src/jail_packet.c | 485 | ||||
-rw-r--r-- | src/jail_packet.h | 88 | ||||
-rw-r--r-- | src/log.h | 6 | ||||
-rw-r--r-- | src/log_colored.c | 6 | ||||
-rw-r--r-- | src/log_file.c | 6 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/pevent.c | 96 | ||||
-rw-r--r-- | src/pevent.h | 53 | ||||
-rw-r--r-- | src/protocol_ssh.c | 96 | ||||
-rw-r--r-- | src/redirector.c | 17 |
12 files changed, 797 insertions, 171 deletions
@@ -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) @@ -233,24 +229,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; @@ -430,6 +425,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" @@ -448,22 +458,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); @@ -501,13 +495,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)) { @@ -549,20 +544,29 @@ static int jail_socket_tty(prisoner_process *ctx, int tty_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; @@ -570,10 +574,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; @@ -597,18 +601,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); @@ -618,12 +621,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, @@ -633,5 +638,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 @@ -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 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; } diff --git a/src/jail_packet.h b/src/jail_packet.h index 55016d5..274916d 100644 --- a/src/jail_packet.h +++ b/src/jail_packet.h @@ -37,24 +37,94 @@ #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 */ -#define PKT_HELLO 0x1 /* request(PKT_HELLO) -> response(PKT_HELLO) */ -#define PKT_USER 0x2 /* request(PKT_USER) -> response(PKT_USER) */ -#define PKT_PASS 0x3 /* request(PKT_PASS) -> response(PKT_PASS) */ + +/* 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_NONE, JP_INVALID, JP_HELLO + 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_server; - jail_packet_state pstate; - on_data_cb on_data; - void *user_data; + 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_packet_loop(event_ctx *ctx, jail_packet_ctx *pkt_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 @@ -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; @@ -349,7 +349,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 452b621..64b2d0b 100644 --- a/src/pevent.c +++ b/src/pevent.c @@ -226,7 +226,7 @@ int event_loop(event_ctx *ctx, on_event_cb on_event, void *user_data) ctx->has_error = 1; } else { - if (!on_event(ctx, (event_buf *) ctx->events[i].data.ptr, + if (!on_event(ctx, buf->fd, user_data) && !ctx->has_error) { W2("Event callback failed: [fd: %d , npoll: %d]", @@ -251,21 +251,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 (data_avail) { + 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) @@ -273,16 +273,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; } @@ -290,29 +290,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); - - 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,18 +339,19 @@ 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); } return rc; } -int event_buf_fill(event_buf *buf, unsigned char *data, size_t size) +int event_buf_fill(event_buf *buf, char *data, size_t size) { - if (size > buf->buf_used && event_buf_drain(buf) < 0) - return 1; - if (size > sizeof(buf->buf)) + 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; @@ -343,13 +362,18 @@ ssize_t event_buf_drain(event_buf *buf) { ssize_t written; - if (!buf->buf_used) + if (!buf->buf_used || buf->fd < 0) return 0; written = write(buf->fd, buf->buf, buf->buf_used); - if (written < 0) - return -1; - buf->buf_used -= written; + 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 714a7f8..9384405 100644 --- a/src/pevent.h +++ b/src/pevent.h @@ -35,13 +35,17 @@ #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, @@ -70,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); @@ -94,8 +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, unsigned char *data, size_t size); - -ssize_t event_buf_drain(event_buf *buf); +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 da7bbd1..3ba6bd6 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; @@ -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); @@ -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; + 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; |