diff options
author | Colin Watson <cjwatson@debian.org> | 2016-02-29 12:15:15 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2016-03-08 11:51:22 +0000 |
commit | 46961f5704f8e86cea3e99253faad55aef4d8f35 (patch) | |
tree | 0dd97fa4fb649a62b4639fe2674380872b1f3e98 /kex.c | |
parent | c753fe267efb1b027424fa8706cf0385fc3d14c1 (diff) | |
parent | 85e40e87a75fb80a0bf893ac05a417d6c353537d (diff) |
New upstream release (7.2).
Diffstat (limited to 'kex.c')
-rw-r--r-- | kex.c | 168 |
1 files changed, 134 insertions, 34 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: kex.c,v 1.109 2015/07/30 00:01:34 djm 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 | * |
@@ -49,7 +49,6 @@ | |||
49 | #include "misc.h" | 49 | #include "misc.h" |
50 | #include "dispatch.h" | 50 | #include "dispatch.h" |
51 | #include "monitor.h" | 51 | #include "monitor.h" |
52 | #include "roaming.h" | ||
53 | 52 | ||
54 | #include "ssherr.h" | 53 | #include "ssherr.h" |
55 | #include "sshbuf.h" | 54 | #include "sshbuf.h" |
@@ -71,6 +70,19 @@ extern const EVP_MD *evp_ssh_sha256(void); | |||
71 | static int kex_choose_conf(struct ssh *); | 70 | static int kex_choose_conf(struct ssh *); |
72 | static int kex_input_newkeys(int, u_int32_t, void *); | 71 | static int kex_input_newkeys(int, u_int32_t, void *); |
73 | 72 | ||
73 | static const char *proposal_names[PROPOSAL_MAX] = { | ||
74 | "KEX algorithms", | ||
75 | "host key algorithms", | ||
76 | "ciphers ctos", | ||
77 | "ciphers stoc", | ||
78 | "MACs ctos", | ||
79 | "MACs stoc", | ||
80 | "compression ctos", | ||
81 | "compression stoc", | ||
82 | "languages ctos", | ||
83 | "languages stoc", | ||
84 | }; | ||
85 | |||
74 | struct kexalg { | 86 | struct kexalg { |
75 | char *name; | 87 | char *name; |
76 | u_int type; | 88 | u_int type; |
@@ -283,7 +295,7 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) | |||
283 | for (i = 0; i < PROPOSAL_MAX; i++) { | 295 | for (i = 0; i < PROPOSAL_MAX; i++) { |
284 | if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) | 296 | if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) |
285 | goto out; | 297 | goto out; |
286 | debug2("kex_parse_kexinit: %s", proposal[i]); | 298 | debug2("%s: %s", proposal_names[i], proposal[i]); |
287 | } | 299 | } |
288 | /* first kex follows / reserved */ | 300 | /* first kex follows / reserved */ |
289 | if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ | 301 | if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ |
@@ -318,7 +330,14 @@ kex_prop_free(char **proposal) | |||
318 | static int | 330 | static int |
319 | kex_protocol_error(int type, u_int32_t seq, void *ctxt) | 331 | kex_protocol_error(int type, u_int32_t seq, void *ctxt) |
320 | { | 332 | { |
321 | error("Hm, kex protocol error: type %d seq %u", type, seq); | 333 | struct ssh *ssh = active_state; /* XXX */ |
334 | int r; | ||
335 | |||
336 | error("kex protocol error: type %d seq %u", type, seq); | ||
337 | if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || | ||
338 | (r = sshpkt_put_u32(ssh, seq)) != 0 || | ||
339 | (r = sshpkt_send(ssh)) != 0) | ||
340 | return r; | ||
322 | return 0; | 341 | return 0; |
323 | } | 342 | } |
324 | 343 | ||
@@ -330,6 +349,20 @@ kex_reset_dispatch(struct ssh *ssh) | |||
330 | ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); | 349 | ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); |
331 | } | 350 | } |
332 | 351 | ||
352 | static int | ||
353 | kex_send_ext_info(struct ssh *ssh) | ||
354 | { | ||
355 | int r; | ||
356 | |||
357 | if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || | ||
358 | (r = sshpkt_put_u32(ssh, 1)) != 0 || | ||
359 | (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || | ||
360 | (r = sshpkt_put_cstring(ssh, "rsa-sha2-256,rsa-sha2-512")) != 0 || | ||
361 | (r = sshpkt_send(ssh)) != 0) | ||
362 | return r; | ||
363 | return 0; | ||
364 | } | ||
365 | |||
333 | int | 366 | int |
334 | kex_send_newkeys(struct ssh *ssh) | 367 | kex_send_newkeys(struct ssh *ssh) |
335 | { | 368 | { |
@@ -342,9 +375,51 @@ kex_send_newkeys(struct ssh *ssh) | |||
342 | debug("SSH2_MSG_NEWKEYS sent"); | 375 | debug("SSH2_MSG_NEWKEYS sent"); |
343 | debug("expecting SSH2_MSG_NEWKEYS"); | 376 | debug("expecting SSH2_MSG_NEWKEYS"); |
344 | ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); | 377 | ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); |
378 | if (ssh->kex->ext_info_c) | ||
379 | if ((r = kex_send_ext_info(ssh)) != 0) | ||
380 | return r; | ||
345 | return 0; | 381 | return 0; |
346 | } | 382 | } |
347 | 383 | ||
384 | int | ||
385 | kex_input_ext_info(int type, u_int32_t seq, void *ctxt) | ||
386 | { | ||
387 | struct ssh *ssh = ctxt; | ||
388 | struct kex *kex = ssh->kex; | ||
389 | u_int32_t i, ninfo; | ||
390 | char *name, *val, *found; | ||
391 | int r; | ||
392 | |||
393 | debug("SSH2_MSG_EXT_INFO received"); | ||
394 | ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); | ||
395 | if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) | ||
396 | return r; | ||
397 | for (i = 0; i < ninfo; i++) { | ||
398 | if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) | ||
399 | return r; | ||
400 | if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) { | ||
401 | free(name); | ||
402 | return r; | ||
403 | } | ||
404 | debug("%s: %s=<%s>", __func__, name, val); | ||
405 | if (strcmp(name, "server-sig-algs") == 0) { | ||
406 | found = match_list("rsa-sha2-256", val, NULL); | ||
407 | if (found) { | ||
408 | kex->rsa_sha2 = 256; | ||
409 | free(found); | ||
410 | } | ||
411 | found = match_list("rsa-sha2-512", val, NULL); | ||
412 | if (found) { | ||
413 | kex->rsa_sha2 = 512; | ||
414 | free(found); | ||
415 | } | ||
416 | } | ||
417 | free(name); | ||
418 | free(val); | ||
419 | } | ||
420 | return sshpkt_get_end(ssh); | ||
421 | } | ||
422 | |||
348 | static int | 423 | static int |
349 | kex_input_newkeys(int type, u_int32_t seq, void *ctxt) | 424 | kex_input_newkeys(int type, u_int32_t seq, void *ctxt) |
350 | { | 425 | { |
@@ -484,7 +559,7 @@ kex_free_newkeys(struct newkeys *newkeys) | |||
484 | newkeys->enc.key = NULL; | 559 | newkeys->enc.key = NULL; |
485 | } | 560 | } |
486 | if (newkeys->enc.iv) { | 561 | if (newkeys->enc.iv) { |
487 | explicit_bzero(newkeys->enc.iv, newkeys->enc.block_size); | 562 | explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); |
488 | free(newkeys->enc.iv); | 563 | free(newkeys->enc.iv); |
489 | newkeys->enc.iv = NULL; | 564 | newkeys->enc.iv = NULL; |
490 | } | 565 | } |
@@ -527,6 +602,8 @@ kex_free(struct kex *kex) | |||
527 | free(kex->client_version_string); | 602 | free(kex->client_version_string); |
528 | free(kex->server_version_string); | 603 | free(kex->server_version_string); |
529 | free(kex->failed_choice); | 604 | free(kex->failed_choice); |
605 | free(kex->hostkey_alg); | ||
606 | free(kex->name); | ||
530 | free(kex); | 607 | free(kex); |
531 | } | 608 | } |
532 | 609 | ||
@@ -545,6 +622,25 @@ kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) | |||
545 | return 0; | 622 | return 0; |
546 | } | 623 | } |
547 | 624 | ||
625 | /* | ||
626 | * Request key re-exchange, returns 0 on success or a ssherr.h error | ||
627 | * code otherwise. Must not be called if KEX is incomplete or in-progress. | ||
628 | */ | ||
629 | int | ||
630 | kex_start_rekex(struct ssh *ssh) | ||
631 | { | ||
632 | if (ssh->kex == NULL) { | ||
633 | error("%s: no kex", __func__); | ||
634 | return SSH_ERR_INTERNAL_ERROR; | ||
635 | } | ||
636 | if (ssh->kex->done == 0) { | ||
637 | error("%s: requested twice", __func__); | ||
638 | return SSH_ERR_INTERNAL_ERROR; | ||
639 | } | ||
640 | ssh->kex->done = 0; | ||
641 | return kex_send_kexinit(ssh); | ||
642 | } | ||
643 | |||
548 | static int | 644 | static int |
549 | choose_enc(struct sshenc *enc, char *client, char *server) | 645 | choose_enc(struct sshenc *enc, char *client, char *server) |
550 | { | 646 | { |
@@ -609,6 +705,7 @@ choose_kex(struct kex *k, char *client, char *server) | |||
609 | 705 | ||
610 | k->name = match_list(client, server, NULL); | 706 | k->name = match_list(client, server, NULL); |
611 | 707 | ||
708 | debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); | ||
612 | if (k->name == NULL) | 709 | if (k->name == NULL) |
613 | return SSH_ERR_NO_KEX_ALG_MATCH; | 710 | return SSH_ERR_NO_KEX_ALG_MATCH; |
614 | if ((kexalg = kex_alg_by_name(k->name)) == NULL) | 711 | if ((kexalg = kex_alg_by_name(k->name)) == NULL) |
@@ -622,15 +719,16 @@ choose_kex(struct kex *k, char *client, char *server) | |||
622 | static int | 719 | static int |
623 | choose_hostkeyalg(struct kex *k, char *client, char *server) | 720 | choose_hostkeyalg(struct kex *k, char *client, char *server) |
624 | { | 721 | { |
625 | char *hostkeyalg = match_list(client, server, NULL); | 722 | k->hostkey_alg = match_list(client, server, NULL); |
626 | 723 | ||
627 | if (hostkeyalg == NULL) | 724 | debug("kex: host key algorithm: %s", |
725 | k->hostkey_alg ? k->hostkey_alg : "(no match)"); | ||
726 | if (k->hostkey_alg == NULL) | ||
628 | return SSH_ERR_NO_HOSTKEY_ALG_MATCH; | 727 | return SSH_ERR_NO_HOSTKEY_ALG_MATCH; |
629 | k->hostkey_type = sshkey_type_from_name(hostkeyalg); | 728 | k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); |
630 | if (k->hostkey_type == KEY_UNSPEC) | 729 | if (k->hostkey_type == KEY_UNSPEC) |
631 | return SSH_ERR_INTERNAL_ERROR; | 730 | return SSH_ERR_INTERNAL_ERROR; |
632 | k->hostkey_nid = sshkey_ecdsa_nid_from_name(hostkeyalg); | 731 | k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); |
633 | free(hostkeyalg); | ||
634 | return 0; | 732 | return 0; |
635 | } | 733 | } |
636 | 734 | ||
@@ -669,8 +767,11 @@ kex_choose_conf(struct ssh *ssh) | |||
669 | u_int mode, ctos, need, dh_need, authlen; | 767 | u_int mode, ctos, need, dh_need, authlen; |
670 | int r, first_kex_follows; | 768 | int r, first_kex_follows; |
671 | 769 | ||
672 | if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0 || | 770 | debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); |
673 | (r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) | 771 | if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) |
772 | goto out; | ||
773 | debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); | ||
774 | if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) | ||
674 | goto out; | 775 | goto out; |
675 | 776 | ||
676 | if (kex->server) { | 777 | if (kex->server) { |
@@ -681,18 +782,30 @@ kex_choose_conf(struct ssh *ssh) | |||
681 | sprop=peer; | 782 | sprop=peer; |
682 | } | 783 | } |
683 | 784 | ||
684 | /* Check whether server offers roaming */ | 785 | /* Check whether client supports ext_info_c */ |
685 | if (!kex->server) { | 786 | if (kex->server) { |
686 | char *roaming = match_list(KEX_RESUME, | 787 | char *ext; |
687 | peer[PROPOSAL_KEX_ALGS], NULL); | ||
688 | 788 | ||
689 | if (roaming) { | 789 | ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); |
690 | kex->roaming = 1; | 790 | if (ext) { |
691 | free(roaming); | 791 | kex->ext_info_c = 1; |
792 | free(ext); | ||
692 | } | 793 | } |
693 | } | 794 | } |
694 | 795 | ||
695 | /* Algorithm Negotiation */ | 796 | /* Algorithm Negotiation */ |
797 | if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], | ||
798 | sprop[PROPOSAL_KEX_ALGS])) != 0) { | ||
799 | kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; | ||
800 | peer[PROPOSAL_KEX_ALGS] = NULL; | ||
801 | goto out; | ||
802 | } | ||
803 | if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
804 | sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { | ||
805 | kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
806 | peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; | ||
807 | goto out; | ||
808 | } | ||
696 | for (mode = 0; mode < MODE_MAX; mode++) { | 809 | for (mode = 0; mode < MODE_MAX; mode++) { |
697 | if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { | 810 | if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { |
698 | r = SSH_ERR_ALLOC_FAIL; | 811 | r = SSH_ERR_ALLOC_FAIL; |
@@ -725,24 +838,12 @@ kex_choose_conf(struct ssh *ssh) | |||
725 | peer[ncomp] = NULL; | 838 | peer[ncomp] = NULL; |
726 | goto out; | 839 | goto out; |
727 | } | 840 | } |
728 | debug("kex: %s %s %s %s", | 841 | debug("kex: %s cipher: %s MAC: %s compression: %s", |
729 | ctos ? "client->server" : "server->client", | 842 | ctos ? "client->server" : "server->client", |
730 | newkeys->enc.name, | 843 | newkeys->enc.name, |
731 | authlen == 0 ? newkeys->mac.name : "<implicit>", | 844 | authlen == 0 ? newkeys->mac.name : "<implicit>", |
732 | newkeys->comp.name); | 845 | newkeys->comp.name); |
733 | } | 846 | } |
734 | if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], | ||
735 | sprop[PROPOSAL_KEX_ALGS])) != 0) { | ||
736 | kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; | ||
737 | peer[PROPOSAL_KEX_ALGS] = NULL; | ||
738 | goto out; | ||
739 | } | ||
740 | if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
741 | sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { | ||
742 | kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
743 | peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; | ||
744 | goto out; | ||
745 | } | ||
746 | need = dh_need = 0; | 847 | need = dh_need = 0; |
747 | for (mode = 0; mode < MODE_MAX; mode++) { | 848 | for (mode = 0; mode < MODE_MAX; mode++) { |
748 | newkeys = kex->newkeys[mode]; | 849 | newkeys = kex->newkeys[mode]; |
@@ -828,8 +929,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, | |||
828 | digest = NULL; | 929 | digest = NULL; |
829 | r = 0; | 930 | r = 0; |
830 | out: | 931 | out: |
831 | if (digest) | 932 | free(digest); |
832 | free(digest); | ||
833 | ssh_digest_free(hashctx); | 933 | ssh_digest_free(hashctx); |
834 | return r; | 934 | return r; |
835 | } | 935 | } |