diff options
author | lns <matzeton@googlemail.com> | 2019-02-04 01:54:57 +0100 |
---|---|---|
committer | lns <matzeton@googlemail.com> | 2019-02-04 01:54:57 +0100 |
commit | 4f74e5f5c4ba15f1ea02e7425c18cbd06642ce2e (patch) | |
tree | 18ebe7301a7f4b1c25f516b71722d7e49e87c182 | |
parent | e21e46d83d3b4292ad6de84b26902246ee398bcb (diff) | |
parent | f68988980d72d837f49451068b98bc96d1a548fc (diff) |
Merge branch 'feature/jail_packet'
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/jail.c | 114 | ||||
-rw-r--r-- | src/jail.h | 7 | ||||
-rw-r--r-- | src/jail_packet.c | 527 | ||||
-rw-r--r-- | src/jail_packet.h | 130 | ||||
-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 | 124 | ||||
-rw-r--r-- | src/pevent.h | 58 | ||||
-rw-r--r-- | src/protocol_ssh.c | 96 | ||||
-rw-r--r-- | src/redirector.c | 23 |
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 @@ -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 @@ -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 @@ -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; @@ -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; |