diff options
Diffstat (limited to 'crypto/openssh/packet.c')
-rw-r--r-- | crypto/openssh/packet.c | 366 |
1 files changed, 206 insertions, 160 deletions
diff --git a/crypto/openssh/packet.c b/crypto/openssh/packet.c index 8c1498c..f3b3ce9 100644 --- a/crypto/openssh/packet.c +++ b/crypto/openssh/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.214 2015/08/20 22:32:42 deraadt Exp $ */ +/* $OpenBSD: packet.c,v 1.229 2016/02/17 22:20:14 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -84,7 +84,6 @@ __RCSID("$FreeBSD$"); #include "channels.h" #include "ssh.h" #include "packet.h" -#include "roaming.h" #include "ssherr.h" #include "sshbuf.h" @@ -182,8 +181,7 @@ struct session_state { struct packet_state p_read, p_send; /* Volume-based rekeying */ - u_int64_t max_blocks_in, max_blocks_out; - u_int32_t rekey_limit; + u_int64_t max_blocks_in, max_blocks_out, rekey_limit; /* Time-based rekeying */ u_int32_t rekey_interval; /* how often in seconds */ @@ -262,6 +260,14 @@ ssh_alloc_session_state(void) return NULL; } +/* Returns nonzero if rekeying is in progress */ +int +ssh_packet_is_rekeying(struct ssh *ssh) +{ + return compat20 && + (ssh->state->rekeying || (ssh->kex != NULL && ssh->kex->done == 0)); +} + /* * Sets the descriptors used for communication. Disables encryption until * packet_set_encryption_key is called. @@ -339,7 +345,8 @@ ssh_packet_stop_discard(struct ssh *ssh) sshbuf_ptr(state->incoming_packet), PACKET_MAX_SIZE, NULL, 0); } - logit("Finished discarding for %.200s", ssh_remote_ipaddr(ssh)); + logit("Finished discarding for %.200s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return SSH_ERR_MAC_INVALID; } @@ -456,16 +463,30 @@ ssh_packet_get_connection_out(struct ssh *ssh) const char * ssh_remote_ipaddr(struct ssh *ssh) { + const int sock = ssh->state->connection_in; + /* Check whether we have cached the ipaddr. */ - if (ssh->remote_ipaddr == NULL) - ssh->remote_ipaddr = ssh_packet_connection_is_on_socket(ssh) ? - get_peer_ipaddr(ssh->state->connection_in) : - strdup("UNKNOWN"); - if (ssh->remote_ipaddr == NULL) - return "UNKNOWN"; + if (ssh->remote_ipaddr == NULL) { + if (ssh_packet_connection_is_on_socket(ssh)) { + ssh->remote_ipaddr = get_peer_ipaddr(sock); + ssh->remote_port = get_sock_port(sock, 0); + } else { + ssh->remote_ipaddr = strdup("UNKNOWN"); + ssh->remote_port = 0; + } + } return ssh->remote_ipaddr; } +/* Returns the port number of the remote host. */ + +int +ssh_remote_port(struct ssh *ssh) +{ + (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ + return ssh->remote_port; +} + /* Closes the connection and clears and frees internal data structures. */ void @@ -520,10 +541,8 @@ ssh_packet_close(struct ssh *ssh) error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); if ((r = cipher_cleanup(&state->receive_context)) != 0) error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); - if (ssh->remote_ipaddr) { - free(ssh->remote_ipaddr); - ssh->remote_ipaddr = NULL; - } + free(ssh->remote_ipaddr); + ssh->remote_ipaddr = NULL; free(ssh->state); ssh->state = NULL; } @@ -942,7 +961,12 @@ ssh_set_newkeys(struct ssh *ssh, int mode) max_blocks = &state->max_blocks_in; } if (state->newkeys[mode] != NULL) { - debug("set_newkeys: rekeying"); + debug("set_newkeys: rekeying, input %llu bytes %llu blocks, " + "output %llu bytes %llu blocks", + (unsigned long long)state->p_read.bytes, + (unsigned long long)state->p_read.blocks, + (unsigned long long)state->p_send.bytes, + (unsigned long long)state->p_send.blocks); if ((r = cipher_cleanup(cc)) != 0) return r; enc = &state->newkeys[mode]->enc; @@ -1010,9 +1034,55 @@ ssh_set_newkeys(struct ssh *ssh, int mode) if (state->rekey_limit) *max_blocks = MIN(*max_blocks, state->rekey_limit / enc->block_size); + debug("rekey after %llu blocks", (unsigned long long)*max_blocks); return 0; } +#define MAX_PACKETS (1U<<31) +static int +ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) +{ + struct session_state *state = ssh->state; + u_int32_t out_blocks; + + /* XXX client can't cope with rekeying pre-auth */ + if (!state->after_authentication) + return 0; + + /* Haven't keyed yet or KEX in progress. */ + if (ssh->kex == NULL || ssh_packet_is_rekeying(ssh)) + return 0; + + /* Peer can't rekey */ + if (ssh->compat & SSH_BUG_NOREKEY) + return 0; + + /* + * Permit one packet in or out per rekey - this allows us to + * make progress when rekey limits are very small. + */ + if (state->p_send.packets == 0 && state->p_read.packets == 0) + return 0; + + /* Time-based rekeying */ + if (state->rekey_interval != 0 && + state->rekey_time + state->rekey_interval <= monotime()) + return 1; + + /* Always rekey when MAX_PACKETS sent in either direction */ + if (state->p_send.packets > MAX_PACKETS || + state->p_read.packets > MAX_PACKETS) + return 1; + + /* Rekey after (cipher-specific) maxiumum blocks */ + out_blocks = roundup(outbound_packet_len, + state->newkeys[MODE_OUT]->enc.block_size); + return (state->max_blocks_out && + (state->p_send.blocks + out_blocks > state->max_blocks_out)) || + (state->max_blocks_in && + (state->p_read.blocks > state->max_blocks_in)); +} + /* * Delayed compression for SSH2 is enabled after authentication: * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent, @@ -1051,6 +1121,20 @@ ssh_packet_enable_delayed_compress(struct ssh *ssh) return 0; } +/* Used to mute debug logging for noisy packet types */ +static int +ssh_packet_log_type(u_char type) +{ + switch (type) { + case SSH2_MSG_CHANNEL_DATA: + case SSH2_MSG_CHANNEL_EXTENDED_DATA: + case SSH2_MSG_CHANNEL_WINDOW_ADJUST: + return 0; + default: + return 1; + } +} + /* * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) */ @@ -1079,7 +1163,8 @@ ssh_packet_send2_wrapped(struct ssh *ssh) aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; type = (sshbuf_ptr(state->outgoing_packet))[5]; - + if (ssh_packet_log_type(type)) + debug3("send packet: type %u", type); #ifdef PACKET_DEBUG fprintf(stderr, "plain: "); sshbuf_dump(state->outgoing_packet, stderr); @@ -1201,34 +1286,58 @@ ssh_packet_send2_wrapped(struct ssh *ssh) return r; } +/* returns non-zero if the specified packet type is usec by KEX */ +static int +ssh_packet_type_is_kex(u_char type) +{ + return + type >= SSH2_MSG_TRANSPORT_MIN && + type <= SSH2_MSG_TRANSPORT_MAX && + type != SSH2_MSG_SERVICE_REQUEST && + type != SSH2_MSG_SERVICE_ACCEPT && + type != SSH2_MSG_EXT_INFO; +} + int ssh_packet_send2(struct ssh *ssh) { struct session_state *state = ssh->state; struct packet *p; u_char type; - int r; + int r, need_rekey; + if (sshbuf_len(state->outgoing_packet) < 6) + return SSH_ERR_INTERNAL_ERROR; type = sshbuf_ptr(state->outgoing_packet)[5]; + need_rekey = !ssh_packet_type_is_kex(type) && + ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet)); - /* during rekeying we can only send key exchange messages */ - if (state->rekeying) { - if ((type < SSH2_MSG_TRANSPORT_MIN) || - (type > SSH2_MSG_TRANSPORT_MAX) || - (type == SSH2_MSG_SERVICE_REQUEST) || - (type == SSH2_MSG_SERVICE_ACCEPT)) { - debug("enqueue packet: %u", type); - p = calloc(1, sizeof(*p)); - if (p == NULL) - return SSH_ERR_ALLOC_FAIL; - p->type = type; - p->payload = state->outgoing_packet; - TAILQ_INSERT_TAIL(&state->outgoing, p, next); - state->outgoing_packet = sshbuf_new(); - if (state->outgoing_packet == NULL) - return SSH_ERR_ALLOC_FAIL; - return 0; + /* + * During rekeying we can only send key exchange messages. + * Queue everything else. + */ + if ((need_rekey || state->rekeying) && !ssh_packet_type_is_kex(type)) { + if (need_rekey) + debug3("%s: rekex triggered", __func__); + debug("enqueue packet: %u", type); + p = calloc(1, sizeof(*p)); + if (p == NULL) + return SSH_ERR_ALLOC_FAIL; + p->type = type; + p->payload = state->outgoing_packet; + TAILQ_INSERT_TAIL(&state->outgoing, p, next); + state->outgoing_packet = sshbuf_new(); + if (state->outgoing_packet == NULL) + return SSH_ERR_ALLOC_FAIL; + if (need_rekey) { + /* + * This packet triggered a rekey, so send the + * KEXINIT now. + * NB. reenters this function via kex_start_rekex(). + */ + return kex_start_rekex(ssh); } + return 0; } /* rekeying starts with sending KEXINIT */ @@ -1244,10 +1353,22 @@ ssh_packet_send2(struct ssh *ssh) state->rekey_time = monotime(); while ((p = TAILQ_FIRST(&state->outgoing))) { type = p->type; + /* + * If this packet triggers a rekex, then skip the + * remaining packets in the queue for now. + * NB. re-enters this function via kex_start_rekex. + */ + if (ssh_packet_need_rekeying(ssh, + sshbuf_len(p->payload))) { + debug3("%s: queued packet triggered rekex", + __func__); + return kex_start_rekex(ssh); + } debug("dequeue packet: %u", type); sshbuf_free(state->outgoing_packet); state->outgoing_packet = p->payload; TAILQ_REMOVE(&state->outgoing, p, next); + memset(p, 0, sizeof(*p)); free(p); if ((r = ssh_packet_send2_wrapped(ssh)) != 0) return r; @@ -1266,7 +1387,7 @@ int ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; - int len, r, ms_remain, cont; + int len, r, ms_remain; fd_set *setp; char buf[8192]; struct timeval timeout, start, *timeoutp = NULL; @@ -1336,11 +1457,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) if (r == 0) return SSH_ERR_CONN_TIMEOUT; /* Read data from the socket. */ - do { - cont = 0; - len = roaming_read(state->connection_in, buf, - sizeof(buf), &cont); - } while (len == 0 && cont); + len = read(state->connection_in, buf, sizeof(buf)); if (len == 0) { r = SSH_ERR_CONN_CLOSED; goto out; @@ -1735,6 +1852,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) */ if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) goto out; + if (ssh_packet_log_type(*typep)) + debug3("receive packet: type %u", *typep); if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) { if ((r = sshpkt_disconnect(ssh, "Invalid ssh2 packet type: %d", *typep)) != 0 || @@ -1754,6 +1873,13 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) #endif /* reset for next packet */ state->packlen = 0; + + /* do we need to rekey? */ + if (ssh_packet_need_rekeying(ssh, 0)) { + debug3("%s: rekex triggered", __func__); + if ((r = kex_start_rekex(ssh)) != 0) + return r; + } out: return r; } @@ -1784,8 +1910,7 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { - if (msg) - free(msg); + free(msg); return r; } debug("Remote: %.900s", msg); @@ -1799,8 +1924,9 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) do_log2(ssh->state->server_side && reason == SSH2_DISCONNECT_BY_APPLICATION ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, - "Received disconnect from %s: %u: %.400s", - ssh_remote_ipaddr(ssh), reason, msg); + "Received disconnect from %s port %d:" + "%u: %.400s", ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), reason, msg); free(msg); return SSH_ERR_DISCONNECTED; case SSH2_MSG_UNIMPLEMENTED: @@ -1828,8 +1954,9 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) case SSH_MSG_DISCONNECT: if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0) return r; - logit("Received disconnect from %s: %.400s", - ssh_remote_ipaddr(ssh), msg); + logit("Received disconnect from %s port %d: " + "%.400s", ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), msg); free(msg); return SSH_ERR_DISCONNECTED; default: @@ -1919,19 +2046,22 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r) { switch (r) { case SSH_ERR_CONN_CLOSED: - logit("Connection closed by %.200s", ssh_remote_ipaddr(ssh)); + logit("Connection closed by %.200s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); cleanup_exit(255); case SSH_ERR_CONN_TIMEOUT: - logit("Connection to %.200s timed out", ssh_remote_ipaddr(ssh)); + logit("Connection %s %.200s port %d timed out", + ssh->state->server_side ? "from" : "to", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); cleanup_exit(255); case SSH_ERR_DISCONNECTED: - logit("Disconnected from %.200s", - ssh_remote_ipaddr(ssh)); + logit("Disconnected from %.200s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); cleanup_exit(255); case SSH_ERR_SYSTEM_ERROR: if (errno == ECONNRESET) { - logit("Connection reset by %.200s", - ssh_remote_ipaddr(ssh)); + logit("Connection reset by %.200s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); cleanup_exit(255); } /* FALLTHROUGH */ @@ -1941,15 +2071,17 @@ sshpkt_fatal(struct ssh *ssh, const char *tag, int r) case SSH_ERR_NO_KEX_ALG_MATCH: case SSH_ERR_NO_HOSTKEY_ALG_MATCH: if (ssh && ssh->kex && ssh->kex->failed_choice) { - fatal("Unable to negotiate with %.200s: %s. " + fatal("Unable to negotiate with %.200s port %d: %s. " "Their offer: %s", ssh_remote_ipaddr(ssh), - ssh_err(r), ssh->kex->failed_choice); + ssh_remote_port(ssh), ssh_err(r), + ssh->kex->failed_choice); } /* FALLTHROUGH */ default: - fatal("%s%sConnection to %.200s: %s", + fatal("%s%sConnection %s %.200s port %d: %s", tag != NULL ? tag : "", tag != NULL ? ": " : "", - ssh_remote_ipaddr(ssh), ssh_err(r)); + ssh->state->server_side ? "from" : "to", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_err(r)); } } @@ -2006,19 +2138,18 @@ ssh_packet_write_poll(struct ssh *ssh) { struct session_state *state = ssh->state; int len = sshbuf_len(state->output); - int cont, r; + int r; if (len > 0) { - cont = 0; - len = roaming_write(state->connection_out, - sshbuf_ptr(state->output), len, &cont); + len = write(state->connection_out, + sshbuf_ptr(state->output), len); if (len == -1) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) return 0; return SSH_ERR_SYSTEM_ERROR; } - if (len == 0 && !cont) + if (len == 0) return SSH_ERR_CONN_CLOSED; if ((r = sshbuf_consume(state->output, len)) != 0) return r; @@ -2042,7 +2173,10 @@ ssh_packet_write_wait(struct ssh *ssh) NFDBITS), sizeof(fd_mask)); if (setp == NULL) return SSH_ERR_ALLOC_FAIL; - ssh_packet_write_poll(ssh); + if ((r = ssh_packet_write_poll(ssh)) != 0) { + free(setp); + return r; + } while (ssh_packet_have_data_to_write(ssh)) { memset(setp, 0, howmany(state->connection_out + 1, NFDBITS) * sizeof(fd_mask)); @@ -2230,29 +2364,10 @@ ssh_packet_send_ignore(struct ssh *ssh, int nbytes) } } -#define MAX_PACKETS (1U<<31) -int -ssh_packet_need_rekeying(struct ssh *ssh) -{ - struct session_state *state = ssh->state; - - if (ssh->compat & SSH_BUG_NOREKEY) - return 0; - return - (state->p_send.packets > MAX_PACKETS) || - (state->p_read.packets > MAX_PACKETS) || - (state->max_blocks_out && - (state->p_send.blocks > state->max_blocks_out)) || - (state->max_blocks_in && - (state->p_read.blocks > state->max_blocks_in)) || - (state->rekey_interval != 0 && state->rekey_time + - state->rekey_interval <= monotime()); -} - void -ssh_packet_set_rekey_limits(struct ssh *ssh, u_int32_t bytes, time_t seconds) +ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, time_t seconds) { - debug3("rekey after %lld bytes, %d seconds", (long long)bytes, + debug3("rekey after %llu bytes, %d seconds", (unsigned long long)bytes, (int)seconds); ssh->state->rekey_limit = bytes; ssh->state->rekey_interval = seconds; @@ -2292,58 +2407,6 @@ ssh_packet_get_output(struct ssh *ssh) return (void *)ssh->state->output; } -/* XXX TODO update roaming to new API (does not work anyway) */ -/* - * Save the state for the real connection, and use a separate state when - * resuming a suspended connection. - */ -void -ssh_packet_backup_state(struct ssh *ssh, - struct ssh *backup_state) -{ - struct ssh *tmp; - - close(ssh->state->connection_in); - ssh->state->connection_in = -1; - close(ssh->state->connection_out); - ssh->state->connection_out = -1; - if (backup_state) - tmp = backup_state; - else - tmp = ssh_alloc_session_state(); - backup_state = ssh; - ssh = tmp; -} - -/* XXX FIXME FIXME FIXME */ -/* - * Swap in the old state when resuming a connecion. - */ -void -ssh_packet_restore_state(struct ssh *ssh, - struct ssh *backup_state) -{ - struct ssh *tmp; - u_int len; - int r; - - tmp = backup_state; - backup_state = ssh; - ssh = tmp; - ssh->state->connection_in = backup_state->state->connection_in; - backup_state->state->connection_in = -1; - ssh->state->connection_out = backup_state->state->connection_out; - backup_state->state->connection_out = -1; - len = sshbuf_len(backup_state->state->input); - if (len > 0) { - if ((r = sshbuf_putb(ssh->state->input, - backup_state->state->input)) != 0) - fatal("%s: %s", __func__, ssh_err(r)); - sshbuf_reset(backup_state->state->input); - add_recv_bytes(len); - } -} - /* Reset after_authentication and reset compression in post-auth privsep */ static int ssh_packet_set_postauth(struct ssh *ssh) @@ -2431,8 +2494,7 @@ newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode) goto out; r = sshbuf_put_stringb(m, b); out: - if (b != NULL) - sshbuf_free(b); + sshbuf_free(b); return r; } @@ -2463,7 +2525,7 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) if ((r = kex_to_blob(m, ssh->kex)) != 0 || (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 || (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 || - (r = sshbuf_put_u32(m, state->rekey_limit)) != 0 || + (r = sshbuf_put_u64(m, state->rekey_limit)) != 0 || (r = sshbuf_put_u32(m, state->rekey_interval)) != 0 || (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 || (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 || @@ -2494,11 +2556,6 @@ ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) (r = sshbuf_put_stringb(m, state->output)) != 0) return r; - if (compat20) { - if ((r = sshbuf_put_u64(m, get_sent_bytes())) != 0 || - (r = sshbuf_put_u64(m, get_recv_bytes())) != 0) - return r; - } return 0; } @@ -2567,10 +2624,8 @@ newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) newkey = NULL; r = 0; out: - if (newkey != NULL) - free(newkey); - if (b != NULL) - sshbuf_free(b); + free(newkey); + sshbuf_free(b); return r; } @@ -2603,10 +2658,8 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp) out: if (r != 0 || kexp == NULL) { if (kex != NULL) { - if (kex->my != NULL) - sshbuf_free(kex->my); - if (kex->peer != NULL) - sshbuf_free(kex->peer); + sshbuf_free(kex->my); + sshbuf_free(kex->peer); free(kex); } if (kexp != NULL) @@ -2629,7 +2682,6 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) size_t ssh1keylen, rlen, slen, ilen, olen; int r; u_int ssh1cipher = 0; - u_int64_t sent_bytes = 0, recv_bytes = 0; if (!compat20) { if ((r = sshbuf_get_u32(m, &state->remote_protocol_flags)) != 0 || @@ -2652,7 +2704,7 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) if ((r = kex_from_blob(m, &ssh->kex)) != 0 || (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 || (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 || - (r = sshbuf_get_u32(m, &state->rekey_limit)) != 0 || + (r = sshbuf_get_u64(m, &state->rekey_limit)) != 0 || (r = sshbuf_get_u32(m, &state->rekey_interval)) != 0 || (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 || (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 || @@ -2694,12 +2746,6 @@ ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) (r = sshbuf_put(state->output, output, olen)) != 0) return r; - if (compat20) { - if ((r = sshbuf_get_u64(m, &sent_bytes)) != 0 || - (r = sshbuf_get_u64(m, &recv_bytes)) != 0) - return r; - roam_set_bytes(sent_bytes, recv_bytes); - } if (sshbuf_len(m)) return SSH_ERR_INVALID_FORMAT; debug3("%s: done", __func__); |