📄 packet.c
字号:
packet_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; u_int32_t rand = 0; /* * If using packet compression, compress the payload of the outgoing * packet. */ if (packet_compression) { buffer_clear(&compression_buffer); /* Skip padding. */ buffer_consume(&outgoing_packet, 8); /* padding */ buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); buffer_compress(&outgoing_packet, &compression_buffer); buffer_clear(&outgoing_packet); buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), buffer_len(&compression_buffer)); } /* Compute packet length without padding (add checksum, remove padding). */ len = buffer_len(&outgoing_packet) + 4 - 8; /* Insert padding. Initialized to zero in packet_start1() */ padding = 8 - len % 8; if (!send_context.plaintext) { cp = buffer_ptr(&outgoing_packet); for (i = 0; i < padding; i++) { if (i % 4 == 0) rand = arc4random(); cp[7 - i] = rand & 0xff; rand >>= 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); buffer_append(&outgoing_packet, buf, 4);#ifdef PACKET_DEBUG fprintf(stderr, "packet_send plain: "); buffer_dump(&outgoing_packet);#endif /* Append to output. */ PUT_32BIT(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_len(&outgoing_packet));#ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); buffer_dump(&output);#endif buffer_clear(&outgoing_packet); /* * 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. */}voidset_newkeys(int mode){ Enc *enc; Mac *mac; Comp *comp; CipherContext *cc; int encrypt; debug("newkeys: mode %d", mode); if (mode == MODE_OUT) { cc = &send_context; encrypt = CIPHER_ENCRYPT; } else { cc = &receive_context; encrypt = CIPHER_DECRYPT; } if (newkeys[mode] != NULL) { debug("newkeys: rekeying"); cipher_cleanup(cc); enc = &newkeys[mode]->enc; mac = &newkeys[mode]->mac; comp = &newkeys[mode]->comp; memset(mac->key, 0, mac->key_len); xfree(enc->name); xfree(enc->iv); xfree(enc->key); xfree(mac->name); xfree(mac->key); xfree(comp->name); xfree(newkeys[mode]); } newkeys[mode] = kex_get_newkeys(mode); if (newkeys[mode] == NULL) fatal("newkeys: no keys for mode %d", mode); enc = &newkeys[mode]->enc; mac = &newkeys[mode]->mac; comp = &newkeys[mode]->comp; if (mac->md != NULL) 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); /* 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) { 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) */static voidpacket_send2(void){ u_char type, *cp, *macbuf = NULL; u_char padlen, pad; u_int packet_length = 0; u_int i, len; u_int32_t rand = 0; Enc *enc = NULL; Mac *mac = NULL; Comp *comp = NULL; int block_size; if (newkeys[MODE_OUT] != NULL) { enc = &newkeys[MODE_OUT]->enc; mac = &newkeys[MODE_OUT]->mac; comp = &newkeys[MODE_OUT]->comp; } block_size = enc ? enc->block_size : 8; cp = buffer_ptr(&outgoing_packet); type = cp[5];#ifdef PACKET_DEBUG fprintf(stderr, "plain: "); buffer_dump(&outgoing_packet);#endif if (comp && comp->enabled) { len = buffer_len(&outgoing_packet); /* skip header, compress only payload */ buffer_consume(&outgoing_packet, 5); buffer_clear(&compression_buffer); buffer_compress(&outgoing_packet, &compression_buffer); buffer_clear(&outgoing_packet); buffer_append(&outgoing_packet, "\0\0\0\0\0", 5); buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), buffer_len(&compression_buffer)); DBG(debug("compression: raw %d compressed %d", len, buffer_len(&outgoing_packet))); } /* sizeof (packet_len + pad_len + payload) */ len = buffer_len(&outgoing_packet); /* * calc size of padding, alloc space, get random data, * minimum padding is 4 bytes */ padlen = block_size - (len % block_size); if (padlen < 4) padlen += block_size; if (extra_pad) { /* will wrap if extra_pad+padlen > 255 */ extra_pad = roundup(extra_pad, block_size); pad = extra_pad - ((len + padlen) % extra_pad); debug3("packet_send2: adding %d (len %d padlen %d extra_pad %d)", pad, len, padlen, extra_pad); padlen += pad; extra_pad = 0; } cp = buffer_append_space(&outgoing_packet, padlen); if (enc && !send_context.plaintext) { /* random padding */ for (i = 0; i < padlen; i++) { if (i % 4 == 0) rand = arc4random(); cp[i] = rand & 0xff; rand >>= 8; } } else { /* clear padding */ memset(cp, 0, padlen); } /* 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); cp[4] = padlen; DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen)); /* compute MAC over seqnr and packet(length fields, payload, padding) */ if (mac && mac->enabled) { macbuf = mac_compute(mac, send_seqnr, buffer_ptr(&outgoing_packet), buffer_len(&outgoing_packet)); DBG(debug("done calc MAC out #%d", send_seqnr)); } /* encrypt packet and append to output buffer. */ cp = buffer_append_space(&output, buffer_len(&outgoing_packet)); cipher_crypt(&send_context, cp, buffer_ptr(&outgoing_packet), buffer_len(&outgoing_packet)); /* append unencrypted MAC */ if (mac && mac->enabled) buffer_append(&output, (char *)macbuf, mac->mac_len);#ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); buffer_dump(&output);#endif /* increment sequence number for outgoing packets */ if (++send_seqnr == 0) log("outgoing seqnr wraps around"); buffer_clear(&outgoing_packet); if (type == SSH2_MSG_NEWKEYS) set_newkeys(MODE_OUT);}voidpacket_send(void){ if (compat20) packet_send2(); else packet_send1(); DBG(debug("packet_send done"));}/* * Waits until a packet has been received, and returns its type. Note that * no other data is processed until this returns, so this function should not * be used during the interactive session. */intpacket_read_seqnr(u_int32_t *seqnr_p){ int type, len; fd_set *setp; char buf[8192]; DBG(debug("packet_read()")); setp = (fd_set *)xmalloc(howmany(connection_in+1, NFDBITS) * sizeof(fd_mask)); /* Since we are blocking, ensure that all written packets have been sent. */ packet_write_wait(); /* Stay in the loop until we have received a complete packet. */ for (;;) { /* Try to read a packet from the buffer. */ type = packet_read_poll_seqnr(seqnr_p); if (!compat20 && ( type == SSH_SMSG_SUCCESS || type == SSH_SMSG_FAILURE || type == SSH_CMSG_EOF || type == SSH_CMSG_EXIT_CONFIRMATION)) packet_check_eom(); /* If we got a packet, return it. */ if (type != SSH_MSG_NONE) { xfree(setp); return type; } /* * Otherwise, wait for some data to arrive, add it to the * buffer, and try again. */ memset(setp, 0, howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask)); FD_SET(connection_in, setp); /* Wait for some data to arrive. */ while (select(connection_in + 1, setp, NULL, NULL, NULL) == -1 && (errno == EAGAIN || errno == EINTR)) ; /* Read data from the socket. */ len = read(connection_in, buf, sizeof(buf)); if (len == 0) { log("Connection closed by %.200s", get_remote_ipaddr()); fatal_cleanup(); } if (len < 0) fatal("Read from socket failed: %.100s", strerror(errno)); /* Append it to the buffer. */ packet_process_incoming(buf, len); } /* NOTREACHED */}intpacket_read(void){ return packet_read_seqnr(NULL);}/* * Waits until a packet has been received, verifies that its type matches * that given, and gives a fatal error and exits if there is a mismatch. */voidpacket_read_expect(int expected_type){ int type; type = packet_read(); if (type != expected_type) packet_disconnect("Protocol error: expected packet type %d, got %d", expected_type, type);}/* Checks if a full packet is available in the data received so far via * packet_process_incoming. If so, reads the packet; otherwise returns * SSH_MSG_NONE. This does not wait for data from the connection. * * SSH_MSG_DISCONNECT is handled specially here. Also, * SSH_MSG_IGNORE messages are skipped by this function and are never returned * to higher levels. */static intpacket_read_poll1(void){ u_int len, padded_len; u_char *cp, type; u_int checksum, stored_checksum; /* Check if input size is less than minimum packet size. */ if (buffer_len(&input) < 4 + 8) return SSH_MSG_NONE; /* Get length of incoming packet. */ cp = buffer_ptr(&input); len = GET_32BIT(cp); if (len < 1 + 2 + 2 || len > 256 * 1024) packet_disconnect("Bad packet length %d.", len); padded_len = (len + 8) & ~7; /* Check if the packet has been entirely received. */ if (buffer_len(&input) < 4 + padded_len) return SSH_MSG_NONE; /* The entire packet is in buffer. */ /* Consume packet length. */ buffer_consume(&input, 4); /* * Cryptographic attack detector for ssh * (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"); /* Decrypt data to incoming_packet. */ buffer_clear(&incoming_packet); cp = buffer_append_space(&incoming_packet, padded_len); cipher_crypt(&receive_context, cp, buffer_ptr(&input), padded_len); buffer_consume(&input, padded_len);#ifdef PACKET_DEBUG fprintf(stderr, "read_poll plain: "); buffer_dump(&incoming_packet);#endif /* Compute packet checksum. */ checksum = ssh_crc32(buffer_ptr(&incoming_packet), buffer_len(&incoming_packet) - 4); /* Skip padding. */ buffer_consume(&incoming_packet, 8 - len % 8); /* Test check bytes. */ if (len != buffer_len(&incoming_packet)) packet_disconnect("packet_read_poll1: len %d != buffer_len %d.", len, buffer_len(&incoming_packet)); cp = (u_char *)buffer_ptr(&incoming_packet) + len - 4; stored_checksum = GET_32BIT(cp); if (checksum != stored_checksum) packet_disconnect("Corrupted check bytes on input."); buffer_consume_end(&incoming_packet, 4); if (packet_compression) { buffer_clear(&compression_buffer); buffer_uncompress(&incoming_packet, &compression_buffer); buffer_clear(&incoming_packet); buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), buffer_len(&compression_buffer)); } type = buffer_get_char(&incoming_packet); return type;}static intpacket_read_poll2(u_int32_t *seqnr_p){ static u_int packet_length = 0; u_int padlen, need; u_char *macbuf, *cp, type; int maclen, block_size; Enc *enc = NULL; Mac *mac = NULL; Comp *comp = NULL; if (newkeys[MODE_IN] != NULL) { enc = &newkeys[MODE_IN]->enc; mac = &newkeys[MODE_IN]->mac; comp = &newkeys[MODE_IN]->comp; } maclen = mac && mac->enabled ? mac->mac_len : 0; block_size = enc ? enc->block_size : 8; if (packet_length == 0) { /* * check if input size is less than the cipher block size, * decrypt first block and extract length of incoming packet */ if (buffer_len(&input) < block_size) return SSH_MSG_NONE; buffer_clear(&incoming_packet); cp = buffer_append_space(&incoming_packet, block_size); cipher_crypt(&receive_context, cp, buffer_ptr(&input), block_size); cp = buffer_ptr(&incoming_packet); packet_length = GET_32BIT(cp); if (packet_length < 1 + 4 || packet_length > 256 * 1024) { buffer_dump(&incoming_packet); packet_disconnect("Bad packet length %d.", packet_length); } DBG(debug("input: packet len %d", packet_length+4)); buffer_consume(&input, block_size); } /* we have a partial packet of block_size bytes */ need = 4 + packet_length - block_size; DBG(debug("partial packet %d, need %d, maclen %d", block_size, need, maclen)); if (need % block_size != 0) fatal("padding error: need %d block %d mod %d",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -