| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926 | 
							- /*
 
-  * Binary packet protocol for SSH-2.
 
-  */
 
- #include <assert.h>
 
- #include "putty.h"
 
- #include "ssh.h"
 
- #include "sshbpp.h"
 
- #include "sshcr.h"
 
- struct ssh2_bpp_direction {
 
-     unsigned long sequence;
 
-     ssh2_cipher *cipher;
 
-     ssh2_mac *mac;
 
-     bool etm_mode;
 
-     const struct ssh_compression_alg *pending_compression;
 
- };
 
- struct ssh2_bpp_state {
 
-     int crState;
 
-     long len, pad, payload, packetlen, maclen, length, maxlen;
 
-     unsigned char *buf;
 
-     size_t bufsize;
 
-     unsigned char *data;
 
-     unsigned cipherblk;
 
-     PktIn *pktin;
 
-     struct DataTransferStats *stats;
 
-     bool cbc_ignore_workaround;
 
-     struct ssh2_bpp_direction in, out;
 
-     /* comp and decomp logically belong in the per-direction
 
-      * substructure, except that they have different types */
 
-     ssh_decompressor *in_decomp;
 
-     ssh_compressor *out_comp;
 
-     bool is_server;
 
-     bool pending_newkeys;
 
-     bool pending_compression, seen_userauth_success;
 
-     BinaryPacketProtocol bpp;
 
- };
 
- static void ssh2_bpp_free(BinaryPacketProtocol *bpp);
 
- static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp);
 
- static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp);
 
- static PktOut *ssh2_bpp_new_pktout(int type);
 
- static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
 
-     ssh2_bpp_free,
 
-     ssh2_bpp_handle_input,
 
-     ssh2_bpp_handle_output,
 
-     ssh2_bpp_new_pktout,
 
-     ssh2_bpp_queue_disconnect, /* in sshcommon.c */
 
- };
 
- BinaryPacketProtocol *ssh2_bpp_new(
 
-     LogContext *logctx, struct DataTransferStats *stats, bool is_server)
 
- {
 
-     struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
 
-     memset(s, 0, sizeof(*s));
 
-     s->bpp.vt = &ssh2_bpp_vtable;
 
-     s->bpp.logctx = logctx;
 
-     s->stats = stats;
 
-     s->is_server = is_server;
 
-     ssh_bpp_common_setup(&s->bpp);
 
-     return &s->bpp;
 
- }
 
- static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
 
- {
 
-     struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp);
 
-     sfree(s->buf);
 
-     if (s->out.cipher)
 
-         ssh2_cipher_free(s->out.cipher);
 
-     if (s->out.mac)
 
-         ssh2_mac_free(s->out.mac);
 
-     if (s->out_comp)
 
-         ssh_compressor_free(s->out_comp);
 
-     if (s->in.cipher)
 
-         ssh2_cipher_free(s->in.cipher);
 
-     if (s->in.mac)
 
-         ssh2_mac_free(s->in.mac);
 
-     if (s->in_decomp)
 
-         ssh_decompressor_free(s->in_decomp);
 
-     sfree(s->pktin);
 
-     sfree(s);
 
- }
 
- void ssh2_bpp_new_outgoing_crypto(
 
-     BinaryPacketProtocol *bpp,
 
-     const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
 
-     const struct ssh2_macalg *mac, bool etm_mode, const void *mac_key,
 
-     const struct ssh_compression_alg *compression, bool delayed_compression)
 
- {
 
-     struct ssh2_bpp_state *s;
 
-     assert(bpp->vt == &ssh2_bpp_vtable);
 
-     s = container_of(bpp, struct ssh2_bpp_state, bpp);
 
-     if (s->out.cipher)
 
-         ssh2_cipher_free(s->out.cipher);
 
-     if (s->out.mac)
 
-         ssh2_mac_free(s->out.mac);
 
-     if (s->out_comp)
 
-         ssh_compressor_free(s->out_comp);
 
-     if (cipher) {
 
-         s->out.cipher = ssh2_cipher_new(cipher);
 
-         ssh2_cipher_setkey(s->out.cipher, ckey);
 
-         ssh2_cipher_setiv(s->out.cipher, iv);
 
-         s->cbc_ignore_workaround = (
 
-             (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) &&
 
-             !(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
 
-         bpp_logevent(("Initialised %.200s outbound encryption",
 
-                       ssh2_cipher_alg(s->out.cipher)->text_name));
 
-     } else {
 
-         s->out.cipher = NULL;
 
-         s->cbc_ignore_workaround = false;
 
-     }
 
-     s->out.etm_mode = etm_mode;
 
-     if (mac) {
 
-         s->out.mac = ssh2_mac_new(mac, s->out.cipher);
 
-         mac->setkey(s->out.mac, mac_key);
 
-         bpp_logevent(("Initialised %.200s outbound MAC algorithm%s%s",
 
-                       ssh2_mac_alg(s->out.mac)->text_name,
 
-                       etm_mode ? " (in ETM mode)" : "",
 
-                       (s->out.cipher &&
 
-                        ssh2_cipher_alg(s->out.cipher)->required_mac ?
 
-                        " (required by cipher)" : "")));
 
-     } else {
 
-         s->out.mac = NULL;
 
-     }
 
-     if (delayed_compression && !s->seen_userauth_success) {
 
-         s->out.pending_compression = compression;
 
-         s->out_comp = NULL;
 
-         bpp_logevent(("Will enable %s compression after user authentication",
 
-                       s->out.pending_compression->text_name));
 
-     } else {
 
-         s->out.pending_compression = NULL;
 
-         /* 'compression' is always non-NULL, because no compression is
 
-          * indicated by ssh_comp_none. But this setup call may return a
 
-          * null out_comp. */
 
-         s->out_comp = ssh_compressor_new(compression);
 
-         if (s->out_comp)
 
-             bpp_logevent(("Initialised %s compression",
 
-                           ssh_compressor_alg(s->out_comp)->text_name));
 
-     }
 
- }
 
- void ssh2_bpp_new_incoming_crypto(
 
-     BinaryPacketProtocol *bpp,
 
-     const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
 
-     const struct ssh2_macalg *mac, bool etm_mode, const void *mac_key,
 
-     const struct ssh_compression_alg *compression, bool delayed_compression)
 
- {
 
-     struct ssh2_bpp_state *s;
 
-     assert(bpp->vt == &ssh2_bpp_vtable);
 
-     s = container_of(bpp, struct ssh2_bpp_state, bpp);
 
-     if (s->in.cipher)
 
-         ssh2_cipher_free(s->in.cipher);
 
-     if (s->in.mac)
 
-         ssh2_mac_free(s->in.mac);
 
-     if (s->in_decomp)
 
-         ssh_decompressor_free(s->in_decomp);
 
-     if (cipher) {
 
-         s->in.cipher = ssh2_cipher_new(cipher);
 
-         ssh2_cipher_setkey(s->in.cipher, ckey);
 
-         ssh2_cipher_setiv(s->in.cipher, iv);
 
-         bpp_logevent(("Initialised %.200s inbound encryption",
 
-                       ssh2_cipher_alg(s->in.cipher)->text_name));
 
-     } else {
 
-         s->in.cipher = NULL;
 
-     }
 
-     s->in.etm_mode = etm_mode;
 
-     if (mac) {
 
-         s->in.mac = ssh2_mac_new(mac, s->in.cipher);
 
-         mac->setkey(s->in.mac, mac_key);
 
-         bpp_logevent(("Initialised %.200s inbound MAC algorithm%s%s",
 
-                       ssh2_mac_alg(s->in.mac)->text_name,
 
-                       etm_mode ? " (in ETM mode)" : "",
 
-                       (s->in.cipher &&
 
-                        ssh2_cipher_alg(s->in.cipher)->required_mac ?
 
-                        " (required by cipher)" : "")));
 
-     } else {
 
-         s->in.mac = NULL;
 
-     }
 
-     if (delayed_compression && !s->seen_userauth_success) {
 
-         s->in.pending_compression = compression;
 
-         s->in_decomp = NULL;
 
-         bpp_logevent(("Will enable %s decompression after user authentication",
 
-                       s->in.pending_compression->text_name));
 
-     } else {
 
-         s->in.pending_compression = NULL;
 
-         /* 'compression' is always non-NULL, because no compression is
 
-          * indicated by ssh_comp_none. But this setup call may return a
 
-          * null in_decomp. */
 
-         s->in_decomp = ssh_decompressor_new(compression);
 
-         if (s->in_decomp)
 
-             bpp_logevent(("Initialised %s decompression",
 
-                           ssh_decompressor_alg(s->in_decomp)->text_name));
 
-     }
 
-     /* Clear the pending_newkeys flag, so that handle_input below will
 
-      * start consuming the input data again. */
 
-     s->pending_newkeys = false;
 
-     /* And schedule a run of handle_input, in case there's already
 
-      * input data in the queue. */
 
-     queue_idempotent_callback(&s->bpp.ic_in_raw);
 
- }
 
- bool ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp)
 
- {
 
-     struct ssh2_bpp_state *s;
 
-     assert(bpp->vt == &ssh2_bpp_vtable);
 
-     s = container_of(bpp, struct ssh2_bpp_state, bpp);
 
-     return s->pending_compression;
 
- }
 
- static void ssh2_bpp_enable_pending_compression(struct ssh2_bpp_state *s)
 
- {
 
-     BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */
 
-     if (s->in.pending_compression) {
 
-         s->in_decomp = ssh_decompressor_new(s->in.pending_compression);
 
-         bpp_logevent(("Initialised delayed %s decompression",
 
-                       ssh_decompressor_alg(s->in_decomp)->text_name));
 
-         s->in.pending_compression = NULL;
 
-     }
 
-     if (s->out.pending_compression) {
 
-         s->out_comp = ssh_compressor_new(s->out.pending_compression);
 
-         bpp_logevent(("Initialised delayed %s compression",
 
-                       ssh_compressor_alg(s->out_comp)->text_name));
 
-         s->out.pending_compression = NULL;
 
-     }
 
- }
 
- #define BPP_READ(ptr, len) do                                   \
 
-     {                                                           \
 
-         crMaybeWaitUntilV(s->bpp.input_eof ||                   \
 
-                           bufchain_try_fetch_consume(           \
 
-                               s->bpp.in_raw, ptr, len));        \
 
-         if (s->bpp.input_eof)                                   \
 
-             goto eof;                                           \
 
-     } while (0)
 
- #define userauth_range(pkttype) ((unsigned)((pkttype) - 50) < 20)
 
- static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
 
- {
 
-     struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp);
 
-     crBegin(s->crState);
 
-     while (1) {
 
-         s->maxlen = 0;
 
-         s->length = 0;
 
-         if (s->in.cipher)
 
-             s->cipherblk = ssh2_cipher_alg(s->in.cipher)->blksize;
 
-         else
 
-             s->cipherblk = 8;
 
-         if (s->cipherblk < 8)
 
-             s->cipherblk = 8;
 
-         s->maclen = s->in.mac ? ssh2_mac_alg(s->in.mac)->len : 0;
 
-         if (s->in.cipher &&
 
-             (ssh2_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) &&
 
-             s->in.mac && !s->in.etm_mode) {
 
-             /*
 
-              * When dealing with a CBC-mode cipher, we want to avoid the
 
-              * possibility of an attacker's tweaking the ciphertext stream
 
-              * so as to cause us to feed the same block to the block
 
-              * cipher more than once and thus leak information
 
-              * (VU#958563).  The way we do this is not to take any
 
-              * decisions on the basis of anything we've decrypted until
 
-              * we've verified it with a MAC.  That includes the packet
 
-              * length, so we just read data and check the MAC repeatedly,
 
-              * and when the MAC passes, see if the length we've got is
 
-              * plausible.
 
-              *
 
-              * This defence is unnecessary in OpenSSH ETM mode, because
 
-              * the whole point of ETM mode is that the attacker can't
 
-              * tweak the ciphertext stream at all without the MAC
 
-              * detecting it before we decrypt anything.
 
-              */
 
-             /*
 
-              * Make sure we have buffer space for a maximum-size packet.
 
-              */
 
-             unsigned buflimit = OUR_V2_PACKETLIMIT + s->maclen;
 
-             if (s->bufsize < buflimit) {
 
-                 s->bufsize = buflimit;
 
-                 s->buf = sresize(s->buf, s->bufsize, unsigned char);
 
-             }
 
-             /* Read an amount corresponding to the MAC. */
 
-             BPP_READ(s->buf, s->maclen);
 
-             s->packetlen = 0;
 
-             ssh2_mac_start(s->in.mac);
 
-             put_uint32(s->in.mac, s->in.sequence);
 
-             for (;;) { /* Once around this loop per cipher block. */
 
-                 /* Read another cipher-block's worth, and tack it on to
 
-                  * the end. */
 
-                 BPP_READ(s->buf + (s->packetlen + s->maclen), s->cipherblk);
 
-                 /* Decrypt one more block (a little further back in
 
-                  * the stream). */
 
-                 ssh2_cipher_decrypt(s->in.cipher,
 
-                                     s->buf + s->packetlen, s->cipherblk);
 
-                 /* Feed that block to the MAC. */
 
-                 put_data(s->in.mac,
 
-                          s->buf + s->packetlen, s->cipherblk);
 
-                 s->packetlen += s->cipherblk;
 
-                 /* See if that gives us a valid packet. */
 
-                 if (ssh2_mac_verresult(s->in.mac, s->buf + s->packetlen) &&
 
-                     ((s->len = toint(GET_32BIT(s->buf))) ==
 
-                      s->packetlen-4))
 
-                     break;
 
-                 if (s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
 
-                     ssh_sw_abort(s->bpp.ssh,
 
-                                  "No valid incoming packet found");
 
-                     crStopV;
 
-                 }
 
-             }
 
-             s->maxlen = s->packetlen + s->maclen;
 
-             /*
 
-              * Now transfer the data into an output packet.
 
-              */
 
-             s->pktin = snew_plus(PktIn, s->maxlen);
 
-             s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
 
-             s->pktin->type = 0;
 
-             s->pktin->qnode.on_free_queue = false;
 
-             s->data = snew_plus_get_aux(s->pktin);
 
-             memcpy(s->data, s->buf, s->maxlen);
 
-         } else if (s->in.mac && s->in.etm_mode) {
 
-             if (s->bufsize < 4) {
 
-                 s->bufsize = 4;
 
-                 s->buf = sresize(s->buf, s->bufsize, unsigned char);
 
-             }
 
-             /*
 
-              * OpenSSH encrypt-then-MAC mode: the packet length is
 
-              * unencrypted, unless the cipher supports length encryption.
 
-              */
 
-             BPP_READ(s->buf, 4);
 
-             /* Cipher supports length decryption, so do it */
 
-             if (s->in.cipher && (ssh2_cipher_alg(s->in.cipher)->flags &
 
-                                  SSH_CIPHER_SEPARATE_LENGTH)) {
 
-                 /* Keep the packet the same though, so the MAC passes */
 
-                 unsigned char len[4];
 
-                 memcpy(len, s->buf, 4);
 
-                 ssh2_cipher_decrypt_length(
 
-                     s->in.cipher, len, 4, s->in.sequence);
 
-                 s->len = toint(GET_32BIT(len));
 
-             } else {
 
-                 s->len = toint(GET_32BIT(s->buf));
 
-             }
 
-             /*
 
-              * _Completely_ silly lengths should be stomped on before they
 
-              * do us any more damage.
 
-              */
 
-             if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
 
-                 s->len % s->cipherblk != 0) {
 
-                 ssh_sw_abort(s->bpp.ssh,
 
-                              "Incoming packet length field was garbled");
 
-                 crStopV;
 
-             }
 
-             /*
 
-              * So now we can work out the total packet length.
 
-              */
 
-             s->packetlen = s->len + 4;
 
-             /*
 
-              * Allocate the packet to return, now we know its length.
 
-              */
 
-             s->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + s->maclen);
 
-             s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
 
-             s->pktin->type = 0;
 
-             s->pktin->qnode.on_free_queue = false;
 
-             s->data = snew_plus_get_aux(s->pktin);
 
-             memcpy(s->data, s->buf, 4);
 
-             /*
 
-              * Read the remainder of the packet.
 
-              */
 
-             BPP_READ(s->data + 4, s->packetlen + s->maclen - 4);
 
-             /*
 
-              * Check the MAC.
 
-              */
 
-             if (s->in.mac && !ssh2_mac_verify(
 
-                     s->in.mac, s->data, s->len + 4, s->in.sequence)) {
 
-                 ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet");
 
-                 crStopV;
 
-             }
 
-             /* Decrypt everything between the length field and the MAC. */
 
-             if (s->in.cipher)
 
-                 ssh2_cipher_decrypt(
 
-                     s->in.cipher, s->data + 4, s->packetlen - 4);
 
-         } else {
 
-             if (s->bufsize < s->cipherblk) {
 
-                 s->bufsize = s->cipherblk;
 
-                 s->buf = sresize(s->buf, s->bufsize, unsigned char);
 
-             }
 
-             /*
 
-              * Acquire and decrypt the first block of the packet. This will
 
-              * contain the length and padding details.
 
-              */
 
-             BPP_READ(s->buf, s->cipherblk);
 
-             if (s->in.cipher)
 
-                 ssh2_cipher_decrypt(
 
-                     s->in.cipher, s->buf, s->cipherblk);
 
-             /*
 
-              * Now get the length figure.
 
-              */
 
-             s->len = toint(GET_32BIT(s->buf));
 
-             /*
 
-              * _Completely_ silly lengths should be stomped on before they
 
-              * do us any more damage.
 
-              */
 
-             if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
 
-                 (s->len + 4) % s->cipherblk != 0) {
 
-                 ssh_sw_abort(s->bpp.ssh,
 
-                              "Incoming packet was garbled on decryption");
 
-                 crStopV;
 
-             }
 
-             /*
 
-              * So now we can work out the total packet length.
 
-              */
 
-             s->packetlen = s->len + 4;
 
-             /*
 
-              * Allocate the packet to return, now we know its length.
 
-              */
 
-             s->maxlen = s->packetlen + s->maclen;
 
-             s->pktin = snew_plus(PktIn, s->maxlen);
 
-             s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
 
-             s->pktin->type = 0;
 
-             s->pktin->qnode.on_free_queue = false;
 
-             s->data = snew_plus_get_aux(s->pktin);
 
-             memcpy(s->data, s->buf, s->cipherblk);
 
-             /*
 
-              * Read and decrypt the remainder of the packet.
 
-              */
 
-             BPP_READ(s->data + s->cipherblk,
 
-                      s->packetlen + s->maclen - s->cipherblk);
 
-             /* Decrypt everything _except_ the MAC. */
 
-             if (s->in.cipher)
 
-                 ssh2_cipher_decrypt(
 
-                     s->in.cipher,
 
-                     s->data + s->cipherblk, s->packetlen - s->cipherblk);
 
-             /*
 
-              * Check the MAC.
 
-              */
 
-             if (s->in.mac && !ssh2_mac_verify(
 
-                     s->in.mac, s->data, s->len + 4, s->in.sequence)) {
 
-                 ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet");
 
-                 crStopV;
 
-             }
 
-         }
 
-         /* Get and sanity-check the amount of random padding. */
 
-         s->pad = s->data[4];
 
-         if (s->pad < 4 || s->len - s->pad < 1) {
 
-             ssh_sw_abort(s->bpp.ssh,
 
-                          "Invalid padding length on received packet");
 
-             crStopV;
 
-         }
 
-         /*
 
-          * This enables us to deduce the payload length.
 
-          */
 
-         s->payload = s->len - s->pad - 1;
 
-         s->length = s->payload + 5;
 
-         DTS_CONSUME(s->stats, in, s->packetlen);
 
-         s->pktin->sequence = s->in.sequence++;
 
-         s->length = s->packetlen - s->pad;
 
-         assert(s->length >= 0);
 
-         /*
 
-          * Decompress packet payload.
 
-          */
 
-         {
 
-             unsigned char *newpayload;
 
-             int newlen;
 
-             if (s->in_decomp && ssh_decompressor_decompress(
 
-                     s->in_decomp, s->data + 5, s->length - 5,
 
-                     &newpayload, &newlen)) {
 
-                 if (s->maxlen < newlen + 5) {
 
-                     PktIn *old_pktin = s->pktin;
 
-                     s->maxlen = newlen + 5;
 
-                     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->packetlen + s->maclen);
 
-                     sfree(old_pktin);
 
-                 }
 
-                 s->length = 5 + newlen;
 
-                 memcpy(s->data + 5, newpayload, newlen);
 
-                 sfree(newpayload);
 
-             }
 
-         }
 
-         /*
 
-          * Now we can identify the semantic content of the packet,
 
-          * and also the initial type byte.
 
-          */
 
-         if (s->length <= 5) { /* == 5 we hope, but robustness */
 
-             /*
 
-              * RFC 4253 doesn't explicitly say that completely empty
 
-              * packets with no type byte are forbidden. We handle them
 
-              * here by giving them a type code larger than 0xFF, which
 
-              * will be picked up at the next layer and trigger
 
-              * SSH_MSG_UNIMPLEMENTED.
 
-              */
 
-             s->pktin->type = SSH_MSG_NO_TYPE_CODE;
 
-             s->data += 5;
 
-             s->length = 0;
 
-         } else {
 
-             s->pktin->type = s->data[5];
 
-             s->data += 6;
 
-             s->length -= 6;
 
-         }
 
-         BinarySource_INIT(s->pktin, s->data, s->length);
 
-         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->length), 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),
 
-                        s->data, s->length, nblanks, blanks,
 
-                        &s->pktin->sequence, 0, NULL);
 
-         }
 
-         if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) {
 
-             sfree(s->pktin);
 
-             s->pktin = NULL;
 
-             continue;
 
-         }
 
-         pq_push(&s->bpp.in_pq, s->pktin);
 
-         {
 
-             int type = s->pktin->type;
 
-             s->pktin = NULL;
 
-             if (type == SSH2_MSG_NEWKEYS) {
 
-                 /*
 
-                  * Mild layer violation: in this situation we must
 
-                  * suspend processing of the input byte stream until
 
-                  * the transport layer has initialised the new keys by
 
-                  * calling ssh2_bpp_new_incoming_crypto above.
 
-                  */
 
-                 s->pending_newkeys = true;
 
-                 crWaitUntilV(!s->pending_newkeys);
 
-                 continue;
 
-             }
 
-             if (type == SSH2_MSG_USERAUTH_SUCCESS && !s->is_server) {
 
-                 /*
 
-                  * Another one: if we were configured with OpenSSH's
 
-                  * deferred compression which is triggered on receipt
 
-                  * of USERAUTH_SUCCESS, then this is the moment to
 
-                  * turn on compression.
 
-                  */
 
-                 ssh2_bpp_enable_pending_compression(s);
 
-                 /*
 
-                  * Whether or not we were doing delayed compression in
 
-                  * _this_ set of crypto parameters, we should set a
 
-                  * flag indicating that we're now authenticated, so
 
-                  * that a delayed compression method enabled in any
 
-                  * future rekey will be treated as un-delayed.
 
-                  */
 
-                 s->seen_userauth_success = true;
 
-             }
 
-             if (s->pending_compression && userauth_range(type)) {
 
-                 /*
 
-                  * Receiving any userauth message at all indicates
 
-                  * that we're not about to turn on delayed compression
 
-                  * - either because we just _have_ done, or because
 
-                  * this message is a USERAUTH_FAILURE or some kind of
 
-                  * intermediate 'please send more data' continuation
 
-                  * message. Either way, we turn off the outgoing
 
-                  * packet blockage for now, and release any queued
 
-                  * output packets, so that we can make another attempt
 
-                  * to authenticate. The next userauth packet we send
 
-                  * will re-block the output direction.
 
-                  */
 
-                 s->pending_compression = false;
 
-                 queue_idempotent_callback(&s->bpp.ic_out_pq);
 
-             }
 
-         }
 
-     }
 
-   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_bpp_new_pktout(int pkt_type)
 
- {
 
-     PktOut *pkt = ssh_new_packet();
 
-     pkt->length = 5; /* space for packet length + padding length */
 
-     pkt->minlen = 0;
 
-     pkt->type = pkt_type;
 
-     put_byte(pkt, pkt_type);
 
-     pkt->prefix = pkt->length;
 
-     return pkt;
 
- }
 
- static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
 
- {
 
-     int origlen, cipherblk, maclen, padding, unencrypted_prefix, i;
 
-     if (s->bpp.logctx) {
 
-         ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
 
-                                      pkt->length - pkt->prefix);
 
-         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->out.sequence,
 
-                    pkt->downstream_id, pkt->additional_log_text);
 
-     }
 
-     cipherblk = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 8;
 
-     cipherblk = cipherblk < 8 ? 8 : cipherblk;  /* or 8 if blksize < 8 */
 
-     if (s->out_comp) {
 
-         unsigned char *newpayload;
 
-         int minlen, newlen;
 
-         /*
 
-          * Compress packet payload.
 
-          */
 
-         minlen = pkt->minlen;
 
-         if (minlen) {
 
-             /*
 
-              * Work out how much compressed data we need (at least) to
 
-              * make the overall packet length come to pkt->minlen.
 
-              */
 
-             if (s->out.mac)
 
-                 minlen -= ssh2_mac_alg(s->out.mac)->len;
 
-             minlen -= 8;              /* length field + min padding */
 
-         }
 
-         ssh_compressor_compress(s->out_comp, pkt->data + 5, pkt->length - 5,
 
-                                 &newpayload, &newlen, minlen);
 
-         pkt->length = 5;
 
-         put_data(pkt, newpayload, newlen);
 
-         sfree(newpayload);
 
-     }
 
-     /*
 
-      * Add padding. At least four bytes, and must also bring total
 
-      * length (minus MAC) up to a multiple of the block size.
 
-      * If pkt->forcepad is set, make sure the packet is at least that size
 
-      * after padding.
 
-      */
 
-     padding = 4;
 
-     unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0;
 
-     padding +=
 
-         (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
 
-         % cipherblk;
 
-     assert(padding <= 255);
 
-     maclen = s->out.mac ? ssh2_mac_alg(s->out.mac)->len : 0;
 
-     origlen = pkt->length;
 
-     for (i = 0; i < padding; i++)
 
-         put_byte(pkt, random_byte());
 
-     pkt->data[4] = padding;
 
-     PUT_32BIT(pkt->data, origlen + padding - 4);
 
-     /* Encrypt length if the scheme requires it */
 
-     if (s->out.cipher &&
 
-         (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
 
-         ssh2_cipher_encrypt_length(s->out.cipher, pkt->data, 4,
 
-                                    s->out.sequence);
 
-     }
 
-     put_padding(pkt, maclen, 0);
 
-     if (s->out.mac && s->out.etm_mode) {
 
-         /*
 
-          * OpenSSH-defined encrypt-then-MAC protocol.
 
-          */
 
-         if (s->out.cipher)
 
-             ssh2_cipher_encrypt(s->out.cipher,
 
-                                 pkt->data + 4, origlen + padding - 4);
 
-         ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
 
-                           s->out.sequence);
 
-     } else {
 
-         /*
 
-          * SSH-2 standard protocol.
 
-          */
 
-         if (s->out.mac)
 
-             ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
 
-                               s->out.sequence);
 
-         if (s->out.cipher)
 
-             ssh2_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
 
-     }
 
-     s->out.sequence++;       /* whether or not we MACed */
 
-     DTS_CONSUME(s->stats, out, origlen + padding);
 
- }
 
- static void ssh2_bpp_format_packet(struct ssh2_bpp_state *s, PktOut *pkt)
 
- {
 
-     if (pkt->minlen > 0 && !s->out_comp) {
 
-         /*
 
-          * If we've been told to pad the packet out to a given minimum
 
-          * length, but we're not compressing (and hence can't get the
 
-          * compression to do the padding by pointlessly opening and
 
-          * closing zlib blocks), then our other strategy is to precede
 
-          * this message with an SSH_MSG_IGNORE that makes it up to the
 
-          * right length.
 
-          *
 
-          * A third option in principle, and the most obviously
 
-          * sensible, would be to set the explicit padding field in the
 
-          * packet to more than its minimum value. Sadly, that turns
 
-          * out to break some servers (our institutional memory thinks
 
-          * Cisco in particular) and so we abandoned that idea shortly
 
-          * after trying it.
 
-          */
 
-         /*
 
-          * Calculate the length we expect the real packet to have.
 
-          */
 
-         int block, length;
 
-         PktOut *ignore_pkt;
 
-         block = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 0;
 
-         if (block < 8)
 
-             block = 8;
 
-         length = pkt->length;
 
-         length += 4;       /* minimum 4 byte padding */
 
-         length += block-1;
 
-         length -= (length % block);
 
-         if (s->out.mac)
 
-             length += ssh2_mac_alg(s->out.mac)->len;
 
-         if (length < pkt->minlen) {
 
-             /*
 
-              * We need an ignore message. Calculate its length.
 
-              */
 
-             length = pkt->minlen - length;
 
-             /*
 
-              * And work backwards from that to the length of the
 
-              * contained string.
 
-              */
 
-             if (s->out.mac)
 
-                 length -= ssh2_mac_alg(s->out.mac)->len;
 
-             length -= 8;               /* length field + min padding */
 
-             length -= 5;               /* type code + string length prefix */
 
-             if (length < 0)
 
-                 length = 0;
 
-             ignore_pkt = ssh2_bpp_new_pktout(SSH2_MSG_IGNORE);
 
-             put_uint32(ignore_pkt, length);
 
-             while (length-- > 0)
 
-                 put_byte(ignore_pkt, random_byte());
 
-             ssh2_bpp_format_packet_inner(s, ignore_pkt);
 
-             bufchain_add(s->bpp.out_raw, ignore_pkt->data, ignore_pkt->length);
 
-             ssh_free_pktout(ignore_pkt);
 
-         }
 
-     }
 
-     ssh2_bpp_format_packet_inner(s, pkt);
 
-     bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
 
- }
 
- static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
 
- {
 
-     struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp);
 
-     PktOut *pkt;
 
-     int n_userauth;
 
-     /*
 
-      * Count the userauth packets in the queue.
 
-      */
 
-     n_userauth = 0;
 
-     for (pkt = pq_first(&s->bpp.out_pq); pkt != NULL;
 
-          pkt = pq_next(&s->bpp.out_pq, pkt))
 
-         if (userauth_range(pkt->type))
 
-             n_userauth++;
 
-     if (s->pending_compression && !n_userauth) {
 
-         /*
 
-          * We're currently blocked from sending any outgoing packets
 
-          * until the other end tells us whether we're going to have to
 
-          * enable compression or not.
 
-          *
 
-          * If our end has pushed a userauth packet on the queue, that
 
-          * must mean it knows that a USERAUTH_SUCCESS is not
 
-          * immediately forthcoming, so we unblock ourselves and send
 
-          * up to and including that packet. But in this if statement,
 
-          * there aren't any, so we're still blocked.
 
-          */
 
-         return;
 
-     }
 
-     if (s->cbc_ignore_workaround) {
 
-         /*
 
-          * When using a CBC-mode cipher in SSH-2, it's necessary to
 
-          * ensure that an attacker can't provide data to be encrypted
 
-          * using an IV that they know. We ensure this by inserting an
 
-          * SSH_MSG_IGNORE if the last cipher block of the previous
 
-          * packet has already been sent to the network (which we
 
-          * approximate conservatively by checking if it's vanished
 
-          * from out_raw).
 
-          */
 
-         if (bufchain_size(s->bpp.out_raw) <
 
-             (ssh2_cipher_alg(s->out.cipher)->blksize +
 
-              ssh2_mac_alg(s->out.mac)->len)) {
 
-             /*
 
-              * There's less data in out_raw than the MAC size plus the
 
-              * cipher block size, which means at least one byte of
 
-              * that cipher block must already have left. Add an
 
-              * IGNORE.
 
-              */
 
-             pkt = ssh_bpp_new_pktout(&s->bpp, SSH2_MSG_IGNORE);
 
-             put_stringz(pkt, "");
 
-             ssh2_bpp_format_packet(s, pkt);
 
-         }
 
-     }
 
-     while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
 
-         int type = pkt->type;
 
-         if (userauth_range(type))
 
-             n_userauth--;
 
-         ssh2_bpp_format_packet(s, pkt);
 
-         ssh_free_pktout(pkt);
 
-         if (n_userauth == 0 && s->out.pending_compression && !s->is_server) {
 
-             /*
 
-              * This is the last userauth packet in the queue, so
 
-              * unless our side decides to send another one in future,
 
-              * we have to assume will potentially provoke
 
-              * USERAUTH_SUCCESS. Block (non-userauth) outgoing packets
 
-              * until we see the reply.
 
-              */
 
-             s->pending_compression = true;
 
-             return;
 
-         } else if (type == SSH2_MSG_USERAUTH_SUCCESS && s->is_server) {
 
-             ssh2_bpp_enable_pending_compression(s);
 
-         }
 
-     }
 
- }
 
- #ifdef MPEXT
 
- const ssh2_cipher * ssh2_bpp_get_cscipher(BinaryPacketProtocol *bpp)
 
- {
 
-     return container_of(bpp, struct ssh2_bpp_state, bpp)->out.cipher;
 
- }
 
- const ssh2_cipher * ssh2_bpp_get_sccipher(BinaryPacketProtocol *bpp)
 
- {
 
-     return container_of(bpp, struct ssh2_bpp_state, bpp)->in.cipher;
 
- }
 
- const struct ssh_compressor * ssh2_bpp_get_cscomp(BinaryPacketProtocol *bpp)
 
- {
 
-     return container_of(bpp, struct ssh2_bpp_state, bpp)->out_comp;
 
- }
 
- const struct ssh_decompressor * ssh2_bpp_get_sccomp(BinaryPacketProtocol *bpp)
 
- {
 
-     return container_of(bpp, struct ssh2_bpp_state, bpp)->in_decomp;
 
- }
 
- #endif
 
 
  |