diff options
Diffstat (limited to 'kex.c')
-rw-r--r-- | kex.c | 105 |
1 files changed, 79 insertions, 26 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: kex.c,v 1.150 2019/01/21 12:08:13 djm Exp $ */ | 1 | /* $OpenBSD: kex.c,v 1.155 2019/10/08 22:40:39 dtucker 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 | * |
@@ -111,7 +111,7 @@ static const struct kexalg kexalgs[] = { | |||
111 | { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0, | 111 | { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0, |
112 | SSH_DIGEST_SHA512 }, | 112 | SSH_DIGEST_SHA512 }, |
113 | #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ | 113 | #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ |
114 | { NULL, -1, -1, -1}, | 114 | { NULL, 0, -1, -1}, |
115 | }; | 115 | }; |
116 | 116 | ||
117 | char * | 117 | char * |
@@ -213,8 +213,9 @@ kex_names_cat(const char *a, const char *b) | |||
213 | /* | 213 | /* |
214 | * Assemble a list of algorithms from a default list and a string from a | 214 | * Assemble a list of algorithms from a default list and a string from a |
215 | * configuration file. The user-provided string may begin with '+' to | 215 | * configuration file. The user-provided string may begin with '+' to |
216 | * indicate that it should be appended to the default or '-' that the | 216 | * indicate that it should be appended to the default, '-' that the |
217 | * specified names should be removed. | 217 | * specified names should be removed, or '^' that they should be placed |
218 | * at the head. | ||
218 | */ | 219 | */ |
219 | int | 220 | int |
220 | kex_assemble_names(char **listp, const char *def, const char *all) | 221 | kex_assemble_names(char **listp, const char *def, const char *all) |
@@ -223,7 +224,10 @@ kex_assemble_names(char **listp, const char *def, const char *all) | |||
223 | char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL; | 224 | char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL; |
224 | int r = SSH_ERR_INTERNAL_ERROR; | 225 | int r = SSH_ERR_INTERNAL_ERROR; |
225 | 226 | ||
226 | if (listp == NULL || *listp == NULL || **listp == '\0') { | 227 | if (listp == NULL || def == NULL || all == NULL) |
228 | return SSH_ERR_INVALID_ARGUMENT; | ||
229 | |||
230 | if (*listp == NULL || **listp == '\0') { | ||
227 | if ((*listp = strdup(def)) == NULL) | 231 | if ((*listp = strdup(def)) == NULL) |
228 | return SSH_ERR_ALLOC_FAIL; | 232 | return SSH_ERR_ALLOC_FAIL; |
229 | return 0; | 233 | return 0; |
@@ -248,6 +252,14 @@ kex_assemble_names(char **listp, const char *def, const char *all) | |||
248 | free(list); | 252 | free(list); |
249 | /* filtering has already been done */ | 253 | /* filtering has already been done */ |
250 | return 0; | 254 | return 0; |
255 | } else if (*list == '^') { | ||
256 | /* Place names at head of default list */ | ||
257 | if ((tmp = kex_names_cat(list + 1, def)) == NULL) { | ||
258 | r = SSH_ERR_ALLOC_FAIL; | ||
259 | goto fail; | ||
260 | } | ||
261 | free(list); | ||
262 | list = tmp; | ||
251 | } else { | 263 | } else { |
252 | /* Explicit list, overrides default - just use "list" as is */ | 264 | /* Explicit list, overrides default - just use "list" as is */ |
253 | } | 265 | } |
@@ -345,18 +357,25 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) | |||
345 | r = SSH_ERR_ALLOC_FAIL; | 357 | r = SSH_ERR_ALLOC_FAIL; |
346 | goto out; | 358 | goto out; |
347 | } | 359 | } |
348 | if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */ | 360 | if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */ |
361 | error("%s: consume cookie: %s", __func__, ssh_err(r)); | ||
349 | goto out; | 362 | goto out; |
363 | } | ||
350 | /* extract kex init proposal strings */ | 364 | /* extract kex init proposal strings */ |
351 | for (i = 0; i < PROPOSAL_MAX; i++) { | 365 | for (i = 0; i < PROPOSAL_MAX; i++) { |
352 | if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) | 366 | if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) { |
367 | error("%s: parse proposal %u: %s", __func__, | ||
368 | i, ssh_err(r)); | ||
353 | goto out; | 369 | goto out; |
370 | } | ||
354 | debug2("%s: %s", proposal_names[i], proposal[i]); | 371 | debug2("%s: %s", proposal_names[i], proposal[i]); |
355 | } | 372 | } |
356 | /* first kex follows / reserved */ | 373 | /* first kex follows / reserved */ |
357 | if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ | 374 | if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ |
358 | (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */ | 375 | (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */ |
376 | error("%s: parse: %s", __func__, ssh_err(r)); | ||
359 | goto out; | 377 | goto out; |
378 | } | ||
360 | if (first_kex_follows != NULL) | 379 | if (first_kex_follows != NULL) |
361 | *first_kex_follows = v; | 380 | *first_kex_follows = v; |
362 | debug2("first_kex_follows %d ", v); | 381 | debug2("first_kex_follows %d ", v); |
@@ -409,6 +428,7 @@ kex_send_ext_info(struct ssh *ssh) | |||
409 | int r; | 428 | int r; |
410 | char *algs; | 429 | char *algs; |
411 | 430 | ||
431 | debug("Sending SSH2_MSG_EXT_INFO"); | ||
412 | if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) | 432 | if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) |
413 | return SSH_ERR_ALLOC_FAIL; | 433 | return SSH_ERR_ALLOC_FAIL; |
414 | /* XXX filter algs list by allowed pubkey/hostbased types */ | 434 | /* XXX filter algs list by allowed pubkey/hostbased types */ |
@@ -416,8 +436,10 @@ kex_send_ext_info(struct ssh *ssh) | |||
416 | (r = sshpkt_put_u32(ssh, 1)) != 0 || | 436 | (r = sshpkt_put_u32(ssh, 1)) != 0 || |
417 | (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || | 437 | (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || |
418 | (r = sshpkt_put_cstring(ssh, algs)) != 0 || | 438 | (r = sshpkt_put_cstring(ssh, algs)) != 0 || |
419 | (r = sshpkt_send(ssh)) != 0) | 439 | (r = sshpkt_send(ssh)) != 0) { |
440 | error("%s: compose: %s", __func__, ssh_err(r)); | ||
420 | goto out; | 441 | goto out; |
442 | } | ||
421 | /* success */ | 443 | /* success */ |
422 | r = 0; | 444 | r = 0; |
423 | out: | 445 | out: |
@@ -435,11 +457,11 @@ kex_send_newkeys(struct ssh *ssh) | |||
435 | (r = sshpkt_send(ssh)) != 0) | 457 | (r = sshpkt_send(ssh)) != 0) |
436 | return r; | 458 | return r; |
437 | debug("SSH2_MSG_NEWKEYS sent"); | 459 | debug("SSH2_MSG_NEWKEYS sent"); |
438 | debug("expecting SSH2_MSG_NEWKEYS"); | ||
439 | ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); | 460 | ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); |
440 | if (ssh->kex->ext_info_c) | 461 | if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0) |
441 | if ((r = kex_send_ext_info(ssh)) != 0) | 462 | if ((r = kex_send_ext_info(ssh)) != 0) |
442 | return r; | 463 | return r; |
464 | debug("expecting SSH2_MSG_NEWKEYS"); | ||
443 | return 0; | 465 | return 0; |
444 | } | 466 | } |
445 | 467 | ||
@@ -511,23 +533,32 @@ kex_send_kexinit(struct ssh *ssh) | |||
511 | struct kex *kex = ssh->kex; | 533 | struct kex *kex = ssh->kex; |
512 | int r; | 534 | int r; |
513 | 535 | ||
514 | if (kex == NULL) | 536 | if (kex == NULL) { |
537 | error("%s: no hex", __func__); | ||
515 | return SSH_ERR_INTERNAL_ERROR; | 538 | return SSH_ERR_INTERNAL_ERROR; |
539 | } | ||
516 | if (kex->flags & KEX_INIT_SENT) | 540 | if (kex->flags & KEX_INIT_SENT) |
517 | return 0; | 541 | return 0; |
518 | kex->done = 0; | 542 | kex->done = 0; |
519 | 543 | ||
520 | /* generate a random cookie */ | 544 | /* generate a random cookie */ |
521 | if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) | 545 | if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) { |
546 | error("%s: bad kex length: %zu < %d", __func__, | ||
547 | sshbuf_len(kex->my), KEX_COOKIE_LEN); | ||
522 | return SSH_ERR_INVALID_FORMAT; | 548 | return SSH_ERR_INVALID_FORMAT; |
523 | if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) | 549 | } |
550 | if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) { | ||
551 | error("%s: buffer error", __func__); | ||
524 | return SSH_ERR_INTERNAL_ERROR; | 552 | return SSH_ERR_INTERNAL_ERROR; |
553 | } | ||
525 | arc4random_buf(cookie, KEX_COOKIE_LEN); | 554 | arc4random_buf(cookie, KEX_COOKIE_LEN); |
526 | 555 | ||
527 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || | 556 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || |
528 | (r = sshpkt_putb(ssh, kex->my)) != 0 || | 557 | (r = sshpkt_putb(ssh, kex->my)) != 0 || |
529 | (r = sshpkt_send(ssh)) != 0) | 558 | (r = sshpkt_send(ssh)) != 0) { |
559 | error("%s: compose reply: %s", __func__, ssh_err(r)); | ||
530 | return r; | 560 | return r; |
561 | } | ||
531 | debug("SSH2_MSG_KEXINIT sent"); | 562 | debug("SSH2_MSG_KEXINIT sent"); |
532 | kex->flags |= KEX_INIT_SENT; | 563 | kex->flags |= KEX_INIT_SENT; |
533 | return 0; | 564 | return 0; |
@@ -544,21 +575,28 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) | |||
544 | int r; | 575 | int r; |
545 | 576 | ||
546 | debug("SSH2_MSG_KEXINIT received"); | 577 | debug("SSH2_MSG_KEXINIT received"); |
547 | if (kex == NULL) | 578 | if (kex == NULL) { |
548 | return SSH_ERR_INVALID_ARGUMENT; | 579 | error("%s: no hex", __func__); |
549 | 580 | return SSH_ERR_INTERNAL_ERROR; | |
581 | } | ||
550 | ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); | 582 | ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); |
551 | ptr = sshpkt_ptr(ssh, &dlen); | 583 | ptr = sshpkt_ptr(ssh, &dlen); |
552 | if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) | 584 | if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) |
553 | return r; | 585 | return r; |
554 | 586 | ||
555 | /* discard packet */ | 587 | /* discard packet */ |
556 | for (i = 0; i < KEX_COOKIE_LEN; i++) | 588 | for (i = 0; i < KEX_COOKIE_LEN; i++) { |
557 | if ((r = sshpkt_get_u8(ssh, NULL)) != 0) | 589 | if ((r = sshpkt_get_u8(ssh, NULL)) != 0) { |
590 | error("%s: discard cookie: %s", __func__, ssh_err(r)); | ||
558 | return r; | 591 | return r; |
559 | for (i = 0; i < PROPOSAL_MAX; i++) | 592 | } |
560 | if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) | 593 | } |
594 | for (i = 0; i < PROPOSAL_MAX; i++) { | ||
595 | if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { | ||
596 | error("%s: discard proposal: %s", __func__, ssh_err(r)); | ||
561 | return r; | 597 | return r; |
598 | } | ||
599 | } | ||
562 | /* | 600 | /* |
563 | * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported | 601 | * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported |
564 | * KEX method has the server move first, but a server might be using | 602 | * KEX method has the server move first, but a server might be using |
@@ -583,6 +621,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) | |||
583 | if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) | 621 | if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) |
584 | return (kex->kex[kex->kex_type])(ssh); | 622 | return (kex->kex[kex->kex_type])(ssh); |
585 | 623 | ||
624 | error("%s: unknown kex type %u", __func__, kex->kex_type); | ||
586 | return SSH_ERR_INTERNAL_ERROR; | 625 | return SSH_ERR_INTERNAL_ERROR; |
587 | } | 626 | } |
588 | 627 | ||
@@ -718,6 +757,7 @@ choose_enc(struct sshenc *enc, char *client, char *server) | |||
718 | if (name == NULL) | 757 | if (name == NULL) |
719 | return SSH_ERR_NO_CIPHER_ALG_MATCH; | 758 | return SSH_ERR_NO_CIPHER_ALG_MATCH; |
720 | if ((enc->cipher = cipher_by_name(name)) == NULL) { | 759 | if ((enc->cipher = cipher_by_name(name)) == NULL) { |
760 | error("%s: unsupported cipher %s", __func__, name); | ||
721 | free(name); | 761 | free(name); |
722 | return SSH_ERR_INTERNAL_ERROR; | 762 | return SSH_ERR_INTERNAL_ERROR; |
723 | } | 763 | } |
@@ -739,6 +779,7 @@ choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) | |||
739 | if (name == NULL) | 779 | if (name == NULL) |
740 | return SSH_ERR_NO_MAC_ALG_MATCH; | 780 | return SSH_ERR_NO_MAC_ALG_MATCH; |
741 | if (mac_setup(mac, name) < 0) { | 781 | if (mac_setup(mac, name) < 0) { |
782 | error("%s: unsupported MAC %s", __func__, name); | ||
742 | free(name); | 783 | free(name); |
743 | return SSH_ERR_INTERNAL_ERROR; | 784 | return SSH_ERR_INTERNAL_ERROR; |
744 | } | 785 | } |
@@ -762,6 +803,7 @@ choose_comp(struct sshcomp *comp, char *client, char *server) | |||
762 | } else if (strcmp(name, "none") == 0) { | 803 | } else if (strcmp(name, "none") == 0) { |
763 | comp->type = COMP_NONE; | 804 | comp->type = COMP_NONE; |
764 | } else { | 805 | } else { |
806 | error("%s: unsupported compression scheme %s", __func__, name); | ||
765 | free(name); | 807 | free(name); |
766 | return SSH_ERR_INTERNAL_ERROR; | 808 | return SSH_ERR_INTERNAL_ERROR; |
767 | } | 809 | } |
@@ -779,8 +821,10 @@ choose_kex(struct kex *k, char *client, char *server) | |||
779 | debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); | 821 | debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); |
780 | if (k->name == NULL) | 822 | if (k->name == NULL) |
781 | return SSH_ERR_NO_KEX_ALG_MATCH; | 823 | return SSH_ERR_NO_KEX_ALG_MATCH; |
782 | if ((kexalg = kex_alg_by_name(k->name)) == NULL) | 824 | if ((kexalg = kex_alg_by_name(k->name)) == NULL) { |
825 | error("%s: unsupported KEX method %s", __func__, k->name); | ||
783 | return SSH_ERR_INTERNAL_ERROR; | 826 | return SSH_ERR_INTERNAL_ERROR; |
827 | } | ||
784 | k->kex_type = kexalg->type; | 828 | k->kex_type = kexalg->type; |
785 | k->hash_alg = kexalg->hash_alg; | 829 | k->hash_alg = kexalg->hash_alg; |
786 | k->ec_nid = kexalg->ec_nid; | 830 | k->ec_nid = kexalg->ec_nid; |
@@ -797,8 +841,11 @@ choose_hostkeyalg(struct kex *k, char *client, char *server) | |||
797 | if (k->hostkey_alg == NULL) | 841 | if (k->hostkey_alg == NULL) |
798 | return SSH_ERR_NO_HOSTKEY_ALG_MATCH; | 842 | return SSH_ERR_NO_HOSTKEY_ALG_MATCH; |
799 | k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); | 843 | k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); |
800 | if (k->hostkey_type == KEY_UNSPEC) | 844 | if (k->hostkey_type == KEY_UNSPEC) { |
845 | error("%s: unsupported hostkey algorithm %s", __func__, | ||
846 | k->hostkey_alg); | ||
801 | return SSH_ERR_INTERNAL_ERROR; | 847 | return SSH_ERR_INTERNAL_ERROR; |
848 | } | ||
802 | k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); | 849 | k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); |
803 | return 0; | 850 | return 0; |
804 | } | 851 | } |
@@ -967,6 +1014,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, | |||
967 | kex->session_id_len) != 0 || | 1014 | kex->session_id_len) != 0 || |
968 | ssh_digest_final(hashctx, digest, mdsz) != 0) { | 1015 | ssh_digest_final(hashctx, digest, mdsz) != 0) { |
969 | r = SSH_ERR_LIBCRYPTO_ERROR; | 1016 | r = SSH_ERR_LIBCRYPTO_ERROR; |
1017 | error("%s: KEX hash failed", __func__); | ||
970 | goto out; | 1018 | goto out; |
971 | } | 1019 | } |
972 | ssh_digest_free(hashctx); | 1020 | ssh_digest_free(hashctx); |
@@ -983,6 +1031,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, | |||
983 | ssh_digest_update(hashctx, hash, hashlen) != 0 || | 1031 | ssh_digest_update(hashctx, hash, hashlen) != 0 || |
984 | ssh_digest_update(hashctx, digest, have) != 0 || | 1032 | ssh_digest_update(hashctx, digest, have) != 0 || |
985 | ssh_digest_final(hashctx, digest + have, mdsz) != 0) { | 1033 | ssh_digest_final(hashctx, digest + have, mdsz) != 0) { |
1034 | error("%s: KDF failed", __func__); | ||
986 | r = SSH_ERR_LIBCRYPTO_ERROR; | 1035 | r = SSH_ERR_LIBCRYPTO_ERROR; |
987 | goto out; | 1036 | goto out; |
988 | } | 1037 | } |
@@ -1046,8 +1095,10 @@ kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) | |||
1046 | *pubp = NULL; | 1095 | *pubp = NULL; |
1047 | *prvp = NULL; | 1096 | *prvp = NULL; |
1048 | if (kex->load_host_public_key == NULL || | 1097 | if (kex->load_host_public_key == NULL || |
1049 | kex->load_host_private_key == NULL) | 1098 | kex->load_host_private_key == NULL) { |
1099 | error("%s: missing hostkey loader", __func__); | ||
1050 | return SSH_ERR_INVALID_ARGUMENT; | 1100 | return SSH_ERR_INVALID_ARGUMENT; |
1101 | } | ||
1051 | *pubp = kex->load_host_public_key(kex->hostkey_type, | 1102 | *pubp = kex->load_host_public_key(kex->hostkey_type, |
1052 | kex->hostkey_nid, ssh); | 1103 | kex->hostkey_nid, ssh); |
1053 | *prvp = kex->load_host_private_key(kex->hostkey_type, | 1104 | *prvp = kex->load_host_private_key(kex->hostkey_type, |
@@ -1062,8 +1113,10 @@ kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) | |||
1062 | { | 1113 | { |
1063 | struct kex *kex = ssh->kex; | 1114 | struct kex *kex = ssh->kex; |
1064 | 1115 | ||
1065 | if (kex->verify_host_key == NULL) | 1116 | if (kex->verify_host_key == NULL) { |
1117 | error("%s: missing hostkey verifier", __func__); | ||
1066 | return SSH_ERR_INVALID_ARGUMENT; | 1118 | return SSH_ERR_INVALID_ARGUMENT; |
1119 | } | ||
1067 | if (server_host_key->type != kex->hostkey_type || | 1120 | if (server_host_key->type != kex->hostkey_type || |
1068 | (kex->hostkey_type == KEY_ECDSA && | 1121 | (kex->hostkey_type == KEY_ECDSA && |
1069 | server_host_key->ecdsa_nid != kex->hostkey_nid)) | 1122 | server_host_key->ecdsa_nid != kex->hostkey_nid)) |