📄 packet.c
字号:
/* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * 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. * * 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. * Copyright (c) 2000, 2001 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"RCSID("$OpenBSD: packet.c,v 1.116 2004/10/20 11:48:53 markus Exp $");#include "openbsd-compat/sys-queue.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 "kex.h"#include "mac.h"#include "log.h"#include "canohost.h"#include "misc.h"#include "ssh.h"#ifdef PACKET_DEBUG#define DBG(x) x#else#define DBG(x)#endif/* * This variable contains the file descriptors used for communicating with * the other side. connection_in is used for reading; connection_out for * writing. These can be the same descriptor, in which case it is assumed to * be a socket. */static int connection_in = -1;static int connection_out = -1;/* Protocol flags for the remote side. */static u_int remote_protocol_flags = 0;/* Encryption context for receiving data. This is only used for decryption. */static CipherContext receive_context;/* Encryption context for sending data. This is only used for encryption. */static CipherContext send_context;/* Buffer for raw input data from the socket. */Buffer input;/* Buffer for raw output data going to the socket. */Buffer output;/* Buffer for the partial outgoing packet being constructed. */static Buffer outgoing_packet;/* Buffer for the incoming packet currently being processed. */static Buffer incoming_packet;/* Scratch buffer for packet compression/decompression. */static Buffer compression_buffer;static int compression_buffer_ready = 0;/* Flag indicating whether packet compression/decompression is enabled. */static int packet_compression = 0;/* default maximum packet size */u_int max_packet_size = 32768;/* Flag indicating whether this module has been initialized. */static int initialized = 0;/* Set to true if the connection is interactive. */static int interactive_mode = 0;/* Session key information for Encryption and MAC */Newkeys *newkeys[MODE_MAX];static struct packet_state { u_int32_t seqnr; u_int32_t packets; u_int64_t blocks;} p_read, p_send;static u_int64_t max_blocks_in, max_blocks_out;static u_int32_t rekey_limit;/* 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 */static u_char extra_pad = 0;struct packet { TAILQ_ENTRY(packet) next; u_char type; Buffer payload;};TAILQ_HEAD(, packet) outgoing;/* * Sets the descriptors used for communication. Disables encryption until * packet_set_encryption_key is called. */voidpacket_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_out = fd_out; 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; buffer_init(&input); buffer_init(&output); buffer_init(&outgoing_packet); buffer_init(&incoming_packet); TAILQ_INIT(&outgoing); }}/* Returns 1 if remote host is connected via socket, 0 if not. */intpacket_connection_is_on_socket(void){ struct sockaddr_storage from, to; socklen_t fromlen, tolen; /* filedescriptors in and out are the same, so it's a socket */ if (connection_in == connection_out) return 1; fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(connection_in, (struct sockaddr *)&from, &fromlen) < 0) return 0; tolen = sizeof(to); memset(&to, 0, sizeof(to)); if (getpeername(connection_out, (struct sockaddr *)&to, &tolen) < 0) return 0; if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0) return 0; if (from.ss_family != AF_INET && from.ss_family != AF_INET6) return 0; return 1;}/* * Exports an IV from the CipherContext required to export the key * state back from the unprivileged child to the privileged parent * process. */voidpacket_get_keyiv(int mode, u_char *iv, u_int len){ CipherContext *cc; if (mode == MODE_OUT) cc = &send_context; else cc = &receive_context; cipher_get_keyiv(cc, iv, len);}intpacket_get_keycontext(int mode, u_char *dat){ CipherContext *cc; if (mode == MODE_OUT) cc = &send_context; else cc = &receive_context; return (cipher_get_keycontext(cc, dat));}voidpacket_set_keycontext(int mode, u_char *dat){ CipherContext *cc; if (mode == MODE_OUT) cc = &send_context; else cc = &receive_context; cipher_set_keycontext(cc, dat);}intpacket_get_keyiv_len(int mode){ CipherContext *cc; if (mode == MODE_OUT) cc = &send_context; else cc = &receive_context; return (cipher_get_keyiv_len(cc));}voidpacket_set_iv(int mode, u_char *dat){ CipherContext *cc; if (mode == MODE_OUT) cc = &send_context; else cc = &receive_context; cipher_set_keyiv(cc, dat);}intpacket_get_ssh1_cipher(void){ return (cipher_get_number(receive_context.cipher));}voidpacket_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks, u_int32_t *packets){ struct packet_state *state; state = (mode == MODE_IN) ? &p_read : &p_send; *seqnr = state->seqnr; *blocks = state->blocks; *packets = state->packets;}voidpacket_set_state(int mode, u_int32_t seqnr, u_int64_t blocks, u_int32_t packets){ struct packet_state *state; state = (mode == MODE_IN) ? &p_read : &p_send; state->seqnr = seqnr; state->blocks = blocks; state->packets = packets;}/* returns 1 if connection is via ipv4 */intpacket_connection_is_ipv4(void){ struct sockaddr_storage to; socklen_t tolen = sizeof(to); memset(&to, 0, sizeof(to)); if (getsockname(connection_out, (struct sockaddr *)&to, &tolen) < 0) return 0; if (to.ss_family == AF_INET) return 1;#ifdef IPV4_IN_IPV6 if (to.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&to)->sin6_addr)) return 1;#endif return 0;}/* Sets the connection into non-blocking mode. */voidpacket_set_nonblocking(void){ /* Set the socket into non-blocking mode. */ set_nonblock(connection_in); if (connection_out != connection_in) set_nonblock(connection_out);}/* Returns the socket used for reading. */intpacket_get_connection_in(void){ return connection_in;}/* Returns the descriptor used for writing. */intpacket_get_connection_out(void){ return connection_out;}/* Closes the connection and clears and frees internal data structures. */voidpacket_close(void){ if (!initialized) return; initialized = 0; if (connection_in == connection_out) { shutdown(connection_out, SHUT_RDWR); close(connection_out); } else { close(connection_in); close(connection_out); } buffer_free(&input); buffer_free(&output); buffer_free(&outgoing_packet); buffer_free(&incoming_packet); if (compression_buffer_ready) { buffer_free(&compression_buffer); buffer_compress_uninit(); } cipher_cleanup(&send_context); cipher_cleanup(&receive_context);}/* Sets remote side protocol flags. */voidpacket_set_protocol_flags(u_int protocol_flags){ remote_protocol_flags = protocol_flags;}/* Returns the remote protocol flags set earlier by the above function. */u_intpacket_get_protocol_flags(void){ return remote_protocol_flags;}/* * Starts packet compression from the next packet on in both directions. * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */static voidpacket_init_compression(void){ if (compression_buffer_ready == 1) return; compression_buffer_ready = 1; buffer_init(&compression_buffer);}voidpacket_start_compression(int level){ if (packet_compression && !compat20) fatal("Compression already enabled."); packet_compression = 1; packet_init_compression(); buffer_compress_init_send(level); buffer_compress_init_recv();}/* * Causes any further packets to be encrypted using the given key. The same * key is used for both sending and reception. However, both directions are * encrypted independently of each other. */voidpacket_set_encryption_key(const u_char *key, u_int keylen, int number){ Cipher *cipher = cipher_by_number(number); if (cipher == NULL) fatal("packet_set_encryption_key: unknown cipher number %d", number); if (keylen < 20) 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(&receive_context, cipher, key, keylen, NULL, 0, CIPHER_DECRYPT);}u_intpacket_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. */voidpacket_start(u_char type){ u_char buf[9]; int len; DBG(debug("packet_start[%d]", type)); len = compat20 ? 6 : 9; memset(buf, 0, len - 1); buf[len - 1] = type; buffer_clear(&outgoing_packet); buffer_append(&outgoing_packet, buf, len);}/* Append payload. */voidpacket_put_char(int value){ char ch = value; buffer_append(&outgoing_packet, &ch, 1);}voidpacket_put_int(u_int value){ buffer_put_int(&outgoing_packet, value);}voidpacket_put_string(const void *buf, u_int len){ buffer_put_string(&outgoing_packet, buf, len);}voidpacket_put_cstring(const char *str){ buffer_put_cstring(&outgoing_packet, str);}voidpacket_put_raw(const void *buf, u_int len){ buffer_append(&outgoing_packet, buf, len);}voidpacket_put_bignum(BIGNUM * value){ buffer_put_bignum(&outgoing_packet, value);}voidpacket_put_bignum2(BIGNUM * value){ buffer_put_bignum2(&outgoing_packet, value);}/* * Finalizes and sends the packet. If the encryption key has been set, * encrypts the packet before sending. */static voidpacket_send1(void){ u_char buf[8], *cp; int i, padding, len; u_int checksum;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -