diff options
author | jp161948 <none@none> | 2007-11-30 08:38:32 -0800 |
---|---|---|
committer | jp161948 <none@none> | 2007-11-30 08:38:32 -0800 |
commit | 9a8058b57457911fab0e3b4b6f0a97740e7a816d (patch) | |
tree | abc8710af81f76e7213e3c4ca6bedab695f049a3 /usr/src/cmd/ssh/libssh/common/packet.c | |
parent | e46e4715a19570ec80958ef89a3f1232c476728a (diff) | |
download | illumos-gate-9a8058b57457911fab0e3b4b6f0a97740e7a816d.tar.gz |
PSARC/2007/034 ssh/sshd resync with OpenSSH
5040151 ssh(1) and sshd(1M) should re-key periodically as per-recent recommendations
6492415 ignore UsePrivilegeSeparation keyword in SunSSH
6624784 a few typos in SunSSH source code or its documentation
6626371 cmd/ssh/libssh/common/xlist.c should be cstyle clean
6627880 memory leaks in SunSSH's GSS-API code
6627881 memory leaks in SunSSH's g11n code
6627884 memory leaks in SunSSH's alternative privilege separation code
6627890 memory leaks in cmd/ssh/sshd/auth2-pam.c
6628516 old OpenSSH privilege separation code is not needed in SunSSH
--HG--
rename : usr/src/cmd/ssh/include/monitor.h => deleted_files/usr/src/cmd/ssh/include/monitor.h
rename : usr/src/cmd/ssh/include/monitor_fdpass.h => deleted_files/usr/src/cmd/ssh/include/monitor_fdpass.h
rename : usr/src/cmd/ssh/include/monitor_mm.h => deleted_files/usr/src/cmd/ssh/include/monitor_mm.h
rename : usr/src/cmd/ssh/include/monitor_wrap.h => deleted_files/usr/src/cmd/ssh/include/monitor_wrap.h
rename : usr/src/cmd/ssh/libssh/common/monitor_fdpass.c => deleted_files/usr/src/cmd/ssh/libssh/common/monitor_fdpass.c
rename : usr/src/cmd/ssh/libssh/common/monitor_wrap.c => deleted_files/usr/src/cmd/ssh/libssh/common/monitor_wrap.c
rename : usr/src/cmd/ssh/sshd/monitor.c => deleted_files/usr/src/cmd/ssh/sshd/monitor.c
rename : usr/src/cmd/ssh/sshd/monitor_mm.c => deleted_files/usr/src/cmd/ssh/sshd/monitor_mm.c
Diffstat (limited to 'usr/src/cmd/ssh/libssh/common/packet.c')
-rw-r--r-- | usr/src/cmd/ssh/libssh/common/packet.c | 341 |
1 files changed, 202 insertions, 139 deletions
diff --git a/usr/src/cmd/ssh/libssh/common/packet.c b/usr/src/cmd/ssh/libssh/common/packet.c index 10fbdd0895..985666a576 100644 --- a/usr/src/cmd/ssh/libssh/common/packet.c +++ b/usr/src/cmd/ssh/libssh/common/packet.c @@ -36,30 +36,29 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#include "includes.h" -RCSID("$OpenBSD: packet.c,v 1.97 2002/07/04 08:12:15 deraadt Exp $"); +/* $OpenBSD: packet.c,v 1.148 2007/06/07 19:37:34 pvalchev Exp $ */ #pragma ident "%Z%%M% %I% %E% SMI" +#include "includes.h" + +#include "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" @@ -127,8 +126,14 @@ static int interactive_mode = 0; /* Session key information for Encryption and MAC */ Newkeys *newkeys[MODE_MAX]; -static u_int32_t read_seqnr = 0; -static u_int32_t send_seqnr = 0; +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]; @@ -137,6 +142,13 @@ 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. @@ -159,6 +171,7 @@ packet_set_connection(int fd_in, int fd_out) buffer_init(&output); buffer_init(&outgoing_packet); buffer_init(&incoming_packet); + TAILQ_INIT(&outgoing); } else { buffer_clear(&input); buffer_clear(&output); @@ -202,99 +215,6 @@ packet_connection_is_on_socket(void) return 1; } -/* - * Exports an IV from the CipherContext required to export the key - * state back from the unprivileged child to the privileged parent - * process. - */ - -void -packet_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); -} - -int -packet_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)); -} - -void -packet_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); -} - -int -packet_get_keyiv_len(int mode) -{ - CipherContext *cc; - - if (mode == MODE_OUT) - cc = &send_context; - else - cc = &receive_context; - - return (cipher_get_keyiv_len(cc)); -} -void -packet_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); -} -int -packet_get_ssh1_cipher() -{ - return (cipher_get_number(receive_context.cipher)); -} - - -u_int32_t -packet_get_seqnr(int mode) -{ - return (mode == MODE_IN ? read_seqnr : send_seqnr); -} - -void -packet_set_seqnr(int mode, u_int32_t seqnr) -{ - if (mode == MODE_IN) - read_seqnr = seqnr; - else if (mode == MODE_OUT) - send_seqnr = seqnr; - else - fatal("packet_set_seqnr: bad mode %d", mode); -} - /* returns 1 if connection is via ipv4 */ int @@ -478,21 +398,25 @@ packet_put_char(int value) 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_ascii_cstring(const char *str) { @@ -520,11 +444,13 @@ 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) { @@ -542,7 +468,7 @@ packet_send1(void) 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 @@ -568,9 +494,9 @@ packet_send1(void) 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); @@ -614,31 +540,26 @@ set_newkeys(int mode) Mac *mac; Comp *comp; CipherContext *cc; - int encrypt; + u_int64_t *max_blocks; + int crypt_type; - debug("newkeys: mode %d", mode); + 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; } if (newkeys[mode] != NULL) { - debug("newkeys: rekeying"); + debug("set_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]); + free_keys(newkeys[mode]); } newkeys[mode] = kex_get_newkeys(mode); if (newkeys[mode] == NULL) @@ -650,7 +571,7 @@ set_newkeys(int mode) 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); */ @@ -662,19 +583,74 @@ set_newkeys(int mode) buffer_compress_init_recv(); comp->enabled = 1; } + + /* + * In accordance to the RFCs listed below we enforce the key + * re-exchange for: + * + * - every 1GB of transmitted data if the selected cipher block size + * is less than 16 bytes (3DES, Blowfish) + * - every 2^(2*B) cipher blocks transmitted (B is block size in bytes) + * if the cipher block size is greater than or equal to 16 bytes (AES) + * - and we never send more than 2^32 SSH packets using the same keys. + * The recommendation of 2^31 packets is not enforced here but in + * packet_need_rekeying(). There is also a hard check in + * packet_send2_wrapped() that we don't send more than 2^32 packets. + * + * Note that if the SSH_BUG_NOREKEY compatibility flag is set then no + * automatic rekeying is performed nor do we enforce the 3rd rule. + * This means that we can be always forced by the opposite side to never + * initiate automatic key re-exchange. This might change in the future. + * + * The RekeyLimit option keyword may only enforce more frequent key + * renegotiation, never less. For more information on key renegotiation, + * see: + * + * - RFC 4253 (SSH Transport Layer Protocol), section "9. Key + * Re-Exchange" + * - RFC 4344 (SSH Transport Layer Encryption Modes), sections "3. + * Rekeying" and "6.1 Rekeying Considerations" + */ + if (enc->block_size >= 16) + *max_blocks = (u_int64_t)1 << (enc->block_size * 2); + else + *max_blocks = ((u_int64_t)1 << 30) / enc->block_size; + + if (rekey_limit) + *max_blocks = MIN(*max_blocks, rekey_limit / enc->block_size); +} + +void +free_keys(Newkeys *keys) +{ + Enc *enc; + Mac *mac; + Comp *comp; + + enc = &keys->enc; + mac = &keys->mac; + comp = &keys->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(keys); } /* * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) */ static void -packet_send2(void) +packet_send2_wrapped(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; + u_int32_t rnd = 0; Enc *enc = NULL; Mac *mac = NULL; Comp *comp = NULL; @@ -733,9 +709,9 @@ packet_send2(void) /* 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 */ @@ -750,10 +726,10 @@ packet_send2(void) /* compute MAC over seqnr and packet(length fields, payload, padding) */ if (mac && mac->enabled) { - macbuf = mac_compute(mac, send_seqnr, + macbuf = mac_compute(mac, p_send.seqnr, buffer_ptr(&outgoing_packet), buffer_len(&outgoing_packet)); - DBG(debug("done calc MAC out #%d", send_seqnr)); + DBG(debug("done calc MAC out #%d", p_send.seqnr)); } /* encrypt packet and append to output buffer. */ cp = buffer_append_space(&output, buffer_len(&outgoing_packet)); @@ -767,8 +743,25 @@ packet_send2(void) buffer_dump(&output); #endif /* increment sequence number for outgoing packets */ - if (++send_seqnr == 0) + if (++p_send.seqnr == 0) log("outgoing seqnr wraps around"); + + /* + * RFC 4344: 3.1. First Rekeying Recommendation + * + * "Because of possible information leakage through the MAC tag after a + * key exchange, .... an SSH implementation SHOULD NOT send more than + * 2**32 packets before rekeying again." + * + * The code below is a hard check so that we are sure we don't go across + * the suggestion. However, since the largest cipher block size we have + * (AES) is 16 bytes we can't reach 2^32 SSH packets encrypted with the + * same key while performing periodic rekeying. + */ + if (++p_send.packets == 0) + if (!(datafellows & SSH_BUG_NOREKEY)) + fatal("too many packets encrypted with same key"); + p_send.blocks += (packet_length + 4) / block_size; buffer_clear(&outgoing_packet); if (type == SSH2_MSG_NEWKEYS) @@ -779,6 +772,51 @@ packet_send2(void) set_newkeys(MODE_OUT); } +static void +packet_send2(void) +{ + static int rekeying = 0; + struct packet *p; + u_char type, *cp; + + cp = buffer_ptr(&outgoing_packet); + type = cp[5]; + + /* during rekeying we can only send key exchange messages */ + if (rekeying) { + if (!((type >= SSH2_MSG_TRANSPORT_MIN) && + (type <= SSH2_MSG_TRANSPORT_MAX))) { + debug("enqueue packet: %u", type); + p = xmalloc(sizeof(*p)); + p->type = type; + memcpy(&p->payload, &outgoing_packet, sizeof(Buffer)); + buffer_init(&outgoing_packet); + TAILQ_INSERT_TAIL(&outgoing, p, next); + return; + } + } + + /* rekeying starts with sending KEXINIT */ + if (type == SSH2_MSG_KEXINIT) + rekeying = 1; + + packet_send2_wrapped(); + + /* after a NEWKEYS message we can send the complete queue */ + if (type == SSH2_MSG_NEWKEYS) { + rekeying = 0; + while ((p = TAILQ_FIRST(&outgoing)) != NULL) { + type = p->type; + debug("dequeue packet: %u", type); + buffer_free(&outgoing_packet); + memcpy(&outgoing_packet, &p->payload, sizeof(Buffer)); + TAILQ_REMOVE(&outgoing, p, next); + xfree(p); + packet_send2_wrapped(); + } + } +} + void packet_send(void) { @@ -1003,7 +1041,7 @@ packet_read_poll2(u_int32_t *seqnr_p) buffer_dump(&incoming_packet); packet_disconnect("Bad packet length %d.", packet_length); } - DBG(debug("input: packet len %d", packet_length+4)); + DBG(debug("input: packet len %u", packet_length + 4)); buffer_consume(&input, block_size); } /* we have a partial packet of block_size bytes */ @@ -1031,19 +1069,25 @@ packet_read_poll2(u_int32_t *seqnr_p) * increment sequence number for incoming packet */ if (mac && mac->enabled) { - macbuf = mac_compute(mac, read_seqnr, + macbuf = mac_compute(mac, p_read.seqnr, buffer_ptr(&incoming_packet), buffer_len(&incoming_packet)); if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) packet_disconnect("Corrupted MAC on input."); - DBG(debug("MAC #%d ok", read_seqnr)); + DBG(debug("MAC #%d ok", p_read.seqnr)); buffer_consume(&input, mac->mac_len); } if (seqnr_p != NULL) - *seqnr_p = read_seqnr; - if (++read_seqnr == 0) + *seqnr_p = p_read.seqnr; + if (++p_read.seqnr == 0) log("incoming seqnr wraps around"); + /* see above for the comment on "First Rekeying Recommendation" */ + if (++p_read.packets == 0) + if (!(datafellows & SSH_BUG_NOREKEY)) + fatal("too many packets with same key"); + p_read.blocks += (packet_length + 4) / block_size; + /* get padlen */ cp = buffer_ptr(&incoming_packet); padlen = cp[4]; @@ -1518,7 +1562,7 @@ packet_add_padding(u_char pad) void packet_send_ignore(int nbytes) { - u_int32_t rand = 0; + u_int32_t rnd = 0; int i; #ifdef ALTPRIVSEP @@ -1531,12 +1575,31 @@ packet_send_ignore(int nbytes) 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; } } +#define MAX_PACKETS (1U<<31) +int +packet_need_rekeying(void) +{ + if (datafellows & SSH_BUG_NOREKEY) + return 0; + return + (p_send.packets > MAX_PACKETS) || + (p_read.packets > MAX_PACKETS) || + (max_blocks_out && (p_send.blocks > max_blocks_out)) || + (max_blocks_in && (p_read.blocks > max_blocks_in)); +} + +void +packet_set_rekey_limit(u_int32_t bytes) +{ + rekey_limit = bytes; +} + #ifdef ALTPRIVSEP void packet_set_server(void) |