|
|
@@ -14,6 +14,7 @@ struct ssh2_bpp_direction {
|
|
|
ssh2_cipher *cipher;
|
|
|
ssh2_mac *mac;
|
|
|
int etm_mode;
|
|
|
+ const struct ssh_compression_alg *pending_compression;
|
|
|
};
|
|
|
|
|
|
struct ssh2_bpp_state {
|
|
|
@@ -34,6 +35,7 @@ struct ssh2_bpp_state {
|
|
|
ssh_compressor *out_comp;
|
|
|
|
|
|
int pending_newkeys;
|
|
|
+ int pending_compression, seen_userauth_success;
|
|
|
|
|
|
BinaryPacketProtocol bpp;
|
|
|
};
|
|
|
@@ -90,7 +92,7 @@ void ssh2_bpp_new_outgoing_crypto(
|
|
|
BinaryPacketProtocol *bpp,
|
|
|
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
|
|
|
const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
|
|
|
- const struct ssh_compression_alg *compression)
|
|
|
+ const struct ssh_compression_alg *compression, int delayed_compression)
|
|
|
{
|
|
|
struct ssh2_bpp_state *s;
|
|
|
assert(bpp->vt == &ssh2_bpp_vtable);
|
|
|
@@ -134,20 +136,31 @@ void ssh2_bpp_new_outgoing_crypto(
|
|
|
s->out.mac = 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));
|
|
|
+ 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, int etm_mode, const void *mac_key,
|
|
|
- const struct ssh_compression_alg *compression)
|
|
|
+ const struct ssh_compression_alg *compression, int delayed_compression)
|
|
|
{
|
|
|
struct ssh2_bpp_state *s;
|
|
|
assert(bpp->vt == &ssh2_bpp_vtable);
|
|
|
@@ -185,19 +198,39 @@ void ssh2_bpp_new_incoming_crypto(
|
|
|
s->in.mac = 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));
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
+int 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;
|
|
|
+}
|
|
|
+
|
|
|
#define BPP_READ(ptr, len) do \
|
|
|
{ \
|
|
|
crMaybeWaitUntilV(s->bpp.input_eof || \
|
|
|
@@ -207,6 +240,8 @@ void ssh2_bpp_new_incoming_crypto(
|
|
|
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);
|
|
|
@@ -537,6 +572,58 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
|
|
|
*/
|
|
|
s->pending_newkeys = TRUE;
|
|
|
crWaitUntilV(!s->pending_newkeys);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type == SSH2_MSG_USERAUTH_SUCCESS) {
|
|
|
+ /*
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -734,6 +821,31 @@ 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) {
|
|
|
/*
|
|
|
@@ -761,8 +873,23 @@ static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
|
|
|
}
|
|
|
|
|
|
while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
|
|
|
+ if (userauth_range(pkt->type))
|
|
|
+ n_userauth--;
|
|
|
+
|
|
|
ssh2_bpp_format_packet(s, pkt);
|
|
|
ssh_free_pktout(pkt);
|
|
|
+
|
|
|
+ if (n_userauth == 0 && s->out.pending_compression) {
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|