version 1.204, 2015/01/28 21:15:47 |
version 1.205, 2015/01/30 01:13:33 |
|
|
const struct sshcipher *none = cipher_by_name("none"); |
const struct sshcipher *none = cipher_by_name("none"); |
int r; |
int r; |
|
|
if (none == NULL) |
if (none == NULL) { |
fatal("%s: cannot load cipher 'none'", __func__); |
error("%s: cannot load cipher 'none'", __func__); |
|
return NULL; |
|
} |
if (ssh == NULL) |
if (ssh == NULL) |
ssh = ssh_alloc_session_state(); |
ssh = ssh_alloc_session_state(); |
if (ssh == NULL) |
if (ssh == NULL) { |
fatal("%s: cound not allocate state", __func__); |
error("%s: cound not allocate state", __func__); |
|
return NULL; |
|
} |
state = ssh->state; |
state = ssh->state; |
state->connection_in = fd_in; |
state->connection_in = fd_in; |
state->connection_out = fd_out; |
state->connection_out = fd_out; |
if ((r = cipher_init(&state->send_context, none, |
if ((r = cipher_init(&state->send_context, none, |
(const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || |
(const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || |
(r = cipher_init(&state->receive_context, none, |
(r = cipher_init(&state->receive_context, none, |
(const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) |
(const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) { |
fatal("%s: cipher_init failed: %s", __func__, ssh_err(r)); |
error("%s: cipher_init failed: %s", __func__, ssh_err(r)); |
|
return NULL; |
|
} |
state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; |
state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; |
deattack_init(&state->deattack); |
deattack_init(&state->deattack); |
return ssh; |
return ssh; |
|
|
|
|
/* |
/* |
* Note that the packet is now only buffered in output. It won't be |
* Note that the packet is now only buffered in output. It won't be |
* actually sent until packet_write_wait or packet_write_poll is |
* actually sent until ssh_packet_write_wait or ssh_packet_write_poll |
* called. |
* is called. |
*/ |
*/ |
r = 0; |
r = 0; |
out: |
out: |
|
|
if (setp == NULL) |
if (setp == NULL) |
return SSH_ERR_ALLOC_FAIL; |
return SSH_ERR_ALLOC_FAIL; |
|
|
/* Since we are blocking, ensure that all written packets have been sent. */ |
/* |
ssh_packet_write_wait(ssh); |
* Since we are blocking, ensure that all written packets have |
|
* been sent. |
|
*/ |
|
if ((r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
|
/* Stay in the loop until we have received a complete packet. */ |
/* Stay in the loop until we have received a complete packet. */ |
for (;;) { |
for (;;) { |
|
|
* that given, and gives a fatal error and exits if there is a mismatch. |
* that given, and gives a fatal error and exits if there is a mismatch. |
*/ |
*/ |
|
|
void |
int |
ssh_packet_read_expect(struct ssh *ssh, int expected_type) |
ssh_packet_read_expect(struct ssh *ssh, u_int expected_type) |
{ |
{ |
int type; |
int r; |
|
u_char type; |
|
|
type = ssh_packet_read(ssh); |
if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) |
if (type != expected_type) |
return r; |
ssh_packet_disconnect(ssh, |
if (type != expected_type) { |
|
if ((r = sshpkt_disconnect(ssh, |
"Protocol error: expected packet type %d, got %d", |
"Protocol error: expected packet type %d, got %d", |
expected_type, type); |
expected_type, type)) != 0) |
|
return r; |
|
return SSH_ERR_PROTOCOL_ERROR; |
|
} |
|
return 0; |
} |
} |
|
|
/* Checks if a full packet is available in the data received so far via |
/* Checks if a full packet is available in the data received so far via |
|
|
{ |
{ |
struct session_state *state = ssh->state; |
struct session_state *state = ssh->state; |
u_int len, padded_len; |
u_int len, padded_len; |
|
const char *emsg; |
const u_char *cp; |
const u_char *cp; |
u_char *p; |
u_char *p; |
u_int checksum, stored_checksum; |
u_int checksum, stored_checksum; |
|
|
return 0; |
return 0; |
/* Get length of incoming packet. */ |
/* Get length of incoming packet. */ |
len = PEEK_U32(sshbuf_ptr(state->input)); |
len = PEEK_U32(sshbuf_ptr(state->input)); |
if (len < 1 + 2 + 2 || len > 256 * 1024) |
if (len < 1 + 2 + 2 || len > 256 * 1024) { |
ssh_packet_disconnect(ssh, "Bad packet length %u.", |
if ((r = sshpkt_disconnect(ssh, "Bad packet length %u", |
len); |
len)) != 0) |
|
return r; |
|
return SSH_ERR_CONN_CORRUPT; |
|
} |
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. */ |
|
|
* Ariel Futoransky(futo@core-sdi.com) |
* Ariel Futoransky(futo@core-sdi.com) |
*/ |
*/ |
if (!state->receive_context.plaintext) { |
if (!state->receive_context.plaintext) { |
|
emsg = NULL; |
switch (detect_attack(&state->deattack, |
switch (detect_attack(&state->deattack, |
sshbuf_ptr(state->input), padded_len)) { |
sshbuf_ptr(state->input), padded_len)) { |
case DEATTACK_OK: |
case DEATTACK_OK: |
break; |
break; |
case DEATTACK_DETECTED: |
case DEATTACK_DETECTED: |
ssh_packet_disconnect(ssh, |
emsg = "crc32 compensation attack detected"; |
"crc32 compensation attack: network attack detected" |
break; |
); |
|
case DEATTACK_DOS_DETECTED: |
case DEATTACK_DOS_DETECTED: |
ssh_packet_disconnect(ssh, |
emsg = "deattack denial of service detected"; |
"deattack denial of service detected"); |
break; |
default: |
default: |
ssh_packet_disconnect(ssh, "deattack error"); |
emsg = "deattack error"; |
|
break; |
} |
} |
|
if (emsg != NULL) { |
|
error("%s", emsg); |
|
if ((r = sshpkt_disconnect(ssh, "%s", emsg)) != 0 || |
|
(r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
return SSH_ERR_CONN_CORRUPT; |
|
} |
} |
} |
|
|
/* Decrypt data to incoming_packet. */ |
/* Decrypt data to incoming_packet. */ |
|
|
goto out; |
goto out; |
|
|
/* Test check bytes. */ |
/* Test check bytes. */ |
if (len != sshbuf_len(state->incoming_packet)) |
if (len != sshbuf_len(state->incoming_packet)) { |
ssh_packet_disconnect(ssh, |
error("%s: len %d != sshbuf_len %zd", __func__, |
"packet_read_poll1: len %d != sshbuf_len %zd.", |
|
len, sshbuf_len(state->incoming_packet)); |
len, sshbuf_len(state->incoming_packet)); |
|
if ((r = sshpkt_disconnect(ssh, "invalid packet length")) != 0 || |
|
(r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
return SSH_ERR_CONN_CORRUPT; |
|
} |
|
|
cp = sshbuf_ptr(state->incoming_packet) + len - 4; |
cp = sshbuf_ptr(state->incoming_packet) + len - 4; |
stored_checksum = PEEK_U32(cp); |
stored_checksum = PEEK_U32(cp); |
if (checksum != stored_checksum) |
if (checksum != stored_checksum) { |
ssh_packet_disconnect(ssh, |
error("Corrupted check bytes on input"); |
"Corrupted check bytes on input."); |
if ((r = sshpkt_disconnect(ssh, "connection corrupted")) != 0 || |
|
(r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
return SSH_ERR_CONN_CORRUPT; |
|
} |
if ((r = sshbuf_consume_end(state->incoming_packet, 4)) < 0) |
if ((r = sshbuf_consume_end(state->incoming_packet, 4)) < 0) |
goto out; |
goto out; |
|
|
|
|
state->p_read.bytes += padded_len + 4; |
state->p_read.bytes += padded_len + 4; |
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) |
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) |
goto out; |
goto out; |
if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX) |
if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX) { |
ssh_packet_disconnect(ssh, |
error("Invalid ssh1 packet type: %d", *typep); |
"Invalid ssh1 packet type: %d", *typep); |
if ((r = sshpkt_disconnect(ssh, "invalid packet type")) != 0 || |
|
(r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
return SSH_ERR_PROTOCOL_ERROR; |
|
} |
r = 0; |
r = 0; |
out: |
out: |
return r; |
return r; |
|
|
if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) |
if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) |
goto out; |
goto out; |
} |
} |
/* XXX now it's safe to use fatal/packet_disconnect */ |
|
if (seqnr_p != NULL) |
if (seqnr_p != NULL) |
*seqnr_p = state->p_read.seqnr; |
*seqnr_p = state->p_read.seqnr; |
if (++state->p_read.seqnr == 0) |
if (++state->p_read.seqnr == 0) |
|
|
/* get padlen */ |
/* get padlen */ |
padlen = sshbuf_ptr(state->incoming_packet)[4]; |
padlen = sshbuf_ptr(state->incoming_packet)[4]; |
DBG(debug("input: padlen %d", padlen)); |
DBG(debug("input: padlen %d", padlen)); |
if (padlen < 4) |
if (padlen < 4) { |
ssh_packet_disconnect(ssh, |
if ((r = sshpkt_disconnect(ssh, |
"Corrupted padlen %d on input.", padlen); |
"Corrupted padlen %d on input.", padlen)) != 0 || |
|
(r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
return SSH_ERR_CONN_CORRUPT; |
|
} |
|
|
/* skip packet size + padlen, discard padding */ |
/* skip packet size + padlen, discard padding */ |
if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 || |
if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 || |
|
|
*/ |
*/ |
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) |
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) |
goto out; |
goto out; |
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) |
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) { |
ssh_packet_disconnect(ssh, |
if ((r = sshpkt_disconnect(ssh, |
"Invalid ssh2 packet type: %d", *typep); |
"Invalid ssh2 packet type: %d", *typep)) != 0 || |
|
(r = ssh_packet_write_wait(ssh)) != 0) |
|
return r; |
|
return SSH_ERR_PROTOCOL_ERROR; |
|
} |
if (*typep == SSH2_MSG_NEWKEYS) |
if (*typep == SSH2_MSG_NEWKEYS) |
r = ssh_set_newkeys(ssh, MODE_IN); |
r = ssh_set_newkeys(ssh, MODE_IN); |
else if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side) |
else if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side) |
|
|
* message is printed immediately, but only if the client is being executed |
* message is printed immediately, but only if the client is being executed |
* in verbose mode. These messages are primarily intended to ease debugging |
* in verbose mode. These messages are primarily intended to ease debugging |
* authentication problems. The length of the formatted message must not |
* authentication problems. The length of the formatted message must not |
* exceed 1024 bytes. This will automatically call packet_write_wait. |
* exceed 1024 bytes. This will automatically call ssh_packet_write_wait. |
*/ |
*/ |
|
|
void |
void |
ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...) |
ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...) |
{ |
{ |
|
|
(r = sshpkt_send(ssh)) != 0) |
(r = sshpkt_send(ssh)) != 0) |
fatal("%s: %s", __func__, ssh_err(r)); |
fatal("%s: %s", __func__, ssh_err(r)); |
} |
} |
ssh_packet_write_wait(ssh); |
if ((r = ssh_packet_write_wait(ssh)) != 0) |
|
fatal("%s: %s", __func__, ssh_err(r)); |
} |
} |
|
|
/* |
/* |
|
* Pretty-print connection-terminating errors and exit. |
|
*/ |
|
void |
|
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)); |
|
cleanup_exit(255); |
|
case SSH_ERR_CONN_TIMEOUT: |
|
logit("Connection to %.200s timed out while " |
|
"waiting to write", ssh_remote_ipaddr(ssh)); |
|
cleanup_exit(255); |
|
default: |
|
fatal("%s%sConnection to %.200s: %s", |
|
tag != NULL ? tag : "", tag != NULL ? ": " : "", |
|
ssh_remote_ipaddr(ssh), ssh_err(r)); |
|
} |
|
} |
|
|
|
/* |
* Logs the error plus constructs and sends a disconnect packet, closes the |
* Logs the error plus constructs and sends a disconnect packet, closes the |
* connection, and exits. This function never returns. The error message |
* connection, and exits. This function never returns. The error message |
* should not contain a newline. The length of the formatted message must |
* should not contain a newline. The length of the formatted message must |
* not exceed 1024 bytes. |
* not exceed 1024 bytes. |
*/ |
*/ |
|
|
void |
void |
ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...) |
ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...) |
{ |
{ |
|
|
/* Display the error locally */ |
/* Display the error locally */ |
logit("Disconnecting: %.100s", buf); |
logit("Disconnecting: %.100s", buf); |
|
|
/* Send the disconnect message to the other side, and wait for it to get sent. */ |
/* |
if (compat20) { |
* Send the disconnect message to the other side, and wait |
if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || |
* for it to get sent. |
(r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || |
*/ |
(r = sshpkt_put_cstring(ssh, buf)) != 0 || |
if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0) |
(r = sshpkt_put_cstring(ssh, "")) != 0 || |
sshpkt_fatal(ssh, __func__, r); |
(r = sshpkt_send(ssh)) != 0) |
|
fatal("%s: %s", __func__, ssh_err(r)); |
|
} else { |
|
if ((r = sshpkt_start(ssh, SSH_MSG_DISCONNECT)) != 0 || |
|
(r = sshpkt_put_cstring(ssh, buf)) != 0 || |
|
(r = sshpkt_send(ssh)) != 0) |
|
fatal("%s: %s", __func__, ssh_err(r)); |
|
} |
|
ssh_packet_write_wait(ssh); |
|
|
|
|
if ((r = ssh_packet_write_wait(ssh)) != 0) |
|
sshpkt_fatal(ssh, __func__, r); |
|
|
/* Close the connection. */ |
/* Close the connection. */ |
ssh_packet_close(ssh); |
ssh_packet_close(ssh); |
cleanup_exit(255); |
cleanup_exit(255); |
} |
} |
|
|
/* Checks if there is any buffered output, and tries to write some of the output. */ |
/* |
|
* Checks if there is any buffered output, and tries to write some of |
void |
* the output. |
|
*/ |
|
int |
ssh_packet_write_poll(struct ssh *ssh) |
ssh_packet_write_poll(struct ssh *ssh) |
{ |
{ |
struct session_state *state = ssh->state; |
struct session_state *state = ssh->state; |
|
|
sshbuf_ptr(state->output), len, &cont); |
sshbuf_ptr(state->output), len, &cont); |
if (len == -1) { |
if (len == -1) { |
if (errno == EINTR || errno == EAGAIN) |
if (errno == EINTR || errno == EAGAIN) |
return; |
return 0; |
fatal("Write failed: %.100s", strerror(errno)); |
return SSH_ERR_SYSTEM_ERROR; |
} |
} |
if (len == 0 && !cont) |
if (len == 0 && !cont) |
fatal("Write connection closed"); |
return SSH_ERR_CONN_CLOSED; |
if ((r = sshbuf_consume(state->output, len)) != 0) |
if ((r = sshbuf_consume(state->output, len)) != 0) |
fatal("%s: %s", __func__, ssh_err(r)); |
return r; |
} |
} |
|
return 0; |
} |
} |
|
|
/* |
/* |
* Calls packet_write_poll repeatedly until all pending output data has been |
* Calls packet_write_poll repeatedly until all pending output data has been |
* written. |
* written. |
*/ |
*/ |
|
int |
void |
|
ssh_packet_write_wait(struct ssh *ssh) |
ssh_packet_write_wait(struct ssh *ssh) |
{ |
{ |
fd_set *setp; |
fd_set *setp; |
int ret, ms_remain = 0; |
int ret, r, ms_remain = 0; |
struct timeval start, timeout, *timeoutp = NULL; |
struct timeval start, timeout, *timeoutp = NULL; |
struct session_state *state = ssh->state; |
struct session_state *state = ssh->state; |
|
|
setp = (fd_set *)calloc(howmany(state->connection_out + 1, |
setp = (fd_set *)calloc(howmany(state->connection_out + 1, |
NFDBITS), sizeof(fd_mask)); |
NFDBITS), sizeof(fd_mask)); |
if (setp == NULL) |
if (setp == NULL) |
fatal("%s: calloc failed", __func__); |
return SSH_ERR_ALLOC_FAIL; |
ssh_packet_write_poll(ssh); |
ssh_packet_write_poll(ssh); |
while (ssh_packet_have_data_to_write(ssh)) { |
while (ssh_packet_have_data_to_write(ssh)) { |
memset(setp, 0, howmany(state->connection_out + 1, |
memset(setp, 0, howmany(state->connection_out + 1, |
|
|
} |
} |
} |
} |
if (ret == 0) { |
if (ret == 0) { |
logit("Connection to %.200s timed out while " |
free(setp); |
"waiting to write", ssh_remote_ipaddr(ssh)); |
return SSH_ERR_CONN_TIMEOUT; |
cleanup_exit(255); |
|
} |
} |
ssh_packet_write_poll(ssh); |
if ((r = ssh_packet_write_poll(ssh)) != 0) { |
|
free(setp); |
|
return r; |
|
} |
} |
} |
free(setp); |
free(setp); |
|
return 0; |
} |
} |
|
|
/* Returns true if there is buffered data to write to the connection. */ |
/* Returns true if there is buffered data to write to the connection. */ |