diff options
author | Damien Miller <djm@mindrot.org> | 2009-01-28 16:38:41 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2009-01-28 16:38:41 +1100 |
commit | 13ae44ce5865b720708aae9cb1d2e2f08a0d90cb (patch) | |
tree | b9acd30c2e1edfa1a4b7dcc26b8c11f8ea77b855 | |
parent | 9aa72ba57af907af8f7228f64fca8a474797898f (diff) |
- markus@cvs.openbsd.org 2009/01/26 09:58:15
[cipher.c cipher.h packet.c]
Work around the CPNI-957037 Plaintext Recovery Attack by always
reading 256K of data on packet size or HMAC errors (in CBC mode only).
Help, feedback and ok djm@
Feedback from Martin Albrecht and Paterson Kenny
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | cipher.c | 49 | ||||
-rw-r--r-- | cipher.h | 3 | ||||
-rw-r--r-- | packet.c | 72 |
4 files changed, 102 insertions, 30 deletions
@@ -80,6 +80,12 @@ | |||
80 | - naddy@cvs.openbsd.org 2009/01/24 17:10:22 | 80 | - naddy@cvs.openbsd.org 2009/01/24 17:10:22 |
81 | [ssh_config.5 sshd_config.5] | 81 | [ssh_config.5 sshd_config.5] |
82 | sync list of preferred ciphers; ok djm@ | 82 | sync list of preferred ciphers; ok djm@ |
83 | - markus@cvs.openbsd.org 2009/01/26 09:58:15 | ||
84 | [cipher.c cipher.h packet.c] | ||
85 | Work around the CPNI-957037 Plaintext Recovery Attack by always | ||
86 | reading 256K of data on packet size or HMAC errors (in CBC mode only). | ||
87 | Help, feedback and ok djm@ | ||
88 | Feedback from Martin Albrecht and Paterson Kenny | ||
83 | 89 | ||
84 | 20090107 | 90 | 20090107 |
85 | - (djm) [uidswap.c] bz#1412: Support >16 supplemental groups in OS X. | 91 | - (djm) [uidswap.c] bz#1412: Support >16 supplemental groups in OS X. |
@@ -5089,5 +5095,5 @@ | |||
5089 | OpenServer 6 and add osr5bigcrypt support so when someone migrates | 5095 | OpenServer 6 and add osr5bigcrypt support so when someone migrates |
5090 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ | 5096 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ |
5091 | 5097 | ||
5092 | $Id: ChangeLog,v 1.5178 2009/01/28 05:34:00 djm Exp $ | 5098 | $Id: ChangeLog,v 1.5179 2009/01/28 05:38:41 djm Exp $ |
5093 | 5099 | ||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: cipher.c,v 1.81 2006/08/03 03:34:42 deraadt Exp $ */ | 1 | /* $OpenBSD: cipher.c,v 1.82 2009/01/26 09:58:15 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -63,31 +63,32 @@ struct Cipher { | |||
63 | u_int block_size; | 63 | u_int block_size; |
64 | u_int key_len; | 64 | u_int key_len; |
65 | u_int discard_len; | 65 | u_int discard_len; |
66 | u_int cbc_mode; | ||
66 | const EVP_CIPHER *(*evptype)(void); | 67 | const EVP_CIPHER *(*evptype)(void); |
67 | } ciphers[] = { | 68 | } ciphers[] = { |
68 | { "none", SSH_CIPHER_NONE, 8, 0, 0, EVP_enc_null }, | 69 | { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, EVP_enc_null }, |
69 | { "des", SSH_CIPHER_DES, 8, 8, 0, EVP_des_cbc }, | 70 | { "des", SSH_CIPHER_DES, 8, 8, 0, 1, EVP_des_cbc }, |
70 | { "3des", SSH_CIPHER_3DES, 8, 16, 0, evp_ssh1_3des }, | 71 | { "3des", SSH_CIPHER_3DES, 8, 16, 0, 1, evp_ssh1_3des }, |
71 | { "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, evp_ssh1_bf }, | 72 | { "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 1, evp_ssh1_bf }, |
72 | 73 | ||
73 | { "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, EVP_des_ede3_cbc }, | 74 | { "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 1, EVP_des_ede3_cbc }, |
74 | { "blowfish-cbc", SSH_CIPHER_SSH2, 8, 16, 0, EVP_bf_cbc }, | 75 | { "blowfish-cbc", SSH_CIPHER_SSH2, 8, 16, 0, 1, EVP_bf_cbc }, |
75 | { "cast128-cbc", SSH_CIPHER_SSH2, 8, 16, 0, EVP_cast5_cbc }, | 76 | { "cast128-cbc", SSH_CIPHER_SSH2, 8, 16, 0, 1, EVP_cast5_cbc }, |
76 | { "arcfour", SSH_CIPHER_SSH2, 8, 16, 0, EVP_rc4 }, | 77 | { "arcfour", SSH_CIPHER_SSH2, 8, 16, 0, 0, EVP_rc4 }, |
77 | { "arcfour128", SSH_CIPHER_SSH2, 8, 16, 1536, EVP_rc4 }, | 78 | { "arcfour128", SSH_CIPHER_SSH2, 8, 16, 1536, 0, EVP_rc4 }, |
78 | { "arcfour256", SSH_CIPHER_SSH2, 8, 32, 1536, EVP_rc4 }, | 79 | { "arcfour256", SSH_CIPHER_SSH2, 8, 32, 1536, 0, EVP_rc4 }, |
79 | { "aes128-cbc", SSH_CIPHER_SSH2, 16, 16, 0, EVP_aes_128_cbc }, | 80 | { "aes128-cbc", SSH_CIPHER_SSH2, 16, 16, 0, 1, EVP_aes_128_cbc }, |
80 | { "aes192-cbc", SSH_CIPHER_SSH2, 16, 24, 0, EVP_aes_192_cbc }, | 81 | { "aes192-cbc", SSH_CIPHER_SSH2, 16, 24, 0, 1, EVP_aes_192_cbc }, |
81 | { "aes256-cbc", SSH_CIPHER_SSH2, 16, 32, 0, EVP_aes_256_cbc }, | 82 | { "aes256-cbc", SSH_CIPHER_SSH2, 16, 32, 0, 1, EVP_aes_256_cbc }, |
82 | { "rijndael-cbc@lysator.liu.se", | 83 | { "rijndael-cbc@lysator.liu.se", |
83 | SSH_CIPHER_SSH2, 16, 32, 0, EVP_aes_256_cbc }, | 84 | SSH_CIPHER_SSH2, 16, 32, 0, 1, EVP_aes_256_cbc }, |
84 | { "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, evp_aes_128_ctr }, | 85 | { "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, evp_aes_128_ctr }, |
85 | { "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, evp_aes_128_ctr }, | 86 | { "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, evp_aes_128_ctr }, |
86 | { "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, evp_aes_128_ctr }, | 87 | { "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, 0, evp_aes_128_ctr }, |
87 | #ifdef USE_CIPHER_ACSS | 88 | #ifdef USE_CIPHER_ACSS |
88 | { "acss@openssh.org", SSH_CIPHER_SSH2, 16, 5, 0, EVP_acss }, | 89 | { "acss@openssh.org", SSH_CIPHER_SSH2, 16, 5, 0, 0, EVP_acss }, |
89 | #endif | 90 | #endif |
90 | { NULL, SSH_CIPHER_INVALID, 0, 0, 0, NULL } | 91 | { NULL, SSH_CIPHER_INVALID, 0, 0, 0, 0, NULL } |
91 | }; | 92 | }; |
92 | 93 | ||
93 | /*--*/ | 94 | /*--*/ |
@@ -111,6 +112,12 @@ cipher_get_number(const Cipher *c) | |||
111 | } | 112 | } |
112 | 113 | ||
113 | u_int | 114 | u_int |
115 | cipher_is_cbc(const Cipher *c) | ||
116 | { | ||
117 | return (c->cbc_mode); | ||
118 | } | ||
119 | |||
120 | u_int | ||
114 | cipher_mask_ssh1(int client) | 121 | cipher_mask_ssh1(int client) |
115 | { | 122 | { |
116 | u_int mask = 0; | 123 | u_int mask = 0; |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: cipher.h,v 1.36 2006/03/25 22:22:42 djm Exp $ */ | 1 | /* $OpenBSD: cipher.h,v 1.37 2009/01/26 09:58:15 markus Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -81,6 +81,7 @@ void cipher_cleanup(CipherContext *); | |||
81 | void cipher_set_key_string(CipherContext *, Cipher *, const char *, int); | 81 | void cipher_set_key_string(CipherContext *, Cipher *, const char *, int); |
82 | u_int cipher_blocksize(const Cipher *); | 82 | u_int cipher_blocksize(const Cipher *); |
83 | u_int cipher_keylen(const Cipher *); | 83 | u_int cipher_keylen(const Cipher *); |
84 | u_int cipher_is_cbc(const Cipher *); | ||
84 | 85 | ||
85 | u_int cipher_get_number(const Cipher *); | 86 | u_int cipher_get_number(const Cipher *); |
86 | void cipher_get_keyiv(CipherContext *, u_char *, u_int); | 87 | void cipher_get_keyiv(CipherContext *, u_char *, u_int); |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: packet.c,v 1.158 2008/11/21 15:47:38 markus Exp $ */ | 1 | /* $OpenBSD: packet.c,v 1.159 2009/01/26 09:58:15 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -84,6 +84,8 @@ | |||
84 | #define DBG(x) | 84 | #define DBG(x) |
85 | #endif | 85 | #endif |
86 | 86 | ||
87 | #define PACKET_MAX_SIZE (256 * 1024) | ||
88 | |||
87 | /* | 89 | /* |
88 | * This variable contains the file descriptors used for communicating with | 90 | * This variable contains the file descriptors used for communicating with |
89 | * the other side. connection_in is used for reading; connection_out for | 91 | * the other side. connection_in is used for reading; connection_out for |
@@ -160,6 +162,10 @@ static u_int ssh1_keylen; | |||
160 | /* roundup current message to extra_pad bytes */ | 162 | /* roundup current message to extra_pad bytes */ |
161 | static u_char extra_pad = 0; | 163 | static u_char extra_pad = 0; |
162 | 164 | ||
165 | /* XXX discard incoming data after MAC error */ | ||
166 | static u_int packet_discard = 0; | ||
167 | static Mac *packet_discard_mac = NULL; | ||
168 | |||
163 | struct packet { | 169 | struct packet { |
164 | TAILQ_ENTRY(packet) next; | 170 | TAILQ_ENTRY(packet) next; |
165 | u_char type; | 171 | u_char type; |
@@ -209,6 +215,36 @@ packet_set_timeout(int timeout, int count) | |||
209 | packet_timeout_ms = timeout * count * 1000; | 215 | packet_timeout_ms = timeout * count * 1000; |
210 | } | 216 | } |
211 | 217 | ||
218 | static void | ||
219 | packet_stop_discard(void) | ||
220 | { | ||
221 | if (packet_discard_mac) { | ||
222 | char buf[1024]; | ||
223 | |||
224 | memset(buf, 'a', sizeof(buf)); | ||
225 | while (buffer_len(&incoming_packet) < PACKET_MAX_SIZE) | ||
226 | buffer_append(&incoming_packet, buf, sizeof(buf)); | ||
227 | (void) mac_compute(packet_discard_mac, | ||
228 | p_read.seqnr, | ||
229 | buffer_ptr(&incoming_packet), | ||
230 | PACKET_MAX_SIZE); | ||
231 | } | ||
232 | logit("Finished discarding for %.200s", get_remote_ipaddr()); | ||
233 | cleanup_exit(255); | ||
234 | } | ||
235 | |||
236 | static void | ||
237 | packet_start_discard(Enc *enc, Mac *mac, u_int packet_length, u_int discard) | ||
238 | { | ||
239 | if (!cipher_is_cbc(enc->cipher)) | ||
240 | packet_disconnect("Packet corrupt"); | ||
241 | if (packet_length != PACKET_MAX_SIZE && mac && mac->enabled) | ||
242 | packet_discard_mac = mac; | ||
243 | if (buffer_len(&input) >= discard) | ||
244 | packet_stop_discard(); | ||
245 | packet_discard = discard - buffer_len(&input); | ||
246 | } | ||
247 | |||
212 | /* Returns 1 if remote host is connected via socket, 0 if not. */ | 248 | /* Returns 1 if remote host is connected via socket, 0 if not. */ |
213 | 249 | ||
214 | int | 250 | int |
@@ -1127,6 +1163,9 @@ packet_read_poll2(u_int32_t *seqnr_p) | |||
1127 | Mac *mac = NULL; | 1163 | Mac *mac = NULL; |
1128 | Comp *comp = NULL; | 1164 | Comp *comp = NULL; |
1129 | 1165 | ||
1166 | if (packet_discard) | ||
1167 | return SSH_MSG_NONE; | ||
1168 | |||
1130 | if (newkeys[MODE_IN] != NULL) { | 1169 | if (newkeys[MODE_IN] != NULL) { |
1131 | enc = &newkeys[MODE_IN]->enc; | 1170 | enc = &newkeys[MODE_IN]->enc; |
1132 | mac = &newkeys[MODE_IN]->mac; | 1171 | mac = &newkeys[MODE_IN]->mac; |
@@ -1148,12 +1187,14 @@ packet_read_poll2(u_int32_t *seqnr_p) | |||
1148 | block_size); | 1187 | block_size); |
1149 | cp = buffer_ptr(&incoming_packet); | 1188 | cp = buffer_ptr(&incoming_packet); |
1150 | packet_length = get_u32(cp); | 1189 | packet_length = get_u32(cp); |
1151 | if (packet_length < 1 + 4 || packet_length > 256 * 1024) { | 1190 | if (packet_length < 1 + 4 || packet_length > PACKET_MAX_SIZE) { |
1152 | #ifdef PACKET_DEBUG | 1191 | #ifdef PACKET_DEBUG |
1153 | buffer_dump(&incoming_packet); | 1192 | buffer_dump(&incoming_packet); |
1154 | #endif | 1193 | #endif |
1155 | packet_disconnect("Bad packet length %-10u", | 1194 | logit("Bad packet length %u.", packet_length); |
1156 | packet_length); | 1195 | packet_start_discard(enc, mac, packet_length, |
1196 | PACKET_MAX_SIZE); | ||
1197 | return SSH_MSG_NONE; | ||
1157 | } | 1198 | } |
1158 | DBG(debug("input: packet len %u", packet_length+4)); | 1199 | DBG(debug("input: packet len %u", packet_length+4)); |
1159 | buffer_consume(&input, block_size); | 1200 | buffer_consume(&input, block_size); |
@@ -1165,7 +1206,9 @@ packet_read_poll2(u_int32_t *seqnr_p) | |||
1165 | if (need % block_size != 0) { | 1206 | if (need % block_size != 0) { |
1166 | logit("padding error: need %d block %d mod %d", | 1207 | logit("padding error: need %d block %d mod %d", |
1167 | need, block_size, need % block_size); | 1208 | need, block_size, need % block_size); |
1168 | packet_disconnect("Bad packet length %-10u", packet_length); | 1209 | packet_start_discard(enc, mac, packet_length, |
1210 | PACKET_MAX_SIZE - block_size); | ||
1211 | return SSH_MSG_NONE; | ||
1169 | } | 1212 | } |
1170 | /* | 1213 | /* |
1171 | * check if the entire packet has been received and | 1214 | * check if the entire packet has been received and |
@@ -1188,11 +1231,19 @@ packet_read_poll2(u_int32_t *seqnr_p) | |||
1188 | macbuf = mac_compute(mac, p_read.seqnr, | 1231 | macbuf = mac_compute(mac, p_read.seqnr, |
1189 | buffer_ptr(&incoming_packet), | 1232 | buffer_ptr(&incoming_packet), |
1190 | buffer_len(&incoming_packet)); | 1233 | buffer_len(&incoming_packet)); |
1191 | if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) | 1234 | if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) { |
1192 | packet_disconnect("Corrupted MAC on input."); | 1235 | logit("Corrupted MAC on input."); |
1236 | if (need > PACKET_MAX_SIZE) | ||
1237 | fatal("internal error need %d", need); | ||
1238 | packet_start_discard(enc, mac, packet_length, | ||
1239 | PACKET_MAX_SIZE - need); | ||
1240 | return SSH_MSG_NONE; | ||
1241 | } | ||
1242 | |||
1193 | DBG(debug("MAC #%d ok", p_read.seqnr)); | 1243 | DBG(debug("MAC #%d ok", p_read.seqnr)); |
1194 | buffer_consume(&input, mac->mac_len); | 1244 | buffer_consume(&input, mac->mac_len); |
1195 | } | 1245 | } |
1246 | /* XXX now it's safe to use fatal/packet_disconnect */ | ||
1196 | if (seqnr_p != NULL) | 1247 | if (seqnr_p != NULL) |
1197 | *seqnr_p = p_read.seqnr; | 1248 | *seqnr_p = p_read.seqnr; |
1198 | if (++p_read.seqnr == 0) | 1249 | if (++p_read.seqnr == 0) |
@@ -1325,6 +1376,13 @@ packet_read_poll(void) | |||
1325 | void | 1376 | void |
1326 | packet_process_incoming(const char *buf, u_int len) | 1377 | packet_process_incoming(const char *buf, u_int len) |
1327 | { | 1378 | { |
1379 | if (packet_discard) { | ||
1380 | keep_alive_timeouts = 0; /* ?? */ | ||
1381 | if (len >= packet_discard) | ||
1382 | packet_stop_discard(); | ||
1383 | packet_discard -= len; | ||
1384 | return; | ||
1385 | } | ||
1328 | buffer_append(&input, buf, len); | 1386 | buffer_append(&input, buf, len); |
1329 | } | 1387 | } |
1330 | 1388 | ||