version 1.93, 2002/03/24 16:01:13 |
version 1.93.2.3, 2003/04/03 22:35:17 |
|
|
#include "log.h" |
#include "log.h" |
#include "canohost.h" |
#include "canohost.h" |
#include "misc.h" |
#include "misc.h" |
|
#include "ssh.h" |
|
|
#ifdef PACKET_DEBUG |
#ifdef PACKET_DEBUG |
#define DBG(x) x |
#define DBG(x) x |
|
|
static u_int32_t read_seqnr = 0; |
static u_int32_t read_seqnr = 0; |
static u_int32_t send_seqnr = 0; |
static u_int32_t send_seqnr = 0; |
|
|
|
/* Session key for protocol v1 */ |
|
static u_char ssh1_key[SSH_SESSION_KEY_LENGTH]; |
|
static u_int ssh1_keylen; |
|
|
/* roundup current message to extra_pad bytes */ |
/* roundup current message to extra_pad bytes */ |
static u_char extra_pad = 0; |
static u_char extra_pad = 0; |
|
|
|
|
packet_set_connection(int fd_in, int fd_out) |
packet_set_connection(int fd_in, int fd_out) |
{ |
{ |
Cipher *none = cipher_by_name("none"); |
Cipher *none = cipher_by_name("none"); |
|
|
if (none == NULL) |
if (none == NULL) |
fatal("packet_set_connection: cannot load cipher 'none'"); |
fatal("packet_set_connection: cannot load cipher 'none'"); |
connection_in = fd_in; |
connection_in = fd_in; |
|
|
else if (mode == MODE_OUT) |
else if (mode == MODE_OUT) |
send_seqnr = seqnr; |
send_seqnr = seqnr; |
else |
else |
fatal("%s: bad mode %d", __FUNCTION__, mode); |
fatal("packet_set_seqnr: bad mode %d", mode); |
} |
} |
|
|
/* returns 1 if connection is via ipv4 */ |
/* returns 1 if connection is via ipv4 */ |
|
|
* key is used for both sending and reception. However, both directions are |
* key is used for both sending and reception. However, both directions are |
* encrypted independently of each other. |
* encrypted independently of each other. |
*/ |
*/ |
|
|
void |
void |
packet_set_encryption_key(const u_char *key, u_int keylen, |
packet_set_encryption_key(const u_char *key, u_int keylen, |
int number) |
int number) |
{ |
{ |
Cipher *cipher = cipher_by_number(number); |
Cipher *cipher = cipher_by_number(number); |
|
|
if (cipher == NULL) |
if (cipher == NULL) |
fatal("packet_set_encryption_key: unknown cipher number %d", number); |
fatal("packet_set_encryption_key: unknown cipher number %d", number); |
if (keylen < 20) |
if (keylen < 20) |
fatal("packet_set_encryption_key: keylen too small: %d", keylen); |
fatal("packet_set_encryption_key: keylen too small: %d", keylen); |
|
if (keylen > SSH_SESSION_KEY_LENGTH) |
|
fatal("packet_set_encryption_key: keylen too big: %d", keylen); |
|
memcpy(ssh1_key, key, keylen); |
|
ssh1_keylen = keylen; |
cipher_init(&send_context, cipher, key, keylen, NULL, 0, CIPHER_ENCRYPT); |
cipher_init(&send_context, cipher, key, keylen, NULL, 0, CIPHER_ENCRYPT); |
cipher_init(&receive_context, cipher, key, keylen, NULL, 0, CIPHER_DECRYPT); |
cipher_init(&receive_context, cipher, key, keylen, NULL, 0, CIPHER_DECRYPT); |
} |
} |
|
|
|
u_int |
|
packet_get_encryption_key(u_char *key) |
|
{ |
|
if (key == NULL) |
|
return (ssh1_keylen); |
|
memcpy(key, ssh1_key, ssh1_keylen); |
|
return (ssh1_keylen); |
|
} |
|
|
/* Start constructing a packet to send. */ |
/* Start constructing a packet to send. */ |
void |
void |
packet_start(u_char type) |
packet_start(u_char type) |
|
|
packet_put_char(int value) |
packet_put_char(int value) |
{ |
{ |
char ch = value; |
char ch = value; |
|
|
buffer_append(&outgoing_packet, &ch, 1); |
buffer_append(&outgoing_packet, &ch, 1); |
} |
} |
void |
void |
|
|
CipherContext *cc; |
CipherContext *cc; |
int encrypt; |
int encrypt; |
|
|
debug("newkeys: mode %d", mode); |
debug2("set_newkeys: mode %d", mode); |
|
|
if (mode == MODE_OUT) { |
if (mode == MODE_OUT) { |
cc = &send_context; |
cc = &send_context; |
|
|
encrypt = CIPHER_DECRYPT; |
encrypt = CIPHER_DECRYPT; |
} |
} |
if (newkeys[mode] != NULL) { |
if (newkeys[mode] != NULL) { |
debug("newkeys: rekeying"); |
debug("set_newkeys: rekeying"); |
cipher_cleanup(cc); |
cipher_cleanup(cc); |
enc = &newkeys[mode]->enc; |
enc = &newkeys[mode]->enc; |
mac = &newkeys[mode]->mac; |
mac = &newkeys[mode]->mac; |
|
|
cp = buffer_ptr(&input); |
cp = buffer_ptr(&input); |
len = GET_32BIT(cp); |
len = GET_32BIT(cp); |
if (len < 1 + 2 + 2 || len > 256 * 1024) |
if (len < 1 + 2 + 2 || len > 256 * 1024) |
packet_disconnect("Bad packet length %d.", len); |
packet_disconnect("Bad packet length %u.", len); |
padded_len = (len + 8) & ~7; |
padded_len = (len + 8) & ~7; |
|
|
/* Check if the packet has been entirely received. */ |
/* Check if the packet has been entirely received. */ |
|
|
packet_length = GET_32BIT(cp); |
packet_length = GET_32BIT(cp); |
if (packet_length < 1 + 4 || packet_length > 256 * 1024) { |
if (packet_length < 1 + 4 || packet_length > 256 * 1024) { |
buffer_dump(&incoming_packet); |
buffer_dump(&incoming_packet); |
packet_disconnect("Bad packet length %d.", packet_length); |
packet_disconnect("Bad packet length %u.", packet_length); |
} |
} |
DBG(debug("input: packet len %d", packet_length+4)); |
DBG(debug("input: packet len %u", packet_length+4)); |
buffer_consume(&input, block_size); |
buffer_consume(&input, block_size); |
} |
} |
/* we have a partial packet of block_size bytes */ |
/* we have a partial packet of block_size bytes */ |
|
|
buffer_clear(&incoming_packet); |
buffer_clear(&incoming_packet); |
buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), |
buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), |
buffer_len(&compression_buffer)); |
buffer_len(&compression_buffer)); |
DBG(debug("input: len after de-compress %d", buffer_len(&incoming_packet))); |
DBG(debug("input: len after de-compress %d", |
|
buffer_len(&incoming_packet))); |
} |
} |
/* |
/* |
* get packet type, implies consume. |
* get packet type, implies consume. |
|
|
int |
int |
packet_read_poll_seqnr(u_int32_t *seqnr_p) |
packet_read_poll_seqnr(u_int32_t *seqnr_p) |
{ |
{ |
int reason, seqnr; |
u_int reason, seqnr; |
u_char type; |
u_char type; |
char *msg; |
char *msg; |
|
|
|
|
case SSH2_MSG_DISCONNECT: |
case SSH2_MSG_DISCONNECT: |
reason = packet_get_int(); |
reason = packet_get_int(); |
msg = packet_get_string(NULL); |
msg = packet_get_string(NULL); |
log("Received disconnect from %s: %d: %.400s", get_remote_ipaddr(), |
log("Received disconnect from %s: %u: %.400s", |
reason, msg); |
get_remote_ipaddr(), reason, msg); |
xfree(msg); |
xfree(msg); |
fatal_cleanup(); |
fatal_cleanup(); |
break; |
break; |
case SSH2_MSG_UNIMPLEMENTED: |
case SSH2_MSG_UNIMPLEMENTED: |
seqnr = packet_get_int(); |
seqnr = packet_get_int(); |
debug("Received SSH2_MSG_UNIMPLEMENTED for %d", seqnr); |
debug("Received SSH2_MSG_UNIMPLEMENTED for %u", |
|
seqnr); |
break; |
break; |
default: |
default: |
return type; |
return type; |
|
|
break; |
break; |
case SSH_MSG_DISCONNECT: |
case SSH_MSG_DISCONNECT: |
msg = packet_get_string(NULL); |
msg = packet_get_string(NULL); |
log("Received disconnect from %s: %.400s", get_remote_ipaddr(), |
log("Received disconnect from %s: %.400s", |
msg); |
get_remote_ipaddr(), msg); |
fatal_cleanup(); |
fatal_cleanup(); |
xfree(msg); |
xfree(msg); |
break; |
break; |
|
|
packet_get_char(void) |
packet_get_char(void) |
{ |
{ |
char ch; |
char ch; |
|
|
buffer_get(&incoming_packet, &ch, 1); |
buffer_get(&incoming_packet, &ch, 1); |
return (u_char) ch; |
return (u_char) ch; |
} |
} |
|
|
packet_get_raw(int *length_ptr) |
packet_get_raw(int *length_ptr) |
{ |
{ |
int bytes = buffer_len(&incoming_packet); |
int bytes = buffer_len(&incoming_packet); |
|
|
if (length_ptr != NULL) |
if (length_ptr != NULL) |
*length_ptr = bytes; |
*length_ptr = bytes; |
return buffer_ptr(&incoming_packet); |
return buffer_ptr(&incoming_packet); |
|
|
char buf[1024]; |
char buf[1024]; |
va_list args; |
va_list args; |
static int disconnecting = 0; |
static int disconnecting = 0; |
|
|
if (disconnecting) /* Guard against recursive invocations. */ |
if (disconnecting) /* Guard against recursive invocations. */ |
fatal("packet_disconnect called recursively."); |
fatal("packet_disconnect called recursively."); |
disconnecting = 1; |
disconnecting = 1; |
|
|
vsnprintf(buf, sizeof(buf), fmt, args); |
vsnprintf(buf, sizeof(buf), fmt, args); |
va_end(args); |
va_end(args); |
|
|
|
/* Display the error locally */ |
|
log("Disconnecting: %.100s", buf); |
|
|
/* Send the disconnect message to the other side, and wait for it to get sent. */ |
/* Send the disconnect message to the other side, and wait for it to get sent. */ |
if (compat20) { |
if (compat20) { |
packet_start(SSH2_MSG_DISCONNECT); |
packet_start(SSH2_MSG_DISCONNECT); |
|
|
/* Close the connection. */ |
/* Close the connection. */ |
packet_close(); |
packet_close(); |
|
|
/* Display the error locally and exit. */ |
|
log("Disconnecting: %.100s", buf); |
|
fatal_cleanup(); |
fatal_cleanup(); |
} |
} |
|
|
|
|
packet_write_poll(void) |
packet_write_poll(void) |
{ |
{ |
int len = buffer_len(&output); |
int len = buffer_len(&output); |
|
|
if (len > 0) { |
if (len > 0) { |
len = write(connection_out, buffer_ptr(&output), len); |
len = write(connection_out, buffer_ptr(&output), len); |
if (len <= 0) { |
if (len <= 0) { |
|
|
return buffer_len(&output) < 128 * 1024; |
return buffer_len(&output) < 128 * 1024; |
} |
} |
|
|
|
static void |
|
packet_set_tos(int interactive) |
|
{ |
|
int tos = interactive ? IPTOS_LOWDELAY : IPTOS_THROUGHPUT; |
|
|
|
if (!packet_connection_is_on_socket() || |
|
!packet_connection_is_ipv4()) |
|
return; |
|
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, &tos, |
|
sizeof(tos)) < 0) |
|
error("setsockopt IP_TOS %d: %.100s:", |
|
tos, strerror(errno)); |
|
} |
|
|
/* Informs that the current session is interactive. Sets IP flags for that. */ |
/* Informs that the current session is interactive. Sets IP flags for that. */ |
|
|
void |
void |
packet_set_interactive(int interactive) |
packet_set_interactive(int interactive) |
{ |
{ |
static int called = 0; |
static int called = 0; |
int lowdelay = IPTOS_LOWDELAY; |
|
int throughput = IPTOS_THROUGHPUT; |
|
|
|
if (called) |
if (called) |
return; |
return; |
|
|
/* Only set socket options if using a socket. */ |
/* Only set socket options if using a socket. */ |
if (!packet_connection_is_on_socket()) |
if (!packet_connection_is_on_socket()) |
return; |
return; |
/* |
if (interactive) |
* IPTOS_LOWDELAY and IPTOS_THROUGHPUT are IPv4 only |
|
*/ |
|
if (interactive) { |
|
/* |
|
* Set IP options for an interactive connection. Use |
|
* IPTOS_LOWDELAY and TCP_NODELAY. |
|
*/ |
|
if (packet_connection_is_ipv4()) { |
|
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, |
|
&lowdelay, sizeof(lowdelay)) < 0) |
|
error("setsockopt IPTOS_LOWDELAY: %.100s", |
|
strerror(errno)); |
|
} |
|
set_nodelay(connection_in); |
set_nodelay(connection_in); |
} else if (packet_connection_is_ipv4()) { |
packet_set_tos(interactive); |
/* |
|
* Set IP options for a non-interactive connection. Use |
|
* IPTOS_THROUGHPUT. |
|
*/ |
|
if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, &throughput, |
|
sizeof(throughput)) < 0) |
|
error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); |
|
} |
|
} |
} |
|
|
/* Returns true if the current connection is interactive. */ |
/* Returns true if the current connection is interactive. */ |
|
|
packet_set_maxsize(int s) |
packet_set_maxsize(int s) |
{ |
{ |
static int called = 0; |
static int called = 0; |
|
|
if (called) { |
if (called) { |
log("packet_set_maxsize: called twice: old %d new %d", |
log("packet_set_maxsize: called twice: old %d new %d", |
max_packet_size, s); |
max_packet_size, s); |