diff options
Diffstat (limited to 'ssh1bpp.c')
-rw-r--r-- | ssh1bpp.c | 387 |
1 files changed, 0 insertions, 387 deletions
diff --git a/ssh1bpp.c b/ssh1bpp.c deleted file mode 100644 index 46eccbeb..00000000 --- a/ssh1bpp.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Binary packet protocol for SSH-1. - */ - -#include <assert.h> - -#include "putty.h" -#include "ssh.h" -#include "sshbpp.h" -#include "sshcr.h" - -struct ssh1_bpp_state { - int crState; - long len, pad, biglen, length, maxlen; - unsigned char *data; - uint32_t realcrc, gotcrc; - int chunk; - PktIn *pktin; - - ssh_cipher *cipher_in, *cipher_out; - - struct crcda_ctx *crcda_ctx; - uint8_t iv[8]; /* for crcda */ - - bool pending_compression_request; - ssh_compressor *compctx; - ssh_decompressor *decompctx; - - BinaryPacketProtocol bpp; -}; - -static void ssh1_bpp_free(BinaryPacketProtocol *bpp); -static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp); -static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp); -static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp, - const char *msg, int category); -static PktOut *ssh1_bpp_new_pktout(int type); - -static const BinaryPacketProtocolVtable ssh1_bpp_vtable = { - .free = ssh1_bpp_free, - .handle_input = ssh1_bpp_handle_input, - .handle_output = ssh1_bpp_handle_output, - .new_pktout = ssh1_bpp_new_pktout, - .queue_disconnect = ssh1_bpp_queue_disconnect, - .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ -}; - -BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx) -{ - struct ssh1_bpp_state *s = snew(struct ssh1_bpp_state); - memset(s, 0, sizeof(*s)); - s->bpp.vt = &ssh1_bpp_vtable; - s->bpp.logctx = logctx; - ssh_bpp_common_setup(&s->bpp); - return &s->bpp; -} - -static void ssh1_bpp_free(BinaryPacketProtocol *bpp) -{ - struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp); - if (s->cipher_in) - ssh_cipher_free(s->cipher_in); - if (s->cipher_out) - ssh_cipher_free(s->cipher_out); - if (s->compctx) - ssh_compressor_free(s->compctx); - if (s->decompctx) - ssh_decompressor_free(s->decompctx); - if (s->crcda_ctx) - crcda_free_context(s->crcda_ctx); - sfree(s->pktin); - sfree(s); -} - -void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, - const ssh_cipheralg *cipher, - const void *session_key) -{ - struct ssh1_bpp_state *s; - assert(bpp->vt == &ssh1_bpp_vtable); - s = container_of(bpp, struct ssh1_bpp_state, bpp); - - assert(!s->cipher_in); - assert(!s->cipher_out); - - if (cipher) { - s->cipher_in = ssh_cipher_new(cipher); - s->cipher_out = ssh_cipher_new(cipher); - ssh_cipher_setkey(s->cipher_in, session_key); - ssh_cipher_setkey(s->cipher_out, session_key); - - assert(!s->crcda_ctx); - s->crcda_ctx = crcda_make_context(); - - bpp_logevent("Initialised %s encryption", cipher->text_name); - - memset(s->iv, 0, sizeof(s->iv)); - - assert(cipher->blksize <= sizeof(s->iv)); - ssh_cipher_setiv(s->cipher_in, s->iv); - ssh_cipher_setiv(s->cipher_out, s->iv); - } -} - -void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp) -{ - struct ssh1_bpp_state *s; - assert(bpp->vt == &ssh1_bpp_vtable); - s = container_of(bpp, struct ssh1_bpp_state, bpp); - - assert(!s->compctx); - assert(!s->decompctx); - - s->compctx = ssh_compressor_new(&ssh_zlib); - s->decompctx = ssh_decompressor_new(&ssh_zlib); - - bpp_logevent("Started zlib (RFC1950) compression"); -} - -#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 ssh1_bpp_handle_input(BinaryPacketProtocol *bpp) -{ - struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp); - - crBegin(s->crState); - - while (1) { - s->maxlen = 0; - s->length = 0; - - { - unsigned char lenbuf[4]; - BPP_READ(lenbuf, 4); - s->len = toint(GET_32BIT_MSB_FIRST(lenbuf)); - } - - if (s->len < 5 || s->len > 262144) { /* SSH1.5-mandated max size */ - ssh_sw_abort(s->bpp.ssh, - "Out-of-range packet length from remote suggests" - " data stream corruption"); - crStopV; - } - - s->pad = 8 - (s->len % 8); - s->biglen = s->len + s->pad; - s->length = s->len - 5; - - /* - * Allocate the packet to return, now we know its length. - */ - s->pktin = snew_plus(PktIn, s->biglen); - s->pktin->qnode.prev = s->pktin->qnode.next = NULL; - s->pktin->qnode.on_free_queue = false; - s->pktin->type = 0; - - s->maxlen = s->biglen; - s->data = snew_plus_get_aux(s->pktin); - - BPP_READ(s->data, s->biglen); - - if (s->cipher_in && detect_attack(s->crcda_ctx, - s->data, s->biglen, s->iv)) { - ssh_sw_abort(s->bpp.ssh, - "Network attack (CRC compensation) detected!"); - crStopV; - } - /* Save the last cipher block, to be passed to the next call - * to detect_attack */ - assert(s->biglen >= 8); - memcpy(s->iv, s->data + s->biglen - 8, sizeof(s->iv)); - - if (s->cipher_in) - ssh_cipher_decrypt(s->cipher_in, s->data, s->biglen); - - s->realcrc = crc32_ssh1(make_ptrlen(s->data, s->biglen - 4)); - s->gotcrc = GET_32BIT_MSB_FIRST(s->data + s->biglen - 4); - if (s->gotcrc != s->realcrc) { - ssh_sw_abort(s->bpp.ssh, "Incorrect CRC received on packet"); - crStopV; - } - - if (s->decompctx) { - unsigned char *decompblk; - int decomplen; - if (!ssh_decompressor_decompress( - s->decompctx, s->data + s->pad, s->length + 1, - &decompblk, &decomplen)) { - ssh_sw_abort(s->bpp.ssh, - "Zlib decompression encountered invalid data"); - crStopV; - } - - if (s->maxlen < s->pad + decomplen) { - PktIn *old_pktin = s->pktin; - - s->maxlen = s->pad + decomplen; - s->pktin = snew_plus(PktIn, s->maxlen); - *s->pktin = *old_pktin; /* structure copy */ - s->data = snew_plus_get_aux(s->pktin); - - smemclr(old_pktin, s->biglen); - sfree(old_pktin); - } - - memcpy(s->data + s->pad, decompblk, decomplen); - sfree(decompblk); - s->length = decomplen - 1; - } - - /* - * Now we can find the bounds of the semantic content of the - * packet, and the initial type byte. - */ - s->data += s->pad; - s->pktin->type = *s->data++; - BinarySource_INIT(s->pktin, s->data, s->length); - - if (s->bpp.logctx) { - logblank_t blanks[MAX_BLANKS]; - int nblanks = ssh1_censor_packet( - s->bpp.pls, s->pktin->type, false, - make_ptrlen(s->data, s->length), blanks); - log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type, - ssh1_pkt_type(s->pktin->type), - get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks, - NULL, 0, NULL); - } - - s->pktin->qnode.formal_size = get_avail(s->pktin); - pq_push(&s->bpp.in_pq, s->pktin); - - { - int type = s->pktin->type; - s->pktin = NULL; - - switch (type) { - case SSH1_SMSG_SUCCESS: - case SSH1_SMSG_FAILURE: - if (s->pending_compression_request) { - /* - * This is the response to - * SSH1_CMSG_REQUEST_COMPRESSION. - */ - if (type == SSH1_SMSG_SUCCESS) { - /* - * If the response was positive, start - * compression. - */ - ssh1_bpp_start_compression(&s->bpp); - } - - /* - * Either way, cancel the pending flag, and - * schedule a run of our output side in case we - * had any packets queued up in the meantime. - */ - s->pending_compression_request = false; - queue_idempotent_callback(&s->bpp.ic_out_pq); - } - break; - } - } - } - - 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 *ssh1_bpp_new_pktout(int pkt_type) -{ - PktOut *pkt = ssh_new_packet(); - pkt->length = 4 + 8; /* space for length + max padding */ - put_byte(pkt, pkt_type); - pkt->prefix = pkt->length; - pkt->type = pkt_type; - pkt->downstream_id = 0; - pkt->additional_log_text = NULL; - return pkt; -} - -static void ssh1_bpp_format_packet(struct ssh1_bpp_state *s, PktOut *pkt) -{ - int pad, biglen, pktoffs; - uint32_t crc; - int len; - - if (s->bpp.logctx) { - ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix, - pkt->length - pkt->prefix); - logblank_t blanks[MAX_BLANKS]; - int nblanks = ssh1_censor_packet( - s->bpp.pls, pkt->type, true, pktdata, blanks); - log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type, - ssh1_pkt_type(pkt->type), - pktdata.ptr, pktdata.len, nblanks, blanks, - NULL, 0, NULL); - } - - if (s->compctx) { - unsigned char *compblk; - int complen; - ssh_compressor_compress(s->compctx, pkt->data + 12, pkt->length - 12, - &compblk, &complen, 0); - /* Replace the uncompressed packet data with the compressed - * version. */ - pkt->length = 12; - put_data(pkt, compblk, complen); - sfree(compblk); - } - - put_uint32(pkt, 0); /* space for CRC */ - len = pkt->length - 4 - 8; /* len(type+data+CRC) */ - pad = 8 - (len % 8); - pktoffs = 8 - pad; - biglen = len + pad; /* len(padding+type+data+CRC) */ - - random_read(pkt->data + pktoffs, 4+8 - pktoffs); - crc = crc32_ssh1( - make_ptrlen(pkt->data + pktoffs + 4, biglen - 4)); /* all ex len */ - PUT_32BIT_MSB_FIRST(pkt->data + pktoffs + 4 + biglen - 4, crc); - PUT_32BIT_MSB_FIRST(pkt->data + pktoffs, len); - - if (s->cipher_out) - ssh_cipher_encrypt(s->cipher_out, pkt->data + pktoffs + 4, biglen); - - bufchain_add(s->bpp.out_raw, pkt->data + pktoffs, - biglen + 4); /* len(length+padding+type+data+CRC) */ -} - -static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp) -{ - struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp); - PktOut *pkt; - - if (s->pending_compression_request) { - /* - * Don't send any output packets while we're awaiting a - * response to SSH1_CMSG_REQUEST_COMPRESSION, because if they - * cross over in transit with the responding SSH1_CMSG_SUCCESS - * then the other end could decode them with the wrong - * compression settings. - */ - return; - } - - while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { - int type = pkt->type; - ssh1_bpp_format_packet(s, pkt); - ssh_free_pktout(pkt); - - if (type == SSH1_CMSG_REQUEST_COMPRESSION) { - /* - * When we see the actual compression request go past, set - * the pending flag, and stop processing packets this - * time. - */ - s->pending_compression_request = true; - break; - } - } -} - -static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp, - const char *msg, int category) -{ - PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH1_MSG_DISCONNECT); - put_stringz(pkt, msg); - pq_push(&bpp->out_pq, pkt); -} |