diff options
Diffstat (limited to 'ssh/bpp-bare.c')
-rw-r--r-- | ssh/bpp-bare.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/ssh/bpp-bare.c b/ssh/bpp-bare.c new file mode 100644 index 00000000..f1a889aa --- /dev/null +++ b/ssh/bpp-bare.c @@ -0,0 +1,206 @@ +/* + * Trivial binary packet protocol for the 'bare' ssh-connection + * protocol used in PuTTY's SSH-2 connection sharing system. + */ + +#include <assert.h> + +#include "putty.h" +#include "ssh.h" +#include "bpp.h" +#include "sshcr.h" + +struct ssh2_bare_bpp_state { + int crState; + long packetlen, maxlen; + unsigned char *data; + unsigned long incoming_sequence, outgoing_sequence; + PktIn *pktin; + + BinaryPacketProtocol bpp; +}; + +static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp); +static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp); +static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp); +static PktOut *ssh2_bare_bpp_new_pktout(int type); + +static const BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = { + .free = ssh2_bare_bpp_free, + .handle_input = ssh2_bare_bpp_handle_input, + .handle_output = ssh2_bare_bpp_handle_output, + .new_pktout = ssh2_bare_bpp_new_pktout, + .queue_disconnect = ssh2_bpp_queue_disconnect, /* in common.c */ + + /* packet size limit, per protocol spec in sharing.c comment */ + .packet_size_limit = 0x4000, +}; + +BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx) +{ + struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state); + memset(s, 0, sizeof(*s)); + s->bpp.vt = &ssh2_bare_bpp_vtable; + s->bpp.logctx = logctx; + ssh_bpp_common_setup(&s->bpp); + return &s->bpp; +} + +static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp) +{ + struct ssh2_bare_bpp_state *s = + container_of(bpp, struct ssh2_bare_bpp_state, bpp); + sfree(s->pktin); + sfree(s); +} + +#define BPP_READ(ptr, len) do \ + { \ + bool success; \ + crMaybeWaitUntilV((success = bufchain_try_fetch_consume( \ + s->bpp.in_raw, ptr, len)) || \ + s->bpp.input_eof); \ + if (!success) \ + goto eof; \ + ssh_check_frozen(s->bpp.ssh); \ + } while (0) + +static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp) +{ + struct ssh2_bare_bpp_state *s = + container_of(bpp, struct ssh2_bare_bpp_state, bpp); + + crBegin(s->crState); + + while (1) { + /* Read the length field. */ + { + unsigned char lenbuf[4]; + BPP_READ(lenbuf, 4); + s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf)); + } + + if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) { + ssh_sw_abort(s->bpp.ssh, "Invalid packet length received"); + crStopV; + } + + /* + * Allocate the packet to return, now we know its length. + */ + s->pktin = snew_plus(PktIn, s->packetlen); + s->pktin->qnode.prev = s->pktin->qnode.next = NULL; + s->pktin->qnode.on_free_queue = false; + s->maxlen = 0; + s->data = snew_plus_get_aux(s->pktin); + + s->pktin->sequence = s->incoming_sequence++; + + /* + * Read the remainder of the packet. + */ + BPP_READ(s->data, s->packetlen); + + /* + * The data we just read is precisely the initial type byte + * followed by the packet payload. + */ + s->pktin->type = s->data[0]; + s->data++; + s->packetlen--; + BinarySource_INIT(s->pktin, s->data, s->packetlen); + + if (s->pktin->type == SSH2_MSG_EXT_INFO) { + /* + * Mild layer violation: EXT_INFO is not permitted in the + * bare ssh-connection protocol. Faulting it here means + * that ssh2_common_filter_queue doesn't receive it in the + * first place unless it's legal to have sent it. + */ + ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO " + "in bare connection protocol"); + return; + } + + /* + * Log incoming packet, possibly omitting sensitive fields. + */ + if (s->bpp.logctx) { + logblank_t blanks[MAX_BLANKS]; + int nblanks = ssh2_censor_packet( + s->bpp.pls, s->pktin->type, false, + make_ptrlen(s->data, s->packetlen), blanks); + log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type, + ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx, + s->pktin->type), + get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks, + &s->pktin->sequence, 0, NULL); + } + + if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) { + sfree(s->pktin); + s->pktin = NULL; + continue; + } + + s->pktin->qnode.formal_size = get_avail(s->pktin); + pq_push(&s->bpp.in_pq, s->pktin); + s->pktin = NULL; + } + + eof: + if (!s->bpp.expect_close) { + ssh_remote_error(s->bpp.ssh, + "Remote side unexpectedly closed network connection"); + } else { + ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection"); + } + return; /* avoid touching s now it's been freed */ + + crFinishV; +} + +static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type) +{ + PktOut *pkt = ssh_new_packet(); + pkt->length = 4; /* space for packet length */ + pkt->type = pkt_type; + put_byte(pkt, pkt_type); + return pkt; +} + +static void ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state *s, + PktOut *pkt) +{ + if (s->bpp.logctx) { + ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5); + logblank_t blanks[MAX_BLANKS]; + int nblanks = ssh2_censor_packet( + s->bpp.pls, pkt->type, true, pktdata, blanks); + log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type, + ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx, + pkt->type), + pktdata.ptr, pktdata.len, nblanks, blanks, + &s->outgoing_sequence, + pkt->downstream_id, pkt->additional_log_text); + } + + s->outgoing_sequence++; /* only for diagnostics, really */ + + PUT_32BIT_MSB_FIRST(pkt->data, pkt->length - 4); + bufchain_add(s->bpp.out_raw, pkt->data, pkt->length); +} + +static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp) +{ + struct ssh2_bare_bpp_state *s = + container_of(bpp, struct ssh2_bare_bpp_state, bpp); + PktOut *pkt; + + while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { + ssh2_bare_bpp_format_packet(s, pkt); + ssh_free_pktout(pkt); + } + + ssh_sendbuffer_changed(bpp->ssh); +} |