version 1.32, 2000/05/04 22:22:43 |
version 1.32.2.2, 2000/11/08 21:31:01 |
|
|
/* |
/* |
* |
|
* packet.c |
|
* |
|
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* |
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* All rights reserved |
* All rights reserved |
* |
|
* Created: Sat Mar 18 02:40:40 1995 ylo |
|
* |
|
* This file contains code implementing the packet protocol and communication |
* This file contains code implementing the packet protocol and communication |
* with the other side. This same code is used both on client and server side. |
* with the other side. This same code is used both on client and server side. |
* |
* |
|
* As far as I am concerned, the code I have written for this software |
|
* can be used freely for any purpose. Any derived versions of this |
|
* software must be clearly marked as such, and if the derived work is |
|
* incompatible with the protocol description in the RFC file, it must be |
|
* called by a name other than "ssh" or "Secure Shell". |
|
* |
|
* |
* SSH2 packet format added by Markus Friedl. |
* SSH2 packet format added by Markus Friedl. |
|
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* |
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
*/ |
|
|
#include "includes.h" |
#include "includes.h" |
RCSID("$Id$"); |
RCSID("$OpenBSD$"); |
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "buffer.h" |
#include "buffer.h" |
|
|
#include "bufaux.h" |
#include "bufaux.h" |
#include "ssh.h" |
#include "ssh.h" |
#include "crc32.h" |
#include "crc32.h" |
#include "cipher.h" |
|
#include "getput.h" |
#include "getput.h" |
|
|
#include "compress.h" |
#include "compress.h" |
|
|
#include <openssl/dh.h> |
#include <openssl/dh.h> |
#include <openssl/hmac.h> |
#include <openssl/hmac.h> |
#include "buffer.h" |
#include "buffer.h" |
|
#include "cipher.h" |
#include "kex.h" |
#include "kex.h" |
#include "hmac.h" |
#include "hmac.h" |
|
|
|
|
void |
void |
packet_set_connection(int fd_in, int fd_out) |
packet_set_connection(int fd_in, int fd_out) |
{ |
{ |
|
Cipher *none = cipher_by_name("none"); |
|
if (none == NULL) |
|
fatal("packet_set_connection: cannot load cipher 'none'"); |
connection_in = fd_in; |
connection_in = fd_in; |
connection_out = fd_out; |
connection_out = fd_out; |
cipher_type = SSH_CIPHER_NONE; |
cipher_type = SSH_CIPHER_NONE; |
cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *) "", 0); |
cipher_init(&send_context, none, (unsigned char *) "", 0, NULL, 0); |
cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *) "", 0); |
cipher_init(&receive_context, none, (unsigned char *) "", 0, NULL, 0); |
if (!initialized) { |
if (!initialized) { |
initialized = 1; |
initialized = 1; |
buffer_init(&input); |
buffer_init(&input); |
|
|
*/ |
*/ |
|
|
void |
void |
packet_decrypt(CipherContext * cc, void *dest, void *src, |
packet_decrypt(CipherContext *context, void *dest, void *src, unsigned int bytes) |
unsigned int bytes) |
|
{ |
{ |
int i; |
|
|
|
if ((bytes % 8) != 0) |
|
fatal("packet_decrypt: bad ciphertext length %d", bytes); |
|
|
|
/* |
/* |
* Cryptographic attack detector for ssh - Modifications for packet.c |
* Cryptographic attack detector for ssh - Modifications for packet.c |
* (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) |
* (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) |
*/ |
*/ |
|
if (!compat20 && |
if (cc->type == SSH_CIPHER_NONE || compat20) { |
context->cipher->number != SSH_CIPHER_NONE && |
i = DEATTACK_OK; |
detect_attack(src, bytes, NULL) == DEATTACK_DETECTED) |
} else { |
|
i = detect_attack(src, bytes, NULL); |
|
} |
|
if (i == DEATTACK_DETECTED) |
|
packet_disconnect("crc32 compensation attack: network attack detected"); |
packet_disconnect("crc32 compensation attack: network attack detected"); |
|
|
cipher_decrypt(cc, dest, src, bytes); |
cipher_decrypt(context, dest, src, bytes); |
} |
} |
|
|
/* |
/* |
|
|
|
|
void |
void |
packet_set_encryption_key(const unsigned char *key, unsigned int keylen, |
packet_set_encryption_key(const unsigned char *key, unsigned int keylen, |
int cipher) |
int number) |
{ |
{ |
|
Cipher *cipher = cipher_by_number(number); |
|
if (cipher == NULL) |
|
fatal("packet_set_encryption_key: unknown cipher number %d", number); |
if (keylen < 20) |
if (keylen < 20) |
fatal("keylen too small: %d", keylen); |
fatal("packet_set_encryption_key: keylen too small: %d", keylen); |
|
cipher_init(&receive_context, cipher, key, keylen, NULL, 0); |
/* All other ciphers use the same key in both directions for now. */ |
cipher_init(&send_context, cipher, key, keylen, NULL, 0); |
cipher_set_key(&receive_context, cipher, key, keylen); |
|
cipher_set_key(&send_context, cipher, key, keylen); |
|
} |
} |
|
|
/* Starts constructing a packet to send. */ |
/* Starts constructing a packet to send. */ |
|
|
buffer_consume(&outgoing_packet, 8 - padding); |
buffer_consume(&outgoing_packet, 8 - padding); |
|
|
/* Add check bytes. */ |
/* Add check bytes. */ |
checksum = crc32((unsigned char *) buffer_ptr(&outgoing_packet), |
checksum = ssh_crc32((unsigned char *) buffer_ptr(&outgoing_packet), |
buffer_len(&outgoing_packet)); |
buffer_len(&outgoing_packet)); |
PUT_32BIT(buf, checksum); |
PUT_32BIT(buf, checksum); |
buffer_append(&outgoing_packet, buf, 4); |
buffer_append(&outgoing_packet, buf, 4); |
|
|
|
|
mac = &kex->mac[MODE_OUT]; |
mac = &kex->mac[MODE_OUT]; |
comp = &kex->comp[MODE_OUT]; |
comp = &kex->comp[MODE_OUT]; |
} |
} |
block_size = enc ? enc->block_size : 8; |
block_size = enc ? enc->cipher->block_size : 8; |
|
|
cp = buffer_ptr(&outgoing_packet); |
cp = buffer_ptr(&outgoing_packet); |
type = cp[5] & 0xff; |
type = cp[5] & 0xff; |
|
|
if (padlen < 4) |
if (padlen < 4) |
padlen += block_size; |
padlen += block_size; |
buffer_append_space(&outgoing_packet, &cp, padlen); |
buffer_append_space(&outgoing_packet, &cp, padlen); |
if (enc && enc->type != SSH_CIPHER_NONE) { |
if (enc && enc->cipher->number != SSH_CIPHER_NONE) { |
/* random padding */ |
/* random padding */ |
for (i = 0; i < padlen; i++) { |
for (i = 0; i < padlen; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
|
|
buffer_len(&outgoing_packet), |
buffer_len(&outgoing_packet), |
mac->key, mac->key_len |
mac->key, mac->key_len |
); |
); |
DBG(debug("done calc HMAC out #%d", seqnr)); |
DBG(debug("done calc MAC out #%d", seqnr)); |
} |
} |
/* encrypt packet and append to output buffer. */ |
/* encrypt packet and append to output buffer. */ |
buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); |
buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); |
|
|
fatal("packet_send2: no KEX"); |
fatal("packet_send2: no KEX"); |
if (mac->md != NULL) |
if (mac->md != NULL) |
mac->enabled = 1; |
mac->enabled = 1; |
DBG(debug("cipher_set_key_iv send_context")); |
DBG(debug("cipher_init send_context")); |
cipher_set_key_iv(&send_context, enc->type, |
cipher_init(&send_context, enc->cipher, |
enc->key, enc->key_len, |
enc->key, enc->cipher->key_len, |
enc->iv, enc->iv_len); |
enc->iv, enc->cipher->block_size); |
clear_enc_keys(enc, kex->we_need); |
clear_enc_keys(enc, kex->we_need); |
if (comp->type != 0 && comp->enabled == 0) { |
if (comp->type != 0 && comp->enabled == 0) { |
comp->enabled = 1; |
comp->enabled = 1; |
|
|
#endif |
#endif |
|
|
/* Compute packet checksum. */ |
/* Compute packet checksum. */ |
checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet), |
checksum = ssh_crc32((unsigned char *) buffer_ptr(&incoming_packet), |
buffer_len(&incoming_packet) - 4); |
buffer_len(&incoming_packet) - 4); |
|
|
/* Skip padding. */ |
/* Skip padding. */ |
|
|
comp = &kex->comp[MODE_IN]; |
comp = &kex->comp[MODE_IN]; |
} |
} |
maclen = mac && mac->enabled ? mac->mac_len : 0; |
maclen = mac && mac->enabled ? mac->mac_len : 0; |
block_size = enc ? enc->block_size : 8; |
block_size = enc ? enc->cipher->block_size : 8; |
|
|
if (packet_length == 0) { |
if (packet_length == 0) { |
/* |
/* |
|
|
mac->key, mac->key_len |
mac->key, mac->key_len |
); |
); |
if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) |
if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) |
packet_disconnect("Corrupted HMAC on input."); |
packet_disconnect("Corrupted MAC on input."); |
DBG(debug("HMAC #%d ok", seqnr)); |
DBG(debug("MAC #%d ok", seqnr)); |
buffer_consume(&input, mac->mac_len); |
buffer_consume(&input, mac->mac_len); |
} |
} |
if (++seqnr == 0) |
if (++seqnr == 0) |
|
|
fatal("packet_read_poll2: no KEX"); |
fatal("packet_read_poll2: no KEX"); |
if (mac->md != NULL) |
if (mac->md != NULL) |
mac->enabled = 1; |
mac->enabled = 1; |
DBG(debug("cipher_set_key_iv receive_context")); |
DBG(debug("cipher_init receive_context")); |
cipher_set_key_iv(&receive_context, enc->type, |
cipher_init(&receive_context, enc->cipher, |
enc->key, enc->key_len, |
enc->key, enc->cipher->key_len, |
enc->iv, enc->iv_len); |
enc->iv, enc->cipher->block_size); |
clear_enc_keys(enc, kex->we_need); |
clear_enc_keys(enc, kex->we_need); |
if (comp->type != 0 && comp->enabled == 0) { |
if (comp->type != 0 && comp->enabled == 0) { |
comp->enabled = 1; |
comp->enabled = 1; |