summaryrefslogtreecommitdiff
path: root/kex.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2018-12-27 03:25:24 +0000
committerDamien Miller <djm@mindrot.org>2018-12-27 14:38:22 +1100
commit0a843d9a0e805f14653a555f5c7a8ba99d62c12d (patch)
tree481f36e9fd1918be5449e369a97c086a1a8d2432 /kex.c
parent434b587afe41c19391821e7392005068fda76248 (diff)
upstream: move client/server SSH-* banners to buffers under
ssh->kex and factor out the banner exchange. This eliminates some common code from the client and server. Also be more strict about handling \r characters - these should only be accepted immediately before \n (pointed out by Jann Horn). Inspired by a patch from Markus Schmidt. (lots of) feedback and ok markus@ OpenBSD-Commit-ID: 1cc7885487a6754f63641d7d3279b0941890275b
Diffstat (limited to 'kex.c')
-rw-r--r--kex.c294
1 files changed, 267 insertions, 27 deletions
diff --git a/kex.c b/kex.c
index 3823a9544..30e1c261d 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: kex.c,v 1.142 2018/12/07 03:39:40 djm Exp $ */ 1/* $OpenBSD: kex.c,v 1.143 2018/12/27 03:25:25 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"
@@ -578,32 +584,20 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
578 return SSH_ERR_INTERNAL_ERROR; 584 return SSH_ERR_INTERNAL_ERROR;
579} 585}
580 586
581int 587struct kex *
582kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp) 588kex_new(void)
583{ 589{
584 struct kex *kex; 590 struct kex *kex;
585 int r;
586 591
587 *kexp = NULL; 592 if ((kex = calloc(1, sizeof(*kex))) == NULL ||
588 if ((kex = calloc(1, sizeof(*kex))) == NULL) 593 (kex->peer = sshbuf_new()) == NULL ||
589 return SSH_ERR_ALLOC_FAIL; 594 (kex->my = sshbuf_new()) == NULL ||
590 if ((kex->peer = sshbuf_new()) == NULL || 595 (kex->client_version = sshbuf_new()) == NULL ||
591 (kex->my = sshbuf_new()) == NULL) { 596 (kex->server_version = sshbuf_new()) == NULL) {
592 r = SSH_ERR_ALLOC_FAIL;
593 goto out;
594 }
595 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
596 goto out;
597 kex->done = 0;
598 kex->flags = KEX_INITIAL;
599 kex_reset_dispatch(ssh);
600 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
601 r = 0;
602 *kexp = kex;
603 out:
604 if (r != 0)
605 kex_free(kex); 597 kex_free(kex);
606 return r; 598 return NULL;
599 }
600 return kex;
607} 601}
608 602
609void 603void
@@ -642,6 +636,9 @@ kex_free(struct kex *kex)
642{ 636{
643 u_int mode; 637 u_int mode;
644 638
639 if (kex == NULL)
640 return;
641
645#ifdef WITH_OPENSSL 642#ifdef WITH_OPENSSL
646 DH_free(kex->dh); 643 DH_free(kex->dh);
647#ifdef OPENSSL_HAS_ECC 644#ifdef OPENSSL_HAS_ECC
@@ -654,9 +651,9 @@ kex_free(struct kex *kex)
654 } 651 }
655 sshbuf_free(kex->peer); 652 sshbuf_free(kex->peer);
656 sshbuf_free(kex->my); 653 sshbuf_free(kex->my);
654 sshbuf_free(kex->client_version);
655 sshbuf_free(kex->server_version);
657 free(kex->session_id); 656 free(kex->session_id);
658 free(kex->client_version_string);
659 free(kex->server_version_string);
660 free(kex->failed_choice); 657 free(kex->failed_choice);
661 free(kex->hostkey_alg); 658 free(kex->hostkey_alg);
662 free(kex->name); 659 free(kex->name);
@@ -664,11 +661,24 @@ kex_free(struct kex *kex)
664} 661}
665 662
666int 663int
664kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
665{
666 int r;
667
668 if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
669 return r;
670 ssh->kex->flags = KEX_INITIAL;
671 kex_reset_dispatch(ssh);
672 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
673 return 0;
674}
675
676int
667kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 677kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
668{ 678{
669 int r; 679 int r;
670 680
671 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0) 681 if ((r = kex_ready(ssh, proposal)) != 0)
672 return r; 682 return r;
673 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ 683 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
674 kex_free(ssh->kex); 684 kex_free(ssh->kex);
@@ -1043,3 +1053,233 @@ dump_digest(char *msg, u_char *digest, int len)
1043 sshbuf_dump_data(digest, len, stderr); 1053 sshbuf_dump_data(digest, len, stderr);
1044} 1054}
1045#endif 1055#endif
1056
1057/*
1058 * Send a plaintext error message to the peer, suffixed by \r\n.
1059 * Only used during banner exchange, and there only for the server.
1060 */
1061static void
1062send_error(struct ssh *ssh, char *msg)
1063{
1064 char *crnl = "\r\n";
1065
1066 if (!ssh->kex->server)
1067 return;
1068
1069 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1070 msg, strlen(msg)) != strlen(msg) ||
1071 atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1072 crnl, strlen(crnl)) != strlen(crnl))
1073 error("%s: write: %.100s", __func__, strerror(errno));
1074}
1075
1076/*
1077 * Sends our identification string and waits for the peer's. Will block for
1078 * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1079 * Returns on 0 success or a ssherr.h code on failure.
1080 */
1081int
1082kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1083 const char *version_addendum)
1084{
1085 int remote_major, remote_minor, mismatch;
1086 size_t len, i, n;
1087 int r, expect_nl;
1088 u_char c;
1089 struct sshbuf *our_version = ssh->kex->server ?
1090 ssh->kex->server_version : ssh->kex->client_version;
1091 struct sshbuf *peer_version = ssh->kex->server ?
1092 ssh->kex->client_version : ssh->kex->server_version;
1093 char *our_version_string = NULL, *peer_version_string = NULL;
1094 char *cp, *remote_version = NULL;
1095
1096 /* Prepare and send our banner */
1097 sshbuf_reset(our_version);
1098 if (version_addendum != NULL && *version_addendum == '\0')
1099 version_addendum = NULL;
1100 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
1101 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1102 version_addendum == NULL ? "" : " ",
1103 version_addendum == NULL ? "" : version_addendum)) != 0) {
1104 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
1105 goto out;
1106 }
1107
1108 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1109 sshbuf_mutable_ptr(our_version),
1110 sshbuf_len(our_version)) != sshbuf_len(our_version)) {
1111 error("%s: write: %.100s", __func__, strerror(errno));
1112 r = SSH_ERR_SYSTEM_ERROR;
1113 goto out;
1114 }
1115 if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
1116 error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
1117 goto out;
1118 }
1119 our_version_string = sshbuf_dup_string(our_version);
1120 if (our_version_string == NULL) {
1121 error("%s: sshbuf_dup_string failed", __func__);
1122 r = SSH_ERR_ALLOC_FAIL;
1123 goto out;
1124 }
1125 debug("Local version string %.100s", our_version_string);
1126
1127 /* Read other side's version identification. */
1128 for (n = 0; ; n++) {
1129 if (n >= SSH_MAX_PRE_BANNER_LINES) {
1130 send_error(ssh, "No SSH identification string "
1131 "received.");
1132 error("%s: No SSH version received in first %u lines "
1133 "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
1134 r = SSH_ERR_INVALID_FORMAT;
1135 goto out;
1136 }
1137 sshbuf_reset(peer_version);
1138 expect_nl = 0;
1139 for (i = 0; ; i++) {
1140 if (timeout_ms > 0) {
1141 r = waitrfd(ssh_packet_get_connection_in(ssh),
1142 &timeout_ms);
1143 if (r == -1 && errno == ETIMEDOUT) {
1144 send_error(ssh, "Timed out waiting "
1145 "for SSH identification string.");
1146 error("Connection timed out during "
1147 "banner exchange");
1148 r = SSH_ERR_CONN_TIMEOUT;
1149 goto out;
1150 } else if (r == -1) {
1151 error("%s: %s",
1152 __func__, strerror(errno));
1153 r = SSH_ERR_SYSTEM_ERROR;
1154 goto out;
1155 }
1156 }
1157
1158 len = atomicio(read, ssh_packet_get_connection_in(ssh),
1159 &c, 1);
1160 if (len != 1 && errno == EPIPE) {
1161 error("%s: Connection closed by remote host",
1162 __func__);
1163 r = SSH_ERR_CONN_CLOSED;
1164 goto out;
1165 } else if (len != 1) {
1166 error("%s: read: %.100s",
1167 __func__, strerror(errno));
1168 r = SSH_ERR_SYSTEM_ERROR;
1169 goto out;
1170 }
1171 if (c == '\r') {
1172 expect_nl = 1;
1173 continue;
1174 }
1175 if (c == '\n')
1176 break;
1177 if (c == '\0' || expect_nl) {
1178 error("%s: banner line contains invalid "
1179 "characters", __func__);
1180 goto invalid;
1181 }
1182 if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
1183 error("%s: sshbuf_put: %s",
1184 __func__, ssh_err(r));
1185 goto out;
1186 }
1187 if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1188 error("%s: banner line too long", __func__);
1189 goto invalid;
1190 }
1191 }
1192 /* Is this an actual protocol banner? */
1193 if (sshbuf_len(peer_version) > 4 &&
1194 memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1195 break;
1196 /* If not, then just log the line and continue */
1197 if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
1198 error("%s: sshbuf_dup_string failed", __func__);
1199 r = SSH_ERR_ALLOC_FAIL;
1200 goto out;
1201 }
1202 /* Do not accept lines before the SSH ident from a client */
1203 if (ssh->kex->server) {
1204 error("%s: client sent invalid protocol identifier "
1205 "\"%.256s\"", __func__, cp);
1206 free(cp);
1207 goto invalid;
1208 }
1209 debug("%s: banner line %zu: %s", __func__, n, cp);
1210 free(cp);
1211 }
1212 peer_version_string = sshbuf_dup_string(peer_version);
1213 if (peer_version_string == NULL)
1214 error("%s: sshbuf_dup_string failed", __func__);
1215 /* XXX must be same size for sscanf */
1216 if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
1217 error("%s: calloc failed", __func__);
1218 r = SSH_ERR_ALLOC_FAIL;
1219 goto out;
1220 }
1221
1222 /*
1223 * Check that the versions match. In future this might accept
1224 * several versions and set appropriate flags to handle them.
1225 */
1226 if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1227 &remote_major, &remote_minor, remote_version) != 3) {
1228 error("Bad remote protocol version identification: '%.100s'",
1229 peer_version_string);
1230 invalid:
1231 send_error(ssh, "Invalid SSH identification string.");
1232 r = SSH_ERR_INVALID_FORMAT;
1233 goto out;
1234 }
1235 debug("Remote protocol version %d.%d, remote software version %.100s",
1236 remote_major, remote_minor, remote_version);
1237 ssh->compat = compat_datafellows(remote_version);
1238
1239 mismatch = 0;
1240 switch (remote_major) {
1241 case 2:
1242 break;
1243 case 1:
1244 if (remote_minor != 99)
1245 mismatch = 1;
1246 break;
1247 default:
1248 mismatch = 1;
1249 break;
1250 }
1251 if (mismatch) {
1252 error("Protocol major versions differ: %d vs. %d",
1253 PROTOCOL_MAJOR_2, remote_major);
1254 send_error(ssh, "Protocol major versions differ.");
1255 r = SSH_ERR_NO_PROTOCOL_VERSION;
1256 goto out;
1257 }
1258
1259 if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1260 logit("probed from %s port %d with %s. Don't panic.",
1261 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1262 peer_version_string);
1263 r = SSH_ERR_CONN_CLOSED; /* XXX */
1264 goto out;
1265 }
1266 if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1267 logit("scanned from %s port %d with %s. Don't panic.",
1268 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1269 peer_version_string);
1270 r = SSH_ERR_CONN_CLOSED; /* XXX */
1271 goto out;
1272 }
1273 if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1274 logit("Remote version \"%.100s\" uses unsafe RSA signature "
1275 "scheme; disabling use of RSA keys", remote_version);
1276 }
1277 /* success */
1278 r = 0;
1279 out:
1280 free(our_version_string);
1281 free(peer_version_string);
1282 free(remote_version);
1283 return r;
1284}
1285