+/* $OpenBSD: packet.c,v 1.148 2007/06/07 19:37:34 pvalchev Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
*/
#include "includes.h"
-RCSID("$OpenBSD: packet.c,v 1.112 2003/09/23 20:17:11 markus Exp $");
-
+
+#include <sys/types.h>
#include "openbsd-compat/sys-queue.h"
+#include <sys/param.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
#include "xmalloc.h"
#include "buffer.h"
#include "packet.h"
-#include "bufaux.h"
#include "crc32.h"
-#include "getput.h"
-
#include "compress.h"
#include "deattack.h"
#include "channels.h"
-
#include "compat.h"
#include "ssh1.h"
#include "ssh2.h"
-
#include "cipher.h"
+#include "key.h"
#include "kex.h"
#include "mac.h"
#include "log.h"
/* Set to true if the connection is interactive. */
static int interactive_mode = 0;
+/* Set to true if we are the server side. */
+static int server_side = 0;
+
+/* Set to true if we are authenticated. */
+static int after_authentication = 0;
+
/* Session key information for Encryption and MAC */
Newkeys *newkeys[MODE_MAX];
static struct packet_state {
fatal("packet_set_connection: cannot load cipher 'none'");
connection_in = fd_in;
connection_out = fd_out;
- cipher_init(&send_context, none, "", 0, NULL, 0, CIPHER_ENCRYPT);
- cipher_init(&receive_context, none, "", 0, NULL, 0, CIPHER_DECRYPT);
+ cipher_init(&send_context, none, (const u_char *)"",
+ 0, NULL, 0, CIPHER_ENCRYPT);
+ cipher_init(&receive_context, none, (const u_char *)"",
+ 0, NULL, 0, CIPHER_DECRYPT);
newkeys[MODE_IN] = newkeys[MODE_OUT] = NULL;
if (!initialized) {
initialized = 1;
return (cipher_get_keyiv_len(cc));
}
+
void
packet_set_iv(int mode, u_char *dat)
{
cipher_set_keyiv(cc, dat);
}
+
int
packet_get_ssh1_cipher(void)
{
packet_set_nonblocking(void)
{
/* Set the socket into non-blocking mode. */
- if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0)
- error("fcntl O_NONBLOCK: %.100s", strerror(errno));
+ set_nonblock(connection_in);
- if (connection_out != connection_in) {
- if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0)
- error("fcntl O_NONBLOCK: %.100s", strerror(errno));
- }
+ if (connection_out != connection_in)
+ set_nonblock(connection_out);
}
/* Returns the socket used for reading. */
buffer_append(&outgoing_packet, &ch, 1);
}
+
void
packet_put_int(u_int value)
{
buffer_put_int(&outgoing_packet, value);
}
+
void
packet_put_string(const void *buf, u_int len)
{
buffer_put_string(&outgoing_packet, buf, len);
}
+
void
packet_put_cstring(const char *str)
{
buffer_put_cstring(&outgoing_packet, str);
}
+
void
packet_put_raw(const void *buf, u_int len)
{
buffer_append(&outgoing_packet, buf, len);
}
+
void
packet_put_bignum(BIGNUM * value)
{
buffer_put_bignum(&outgoing_packet, value);
}
+
void
packet_put_bignum2(BIGNUM * value)
{
u_char buf[8], *cp;
int i, padding, len;
u_int checksum;
- u_int32_t rand = 0;
+ u_int32_t rnd = 0;
/*
* If using packet compression, compress the payload of the outgoing
cp = buffer_ptr(&outgoing_packet);
for (i = 0; i < padding; i++) {
if (i % 4 == 0)
- rand = arc4random();
- cp[7 - i] = rand & 0xff;
- rand >>= 8;
+ rnd = arc4random();
+ cp[7 - i] = rnd & 0xff;
+ rnd >>= 8;
}
}
buffer_consume(&outgoing_packet, 8 - padding);
/* Add check bytes. */
checksum = ssh_crc32(buffer_ptr(&outgoing_packet),
buffer_len(&outgoing_packet));
- PUT_32BIT(buf, checksum);
+ put_u32(buf, checksum);
buffer_append(&outgoing_packet, buf, 4);
#ifdef PACKET_DEBUG
#endif
/* Append to output. */
- PUT_32BIT(buf, len);
+ put_u32(buf, len);
buffer_append(&output, buf, 4);
cp = buffer_append_space(&output, buffer_len(&outgoing_packet));
cipher_crypt(&send_context, cp, buffer_ptr(&outgoing_packet),
buffer_clear(&outgoing_packet);
/*
- * 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
* called.
*/
Comp *comp;
CipherContext *cc;
u_int64_t *max_blocks;
- int encrypt;
+ int crypt_type;
debug2("set_newkeys: mode %d", mode);
if (mode == MODE_OUT) {
cc = &send_context;
- encrypt = CIPHER_ENCRYPT;
+ crypt_type = CIPHER_ENCRYPT;
p_send.packets = p_send.blocks = 0;
max_blocks = &max_blocks_out;
} else {
cc = &receive_context;
- encrypt = CIPHER_DECRYPT;
+ crypt_type = CIPHER_DECRYPT;
p_read.packets = p_read.blocks = 0;
max_blocks = &max_blocks_in;
}
enc = &newkeys[mode]->enc;
mac = &newkeys[mode]->mac;
comp = &newkeys[mode]->comp;
- memset(mac->key, 0, mac->key_len);
+ mac_clear(mac);
xfree(enc->name);
xfree(enc->iv);
xfree(enc->key);
enc = &newkeys[mode]->enc;
mac = &newkeys[mode]->mac;
comp = &newkeys[mode]->comp;
- if (mac->md != NULL)
+ if (mac_init(mac) == 0)
mac->enabled = 1;
DBG(debug("cipher_init_context: %d", mode));
cipher_init(cc, enc->cipher, enc->key, enc->key_len,
- enc->iv, enc->block_size, encrypt);
+ enc->iv, enc->block_size, crypt_type);
/* Deleting the keys does not gain extra security */
/* memset(enc->iv, 0, enc->block_size);
- memset(enc->key, 0, enc->key_len); */
- if (comp->type != 0 && comp->enabled == 0) {
+ memset(enc->key, 0, enc->key_len);
+ memset(mac->key, 0, mac->key_len); */
+ if ((comp->type == COMP_ZLIB ||
+ (comp->type == COMP_DELAYED && after_authentication)) &&
+ comp->enabled == 0) {
packet_init_compression();
if (mode == MODE_OUT)
buffer_compress_init_send(6);
*max_blocks = MIN(*max_blocks, rekey_limit / enc->block_size);
}
+/*
+ * Delayed compression for SSH2 is enabled after authentication:
+ * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent,
+ * and on the client side after a SSH2_MSG_USERAUTH_SUCCESS is received.
+ */
+static void
+packet_enable_delayed_compress(void)
+{
+ Comp *comp = NULL;
+ int mode;
+
+ /*
+ * Remember that we are past the authentication step, so rekeying
+ * with COMP_DELAYED will turn on compression immediately.
+ */
+ after_authentication = 1;
+ for (mode = 0; mode < MODE_MAX; mode++) {
+ /* protocol error: USERAUTH_SUCCESS received before NEWKEYS */
+ if (newkeys[mode] == NULL)
+ continue;
+ comp = &newkeys[mode]->comp;
+ if (comp && !comp->enabled && comp->type == COMP_DELAYED) {
+ packet_init_compression();
+ if (mode == MODE_OUT)
+ buffer_compress_init_send(6);
+ else
+ buffer_compress_init_recv();
+ comp->enabled = 1;
+ }
+ }
+}
+
/*
* Finalize packet in SSH2 format (compress, mac, encrypt, enqueue)
*/
u_char padlen, pad;
u_int packet_length = 0;
u_int i, len;
- u_int32_t rand = 0;
+ u_int32_t rnd = 0;
Enc *enc = NULL;
Mac *mac = NULL;
Comp *comp = NULL;
/* random padding */
for (i = 0; i < padlen; i++) {
if (i % 4 == 0)
- rand = arc4random();
- cp[i] = rand & 0xff;
- rand >>= 8;
+ rnd = arc4random();
+ cp[i] = rnd & 0xff;
+ rnd >>= 8;
}
} else {
/* clear padding */
/* packet_length includes payload, padding and padding length field */
packet_length = buffer_len(&outgoing_packet) - 4;
cp = buffer_ptr(&outgoing_packet);
- PUT_32BIT(cp, packet_length);
+ put_u32(cp, packet_length);
cp[4] = padlen;
DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen));
buffer_len(&outgoing_packet));
/* append unencrypted MAC */
if (mac && mac->enabled)
- buffer_append(&output, (char *)macbuf, mac->mac_len);
+ buffer_append(&output, macbuf, mac->mac_len);
#ifdef PACKET_DEBUG
fprintf(stderr, "encrypted: ");
buffer_dump(&output);
if (type == SSH2_MSG_NEWKEYS)
set_newkeys(MODE_OUT);
+ else if (type == SSH2_MSG_USERAUTH_SUCCESS && server_side)
+ packet_enable_delayed_compress();
}
static void
char buf[8192];
DBG(debug("packet_read()"));
- setp = (fd_set *)xmalloc(howmany(connection_in+1, NFDBITS) *
+ setp = (fd_set *)xcalloc(howmany(connection_in+1, NFDBITS),
sizeof(fd_mask));
/* Since we are blocking, ensure that all written packets have been sent. */
return SSH_MSG_NONE;
/* Get length of incoming packet. */
cp = buffer_ptr(&input);
- len = GET_32BIT(cp);
+ len = get_u32(cp);
if (len < 1 + 2 + 2 || len > 256 * 1024)
packet_disconnect("Bad packet length %u.", len);
padded_len = (len + 8) & ~7;
* (C)1998 CORE-SDI, Buenos Aires Argentina
* Ariel Futoransky(futo@core-sdi.com)
*/
- if (!receive_context.plaintext &&
- detect_attack(buffer_ptr(&input), padded_len, NULL) == DEATTACK_DETECTED)
- packet_disconnect("crc32 compensation attack: network attack detected");
+ if (!receive_context.plaintext) {
+ switch (detect_attack(buffer_ptr(&input), padded_len)) {
+ case DEATTACK_DETECTED:
+ packet_disconnect("crc32 compensation attack: "
+ "network attack detected");
+ case DEATTACK_DOS_DETECTED:
+ packet_disconnect("deattack denial of "
+ "service detected");
+ }
+ }
/* Decrypt data to incoming_packet. */
buffer_clear(&incoming_packet);
len, buffer_len(&incoming_packet));
cp = (u_char *)buffer_ptr(&incoming_packet) + len - 4;
- stored_checksum = GET_32BIT(cp);
+ stored_checksum = get_u32(cp);
if (checksum != stored_checksum)
packet_disconnect("Corrupted check bytes on input.");
buffer_consume_end(&incoming_packet, 4);
buffer_len(&compression_buffer));
}
type = buffer_get_char(&incoming_packet);
+ if (type < SSH_MSG_MIN || type > SSH_MSG_MAX)
+ packet_disconnect("Invalid ssh1 packet type: %d", type);
return type;
}
static u_int packet_length = 0;
u_int padlen, need;
u_char *macbuf, *cp, type;
- int maclen, block_size;
+ u_int maclen, block_size;
Enc *enc = NULL;
Mac *mac = NULL;
Comp *comp = NULL;
cipher_crypt(&receive_context, cp, buffer_ptr(&input),
block_size);
cp = buffer_ptr(&incoming_packet);
- packet_length = GET_32BIT(cp);
+ packet_length = get_u32(cp);
if (packet_length < 1 + 4 || packet_length > 256 * 1024) {
#ifdef PACKET_DEBUG
buffer_dump(&incoming_packet);
* return length of payload (without type field)
*/
type = buffer_get_char(&incoming_packet);
+ if (type < SSH2_MSG_MIN || type >= SSH2_MSG_LOCAL_MIN)
+ packet_disconnect("Invalid ssh2 packet type: %d", type);
if (type == SSH2_MSG_NEWKEYS)
set_newkeys(MODE_IN);
+ else if (type == SSH2_MSG_USERAUTH_SUCCESS && !server_side)
+ packet_enable_delayed_compress();
#ifdef PACKET_DEBUG
fprintf(stderr, "read/plain[%d]:\r\n", type);
buffer_dump(&incoming_packet);
break;
default:
return type;
- break;
}
} else {
type = packet_read_poll1();
logit("Received disconnect from %s: %.400s",
get_remote_ipaddr(), msg);
cleanup_exit(255);
- xfree(msg);
break;
default:
if (type)
DBG(debug("received packet type %d", type));
return type;
- break;
}
}
}
}
void *
-packet_get_raw(int *length_ptr)
+packet_get_raw(u_int *length_ptr)
{
- int bytes = buffer_len(&incoming_packet);
+ u_int bytes = buffer_len(&incoming_packet);
if (length_ptr != NULL)
*length_ptr = bytes;
{
fd_set *setp;
- setp = (fd_set *)xmalloc(howmany(connection_out + 1, NFDBITS) *
+ setp = (fd_set *)xcalloc(howmany(connection_out + 1, NFDBITS),
sizeof(fd_mask));
packet_write_poll();
while (packet_have_data_to_write()) {
/* Only set socket options if using a socket. */
if (!packet_connection_is_on_socket())
return;
- if (interactive)
- set_nodelay(connection_in);
+ set_nodelay(connection_in);
packet_set_tos(interactive);
}
return interactive_mode;
}
-u_int
+int
packet_set_maxsize(u_int s)
{
static int called = 0;
void
packet_send_ignore(int nbytes)
{
- u_int32_t rand = 0;
+ u_int32_t rnd = 0;
int i;
packet_start(compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE);
packet_put_int(nbytes);
for (i = 0; i < nbytes; i++) {
if (i % 4 == 0)
- rand = arc4random();
- packet_put_char(rand & 0xff);
- rand >>= 8;
+ rnd = arc4random();
+ packet_put_char((u_char)rnd & 0xff);
+ rnd >>= 8;
}
}
+int rekey_requested = 0;
+
+void
+packet_request_rekeying(void)
+{
+ rekey_requested = 1;
+}
-#define MAX_PACKETS (1<<31)
+#define MAX_PACKETS (1U<<31)
int
packet_need_rekeying(void)
{
if (datafellows & SSH_BUG_NOREKEY)
return 0;
+ if (rekey_requested == 1)
+ {
+ rekey_requested = 0;
+ return 1;
+ }
return
(p_send.packets > MAX_PACKETS) ||
(p_read.packets > MAX_PACKETS) ||
{
rekey_limit = bytes;
}
+
+void
+packet_set_server(void)
+{
+ server_side = 1;
+}
+
+void
+packet_set_authenticated(void)
+{
+ after_authentication = 1;
+}