summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2016-02-08 10:57:07 +0000
committerDamien Miller <djm@mindrot.org>2016-02-08 21:58:32 +1100
commit19bcf2ea2d17413f2d9730dd2a19575ff86b9b6a (patch)
treea87286b290fcd540635890856fbcafef74341ec0
parent603ba41179e4b53951c7b90ee95b6ef3faa3f15d (diff)
upstream commit
refactor activation of rekeying This makes automatic rekeying internal to the packet code (previously the server and client loops needed to assist). In doing to it makes application of rekey limits more accurate by accounting for packets about to be sent as well as packets queued during rekeying events themselves. Based on a patch from dtucker@ which was in turn based on a patch Aleksander Adamowski in bz#2521; ok markus@ Upstream-ID: a441227fd64f9739850ca97b4cf794202860fcd8
-rw-r--r--clientloop.c28
-rw-r--r--kex.c21
-rw-r--r--kex.h3
-rw-r--r--opacket.h2
-rw-r--r--packet.c154
-rw-r--r--packet.h4
-rw-r--r--serverloop.c20
7 files changed, 158 insertions, 74 deletions
diff --git a/clientloop.c b/clientloop.c
index f0a08f234..9820455c4 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: clientloop.c,v 1.283 2016/02/01 21:18:17 millert Exp $ */ 1/* $OpenBSD: clientloop.c,v 1.284 2016/02/08 10:57:07 djm 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
@@ -1502,7 +1502,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1502{ 1502{
1503 fd_set *readset = NULL, *writeset = NULL; 1503 fd_set *readset = NULL, *writeset = NULL;
1504 double start_time, total_time; 1504 double start_time, total_time;
1505 int r, max_fd = 0, max_fd2 = 0, len, rekeying = 0; 1505 int r, max_fd = 0, max_fd2 = 0, len;
1506 u_int64_t ibytes, obytes; 1506 u_int64_t ibytes, obytes;
1507 u_int nalloc = 0; 1507 u_int nalloc = 0;
1508 char buf[100]; 1508 char buf[100];
@@ -1617,10 +1617,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1617 if (compat20 && session_closed && !channel_still_open()) 1617 if (compat20 && session_closed && !channel_still_open())
1618 break; 1618 break;
1619 1619
1620 rekeying = (active_state->kex != NULL && !active_state->kex->done); 1620 if (ssh_packet_is_rekeying(active_state)) {
1621
1622 if (rekeying) {
1623 debug("rekeying in progress"); 1621 debug("rekeying in progress");
1622 } else if (need_rekeying) {
1623 /* manual rekey request */
1624 debug("need rekeying");
1625 if ((r = kex_start_rekex(active_state)) != 0)
1626 fatal("%s: kex_start_rekex: %s", __func__,
1627 ssh_err(r));
1628 need_rekeying = 0;
1624 } else { 1629 } else {
1625 /* 1630 /*
1626 * Make packets of buffered stdin data, and buffer 1631 * Make packets of buffered stdin data, and buffer
@@ -1651,23 +1656,14 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1651 */ 1656 */
1652 max_fd2 = max_fd; 1657 max_fd2 = max_fd;
1653 client_wait_until_can_do_something(&readset, &writeset, 1658 client_wait_until_can_do_something(&readset, &writeset,
1654 &max_fd2, &nalloc, rekeying); 1659 &max_fd2, &nalloc, ssh_packet_is_rekeying(active_state));
1655 1660
1656 if (quit_pending) 1661 if (quit_pending)
1657 break; 1662 break;
1658 1663
1659 /* Do channel operations unless rekeying in progress. */ 1664 /* Do channel operations unless rekeying in progress. */
1660 if (!rekeying) { 1665 if (!ssh_packet_is_rekeying(active_state))
1661 channel_after_select(readset, writeset); 1666 channel_after_select(readset, writeset);
1662 if (need_rekeying || packet_need_rekeying()) {
1663 debug("need rekeying");
1664 active_state->kex->done = 0;
1665 if ((r = kex_send_kexinit(active_state)) != 0)
1666 fatal("%s: kex_send_kexinit: %s",
1667 __func__, ssh_err(r));
1668 need_rekeying = 0;
1669 }
1670 }
1671 1667
1672 /* Buffer input from the connection. */ 1668 /* Buffer input from the connection. */
1673 client_process_net_input(readset); 1669 client_process_net_input(readset);
diff --git a/kex.c b/kex.c
index 335b789fc..d371f47c4 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: kex.c,v 1.116 2016/01/14 16:17:39 markus Exp $ */ 1/* $OpenBSD: kex.c,v 1.117 2016/02/08 10:57:07 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
4 * 4 *
@@ -606,6 +606,25 @@ kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
606 return 0; 606 return 0;
607} 607}
608 608
609/*
610 * Request key re-exchange, returns 0 on success or a ssherr.h error
611 * code otherwise. Must not be called if KEX is incomplete or in-progress.
612 */
613int
614kex_start_rekex(struct ssh *ssh)
615{
616 if (ssh->kex == NULL) {
617 error("%s: no kex", __func__);
618 return SSH_ERR_INTERNAL_ERROR;
619 }
620 if (ssh->kex->done == 0) {
621 error("%s: requested twice", __func__);
622 return SSH_ERR_INTERNAL_ERROR;
623 }
624 ssh->kex->done = 0;
625 return kex_send_kexinit(ssh);
626}
627
609static int 628static int
610choose_enc(struct sshenc *enc, char *client, char *server) 629choose_enc(struct sshenc *enc, char *client, char *server)
611{ 630{
diff --git a/kex.h b/kex.h
index 24d4aa15f..1c5896605 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: kex.h,v 1.75 2016/01/14 16:17:39 markus Exp $ */ 1/* $OpenBSD: kex.h,v 1.76 2016/02/08 10:57:07 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -179,6 +179,7 @@ int kex_input_ext_info(int, u_int32_t, void *);
179int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *); 179int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *);
180int kex_derive_keys_bn(struct ssh *, u_char *, u_int, const BIGNUM *); 180int kex_derive_keys_bn(struct ssh *, u_char *, u_int, const BIGNUM *);
181int kex_send_newkeys(struct ssh *); 181int kex_send_newkeys(struct ssh *);
182int kex_start_rekex(struct ssh *);
182 183
183int kexdh_client(struct ssh *); 184int kexdh_client(struct ssh *);
184int kexdh_server(struct ssh *); 185int kexdh_server(struct ssh *);
diff --git a/opacket.h b/opacket.h
index b14b6769a..c26ade44c 100644
--- a/opacket.h
+++ b/opacket.h
@@ -125,8 +125,6 @@ void packet_disconnect(const char *, ...)
125 sshpkt_add_padding(active_state, (pad)) 125 sshpkt_add_padding(active_state, (pad))
126#define packet_send_ignore(nbytes) \ 126#define packet_send_ignore(nbytes) \
127 ssh_packet_send_ignore(active_state, (nbytes)) 127 ssh_packet_send_ignore(active_state, (nbytes))
128#define packet_need_rekeying() \
129 ssh_packet_need_rekeying(active_state)
130#define packet_set_server() \ 128#define packet_set_server() \
131 ssh_packet_set_server(active_state) 129 ssh_packet_set_server(active_state)
132#define packet_set_authenticated() \ 130#define packet_set_authenticated() \
diff --git a/packet.c b/packet.c
index f7e5bc78f..7ddebeb71 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: packet.c,v 1.227 2016/02/04 23:43:48 djm Exp $ */ 1/* $OpenBSD: packet.c,v 1.228 2016/02/08 10:57:07 djm 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
@@ -259,6 +259,14 @@ ssh_alloc_session_state(void)
259 return NULL; 259 return NULL;
260} 260}
261 261
262/* Returns nonzero if rekeying is in progress */
263int
264ssh_packet_is_rekeying(struct ssh *ssh)
265{
266 return ssh->state->rekeying ||
267 (ssh->kex != NULL && ssh->kex->done == 0);
268}
269
262/* 270/*
263 * Sets the descriptors used for communication. Disables encryption until 271 * Sets the descriptors used for communication. Disables encryption until
264 * packet_set_encryption_key is called. 272 * packet_set_encryption_key is called.
@@ -1029,6 +1037,51 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
1029 return 0; 1037 return 0;
1030} 1038}
1031 1039
1040#define MAX_PACKETS (1U<<31)
1041static int
1042ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len)
1043{
1044 struct session_state *state = ssh->state;
1045 u_int32_t out_blocks;
1046
1047 /* XXX client can't cope with rekeying pre-auth */
1048 if (!state->after_authentication)
1049 return 0;
1050
1051 /* Haven't keyed yet or KEX in progress. */
1052 if (ssh->kex == NULL || ssh_packet_is_rekeying(ssh))
1053 return 0;
1054
1055 /* Peer can't rekey */
1056 if (ssh->compat & SSH_BUG_NOREKEY)
1057 return 0;
1058
1059 /*
1060 * Permit one packet in or out per rekey - this allows us to
1061 * make progress when rekey limits are very small.
1062 */
1063 if (state->p_send.packets == 0 && state->p_read.packets == 0)
1064 return 0;
1065
1066 /* Time-based rekeying */
1067 if (state->rekey_interval != 0 &&
1068 state->rekey_time + state->rekey_interval <= monotime())
1069 return 1;
1070
1071 /* Always rekey when MAX_PACKETS sent in either direction */
1072 if (state->p_send.packets > MAX_PACKETS ||
1073 state->p_read.packets > MAX_PACKETS)
1074 return 1;
1075
1076 /* Rekey after (cipher-specific) maxiumum blocks */
1077 out_blocks = roundup(outbound_packet_len,
1078 state->newkeys[MODE_OUT]->enc.block_size);
1079 return (state->max_blocks_out &&
1080 (state->p_send.blocks + out_blocks > state->max_blocks_out)) ||
1081 (state->max_blocks_in &&
1082 (state->p_read.blocks > state->max_blocks_in));
1083}
1084
1032/* 1085/*
1033 * Delayed compression for SSH2 is enabled after authentication: 1086 * Delayed compression for SSH2 is enabled after authentication:
1034 * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent, 1087 * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent,
@@ -1232,35 +1285,58 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
1232 return r; 1285 return r;
1233} 1286}
1234 1287
1288/* returns non-zero if the specified packet type is usec by KEX */
1289static int
1290ssh_packet_type_is_kex(u_char type)
1291{
1292 return
1293 type >= SSH2_MSG_TRANSPORT_MIN &&
1294 type <= SSH2_MSG_TRANSPORT_MAX &&
1295 type != SSH2_MSG_SERVICE_REQUEST &&
1296 type != SSH2_MSG_SERVICE_ACCEPT &&
1297 type != SSH2_MSG_EXT_INFO;
1298}
1299
1235int 1300int
1236ssh_packet_send2(struct ssh *ssh) 1301ssh_packet_send2(struct ssh *ssh)
1237{ 1302{
1238 struct session_state *state = ssh->state; 1303 struct session_state *state = ssh->state;
1239 struct packet *p; 1304 struct packet *p;
1240 u_char type; 1305 u_char type;
1241 int r; 1306 int r, need_rekey;
1242 1307
1308 if (sshbuf_len(state->outgoing_packet) < 6)
1309 return SSH_ERR_INTERNAL_ERROR;
1243 type = sshbuf_ptr(state->outgoing_packet)[5]; 1310 type = sshbuf_ptr(state->outgoing_packet)[5];
1311 need_rekey = !ssh_packet_type_is_kex(type) &&
1312 ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet));
1244 1313
1245 /* during rekeying we can only send key exchange messages */ 1314 /*
1246 if (state->rekeying) { 1315 * During rekeying we can only send key exchange messages.
1247 if ((type < SSH2_MSG_TRANSPORT_MIN) || 1316 * Queue everything else.
1248 (type > SSH2_MSG_TRANSPORT_MAX) || 1317 */
1249 (type == SSH2_MSG_SERVICE_REQUEST) || 1318 if ((need_rekey || state->rekeying) && !ssh_packet_type_is_kex(type)) {
1250 (type == SSH2_MSG_SERVICE_ACCEPT) || 1319 if (need_rekey)
1251 (type == SSH2_MSG_EXT_INFO)) { 1320 debug3("%s: rekex triggered", __func__);
1252 debug("enqueue packet: %u", type); 1321 debug("enqueue packet: %u", type);
1253 p = calloc(1, sizeof(*p)); 1322 p = calloc(1, sizeof(*p));
1254 if (p == NULL) 1323 if (p == NULL)
1255 return SSH_ERR_ALLOC_FAIL; 1324 return SSH_ERR_ALLOC_FAIL;
1256 p->type = type; 1325 p->type = type;
1257 p->payload = state->outgoing_packet; 1326 p->payload = state->outgoing_packet;
1258 TAILQ_INSERT_TAIL(&state->outgoing, p, next); 1327 TAILQ_INSERT_TAIL(&state->outgoing, p, next);
1259 state->outgoing_packet = sshbuf_new(); 1328 state->outgoing_packet = sshbuf_new();
1260 if (state->outgoing_packet == NULL) 1329 if (state->outgoing_packet == NULL)
1261 return SSH_ERR_ALLOC_FAIL; 1330 return SSH_ERR_ALLOC_FAIL;
1262 return 0; 1331 if (need_rekey) {
1332 /*
1333 * This packet triggered a rekey, so send the
1334 * KEXINIT now.
1335 * NB. reenters this function via kex_start_rekex().
1336 */
1337 return kex_start_rekex(ssh);
1263 } 1338 }
1339 return 0;
1264 } 1340 }
1265 1341
1266 /* rekeying starts with sending KEXINIT */ 1342 /* rekeying starts with sending KEXINIT */
@@ -1276,10 +1352,22 @@ ssh_packet_send2(struct ssh *ssh)
1276 state->rekey_time = monotime(); 1352 state->rekey_time = monotime();
1277 while ((p = TAILQ_FIRST(&state->outgoing))) { 1353 while ((p = TAILQ_FIRST(&state->outgoing))) {
1278 type = p->type; 1354 type = p->type;
1355 /*
1356 * If this packet triggers a rekex, then skip the
1357 * remaining packets in the queue for now.
1358 * NB. re-enters this function via kex_start_rekex.
1359 */
1360 if (ssh_packet_need_rekeying(ssh,
1361 sshbuf_len(p->payload))) {
1362 debug3("%s: queued packet triggered rekex",
1363 __func__);
1364 return kex_start_rekex(ssh);
1365 }
1279 debug("dequeue packet: %u", type); 1366 debug("dequeue packet: %u", type);
1280 sshbuf_free(state->outgoing_packet); 1367 sshbuf_free(state->outgoing_packet);
1281 state->outgoing_packet = p->payload; 1368 state->outgoing_packet = p->payload;
1282 TAILQ_REMOVE(&state->outgoing, p, next); 1369 TAILQ_REMOVE(&state->outgoing, p, next);
1370 memset(p, 0, sizeof(*p));
1283 free(p); 1371 free(p);
1284 if ((r = ssh_packet_send2_wrapped(ssh)) != 0) 1372 if ((r = ssh_packet_send2_wrapped(ssh)) != 0)
1285 return r; 1373 return r;
@@ -1784,6 +1872,13 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
1784#endif 1872#endif
1785 /* reset for next packet */ 1873 /* reset for next packet */
1786 state->packlen = 0; 1874 state->packlen = 0;
1875
1876 /* do we need to rekey? */
1877 if (ssh_packet_need_rekeying(ssh, 0)) {
1878 debug3("%s: rekex triggered", __func__);
1879 if ((r = kex_start_rekex(ssh)) != 0)
1880 return r;
1881 }
1787 out: 1882 out:
1788 return r; 1883 return r;
1789} 1884}
@@ -2268,25 +2363,6 @@ ssh_packet_send_ignore(struct ssh *ssh, int nbytes)
2268 } 2363 }
2269} 2364}
2270 2365
2271#define MAX_PACKETS (1U<<31)
2272int
2273ssh_packet_need_rekeying(struct ssh *ssh)
2274{
2275 struct session_state *state = ssh->state;
2276
2277 if (ssh->compat & SSH_BUG_NOREKEY)
2278 return 0;
2279 return
2280 (state->p_send.packets > MAX_PACKETS) ||
2281 (state->p_read.packets > MAX_PACKETS) ||
2282 (state->max_blocks_out &&
2283 (state->p_send.blocks > state->max_blocks_out)) ||
2284 (state->max_blocks_in &&
2285 (state->p_read.blocks > state->max_blocks_in)) ||
2286 (state->rekey_interval != 0 && state->rekey_time +
2287 state->rekey_interval <= monotime());
2288}
2289
2290void 2366void
2291ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, time_t seconds) 2367ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, time_t seconds)
2292{ 2368{
diff --git a/packet.h b/packet.h
index 62302747d..28516a553 100644
--- a/packet.h
+++ b/packet.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: packet.h,v 1.69 2016/01/29 02:54:45 dtucker Exp $ */ 1/* $OpenBSD: packet.h,v 1.70 2016/02/08 10:57:07 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -86,6 +86,7 @@ int ssh_packet_get_connection_in(struct ssh *);
86int ssh_packet_get_connection_out(struct ssh *); 86int ssh_packet_get_connection_out(struct ssh *);
87void ssh_packet_close(struct ssh *); 87void ssh_packet_close(struct ssh *);
88void ssh_packet_set_encryption_key(struct ssh *, const u_char *, u_int, int); 88void ssh_packet_set_encryption_key(struct ssh *, const u_char *, u_int, int);
89int ssh_packet_is_rekeying(struct ssh *);
89void ssh_packet_set_protocol_flags(struct ssh *, u_int); 90void ssh_packet_set_protocol_flags(struct ssh *, u_int);
90u_int ssh_packet_get_protocol_flags(struct ssh *); 91u_int ssh_packet_get_protocol_flags(struct ssh *);
91int ssh_packet_start_compression(struct ssh *, int); 92int ssh_packet_start_compression(struct ssh *, int);
@@ -145,7 +146,6 @@ int ssh_packet_set_state(struct ssh *, struct sshbuf *);
145const char *ssh_remote_ipaddr(struct ssh *); 146const char *ssh_remote_ipaddr(struct ssh *);
146int ssh_remote_port(struct ssh *); 147int ssh_remote_port(struct ssh *);
147 148
148int ssh_packet_need_rekeying(struct ssh *);
149void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, time_t); 149void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, time_t);
150time_t ssh_packet_get_rekey_timeout(struct ssh *); 150time_t ssh_packet_get_rekey_timeout(struct ssh *);
151 151
diff --git a/serverloop.c b/serverloop.c
index 47bc168b2..80d1db549 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: serverloop.c,v 1.181 2016/01/14 16:17:40 markus Exp $ */ 1/* $OpenBSD: serverloop.c,v 1.182 2016/02/08 10:57:07 djm 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
@@ -820,7 +820,7 @@ void
820server_loop2(Authctxt *authctxt) 820server_loop2(Authctxt *authctxt)
821{ 821{
822 fd_set *readset = NULL, *writeset = NULL; 822 fd_set *readset = NULL, *writeset = NULL;
823 int rekeying = 0, max_fd; 823 int max_fd;
824 u_int nalloc = 0; 824 u_int nalloc = 0;
825 u_int64_t rekey_timeout_ms = 0; 825 u_int64_t rekey_timeout_ms = 0;
826 826
@@ -847,11 +847,11 @@ server_loop2(Authctxt *authctxt)
847 for (;;) { 847 for (;;) {
848 process_buffered_input_packets(); 848 process_buffered_input_packets();
849 849
850 rekeying = (active_state->kex != NULL && !active_state->kex->done); 850 if (!ssh_packet_is_rekeying(active_state) &&
851 851 packet_not_very_much_data_to_write())
852 if (!rekeying && packet_not_very_much_data_to_write())
853 channel_output_poll(); 852 channel_output_poll();
854 if (options.rekey_interval > 0 && compat20 && !rekeying) 853 if (options.rekey_interval > 0 && compat20 &&
854 !ssh_packet_is_rekeying(active_state))
855 rekey_timeout_ms = packet_get_rekey_timeout() * 1000; 855 rekey_timeout_ms = packet_get_rekey_timeout() * 1000;
856 else 856 else
857 rekey_timeout_ms = 0; 857 rekey_timeout_ms = 0;
@@ -866,14 +866,8 @@ server_loop2(Authctxt *authctxt)
866 } 866 }
867 867
868 collect_children(); 868 collect_children();
869 if (!rekeying) { 869 if (!ssh_packet_is_rekeying(active_state))
870 channel_after_select(readset, writeset); 870 channel_after_select(readset, writeset);
871 if (packet_need_rekeying()) {
872 debug("need rekeying");
873 active_state->kex->done = 0;
874 kex_send_kexinit(active_state);
875 }
876 }
877 process_input(readset); 871 process_input(readset);
878 if (connection_closed) 872 if (connection_closed)
879 break; 873 break;