blob: 30e1c261d443c496ccf9301924c7968659a7eee1 [file] [log] [blame]
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001/* $OpenBSD: kex.c,v 1.143 2018/12/27 03:25:25 djm Exp $ */
Damien Millera664e872000-04-16 11:52:47 +10002/*
Ben Lindstrom44697232001-07-04 03:32:30 +00003 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millera664e872000-04-16 11:52:47 +10004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
Damien Millera664e872000-04-16 11:52:47 +100013 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millera664e872000-04-16 11:52:47 +100027
djm@openbsd.org0a843d92018-12-27 03:25:24 +000028#include <sys/types.h>
29#include <errno.h>
Damien Millerd7834352006-08-05 12:39:39 +100030#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100031#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100032#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100033#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100034#include <string.h>
djm@openbsd.org0a843d92018-12-27 03:25:24 +000035#include <unistd.h>
36#include <poll.h>
Damien Millere3476ed2006-07-24 14:13:33 +100037
Damien Miller1f0311c2014-05-15 14:24:09 +100038#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100039#include <openssl/crypto.h>
Damien Millerbd5f2b72016-07-15 19:14:48 +100040#include <openssl/dh.h>
Damien Miller1f0311c2014-05-15 14:24:09 +100041#endif
Damien Millerd7834352006-08-05 12:39:39 +100042
djm@openbsd.org0a843d92018-12-27 03:25:24 +000043#include "ssh.h"
Damien Millerd7834352006-08-05 12:39:39 +100044#include "ssh2.h"
djm@openbsd.org0a843d92018-12-27 03:25:24 +000045#include "atomicio.h"
46#include "version.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000047#include "packet.h"
48#include "compat.h"
49#include "cipher.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000050#include "sshkey.h"
Damien Millerd7834352006-08-05 12:39:39 +100051#include "kex.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000052#include "log.h"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000053#include "mac.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000054#include "match.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000055#include "misc.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000056#include "dispatch.h"
Ben Lindstrom7a2073c2002-03-22 02:30:41 +000057#include "monitor.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000058
59#include "ssherr.h"
60#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110061#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100062
Ben Lindstrombba81212001-06-25 05:01:22 +000063/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000064static int kex_choose_conf(struct ssh *);
markus@openbsd.org2ae666a2017-05-30 14:23:52 +000065static int kex_input_newkeys(int, u_int32_t, struct ssh *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000066
djm@openbsd.org9690b782015-08-21 23:57:48 +000067static const char *proposal_names[PROPOSAL_MAX] = {
68 "KEX algorithms",
69 "host key algorithms",
70 "ciphers ctos",
71 "ciphers stoc",
72 "MACs ctos",
73 "MACs stoc",
74 "compression ctos",
75 "compression stoc",
76 "languages ctos",
77 "languages stoc",
78};
79
Damien Millerea111192013-04-23 19:24:32 +100080struct kexalg {
81 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000082 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100083 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110084 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100085};
86static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100087#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110088 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +000089 { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
90 { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
91 { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
92 { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
Damien Millerb3051d02014-01-10 10:58:53 +110093 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100094#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110095 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100096#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100097#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +110098 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
99 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
100 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
101 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +1100102# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +1100103 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
104 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +1000105# endif /* OPENSSL_HAS_NISTP521 */
106#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000107#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100108#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100109 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.org04937662016-09-22 17:52:53 +0000110 { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100111#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Damien Millerb3051d02014-01-10 10:58:53 +1100112 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000113};
114
115char *
Damien Miller690d9892013-11-08 12:16:49 +1100116kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000117{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000118 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000119 size_t nlen, rlen = 0;
120 const struct kexalg *k;
121
122 for (k = kexalgs; k->name != NULL; k++) {
123 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100124 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000125 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000126 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
127 free(ret);
128 return NULL;
129 }
130 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000131 memcpy(ret + rlen, k->name, nlen + 1);
132 rlen += nlen;
133 }
134 return ret;
135}
136
137static const struct kexalg *
138kex_alg_by_name(const char *name)
139{
140 const struct kexalg *k;
141
142 for (k = kexalgs; k->name != NULL; k++) {
143 if (strcmp(k->name, name) == 0)
144 return k;
145 }
146 return NULL;
147}
148
Damien Millerd5f62bf2010-09-24 22:11:14 +1000149/* Validate KEX method name list */
150int
151kex_names_valid(const char *names)
152{
153 char *s, *cp, *p;
154
155 if (names == NULL || strcmp(names, "") == 0)
156 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000157 if ((s = cp = strdup(names)) == NULL)
158 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000159 for ((p = strsep(&cp, ",")); p && *p != '\0';
160 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000161 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000162 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000163 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000164 return 0;
165 }
166 }
167 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000168 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000169 return 1;
170}
171
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000172/*
173 * Concatenate algorithm names, avoiding duplicates in the process.
174 * Caller must free returned string.
175 */
176char *
177kex_names_cat(const char *a, const char *b)
178{
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000179 char *ret = NULL, *tmp = NULL, *cp, *p, *m;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000180 size_t len;
181
182 if (a == NULL || *a == '\0')
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000183 return strdup(b);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000184 if (b == NULL || *b == '\0')
185 return strdup(a);
186 if (strlen(b) > 1024*1024)
187 return NULL;
188 len = strlen(a) + strlen(b) + 2;
189 if ((tmp = cp = strdup(b)) == NULL ||
190 (ret = calloc(1, len)) == NULL) {
191 free(tmp);
192 return NULL;
193 }
194 strlcpy(ret, a, len);
195 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000196 if ((m = match_list(ret, p, NULL)) != NULL) {
197 free(m);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000198 continue; /* Algorithm already present */
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000199 }
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000200 if (strlcat(ret, ",", len) >= len ||
201 strlcat(ret, p, len) >= len) {
202 free(tmp);
203 free(ret);
204 return NULL; /* Shouldn't happen */
205 }
206 }
207 free(tmp);
208 return ret;
209}
210
211/*
212 * Assemble a list of algorithms from a default list and a string from a
213 * configuration file. The user-provided string may begin with '+' to
djm@openbsd.org68bc8cf2017-02-03 23:01:19 +0000214 * indicate that it should be appended to the default or '-' that the
215 * specified names should be removed.
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000216 */
217int
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000218kex_assemble_names(char **listp, const char *def, const char *all)
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000219{
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000220 char *cp, *tmp, *patterns;
221 char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
222 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000223
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000224 if (listp == NULL || *listp == NULL || **listp == '\0') {
225 if ((*listp = strdup(def)) == NULL)
226 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000227 return 0;
228 }
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000229
230 list = *listp;
231 *listp = NULL;
232 if (*list == '+') {
233 /* Append names to default list */
234 if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
235 r = SSH_ERR_ALLOC_FAIL;
236 goto fail;
237 }
238 free(list);
239 list = tmp;
240 } else if (*list == '-') {
241 /* Remove names from default list */
242 if ((*listp = match_filter_blacklist(def, list + 1)) == NULL) {
243 r = SSH_ERR_ALLOC_FAIL;
244 goto fail;
245 }
246 free(list);
247 /* filtering has already been done */
248 return 0;
249 } else {
250 /* Explicit list, overrides default - just use "list" as is */
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000251 }
252
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000253 /*
254 * The supplied names may be a pattern-list. For the -list case,
255 * the patterns are applied above. For the +list and explicit list
256 * cases we need to do it now.
257 */
258 ret = NULL;
259 if ((patterns = opatterns = strdup(list)) == NULL) {
260 r = SSH_ERR_ALLOC_FAIL;
261 goto fail;
262 }
263 /* Apply positive (i.e. non-negated) patterns from the list */
264 while ((cp = strsep(&patterns, ",")) != NULL) {
265 if (*cp == '!') {
266 /* negated matches are not supported here */
267 r = SSH_ERR_INVALID_ARGUMENT;
268 goto fail;
269 }
270 free(matching);
271 if ((matching = match_filter_whitelist(all, cp)) == NULL) {
272 r = SSH_ERR_ALLOC_FAIL;
273 goto fail;
274 }
275 if ((tmp = kex_names_cat(ret, matching)) == NULL) {
276 r = SSH_ERR_ALLOC_FAIL;
277 goto fail;
278 }
279 free(ret);
280 ret = tmp;
281 }
282 if (ret == NULL || *ret == '\0') {
283 /* An empty name-list is an error */
284 /* XXX better error code? */
285 r = SSH_ERR_INVALID_ARGUMENT;
286 goto fail;
287 }
288
289 /* success */
290 *listp = ret;
291 ret = NULL;
292 r = 0;
293
294 fail:
295 free(matching);
296 free(opatterns);
297 free(list);
298 free(ret);
299 return r;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000300}
301
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000302/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000303int
304kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000305{
Damien Millereccb9de2005-06-17 12:59:34 +1000306 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000307 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000308
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000309 sshbuf_reset(b);
310
Ben Lindstrom59971722002-03-27 17:42:57 +0000311 /*
312 * add a dummy cookie, the cookie will be overwritten by
313 * kex_send_kexinit(), each time a kexinit is set
314 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000315 for (i = 0; i < KEX_COOKIE_LEN; i++) {
316 if ((r = sshbuf_put_u8(b, 0)) != 0)
317 return r;
318 }
319 for (i = 0; i < PROPOSAL_MAX; i++) {
320 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
321 return r;
322 }
323 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
324 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
325 return r;
326 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000327}
328
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000329/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000330int
331kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000332{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000333 struct sshbuf *b = NULL;
334 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000335 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000336 char **proposal = NULL;
337 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000338
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000339 *propp = NULL;
340 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
341 return SSH_ERR_ALLOC_FAIL;
342 if ((b = sshbuf_fromb(raw)) == NULL) {
343 r = SSH_ERR_ALLOC_FAIL;
344 goto out;
345 }
346 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
347 goto out;
Damien Millerb1715dc2000-05-30 13:44:51 +1000348 /* extract kex init proposal strings */
349 for (i = 0; i < PROPOSAL_MAX; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000350 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
351 goto out;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000352 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000353 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000354 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000355 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
356 (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000357 goto out;
Damien Millerbabb47a2003-02-24 11:53:32 +1100358 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000359 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000360 debug2("first_kex_follows %d ", v);
361 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000362 r = 0;
363 *propp = proposal;
364 out:
365 if (r != 0 && proposal != NULL)
366 kex_prop_free(proposal);
367 sshbuf_free(b);
368 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000369}
370
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000371void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000372kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000373{
Damien Millereccb9de2005-06-17 12:59:34 +1000374 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000375
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000376 if (proposal == NULL)
377 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000378 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000379 free(proposal[i]);
380 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000381}
382
Darren Tucker0d0d1952007-06-05 18:23:28 +1000383/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000384static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000385kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
Damien Miller874d77b2000-10-14 16:23:11 +1100386{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000387 int r;
388
389 error("kex protocol error: type %d seq %u", type, seq);
390 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
391 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
392 (r = sshpkt_send(ssh)) != 0)
393 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000394 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100395}
396
Ben Lindstrombba81212001-06-25 05:01:22 +0000397static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000398kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000399{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000400 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100401 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000402}
403
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000404static int
405kex_send_ext_info(struct ssh *ssh)
406{
407 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000408 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000409
djm@openbsd.org183ba552017-03-10 04:07:20 +0000410 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000411 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000412 /* XXX filter algs list by allowed pubkey/hostbased types */
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000413 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
414 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
415 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000416 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000417 (r = sshpkt_send(ssh)) != 0)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000418 goto out;
419 /* success */
420 r = 0;
421 out:
422 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000423 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000424}
425
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000426int
427kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000428{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000429 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000430
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000431 kex_reset_dispatch(ssh);
432 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
433 (r = sshpkt_send(ssh)) != 0)
434 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000435 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000436 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000437 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000438 if (ssh->kex->ext_info_c)
439 if ((r = kex_send_ext_info(ssh)) != 0)
440 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000441 return 0;
442}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000443
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000444int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000445kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000446{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000447 struct kex *kex = ssh->kex;
448 u_int32_t i, ninfo;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000449 char *name;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000450 u_char *val;
451 size_t vlen;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000452 int r;
453
454 debug("SSH2_MSG_EXT_INFO received");
455 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
456 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
457 return r;
458 for (i = 0; i < ninfo; i++) {
459 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
460 return r;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000461 if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000462 free(name);
463 return r;
464 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000465 if (strcmp(name, "server-sig-algs") == 0) {
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000466 /* Ensure no \0 lurking in value */
467 if (memchr(val, '\0', vlen) != NULL) {
468 error("%s: nul byte in %s", __func__, name);
469 return SSH_ERR_INVALID_FORMAT;
470 }
471 debug("%s: %s=<%s>", __func__, name, val);
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000472 kex->server_sig_algs = val;
473 val = NULL;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000474 } else
475 debug("%s: %s (unrecognised)", __func__, name);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000476 free(name);
477 free(val);
478 }
479 return sshpkt_get_end(ssh);
480}
481
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000482static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000483kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000484{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000485 struct kex *kex = ssh->kex;
486 int r;
487
488 debug("SSH2_MSG_NEWKEYS received");
489 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000490 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000491 if ((r = sshpkt_get_end(ssh)) != 0)
492 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000493 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
494 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000495 kex->done = 1;
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000496 kex->flags &= ~KEX_INITIAL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000497 sshbuf_reset(kex->peer);
498 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000499 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000500 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000501 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000502 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000503}
Damien Millera664e872000-04-16 11:52:47 +1000504
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000505int
506kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000507{
Ben Lindstrom59971722002-03-27 17:42:57 +0000508 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000509 struct kex *kex = ssh->kex;
510 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000511
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000512 if (kex == NULL)
513 return SSH_ERR_INTERNAL_ERROR;
514 if (kex->flags & KEX_INIT_SENT)
515 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000516 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000517
518 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000519 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
520 return SSH_ERR_INVALID_FORMAT;
521 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
522 return SSH_ERR_INTERNAL_ERROR;
523 arc4random_buf(cookie, KEX_COOKIE_LEN);
524
525 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
526 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
527 (r = sshpkt_send(ssh)) != 0)
528 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000529 debug("SSH2_MSG_KEXINIT sent");
530 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000531 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000532}
533
Darren Tucker0d0d1952007-06-05 18:23:28 +1000534/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000535int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000536kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000537{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000538 struct kex *kex = ssh->kex;
539 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000540 u_int i;
541 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000542 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000543
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000544 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000545 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000546 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000547
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000548 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000549 ptr = sshpkt_ptr(ssh, &dlen);
550 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
551 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000552
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000553 /* discard packet */
554 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000555 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
556 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000557 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000558 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
559 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000560 /*
561 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
562 * KEX method has the server move first, but a server might be using
563 * a custom method or one that we otherwise don't support. We should
564 * be prepared to remember first_kex_follows here so we can eat a
565 * packet later.
566 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
567 * for cases where the server *doesn't* go first. I guess we should
568 * ignore it when it is set for these cases, which is what we do now.
569 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000570 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
571 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
572 (r = sshpkt_get_end(ssh)) != 0)
573 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000574
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000575 if (!(kex->flags & KEX_INIT_SENT))
576 if ((r = kex_send_kexinit(ssh)) != 0)
577 return r;
578 if ((r = kex_choose_conf(ssh)) != 0)
579 return r;
580
581 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
582 return (kex->kex[kex->kex_type])(ssh);
583
584 return SSH_ERR_INTERNAL_ERROR;
585}
586
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000587struct kex *
588kex_new(void)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000589{
590 struct kex *kex;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000591
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000592 if ((kex = calloc(1, sizeof(*kex))) == NULL ||
593 (kex->peer = sshbuf_new()) == NULL ||
594 (kex->my = sshbuf_new()) == NULL ||
595 (kex->client_version = sshbuf_new()) == NULL ||
596 (kex->server_version = sshbuf_new()) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000597 kex_free(kex);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000598 return NULL;
599 }
600 return kex;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000601}
602
markus@openbsd.org091c3022015-01-19 19:52:16 +0000603void
604kex_free_newkeys(struct newkeys *newkeys)
605{
606 if (newkeys == NULL)
607 return;
608 if (newkeys->enc.key) {
609 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
610 free(newkeys->enc.key);
611 newkeys->enc.key = NULL;
612 }
613 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000614 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000615 free(newkeys->enc.iv);
616 newkeys->enc.iv = NULL;
617 }
618 free(newkeys->enc.name);
619 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
620 free(newkeys->comp.name);
621 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
622 mac_clear(&newkeys->mac);
623 if (newkeys->mac.key) {
624 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
625 free(newkeys->mac.key);
626 newkeys->mac.key = NULL;
627 }
628 free(newkeys->mac.name);
629 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
630 explicit_bzero(newkeys, sizeof(*newkeys));
631 free(newkeys);
632}
633
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000634void
635kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000636{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000637 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000638
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000639 if (kex == NULL)
640 return;
641
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000642#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000643 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100644#ifdef OPENSSL_HAS_ECC
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000645 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100646#endif /* OPENSSL_HAS_ECC */
647#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000648 for (mode = 0; mode < MODE_MAX; mode++) {
649 kex_free_newkeys(kex->newkeys[mode]);
650 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000651 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000652 sshbuf_free(kex->peer);
653 sshbuf_free(kex->my);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000654 sshbuf_free(kex->client_version);
655 sshbuf_free(kex->server_version);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000656 free(kex->session_id);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000657 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000658 free(kex->hostkey_alg);
659 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000660 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000661}
662
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000663int
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000664kex_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
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000677kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000678{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000679 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000680
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000681 if ((r = kex_ready(ssh, proposal)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000682 return r;
683 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
684 kex_free(ssh->kex);
685 ssh->kex = NULL;
686 return r;
Damien Millera664e872000-04-16 11:52:47 +1000687 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000688 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000689}
690
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000691/*
692 * Request key re-exchange, returns 0 on success or a ssherr.h error
693 * code otherwise. Must not be called if KEX is incomplete or in-progress.
694 */
695int
696kex_start_rekex(struct ssh *ssh)
697{
698 if (ssh->kex == NULL) {
699 error("%s: no kex", __func__);
700 return SSH_ERR_INTERNAL_ERROR;
701 }
702 if (ssh->kex->done == 0) {
703 error("%s: requested twice", __func__);
704 return SSH_ERR_INTERNAL_ERROR;
705 }
706 ssh->kex->done = 0;
707 return kex_send_kexinit(ssh);
708}
709
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000710static int
711choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000712{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000713 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000714
Damien Millera664e872000-04-16 11:52:47 +1000715 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000716 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000717 if ((enc->cipher = cipher_by_name(name)) == NULL) {
718 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000720 }
Damien Millera664e872000-04-16 11:52:47 +1000721 enc->name = name;
722 enc->enabled = 0;
723 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100724 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000725 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100726 enc->key_len = cipher_keylen(enc->cipher);
727 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000728 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000729}
Damien Miller4f7becb2006-03-26 14:10:14 +1100730
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000731static int
732choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000733{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000734 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000735
Damien Millera664e872000-04-16 11:52:47 +1000736 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000737 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000738 if (mac_setup(mac, name) < 0) {
739 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000740 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000741 }
Damien Millera664e872000-04-16 11:52:47 +1000742 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000743 mac->key = NULL;
744 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000745 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000746}
Damien Miller4f7becb2006-03-26 14:10:14 +1100747
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000748static int
749choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000750{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000751 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000752
Damien Millera664e872000-04-16 11:52:47 +1000753 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000754 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000755 if (strcmp(name, "zlib@openssh.com") == 0) {
sf@openbsd.org168b46f2018-07-09 13:37:10 +0000756 comp->type = COMP_DELAYED;
757 } else if (strcmp(name, "zlib") == 0) {
sf@openbsd.orgab392672018-07-06 09:06:14 +0000758 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000759 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000760 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000761 } else {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000762 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000763 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000764 }
765 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000766 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000767}
Damien Miller4f7becb2006-03-26 14:10:14 +1100768
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000769static int
770choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000771{
Damien Millerea111192013-04-23 19:24:32 +1000772 const struct kexalg *kexalg;
773
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000774 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000775
djm@openbsd.org9690b782015-08-21 23:57:48 +0000776 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000777 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000778 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000779 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000780 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000781 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100782 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000783 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000784 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000785}
Damien Miller19bb3a52005-11-05 15:19:35 +1100786
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000787static int
788choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000789{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000790 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000791
djm@openbsd.org9690b782015-08-21 23:57:48 +0000792 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000793 k->hostkey_alg ? k->hostkey_alg : "(no match)");
794 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000795 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000796 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100797 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000798 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000799 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000800 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000801}
802
Damien Millera8e06ce2003-11-21 23:48:55 +1100803static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100804proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
805{
806 static int check[] = {
807 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
808 };
809 int *idx;
810 char *p;
811
812 for (idx = &check[0]; *idx != -1; idx++) {
813 if ((p = strchr(my[*idx], ',')) != NULL)
814 *p = '\0';
815 if ((p = strchr(peer[*idx], ',')) != NULL)
816 *p = '\0';
817 if (strcmp(my[*idx], peer[*idx]) != 0) {
818 debug2("proposal mismatch: my %s peer %s",
819 my[*idx], peer[*idx]);
820 return (0);
821 }
822 }
823 debug2("proposals match");
824 return (1);
825}
826
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000827static int
828kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000829{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000830 struct kex *kex = ssh->kex;
831 struct newkeys *newkeys;
832 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000833 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000834 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100835 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000836 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000837
djm@openbsd.org9690b782015-08-21 23:57:48 +0000838 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
839 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
840 goto out;
841 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
842 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000843 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000844
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000845 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000846 cprop=peer;
847 sprop=my;
848 } else {
849 cprop=my;
850 sprop=peer;
851 }
Damien Millera664e872000-04-16 11:52:47 +1000852
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000853 /* Check whether client supports ext_info_c */
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000854 if (kex->server && (kex->flags & KEX_INITIAL)) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000855 char *ext;
856
857 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000858 kex->ext_info_c = (ext != NULL);
859 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000860 }
861
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000862 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000863 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
864 sprop[PROPOSAL_KEX_ALGS])) != 0) {
865 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
866 peer[PROPOSAL_KEX_ALGS] = NULL;
867 goto out;
868 }
869 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
870 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
871 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
872 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
873 goto out;
874 }
Damien Millera664e872000-04-16 11:52:47 +1000875 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000876 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
877 r = SSH_ERR_ALLOC_FAIL;
878 goto out;
879 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000880 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000881 ctos = (!kex->server && mode == MODE_OUT) ||
882 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000883 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
884 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
885 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000886 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000887 sprop[nenc])) != 0) {
888 kex->failed_choice = peer[nenc];
889 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000890 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000891 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100892 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000893 /* ignore mac for authenticated encryption */
894 if (authlen == 0 &&
895 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000896 sprop[nmac])) != 0) {
897 kex->failed_choice = peer[nmac];
898 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000899 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000900 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000901 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000902 sprop[ncomp])) != 0) {
903 kex->failed_choice = peer[ncomp];
904 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000905 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000906 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000907 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000908 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000909 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100910 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000911 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000912 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100913 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000914 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000915 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000916 need = MAXIMUM(need, newkeys->enc.key_len);
917 need = MAXIMUM(need, newkeys->enc.block_size);
918 need = MAXIMUM(need, newkeys->enc.iv_len);
919 need = MAXIMUM(need, newkeys->mac.key_len);
920 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
921 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
922 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
923 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000924 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000925 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000926 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100927 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000928
Damien Millerbabb47a2003-02-24 11:53:32 +1100929 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000930 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000931 ssh->dispatch_skip_packets = 1;
932 r = 0;
933 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000934 kex_prop_free(my);
935 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000936 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000937}
938
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000939static int
940derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
941 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000942{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000943 struct kex *kex = ssh->kex;
944 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000945 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000946 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100947 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000948 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000949 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000950
Damien Millerb3051d02014-01-10 10:58:53 +1100951 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000952 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000953 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000954 r = SSH_ERR_ALLOC_FAIL;
955 goto out;
956 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000957
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000958 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000959 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
960 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100961 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
962 ssh_digest_update(hashctx, &c, 1) != 0 ||
963 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000964 kex->session_id_len) != 0 ||
965 ssh_digest_final(hashctx, digest, mdsz) != 0) {
966 r = SSH_ERR_LIBCRYPTO_ERROR;
967 goto out;
968 }
Damien Millerb3051d02014-01-10 10:58:53 +1100969 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000970 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000971
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000972 /*
973 * expand key:
974 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
975 * Key = K1 || K2 || ... || Kn
976 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000977 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000978 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
979 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100980 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000981 ssh_digest_update(hashctx, digest, have) != 0 ||
982 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
983 r = SSH_ERR_LIBCRYPTO_ERROR;
984 goto out;
985 }
Damien Millerb3051d02014-01-10 10:58:53 +1100986 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000987 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000988 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000989#ifdef DEBUG_KEX
990 fprintf(stderr, "key '%c'== ", c);
991 dump_digest("key", digest, need);
992#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000993 *keyp = digest;
994 digest = NULL;
995 r = 0;
996 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000997 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000998 ssh_digest_free(hashctx);
999 return r;
Damien Millera664e872000-04-16 11:52:47 +10001000}
1001
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001002#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001003int
1004kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1005 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +10001006{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001007 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001008 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001009 u_int i, j, mode, ctos;
1010 int r;
Damien Millera664e872000-04-16 11:52:47 +10001011
Damien Miller19bb3a52005-11-05 15:19:35 +11001012 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001013 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1014 shared_secret, &keys[i])) != 0) {
1015 for (j = 0; j < i; j++)
1016 free(keys[j]);
1017 return r;
1018 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001019 }
Damien Millera664e872000-04-16 11:52:47 +10001020 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001021 ctos = (!kex->server && mode == MODE_OUT) ||
1022 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001023 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1024 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1025 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001026 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001027 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001028}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001029
Damien Miller1f0311c2014-05-15 14:24:09 +10001030#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001031int
1032kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
1033 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +11001034{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001035 struct sshbuf *shared_secret;
1036 int r;
Damien Miller91b580e2014-01-12 19:21:22 +11001037
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001038 if ((shared_secret = sshbuf_new()) == NULL)
1039 return SSH_ERR_ALLOC_FAIL;
1040 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
1041 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
1042 sshbuf_free(shared_secret);
1043 return r;
Damien Miller91b580e2014-01-12 19:21:22 +11001044}
Damien Miller1f0311c2014-05-15 14:24:09 +10001045#endif
Damien Miller91b580e2014-01-12 19:21:22 +11001046
Darren Tuckere14e0052004-05-13 16:30:44 +10001047
Damien Millereb8b60e2010-08-31 22:41:14 +10001048#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001049void
1050dump_digest(char *msg, u_char *digest, int len)
1051{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001052 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001053 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001054}
1055#endif
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001056
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