diff options
author | Colin Watson <cjwatson@debian.org> | 2019-06-05 06:41:44 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2019-06-09 22:09:07 +0100 |
commit | 865a97e05b6aab1619e1c8eeb33ccb8f9a9e48d3 (patch) | |
tree | 7bb2128eb663180bacfabca88f26d26bf0733824 /kex.c | |
parent | ba627ba172d6649919baedff5ba2789610da382a (diff) | |
parent | 7d50f9e5be88179325983a1f58c9d51bb58f025a (diff) |
New upstream release (8.0p1)
Diffstat (limited to 'kex.c')
-rw-r--r-- | kex.c | 402 |
1 files changed, 357 insertions, 45 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: kex.c,v 1.141 2018/07/09 13:37:10 sf Exp $ */ | 1 | /* $OpenBSD: kex.c,v 1.150 2019/01/21 12:08:13 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 | * |
@@ -25,19 +25,25 @@ | |||
25 | 25 | ||
26 | #include "includes.h" | 26 | #include "includes.h" |
27 | 27 | ||
28 | 28 | #include <sys/types.h> | |
29 | #include <errno.h> | ||
29 | #include <signal.h> | 30 | #include <signal.h> |
30 | #include <stdarg.h> | 31 | #include <stdarg.h> |
31 | #include <stdio.h> | 32 | #include <stdio.h> |
32 | #include <stdlib.h> | 33 | #include <stdlib.h> |
33 | #include <string.h> | 34 | #include <string.h> |
35 | #include <unistd.h> | ||
36 | #include <poll.h> | ||
34 | 37 | ||
35 | #ifdef WITH_OPENSSL | 38 | #ifdef WITH_OPENSSL |
36 | #include <openssl/crypto.h> | 39 | #include <openssl/crypto.h> |
37 | #include <openssl/dh.h> | 40 | #include <openssl/dh.h> |
38 | #endif | 41 | #endif |
39 | 42 | ||
43 | #include "ssh.h" | ||
40 | #include "ssh2.h" | 44 | #include "ssh2.h" |
45 | #include "atomicio.h" | ||
46 | #include "version.h" | ||
41 | #include "packet.h" | 47 | #include "packet.h" |
42 | #include "compat.h" | 48 | #include "compat.h" |
43 | #include "cipher.h" | 49 | #include "cipher.h" |
@@ -49,6 +55,7 @@ | |||
49 | #include "misc.h" | 55 | #include "misc.h" |
50 | #include "dispatch.h" | 56 | #include "dispatch.h" |
51 | #include "monitor.h" | 57 | #include "monitor.h" |
58 | #include "xmalloc.h" | ||
52 | 59 | ||
53 | #include "ssherr.h" | 60 | #include "ssherr.h" |
54 | #include "sshbuf.h" | 61 | #include "sshbuf.h" |
@@ -106,26 +113,33 @@ static const struct kexalg kexalgs[] = { | |||
106 | #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) | 113 | #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) |
107 | { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, | 114 | { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, |
108 | { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, | 115 | { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, |
116 | { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0, | ||
117 | SSH_DIGEST_SHA512 }, | ||
109 | #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ | 118 | #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ |
110 | { NULL, -1, -1, -1}, | 119 | { NULL, -1, -1, -1}, |
111 | }; | 120 | }; |
112 | static const struct kexalg kexalg_prefixes[] = { | 121 | static const struct kexalg gss_kexalgs[] = { |
113 | #ifdef GSSAPI | 122 | #ifdef GSSAPI |
114 | { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, | 123 | { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, |
115 | { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, | 124 | { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, |
116 | { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, | 125 | { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, |
126 | { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, | ||
127 | { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, | ||
128 | { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, | ||
129 | NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, | ||
130 | { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, | ||
117 | #endif | 131 | #endif |
118 | { NULL, -1, -1, -1 }, | 132 | { NULL, -1, -1, -1 }, |
119 | }; | 133 | }; |
120 | 134 | ||
121 | char * | 135 | static char * |
122 | kex_alg_list(char sep) | 136 | kex_alg_list_internal(char sep, const struct kexalg *algs) |
123 | { | 137 | { |
124 | char *ret = NULL, *tmp; | 138 | char *ret = NULL, *tmp; |
125 | size_t nlen, rlen = 0; | 139 | size_t nlen, rlen = 0; |
126 | const struct kexalg *k; | 140 | const struct kexalg *k; |
127 | 141 | ||
128 | for (k = kexalgs; k->name != NULL; k++) { | 142 | for (k = algs; k->name != NULL; k++) { |
129 | if (ret != NULL) | 143 | if (ret != NULL) |
130 | ret[rlen++] = sep; | 144 | ret[rlen++] = sep; |
131 | nlen = strlen(k->name); | 145 | nlen = strlen(k->name); |
@@ -140,6 +154,18 @@ kex_alg_list(char sep) | |||
140 | return ret; | 154 | return ret; |
141 | } | 155 | } |
142 | 156 | ||
157 | char * | ||
158 | kex_alg_list(char sep) | ||
159 | { | ||
160 | return kex_alg_list_internal(sep, kexalgs); | ||
161 | } | ||
162 | |||
163 | char * | ||
164 | kex_gss_alg_list(char sep) | ||
165 | { | ||
166 | return kex_alg_list_internal(sep, gss_kexalgs); | ||
167 | } | ||
168 | |||
143 | static const struct kexalg * | 169 | static const struct kexalg * |
144 | kex_alg_by_name(const char *name) | 170 | kex_alg_by_name(const char *name) |
145 | { | 171 | { |
@@ -149,7 +175,7 @@ kex_alg_by_name(const char *name) | |||
149 | if (strcmp(k->name, name) == 0) | 175 | if (strcmp(k->name, name) == 0) |
150 | return k; | 176 | return k; |
151 | } | 177 | } |
152 | for (k = kexalg_prefixes; k->name != NULL; k++) { | 178 | for (k = gss_kexalgs; k->name != NULL; k++) { |
153 | if (strncmp(k->name, name, strlen(k->name)) == 0) | 179 | if (strncmp(k->name, name, strlen(k->name)) == 0) |
154 | return k; | 180 | return k; |
155 | } | 181 | } |
@@ -309,6 +335,29 @@ kex_assemble_names(char **listp, const char *def, const char *all) | |||
309 | return r; | 335 | return r; |
310 | } | 336 | } |
311 | 337 | ||
338 | /* Validate GSS KEX method name list */ | ||
339 | int | ||
340 | kex_gss_names_valid(const char *names) | ||
341 | { | ||
342 | char *s, *cp, *p; | ||
343 | |||
344 | if (names == NULL || *names == '\0') | ||
345 | return 0; | ||
346 | s = cp = xstrdup(names); | ||
347 | for ((p = strsep(&cp, ",")); p && *p != '\0'; | ||
348 | (p = strsep(&cp, ","))) { | ||
349 | if (strncmp(p, "gss-", 4) != 0 | ||
350 | || kex_alg_by_name(p) == NULL) { | ||
351 | error("Unsupported KEX algorithm \"%.100s\"", p); | ||
352 | free(s); | ||
353 | return 0; | ||
354 | } | ||
355 | } | ||
356 | debug3("gss kex names ok: [%s]", names); | ||
357 | free(s); | ||
358 | return 1; | ||
359 | } | ||
360 | |||
312 | /* put algorithm proposal into buffer */ | 361 | /* put algorithm proposal into buffer */ |
313 | int | 362 | int |
314 | kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) | 363 | kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) |
@@ -503,6 +552,7 @@ kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) | |||
503 | if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) | 552 | if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) |
504 | return r; | 553 | return r; |
505 | kex->done = 1; | 554 | kex->done = 1; |
555 | kex->flags &= ~KEX_INITIAL; | ||
506 | sshbuf_reset(kex->peer); | 556 | sshbuf_reset(kex->peer); |
507 | /* sshbuf_reset(kex->my); */ | 557 | /* sshbuf_reset(kex->my); */ |
508 | kex->flags &= ~KEX_INIT_SENT; | 558 | kex->flags &= ~KEX_INIT_SENT; |
@@ -593,31 +643,20 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) | |||
593 | return SSH_ERR_INTERNAL_ERROR; | 643 | return SSH_ERR_INTERNAL_ERROR; |
594 | } | 644 | } |
595 | 645 | ||
596 | int | 646 | struct kex * |
597 | kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp) | 647 | kex_new(void) |
598 | { | 648 | { |
599 | struct kex *kex; | 649 | struct kex *kex; |
600 | int r; | ||
601 | 650 | ||
602 | *kexp = NULL; | 651 | if ((kex = calloc(1, sizeof(*kex))) == NULL || |
603 | if ((kex = calloc(1, sizeof(*kex))) == NULL) | 652 | (kex->peer = sshbuf_new()) == NULL || |
604 | return SSH_ERR_ALLOC_FAIL; | 653 | (kex->my = sshbuf_new()) == NULL || |
605 | if ((kex->peer = sshbuf_new()) == NULL || | 654 | (kex->client_version = sshbuf_new()) == NULL || |
606 | (kex->my = sshbuf_new()) == NULL) { | 655 | (kex->server_version = sshbuf_new()) == NULL) { |
607 | r = SSH_ERR_ALLOC_FAIL; | ||
608 | goto out; | ||
609 | } | ||
610 | if ((r = kex_prop2buf(kex->my, proposal)) != 0) | ||
611 | goto out; | ||
612 | kex->done = 0; | ||
613 | kex_reset_dispatch(ssh); | ||
614 | ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); | ||
615 | r = 0; | ||
616 | *kexp = kex; | ||
617 | out: | ||
618 | if (r != 0) | ||
619 | kex_free(kex); | 656 | kex_free(kex); |
620 | return r; | 657 | return NULL; |
658 | } | ||
659 | return kex; | ||
621 | } | 660 | } |
622 | 661 | ||
623 | void | 662 | void |
@@ -656,6 +695,9 @@ kex_free(struct kex *kex) | |||
656 | { | 695 | { |
657 | u_int mode; | 696 | u_int mode; |
658 | 697 | ||
698 | if (kex == NULL) | ||
699 | return; | ||
700 | |||
659 | #ifdef WITH_OPENSSL | 701 | #ifdef WITH_OPENSSL |
660 | DH_free(kex->dh); | 702 | DH_free(kex->dh); |
661 | #ifdef OPENSSL_HAS_ECC | 703 | #ifdef OPENSSL_HAS_ECC |
@@ -668,12 +710,13 @@ kex_free(struct kex *kex) | |||
668 | } | 710 | } |
669 | sshbuf_free(kex->peer); | 711 | sshbuf_free(kex->peer); |
670 | sshbuf_free(kex->my); | 712 | sshbuf_free(kex->my); |
713 | sshbuf_free(kex->client_version); | ||
714 | sshbuf_free(kex->server_version); | ||
715 | sshbuf_free(kex->client_pub); | ||
671 | free(kex->session_id); | 716 | free(kex->session_id); |
672 | #ifdef GSSAPI | 717 | #ifdef GSSAPI |
673 | free(kex->gss_host); | 718 | free(kex->gss_host); |
674 | #endif /* GSSAPI */ | 719 | #endif /* GSSAPI */ |
675 | free(kex->client_version_string); | ||
676 | free(kex->server_version_string); | ||
677 | free(kex->failed_choice); | 720 | free(kex->failed_choice); |
678 | free(kex->hostkey_alg); | 721 | free(kex->hostkey_alg); |
679 | free(kex->name); | 722 | free(kex->name); |
@@ -681,11 +724,24 @@ kex_free(struct kex *kex) | |||
681 | } | 724 | } |
682 | 725 | ||
683 | int | 726 | int |
727 | kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) | ||
728 | { | ||
729 | int r; | ||
730 | |||
731 | if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0) | ||
732 | return r; | ||
733 | ssh->kex->flags = KEX_INITIAL; | ||
734 | kex_reset_dispatch(ssh); | ||
735 | ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); | ||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | int | ||
684 | kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) | 740 | kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) |
685 | { | 741 | { |
686 | int r; | 742 | int r; |
687 | 743 | ||
688 | if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0) | 744 | if ((r = kex_ready(ssh, proposal)) != 0) |
689 | return r; | 745 | return r; |
690 | if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ | 746 | if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ |
691 | kex_free(ssh->kex); | 747 | kex_free(ssh->kex); |
@@ -858,7 +914,7 @@ kex_choose_conf(struct ssh *ssh) | |||
858 | } | 914 | } |
859 | 915 | ||
860 | /* Check whether client supports ext_info_c */ | 916 | /* Check whether client supports ext_info_c */ |
861 | if (kex->server) { | 917 | if (kex->server && (kex->flags & KEX_INITIAL)) { |
862 | char *ext; | 918 | char *ext; |
863 | 919 | ||
864 | ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); | 920 | ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); |
@@ -1016,6 +1072,14 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, | |||
1016 | u_int i, j, mode, ctos; | 1072 | u_int i, j, mode, ctos; |
1017 | int r; | 1073 | int r; |
1018 | 1074 | ||
1075 | /* save initial hash as session id */ | ||
1076 | if (kex->session_id == NULL) { | ||
1077 | kex->session_id_len = hashlen; | ||
1078 | kex->session_id = malloc(kex->session_id_len); | ||
1079 | if (kex->session_id == NULL) | ||
1080 | return SSH_ERR_ALLOC_FAIL; | ||
1081 | memcpy(kex->session_id, hash, kex->session_id_len); | ||
1082 | } | ||
1019 | for (i = 0; i < NKEYS; i++) { | 1083 | for (i = 0; i < NKEYS; i++) { |
1020 | if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, | 1084 | if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, |
1021 | shared_secret, &keys[i])) != 0) { | 1085 | shared_secret, &keys[i])) != 0) { |
@@ -1034,29 +1098,277 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, | |||
1034 | return 0; | 1098 | return 0; |
1035 | } | 1099 | } |
1036 | 1100 | ||
1037 | #ifdef WITH_OPENSSL | ||
1038 | int | 1101 | int |
1039 | kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen, | 1102 | kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) |
1040 | const BIGNUM *secret) | ||
1041 | { | 1103 | { |
1042 | struct sshbuf *shared_secret; | 1104 | struct kex *kex = ssh->kex; |
1043 | int r; | ||
1044 | 1105 | ||
1045 | if ((shared_secret = sshbuf_new()) == NULL) | 1106 | *pubp = NULL; |
1046 | return SSH_ERR_ALLOC_FAIL; | 1107 | *prvp = NULL; |
1047 | if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0) | 1108 | if (kex->load_host_public_key == NULL || |
1048 | r = kex_derive_keys(ssh, hash, hashlen, shared_secret); | 1109 | kex->load_host_private_key == NULL) |
1049 | sshbuf_free(shared_secret); | 1110 | return SSH_ERR_INVALID_ARGUMENT; |
1050 | return r; | 1111 | *pubp = kex->load_host_public_key(kex->hostkey_type, |
1112 | kex->hostkey_nid, ssh); | ||
1113 | *prvp = kex->load_host_private_key(kex->hostkey_type, | ||
1114 | kex->hostkey_nid, ssh); | ||
1115 | if (*pubp == NULL) | ||
1116 | return SSH_ERR_NO_HOSTKEY_LOADED; | ||
1117 | return 0; | ||
1051 | } | 1118 | } |
1052 | #endif | ||
1053 | 1119 | ||
1120 | int | ||
1121 | kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) | ||
1122 | { | ||
1123 | struct kex *kex = ssh->kex; | ||
1124 | |||
1125 | if (kex->verify_host_key == NULL) | ||
1126 | return SSH_ERR_INVALID_ARGUMENT; | ||
1127 | if (server_host_key->type != kex->hostkey_type || | ||
1128 | (kex->hostkey_type == KEY_ECDSA && | ||
1129 | server_host_key->ecdsa_nid != kex->hostkey_nid)) | ||
1130 | return SSH_ERR_KEY_TYPE_MISMATCH; | ||
1131 | if (kex->verify_host_key(server_host_key, ssh) == -1) | ||
1132 | return SSH_ERR_SIGNATURE_INVALID; | ||
1133 | return 0; | ||
1134 | } | ||
1054 | 1135 | ||
1055 | #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) | 1136 | #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) |
1056 | void | 1137 | void |
1057 | dump_digest(char *msg, u_char *digest, int len) | 1138 | dump_digest(const char *msg, const u_char *digest, int len) |
1058 | { | 1139 | { |
1059 | fprintf(stderr, "%s\n", msg); | 1140 | fprintf(stderr, "%s\n", msg); |
1060 | sshbuf_dump_data(digest, len, stderr); | 1141 | sshbuf_dump_data(digest, len, stderr); |
1061 | } | 1142 | } |
1062 | #endif | 1143 | #endif |
1144 | |||
1145 | /* | ||
1146 | * Send a plaintext error message to the peer, suffixed by \r\n. | ||
1147 | * Only used during banner exchange, and there only for the server. | ||
1148 | */ | ||
1149 | static void | ||
1150 | send_error(struct ssh *ssh, char *msg) | ||
1151 | { | ||
1152 | char *crnl = "\r\n"; | ||
1153 | |||
1154 | if (!ssh->kex->server) | ||
1155 | return; | ||
1156 | |||
1157 | if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), | ||
1158 | msg, strlen(msg)) != strlen(msg) || | ||
1159 | atomicio(vwrite, ssh_packet_get_connection_out(ssh), | ||
1160 | crnl, strlen(crnl)) != strlen(crnl)) | ||
1161 | error("%s: write: %.100s", __func__, strerror(errno)); | ||
1162 | } | ||
1163 | |||
1164 | /* | ||
1165 | * Sends our identification string and waits for the peer's. Will block for | ||
1166 | * up to timeout_ms (or indefinitely if timeout_ms <= 0). | ||
1167 | * Returns on 0 success or a ssherr.h code on failure. | ||
1168 | */ | ||
1169 | int | ||
1170 | kex_exchange_identification(struct ssh *ssh, int timeout_ms, | ||
1171 | int debian_banner, const char *version_addendum) | ||
1172 | { | ||
1173 | int remote_major, remote_minor, mismatch; | ||
1174 | size_t len, i, n; | ||
1175 | int r, expect_nl; | ||
1176 | u_char c; | ||
1177 | struct sshbuf *our_version = ssh->kex->server ? | ||
1178 | ssh->kex->server_version : ssh->kex->client_version; | ||
1179 | struct sshbuf *peer_version = ssh->kex->server ? | ||
1180 | ssh->kex->client_version : ssh->kex->server_version; | ||
1181 | char *our_version_string = NULL, *peer_version_string = NULL; | ||
1182 | char *cp, *remote_version = NULL; | ||
1183 | |||
1184 | /* Prepare and send our banner */ | ||
1185 | sshbuf_reset(our_version); | ||
1186 | if (version_addendum != NULL && *version_addendum == '\0') | ||
1187 | version_addendum = NULL; | ||
1188 | if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n", | ||
1189 | PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, | ||
1190 | debian_banner ? SSH_RELEASE : SSH_RELEASE_MINIMUM, | ||
1191 | version_addendum == NULL ? "" : " ", | ||
1192 | version_addendum == NULL ? "" : version_addendum)) != 0) { | ||
1193 | error("%s: sshbuf_putf: %s", __func__, ssh_err(r)); | ||
1194 | goto out; | ||
1195 | } | ||
1196 | |||
1197 | if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), | ||
1198 | sshbuf_mutable_ptr(our_version), | ||
1199 | sshbuf_len(our_version)) != sshbuf_len(our_version)) { | ||
1200 | error("%s: write: %.100s", __func__, strerror(errno)); | ||
1201 | r = SSH_ERR_SYSTEM_ERROR; | ||
1202 | goto out; | ||
1203 | } | ||
1204 | if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */ | ||
1205 | error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r)); | ||
1206 | goto out; | ||
1207 | } | ||
1208 | our_version_string = sshbuf_dup_string(our_version); | ||
1209 | if (our_version_string == NULL) { | ||
1210 | error("%s: sshbuf_dup_string failed", __func__); | ||
1211 | r = SSH_ERR_ALLOC_FAIL; | ||
1212 | goto out; | ||
1213 | } | ||
1214 | debug("Local version string %.100s", our_version_string); | ||
1215 | |||
1216 | /* Read other side's version identification. */ | ||
1217 | for (n = 0; ; n++) { | ||
1218 | if (n >= SSH_MAX_PRE_BANNER_LINES) { | ||
1219 | send_error(ssh, "No SSH identification string " | ||
1220 | "received."); | ||
1221 | error("%s: No SSH version received in first %u lines " | ||
1222 | "from server", __func__, SSH_MAX_PRE_BANNER_LINES); | ||
1223 | r = SSH_ERR_INVALID_FORMAT; | ||
1224 | goto out; | ||
1225 | } | ||
1226 | sshbuf_reset(peer_version); | ||
1227 | expect_nl = 0; | ||
1228 | for (i = 0; ; i++) { | ||
1229 | if (timeout_ms > 0) { | ||
1230 | r = waitrfd(ssh_packet_get_connection_in(ssh), | ||
1231 | &timeout_ms); | ||
1232 | if (r == -1 && errno == ETIMEDOUT) { | ||
1233 | send_error(ssh, "Timed out waiting " | ||
1234 | "for SSH identification string."); | ||
1235 | error("Connection timed out during " | ||
1236 | "banner exchange"); | ||
1237 | r = SSH_ERR_CONN_TIMEOUT; | ||
1238 | goto out; | ||
1239 | } else if (r == -1) { | ||
1240 | error("%s: %s", | ||
1241 | __func__, strerror(errno)); | ||
1242 | r = SSH_ERR_SYSTEM_ERROR; | ||
1243 | goto out; | ||
1244 | } | ||
1245 | } | ||
1246 | |||
1247 | len = atomicio(read, ssh_packet_get_connection_in(ssh), | ||
1248 | &c, 1); | ||
1249 | if (len != 1 && errno == EPIPE) { | ||
1250 | error("%s: Connection closed by remote host", | ||
1251 | __func__); | ||
1252 | r = SSH_ERR_CONN_CLOSED; | ||
1253 | goto out; | ||
1254 | } else if (len != 1) { | ||
1255 | error("%s: read: %.100s", | ||
1256 | __func__, strerror(errno)); | ||
1257 | r = SSH_ERR_SYSTEM_ERROR; | ||
1258 | goto out; | ||
1259 | } | ||
1260 | if (c == '\r') { | ||
1261 | expect_nl = 1; | ||
1262 | continue; | ||
1263 | } | ||
1264 | if (c == '\n') | ||
1265 | break; | ||
1266 | if (c == '\0' || expect_nl) { | ||
1267 | error("%s: banner line contains invalid " | ||
1268 | "characters", __func__); | ||
1269 | goto invalid; | ||
1270 | } | ||
1271 | if ((r = sshbuf_put_u8(peer_version, c)) != 0) { | ||
1272 | error("%s: sshbuf_put: %s", | ||
1273 | __func__, ssh_err(r)); | ||
1274 | goto out; | ||
1275 | } | ||
1276 | if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { | ||
1277 | error("%s: banner line too long", __func__); | ||
1278 | goto invalid; | ||
1279 | } | ||
1280 | } | ||
1281 | /* Is this an actual protocol banner? */ | ||
1282 | if (sshbuf_len(peer_version) > 4 && | ||
1283 | memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0) | ||
1284 | break; | ||
1285 | /* If not, then just log the line and continue */ | ||
1286 | if ((cp = sshbuf_dup_string(peer_version)) == NULL) { | ||
1287 | error("%s: sshbuf_dup_string failed", __func__); | ||
1288 | r = SSH_ERR_ALLOC_FAIL; | ||
1289 | goto out; | ||
1290 | } | ||
1291 | /* Do not accept lines before the SSH ident from a client */ | ||
1292 | if (ssh->kex->server) { | ||
1293 | error("%s: client sent invalid protocol identifier " | ||
1294 | "\"%.256s\"", __func__, cp); | ||
1295 | free(cp); | ||
1296 | goto invalid; | ||
1297 | } | ||
1298 | debug("%s: banner line %zu: %s", __func__, n, cp); | ||
1299 | free(cp); | ||
1300 | } | ||
1301 | peer_version_string = sshbuf_dup_string(peer_version); | ||
1302 | if (peer_version_string == NULL) | ||
1303 | error("%s: sshbuf_dup_string failed", __func__); | ||
1304 | /* XXX must be same size for sscanf */ | ||
1305 | if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) { | ||
1306 | error("%s: calloc failed", __func__); | ||
1307 | r = SSH_ERR_ALLOC_FAIL; | ||
1308 | goto out; | ||
1309 | } | ||
1310 | |||
1311 | /* | ||
1312 | * Check that the versions match. In future this might accept | ||
1313 | * several versions and set appropriate flags to handle them. | ||
1314 | */ | ||
1315 | if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n", | ||
1316 | &remote_major, &remote_minor, remote_version) != 3) { | ||
1317 | error("Bad remote protocol version identification: '%.100s'", | ||
1318 | peer_version_string); | ||
1319 | invalid: | ||
1320 | send_error(ssh, "Invalid SSH identification string."); | ||
1321 | r = SSH_ERR_INVALID_FORMAT; | ||
1322 | goto out; | ||
1323 | } | ||
1324 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
1325 | remote_major, remote_minor, remote_version); | ||
1326 | ssh->compat = compat_datafellows(remote_version); | ||
1327 | |||
1328 | mismatch = 0; | ||
1329 | switch (remote_major) { | ||
1330 | case 2: | ||
1331 | break; | ||
1332 | case 1: | ||
1333 | if (remote_minor != 99) | ||
1334 | mismatch = 1; | ||
1335 | break; | ||
1336 | default: | ||
1337 | mismatch = 1; | ||
1338 | break; | ||
1339 | } | ||
1340 | if (mismatch) { | ||
1341 | error("Protocol major versions differ: %d vs. %d", | ||
1342 | PROTOCOL_MAJOR_2, remote_major); | ||
1343 | send_error(ssh, "Protocol major versions differ."); | ||
1344 | r = SSH_ERR_NO_PROTOCOL_VERSION; | ||
1345 | goto out; | ||
1346 | } | ||
1347 | |||
1348 | if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) { | ||
1349 | logit("probed from %s port %d with %s. Don't panic.", | ||
1350 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), | ||
1351 | peer_version_string); | ||
1352 | r = SSH_ERR_CONN_CLOSED; /* XXX */ | ||
1353 | goto out; | ||
1354 | } | ||
1355 | if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) { | ||
1356 | logit("scanned from %s port %d with %s. Don't panic.", | ||
1357 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), | ||
1358 | peer_version_string); | ||
1359 | r = SSH_ERR_CONN_CLOSED; /* XXX */ | ||
1360 | goto out; | ||
1361 | } | ||
1362 | if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) { | ||
1363 | logit("Remote version \"%.100s\" uses unsafe RSA signature " | ||
1364 | "scheme; disabling use of RSA keys", remote_version); | ||
1365 | } | ||
1366 | /* success */ | ||
1367 | r = 0; | ||
1368 | out: | ||
1369 | free(our_version_string); | ||
1370 | free(peer_version_string); | ||
1371 | free(remote_version); | ||
1372 | return r; | ||
1373 | } | ||
1374 | |||