blob: ce85f043958edad34e111f1e13e7b3def34dc204 [file] [log] [blame]
dtucker@openbsd.org7f8e66f2020-01-23 10:24:29 +00001/* $OpenBSD: kex.c,v 1.156 2020/01/23 10:24:29 dtucker 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>
Darren Tucker6fc7e1c2019-10-28 15:53:25 +110036#ifdef HAVE_POLL_H
djm@openbsd.org0a843d92018-12-27 03:25:24 +000037#include <poll.h>
Darren Tucker6fc7e1c2019-10-28 15:53:25 +110038#endif
Damien Millere3476ed2006-07-24 14:13:33 +100039
Damien Miller1f0311c2014-05-15 14:24:09 +100040#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100041#include <openssl/crypto.h>
Damien Millerbd5f2b72016-07-15 19:14:48 +100042#include <openssl/dh.h>
Damien Miller1f0311c2014-05-15 14:24:09 +100043#endif
Damien Millerd7834352006-08-05 12:39:39 +100044
djm@openbsd.org0a843d92018-12-27 03:25:24 +000045#include "ssh.h"
Damien Millerd7834352006-08-05 12:39:39 +100046#include "ssh2.h"
djm@openbsd.org0a843d92018-12-27 03:25:24 +000047#include "atomicio.h"
48#include "version.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000049#include "packet.h"
50#include "compat.h"
51#include "cipher.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000052#include "sshkey.h"
Damien Millerd7834352006-08-05 12:39:39 +100053#include "kex.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000054#include "log.h"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000055#include "mac.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000056#include "match.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000057#include "misc.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000058#include "dispatch.h"
Ben Lindstrom7a2073c2002-03-22 02:30:41 +000059#include "monitor.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000060
61#include "ssherr.h"
62#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110063#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100064
Ben Lindstrombba81212001-06-25 05:01:22 +000065/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000066static int kex_choose_conf(struct ssh *);
markus@openbsd.org2ae666a2017-05-30 14:23:52 +000067static int kex_input_newkeys(int, u_int32_t, struct ssh *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000068
djm@openbsd.org9690b782015-08-21 23:57:48 +000069static const char *proposal_names[PROPOSAL_MAX] = {
70 "KEX algorithms",
71 "host key algorithms",
72 "ciphers ctos",
73 "ciphers stoc",
74 "MACs ctos",
75 "MACs stoc",
76 "compression ctos",
77 "compression stoc",
78 "languages ctos",
79 "languages stoc",
80};
81
Damien Millerea111192013-04-23 19:24:32 +100082struct kexalg {
83 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000084 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100085 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110086 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100087};
88static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100089#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110090 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +000091 { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
92 { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
93 { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
94 { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
Damien Millerb3051d02014-01-10 10:58:53 +110095 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100096#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110097 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100098#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100099#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +1100100 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
101 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
102 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
103 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +1100104# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +1100105 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
106 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +1000107# endif /* OPENSSL_HAS_NISTP521 */
108#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000109#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100110#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100111 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.org04937662016-09-22 17:52:53 +0000112 { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.orgdfd59162019-01-21 10:20:12 +0000113 { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0,
114 SSH_DIGEST_SHA512 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100115#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
dtucker@openbsd.orgc2cc2542019-10-08 22:40:39 +0000116 { NULL, 0, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000117};
118
119char *
Damien Miller690d9892013-11-08 12:16:49 +1100120kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000121{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000122 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000123 size_t nlen, rlen = 0;
124 const struct kexalg *k;
125
126 for (k = kexalgs; k->name != NULL; k++) {
127 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100128 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000129 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000130 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
131 free(ret);
132 return NULL;
133 }
134 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000135 memcpy(ret + rlen, k->name, nlen + 1);
136 rlen += nlen;
137 }
138 return ret;
139}
140
141static const struct kexalg *
142kex_alg_by_name(const char *name)
143{
144 const struct kexalg *k;
145
146 for (k = kexalgs; k->name != NULL; k++) {
147 if (strcmp(k->name, name) == 0)
148 return k;
149 }
150 return NULL;
151}
152
Damien Millerd5f62bf2010-09-24 22:11:14 +1000153/* Validate KEX method name list */
154int
155kex_names_valid(const char *names)
156{
157 char *s, *cp, *p;
158
159 if (names == NULL || strcmp(names, "") == 0)
160 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000161 if ((s = cp = strdup(names)) == NULL)
162 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000163 for ((p = strsep(&cp, ",")); p && *p != '\0';
164 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000165 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000166 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000167 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000168 return 0;
169 }
170 }
171 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000172 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000173 return 1;
174}
175
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000176/*
177 * Concatenate algorithm names, avoiding duplicates in the process.
178 * Caller must free returned string.
179 */
180char *
181kex_names_cat(const char *a, const char *b)
182{
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000183 char *ret = NULL, *tmp = NULL, *cp, *p, *m;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000184 size_t len;
185
186 if (a == NULL || *a == '\0')
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000187 return strdup(b);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000188 if (b == NULL || *b == '\0')
189 return strdup(a);
190 if (strlen(b) > 1024*1024)
191 return NULL;
192 len = strlen(a) + strlen(b) + 2;
193 if ((tmp = cp = strdup(b)) == NULL ||
194 (ret = calloc(1, len)) == NULL) {
195 free(tmp);
196 return NULL;
197 }
198 strlcpy(ret, a, len);
199 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000200 if ((m = match_list(ret, p, NULL)) != NULL) {
201 free(m);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000202 continue; /* Algorithm already present */
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000203 }
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000204 if (strlcat(ret, ",", len) >= len ||
205 strlcat(ret, p, len) >= len) {
206 free(tmp);
207 free(ret);
208 return NULL; /* Shouldn't happen */
209 }
210 }
211 free(tmp);
212 return ret;
213}
214
215/*
216 * Assemble a list of algorithms from a default list and a string from a
217 * configuration file. The user-provided string may begin with '+' to
naddy@openbsd.org91a21352019-09-06 14:45:34 +0000218 * indicate that it should be appended to the default, '-' that the
219 * specified names should be removed, or '^' that they should be placed
220 * at the head.
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000221 */
222int
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000223kex_assemble_names(char **listp, const char *def, const char *all)
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000224{
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000225 char *cp, *tmp, *patterns;
226 char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
227 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000228
djm@openbsd.org00865c22019-09-06 01:58:50 +0000229 if (listp == NULL || def == NULL || all == NULL)
230 return SSH_ERR_INVALID_ARGUMENT;
231
232 if (*listp == NULL || **listp == '\0') {
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000233 if ((*listp = strdup(def)) == NULL)
234 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000235 return 0;
236 }
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000237
238 list = *listp;
239 *listp = NULL;
240 if (*list == '+') {
241 /* Append names to default list */
242 if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
243 r = SSH_ERR_ALLOC_FAIL;
244 goto fail;
245 }
246 free(list);
247 list = tmp;
248 } else if (*list == '-') {
249 /* Remove names from default list */
250 if ((*listp = match_filter_blacklist(def, list + 1)) == NULL) {
251 r = SSH_ERR_ALLOC_FAIL;
252 goto fail;
253 }
254 free(list);
255 /* filtering has already been done */
256 return 0;
naddy@openbsd.org91a21352019-09-06 14:45:34 +0000257 } else if (*list == '^') {
258 /* Place names at head of default list */
259 if ((tmp = kex_names_cat(list + 1, def)) == NULL) {
260 r = SSH_ERR_ALLOC_FAIL;
261 goto fail;
262 }
263 free(list);
264 list = tmp;
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000265 } else {
266 /* Explicit list, overrides default - just use "list" as is */
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000267 }
268
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000269 /*
270 * The supplied names may be a pattern-list. For the -list case,
271 * the patterns are applied above. For the +list and explicit list
272 * cases we need to do it now.
273 */
274 ret = NULL;
275 if ((patterns = opatterns = strdup(list)) == NULL) {
276 r = SSH_ERR_ALLOC_FAIL;
277 goto fail;
278 }
279 /* Apply positive (i.e. non-negated) patterns from the list */
280 while ((cp = strsep(&patterns, ",")) != NULL) {
281 if (*cp == '!') {
282 /* negated matches are not supported here */
283 r = SSH_ERR_INVALID_ARGUMENT;
284 goto fail;
285 }
286 free(matching);
287 if ((matching = match_filter_whitelist(all, cp)) == NULL) {
288 r = SSH_ERR_ALLOC_FAIL;
289 goto fail;
290 }
291 if ((tmp = kex_names_cat(ret, matching)) == NULL) {
292 r = SSH_ERR_ALLOC_FAIL;
293 goto fail;
294 }
295 free(ret);
296 ret = tmp;
297 }
298 if (ret == NULL || *ret == '\0') {
299 /* An empty name-list is an error */
300 /* XXX better error code? */
301 r = SSH_ERR_INVALID_ARGUMENT;
302 goto fail;
303 }
304
305 /* success */
306 *listp = ret;
307 ret = NULL;
308 r = 0;
309
310 fail:
311 free(matching);
312 free(opatterns);
313 free(list);
314 free(ret);
315 return r;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000316}
317
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000318/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000319int
320kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000321{
Damien Millereccb9de2005-06-17 12:59:34 +1000322 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000323 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000324
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000325 sshbuf_reset(b);
326
Ben Lindstrom59971722002-03-27 17:42:57 +0000327 /*
328 * add a dummy cookie, the cookie will be overwritten by
329 * kex_send_kexinit(), each time a kexinit is set
330 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000331 for (i = 0; i < KEX_COOKIE_LEN; i++) {
332 if ((r = sshbuf_put_u8(b, 0)) != 0)
333 return r;
334 }
335 for (i = 0; i < PROPOSAL_MAX; i++) {
336 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
337 return r;
338 }
339 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
340 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
341 return r;
342 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000343}
344
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000345/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000346int
347kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000348{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000349 struct sshbuf *b = NULL;
350 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000351 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000352 char **proposal = NULL;
353 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000354
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000355 *propp = NULL;
356 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
357 return SSH_ERR_ALLOC_FAIL;
358 if ((b = sshbuf_fromb(raw)) == NULL) {
359 r = SSH_ERR_ALLOC_FAIL;
360 goto out;
361 }
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000362 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */
363 error("%s: consume cookie: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000364 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000365 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000366 /* extract kex init proposal strings */
367 for (i = 0; i < PROPOSAL_MAX; i++) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000368 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) {
369 error("%s: parse proposal %u: %s", __func__,
370 i, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000371 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000372 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000373 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000374 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000375 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000376 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000377 (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */
378 error("%s: parse: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000379 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000380 }
Damien Millerbabb47a2003-02-24 11:53:32 +1100381 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000382 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000383 debug2("first_kex_follows %d ", v);
384 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000385 r = 0;
386 *propp = proposal;
387 out:
388 if (r != 0 && proposal != NULL)
389 kex_prop_free(proposal);
390 sshbuf_free(b);
391 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000392}
393
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000394void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000395kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000396{
Damien Millereccb9de2005-06-17 12:59:34 +1000397 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000398
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000399 if (proposal == NULL)
400 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000401 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000402 free(proposal[i]);
403 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000404}
405
Darren Tucker0d0d1952007-06-05 18:23:28 +1000406/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000407static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000408kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
Damien Miller874d77b2000-10-14 16:23:11 +1100409{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000410 int r;
411
412 error("kex protocol error: type %d seq %u", type, seq);
413 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
414 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
415 (r = sshpkt_send(ssh)) != 0)
416 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000417 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100418}
419
Ben Lindstrombba81212001-06-25 05:01:22 +0000420static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000421kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000422{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000423 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100424 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000425}
426
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000427static int
428kex_send_ext_info(struct ssh *ssh)
429{
430 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000431 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000432
djm@openbsd.org0ea33242019-09-05 09:25:13 +0000433 debug("Sending SSH2_MSG_EXT_INFO");
djm@openbsd.org183ba552017-03-10 04:07:20 +0000434 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000435 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000436 /* XXX filter algs list by allowed pubkey/hostbased types */
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000437 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
438 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
439 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000440 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000441 (r = sshpkt_send(ssh)) != 0) {
442 error("%s: compose: %s", __func__, ssh_err(r));
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000443 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000444 }
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000445 /* success */
446 r = 0;
447 out:
448 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000449 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000450}
451
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000452int
453kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000454{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000455 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000456
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000457 kex_reset_dispatch(ssh);
458 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
459 (r = sshpkt_send(ssh)) != 0)
460 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000461 debug("SSH2_MSG_NEWKEYS sent");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000462 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
djm@openbsd.org0ea33242019-09-05 09:25:13 +0000463 if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000464 if ((r = kex_send_ext_info(ssh)) != 0)
465 return r;
djm@openbsd.org0ea33242019-09-05 09:25:13 +0000466 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000467 return 0;
468}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000469
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000470int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000471kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000472{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000473 struct kex *kex = ssh->kex;
474 u_int32_t i, ninfo;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000475 char *name;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000476 u_char *val;
477 size_t vlen;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000478 int r;
479
480 debug("SSH2_MSG_EXT_INFO received");
481 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
482 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
483 return r;
484 for (i = 0; i < ninfo; i++) {
485 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
486 return r;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000487 if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000488 free(name);
489 return r;
490 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000491 if (strcmp(name, "server-sig-algs") == 0) {
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000492 /* Ensure no \0 lurking in value */
493 if (memchr(val, '\0', vlen) != NULL) {
494 error("%s: nul byte in %s", __func__, name);
495 return SSH_ERR_INVALID_FORMAT;
496 }
497 debug("%s: %s=<%s>", __func__, name, val);
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000498 kex->server_sig_algs = val;
499 val = NULL;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000500 } else
501 debug("%s: %s (unrecognised)", __func__, name);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000502 free(name);
503 free(val);
504 }
505 return sshpkt_get_end(ssh);
506}
507
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000508static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000509kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000510{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000511 struct kex *kex = ssh->kex;
512 int r;
513
514 debug("SSH2_MSG_NEWKEYS received");
515 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000516 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000517 if ((r = sshpkt_get_end(ssh)) != 0)
518 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000519 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
520 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000521 kex->done = 1;
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000522 kex->flags &= ~KEX_INITIAL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000523 sshbuf_reset(kex->peer);
524 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000525 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000526 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000527 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000528 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000529}
Damien Millera664e872000-04-16 11:52:47 +1000530
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000531int
532kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000533{
Ben Lindstrom59971722002-03-27 17:42:57 +0000534 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000535 struct kex *kex = ssh->kex;
536 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000537
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000538 if (kex == NULL) {
539 error("%s: no hex", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000540 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000541 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000542 if (kex->flags & KEX_INIT_SENT)
543 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000544 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000545
546 /* generate a random cookie */
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000547 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) {
548 error("%s: bad kex length: %zu < %d", __func__,
549 sshbuf_len(kex->my), KEX_COOKIE_LEN);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000550 return SSH_ERR_INVALID_FORMAT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000551 }
552 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) {
553 error("%s: buffer error", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000554 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000555 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000556 arc4random_buf(cookie, KEX_COOKIE_LEN);
557
558 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
559 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000560 (r = sshpkt_send(ssh)) != 0) {
561 error("%s: compose reply: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000562 return r;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000563 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000564 debug("SSH2_MSG_KEXINIT sent");
565 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000566 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000567}
568
Darren Tucker0d0d1952007-06-05 18:23:28 +1000569/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000570int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000571kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000572{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000573 struct kex *kex = ssh->kex;
574 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000575 u_int i;
576 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000577 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000578
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000579 debug("SSH2_MSG_KEXINIT received");
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000580 if (kex == NULL) {
581 error("%s: no hex", __func__);
582 return SSH_ERR_INTERNAL_ERROR;
583 }
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000584 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000585 ptr = sshpkt_ptr(ssh, &dlen);
586 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
587 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000588
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000589 /* discard packet */
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000590 for (i = 0; i < KEX_COOKIE_LEN; i++) {
591 if ((r = sshpkt_get_u8(ssh, NULL)) != 0) {
592 error("%s: discard cookie: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000593 return r;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000594 }
595 }
596 for (i = 0; i < PROPOSAL_MAX; i++) {
597 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
598 error("%s: discard proposal: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000599 return r;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000600 }
601 }
Darren Tuckerae608bd2012-09-06 21:19:51 +1000602 /*
603 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
604 * KEX method has the server move first, but a server might be using
605 * a custom method or one that we otherwise don't support. We should
606 * be prepared to remember first_kex_follows here so we can eat a
607 * packet later.
608 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
609 * for cases where the server *doesn't* go first. I guess we should
610 * ignore it when it is set for these cases, which is what we do now.
611 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000612 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
613 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
614 (r = sshpkt_get_end(ssh)) != 0)
615 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000616
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000617 if (!(kex->flags & KEX_INIT_SENT))
618 if ((r = kex_send_kexinit(ssh)) != 0)
619 return r;
620 if ((r = kex_choose_conf(ssh)) != 0)
621 return r;
622
623 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
624 return (kex->kex[kex->kex_type])(ssh);
625
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000626 error("%s: unknown kex type %u", __func__, kex->kex_type);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000627 return SSH_ERR_INTERNAL_ERROR;
628}
629
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000630struct kex *
631kex_new(void)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000632{
633 struct kex *kex;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000634
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000635 if ((kex = calloc(1, sizeof(*kex))) == NULL ||
636 (kex->peer = sshbuf_new()) == NULL ||
637 (kex->my = sshbuf_new()) == NULL ||
638 (kex->client_version = sshbuf_new()) == NULL ||
639 (kex->server_version = sshbuf_new()) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000640 kex_free(kex);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000641 return NULL;
642 }
643 return kex;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000644}
645
markus@openbsd.org091c3022015-01-19 19:52:16 +0000646void
647kex_free_newkeys(struct newkeys *newkeys)
648{
649 if (newkeys == NULL)
650 return;
651 if (newkeys->enc.key) {
652 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
653 free(newkeys->enc.key);
654 newkeys->enc.key = NULL;
655 }
656 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000657 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000658 free(newkeys->enc.iv);
659 newkeys->enc.iv = NULL;
660 }
661 free(newkeys->enc.name);
662 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
663 free(newkeys->comp.name);
664 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
665 mac_clear(&newkeys->mac);
666 if (newkeys->mac.key) {
667 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
668 free(newkeys->mac.key);
669 newkeys->mac.key = NULL;
670 }
671 free(newkeys->mac.name);
672 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
673 explicit_bzero(newkeys, sizeof(*newkeys));
674 free(newkeys);
675}
676
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000677void
678kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000679{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000680 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000681
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000682 if (kex == NULL)
683 return;
684
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000685#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000686 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100687#ifdef OPENSSL_HAS_ECC
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000688 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100689#endif /* OPENSSL_HAS_ECC */
690#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000691 for (mode = 0; mode < MODE_MAX; mode++) {
692 kex_free_newkeys(kex->newkeys[mode]);
693 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000694 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000695 sshbuf_free(kex->peer);
696 sshbuf_free(kex->my);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000697 sshbuf_free(kex->client_version);
698 sshbuf_free(kex->server_version);
djm@openbsd.orgaaca72d2019-01-21 10:40:11 +0000699 sshbuf_free(kex->client_pub);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000700 free(kex->session_id);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000701 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000702 free(kex->hostkey_alg);
703 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000704 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000705}
706
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000707int
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000708kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
709{
710 int r;
711
712 if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
713 return r;
714 ssh->kex->flags = KEX_INITIAL;
715 kex_reset_dispatch(ssh);
716 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
717 return 0;
718}
719
720int
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000721kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000722{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000723 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000724
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000725 if ((r = kex_ready(ssh, proposal)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000726 return r;
727 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
728 kex_free(ssh->kex);
729 ssh->kex = NULL;
730 return r;
Damien Millera664e872000-04-16 11:52:47 +1000731 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000732 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000733}
734
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000735/*
736 * Request key re-exchange, returns 0 on success or a ssherr.h error
737 * code otherwise. Must not be called if KEX is incomplete or in-progress.
738 */
739int
740kex_start_rekex(struct ssh *ssh)
741{
742 if (ssh->kex == NULL) {
743 error("%s: no kex", __func__);
744 return SSH_ERR_INTERNAL_ERROR;
745 }
746 if (ssh->kex->done == 0) {
747 error("%s: requested twice", __func__);
748 return SSH_ERR_INTERNAL_ERROR;
749 }
750 ssh->kex->done = 0;
751 return kex_send_kexinit(ssh);
752}
753
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000754static int
755choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000756{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000757 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000758
Damien Millera664e872000-04-16 11:52:47 +1000759 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000760 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000761 if ((enc->cipher = cipher_by_name(name)) == NULL) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000762 error("%s: unsupported cipher %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000763 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000764 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000765 }
Damien Millera664e872000-04-16 11:52:47 +1000766 enc->name = name;
767 enc->enabled = 0;
768 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100769 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000770 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100771 enc->key_len = cipher_keylen(enc->cipher);
772 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000773 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000774}
Damien Miller4f7becb2006-03-26 14:10:14 +1100775
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000776static int
777choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000778{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000779 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000780
Damien Millera664e872000-04-16 11:52:47 +1000781 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000782 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000783 if (mac_setup(mac, name) < 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000784 error("%s: unsupported MAC %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000785 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000786 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000787 }
Damien Millera664e872000-04-16 11:52:47 +1000788 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000789 mac->key = NULL;
790 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000791 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000792}
Damien Miller4f7becb2006-03-26 14:10:14 +1100793
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000794static int
795choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000796{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000797 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000798
Damien Millera664e872000-04-16 11:52:47 +1000799 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000800 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
dtucker@openbsd.org7f8e66f2020-01-23 10:24:29 +0000801#ifdef WITH_ZLIB
Damien Miller9786e6e2005-07-26 21:54:56 +1000802 if (strcmp(name, "zlib@openssh.com") == 0) {
sf@openbsd.org168b46f2018-07-09 13:37:10 +0000803 comp->type = COMP_DELAYED;
804 } else if (strcmp(name, "zlib") == 0) {
sf@openbsd.orgab392672018-07-06 09:06:14 +0000805 comp->type = COMP_ZLIB;
dtucker@openbsd.org7f8e66f2020-01-23 10:24:29 +0000806 } else
807#endif /* WITH_ZLIB */
808 if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000809 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000810 } else {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000811 error("%s: unsupported compression scheme %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000812 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000813 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000814 }
815 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000816 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000817}
Damien Miller4f7becb2006-03-26 14:10:14 +1100818
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000819static int
820choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000821{
Damien Millerea111192013-04-23 19:24:32 +1000822 const struct kexalg *kexalg;
823
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000824 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000825
djm@openbsd.org9690b782015-08-21 23:57:48 +0000826 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000827 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000828 return SSH_ERR_NO_KEX_ALG_MATCH;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000829 if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
830 error("%s: unsupported KEX method %s", __func__, k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000831 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000832 }
Damien Millerea111192013-04-23 19:24:32 +1000833 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100834 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000835 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000836 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000837}
Damien Miller19bb3a52005-11-05 15:19:35 +1100838
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000839static int
840choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000841{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000842 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000843
djm@openbsd.org9690b782015-08-21 23:57:48 +0000844 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000845 k->hostkey_alg ? k->hostkey_alg : "(no match)");
846 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000847 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000848 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000849 if (k->hostkey_type == KEY_UNSPEC) {
850 error("%s: unsupported hostkey algorithm %s", __func__,
851 k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000852 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000853 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000854 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000855 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000856}
857
Damien Millera8e06ce2003-11-21 23:48:55 +1100858static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100859proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
860{
861 static int check[] = {
862 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
863 };
864 int *idx;
865 char *p;
866
867 for (idx = &check[0]; *idx != -1; idx++) {
868 if ((p = strchr(my[*idx], ',')) != NULL)
869 *p = '\0';
870 if ((p = strchr(peer[*idx], ',')) != NULL)
871 *p = '\0';
872 if (strcmp(my[*idx], peer[*idx]) != 0) {
873 debug2("proposal mismatch: my %s peer %s",
874 my[*idx], peer[*idx]);
875 return (0);
876 }
877 }
878 debug2("proposals match");
879 return (1);
880}
881
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000882static int
883kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000884{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000885 struct kex *kex = ssh->kex;
886 struct newkeys *newkeys;
887 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000888 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000889 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100890 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000891 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000892
djm@openbsd.org9690b782015-08-21 23:57:48 +0000893 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
894 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
895 goto out;
896 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
897 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000898 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000899
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000900 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000901 cprop=peer;
902 sprop=my;
903 } else {
904 cprop=my;
905 sprop=peer;
906 }
Damien Millera664e872000-04-16 11:52:47 +1000907
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000908 /* Check whether client supports ext_info_c */
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000909 if (kex->server && (kex->flags & KEX_INITIAL)) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000910 char *ext;
911
912 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000913 kex->ext_info_c = (ext != NULL);
914 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000915 }
916
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000917 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000918 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
919 sprop[PROPOSAL_KEX_ALGS])) != 0) {
920 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
921 peer[PROPOSAL_KEX_ALGS] = NULL;
922 goto out;
923 }
924 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
925 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
926 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
927 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
928 goto out;
929 }
Damien Millera664e872000-04-16 11:52:47 +1000930 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000931 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
932 r = SSH_ERR_ALLOC_FAIL;
933 goto out;
934 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000935 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000936 ctos = (!kex->server && mode == MODE_OUT) ||
937 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000938 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
939 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
940 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000941 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000942 sprop[nenc])) != 0) {
943 kex->failed_choice = peer[nenc];
944 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000945 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000946 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100947 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000948 /* ignore mac for authenticated encryption */
949 if (authlen == 0 &&
950 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000951 sprop[nmac])) != 0) {
952 kex->failed_choice = peer[nmac];
953 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000954 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000955 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000956 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000957 sprop[ncomp])) != 0) {
958 kex->failed_choice = peer[ncomp];
959 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000960 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000961 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000962 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000963 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000964 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100965 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000966 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000967 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100968 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000969 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000970 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000971 need = MAXIMUM(need, newkeys->enc.key_len);
972 need = MAXIMUM(need, newkeys->enc.block_size);
973 need = MAXIMUM(need, newkeys->enc.iv_len);
974 need = MAXIMUM(need, newkeys->mac.key_len);
975 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
976 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
977 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
978 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000979 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000980 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000981 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100982 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000983
Damien Millerbabb47a2003-02-24 11:53:32 +1100984 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000985 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000986 ssh->dispatch_skip_packets = 1;
987 r = 0;
988 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000989 kex_prop_free(my);
990 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000991 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000992}
993
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000994static int
995derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
996 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000997{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000998 struct kex *kex = ssh->kex;
999 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001000 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +10001001 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +11001002 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +10001003 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001004 int r;
Damien Miller46d38de2005-07-17 17:02:09 +10001005
Damien Millerb3051d02014-01-10 10:58:53 +11001006 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001007 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001008 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001009 r = SSH_ERR_ALLOC_FAIL;
1010 goto out;
1011 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001012
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001013 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001014 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1015 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001016 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1017 ssh_digest_update(hashctx, &c, 1) != 0 ||
1018 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001019 kex->session_id_len) != 0 ||
1020 ssh_digest_final(hashctx, digest, mdsz) != 0) {
1021 r = SSH_ERR_LIBCRYPTO_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001022 error("%s: KEX hash failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001023 goto out;
1024 }
Damien Millerb3051d02014-01-10 10:58:53 +11001025 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001026 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001027
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001028 /*
1029 * expand key:
1030 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
1031 * Key = K1 || K2 || ... || Kn
1032 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001033 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001034 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1035 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001036 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001037 ssh_digest_update(hashctx, digest, have) != 0 ||
1038 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001039 error("%s: KDF failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001040 r = SSH_ERR_LIBCRYPTO_ERROR;
1041 goto out;
1042 }
Damien Millerb3051d02014-01-10 10:58:53 +11001043 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001044 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001045 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001046#ifdef DEBUG_KEX
1047 fprintf(stderr, "key '%c'== ", c);
1048 dump_digest("key", digest, need);
1049#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001050 *keyp = digest;
1051 digest = NULL;
1052 r = 0;
1053 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00001054 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001055 ssh_digest_free(hashctx);
1056 return r;
Damien Millera664e872000-04-16 11:52:47 +10001057}
1058
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001059#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001060int
1061kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1062 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +10001063{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001064 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001065 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001066 u_int i, j, mode, ctos;
1067 int r;
Damien Millera664e872000-04-16 11:52:47 +10001068
djm@openbsd.org5ae3f6d2019-01-21 09:55:52 +00001069 /* save initial hash as session id */
1070 if (kex->session_id == NULL) {
1071 kex->session_id_len = hashlen;
1072 kex->session_id = malloc(kex->session_id_len);
1073 if (kex->session_id == NULL)
1074 return SSH_ERR_ALLOC_FAIL;
1075 memcpy(kex->session_id, hash, kex->session_id_len);
1076 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001077 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001078 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1079 shared_secret, &keys[i])) != 0) {
1080 for (j = 0; j < i; j++)
1081 free(keys[j]);
1082 return r;
1083 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001084 }
Damien Millera664e872000-04-16 11:52:47 +10001085 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001086 ctos = (!kex->server && mode == MODE_OUT) ||
1087 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001088 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1089 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1090 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001091 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001092 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001093}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001094
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001095int
djm@openbsd.org70edd732019-01-21 12:08:13 +00001096kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001097{
1098 struct kex *kex = ssh->kex;
1099
1100 *pubp = NULL;
1101 *prvp = NULL;
1102 if (kex->load_host_public_key == NULL ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001103 kex->load_host_private_key == NULL) {
1104 error("%s: missing hostkey loader", __func__);
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001105 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001106 }
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001107 *pubp = kex->load_host_public_key(kex->hostkey_type,
1108 kex->hostkey_nid, ssh);
1109 *prvp = kex->load_host_private_key(kex->hostkey_type,
1110 kex->hostkey_nid, ssh);
1111 if (*pubp == NULL)
1112 return SSH_ERR_NO_HOSTKEY_LOADED;
1113 return 0;
1114}
Darren Tuckere14e0052004-05-13 16:30:44 +10001115
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001116int
1117kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
1118{
1119 struct kex *kex = ssh->kex;
1120
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001121 if (kex->verify_host_key == NULL) {
1122 error("%s: missing hostkey verifier", __func__);
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001123 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001124 }
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001125 if (server_host_key->type != kex->hostkey_type ||
1126 (kex->hostkey_type == KEY_ECDSA &&
1127 server_host_key->ecdsa_nid != kex->hostkey_nid))
1128 return SSH_ERR_KEY_TYPE_MISMATCH;
1129 if (kex->verify_host_key(server_host_key, ssh) == -1)
1130 return SSH_ERR_SIGNATURE_INVALID;
1131 return 0;
1132}
1133
Damien Millereb8b60e2010-08-31 22:41:14 +10001134#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001135void
djm@openbsd.orgdfd59162019-01-21 10:20:12 +00001136dump_digest(const char *msg, const u_char *digest, int len)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001137{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001138 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001139 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001140}
1141#endif
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001142
1143/*
1144 * Send a plaintext error message to the peer, suffixed by \r\n.
1145 * Only used during banner exchange, and there only for the server.
1146 */
1147static void
1148send_error(struct ssh *ssh, char *msg)
1149{
1150 char *crnl = "\r\n";
1151
1152 if (!ssh->kex->server)
1153 return;
1154
1155 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1156 msg, strlen(msg)) != strlen(msg) ||
1157 atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1158 crnl, strlen(crnl)) != strlen(crnl))
1159 error("%s: write: %.100s", __func__, strerror(errno));
1160}
1161
1162/*
1163 * Sends our identification string and waits for the peer's. Will block for
1164 * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1165 * Returns on 0 success or a ssherr.h code on failure.
1166 */
1167int
1168kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1169 const char *version_addendum)
1170{
1171 int remote_major, remote_minor, mismatch;
1172 size_t len, i, n;
1173 int r, expect_nl;
1174 u_char c;
1175 struct sshbuf *our_version = ssh->kex->server ?
1176 ssh->kex->server_version : ssh->kex->client_version;
1177 struct sshbuf *peer_version = ssh->kex->server ?
1178 ssh->kex->client_version : ssh->kex->server_version;
1179 char *our_version_string = NULL, *peer_version_string = NULL;
1180 char *cp, *remote_version = NULL;
1181
1182 /* Prepare and send our banner */
1183 sshbuf_reset(our_version);
1184 if (version_addendum != NULL && *version_addendum == '\0')
1185 version_addendum = NULL;
1186 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
1187 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1188 version_addendum == NULL ? "" : " ",
1189 version_addendum == NULL ? "" : version_addendum)) != 0) {
1190 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
1191 goto out;
1192 }
1193
1194 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1195 sshbuf_mutable_ptr(our_version),
1196 sshbuf_len(our_version)) != sshbuf_len(our_version)) {
1197 error("%s: write: %.100s", __func__, strerror(errno));
1198 r = SSH_ERR_SYSTEM_ERROR;
1199 goto out;
1200 }
1201 if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
1202 error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
1203 goto out;
1204 }
1205 our_version_string = sshbuf_dup_string(our_version);
1206 if (our_version_string == NULL) {
1207 error("%s: sshbuf_dup_string failed", __func__);
1208 r = SSH_ERR_ALLOC_FAIL;
1209 goto out;
1210 }
1211 debug("Local version string %.100s", our_version_string);
1212
1213 /* Read other side's version identification. */
1214 for (n = 0; ; n++) {
1215 if (n >= SSH_MAX_PRE_BANNER_LINES) {
1216 send_error(ssh, "No SSH identification string "
1217 "received.");
1218 error("%s: No SSH version received in first %u lines "
1219 "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
1220 r = SSH_ERR_INVALID_FORMAT;
1221 goto out;
1222 }
1223 sshbuf_reset(peer_version);
1224 expect_nl = 0;
1225 for (i = 0; ; i++) {
1226 if (timeout_ms > 0) {
1227 r = waitrfd(ssh_packet_get_connection_in(ssh),
1228 &timeout_ms);
1229 if (r == -1 && errno == ETIMEDOUT) {
1230 send_error(ssh, "Timed out waiting "
1231 "for SSH identification string.");
1232 error("Connection timed out during "
1233 "banner exchange");
1234 r = SSH_ERR_CONN_TIMEOUT;
1235 goto out;
1236 } else if (r == -1) {
1237 error("%s: %s",
1238 __func__, strerror(errno));
1239 r = SSH_ERR_SYSTEM_ERROR;
1240 goto out;
1241 }
1242 }
1243
1244 len = atomicio(read, ssh_packet_get_connection_in(ssh),
1245 &c, 1);
1246 if (len != 1 && errno == EPIPE) {
1247 error("%s: Connection closed by remote host",
1248 __func__);
1249 r = SSH_ERR_CONN_CLOSED;
1250 goto out;
1251 } else if (len != 1) {
1252 error("%s: read: %.100s",
1253 __func__, strerror(errno));
1254 r = SSH_ERR_SYSTEM_ERROR;
1255 goto out;
1256 }
1257 if (c == '\r') {
1258 expect_nl = 1;
1259 continue;
1260 }
1261 if (c == '\n')
1262 break;
1263 if (c == '\0' || expect_nl) {
1264 error("%s: banner line contains invalid "
1265 "characters", __func__);
1266 goto invalid;
1267 }
1268 if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
1269 error("%s: sshbuf_put: %s",
1270 __func__, ssh_err(r));
1271 goto out;
1272 }
1273 if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1274 error("%s: banner line too long", __func__);
1275 goto invalid;
1276 }
1277 }
1278 /* Is this an actual protocol banner? */
1279 if (sshbuf_len(peer_version) > 4 &&
1280 memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1281 break;
1282 /* If not, then just log the line and continue */
1283 if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
1284 error("%s: sshbuf_dup_string failed", __func__);
1285 r = SSH_ERR_ALLOC_FAIL;
1286 goto out;
1287 }
1288 /* Do not accept lines before the SSH ident from a client */
1289 if (ssh->kex->server) {
1290 error("%s: client sent invalid protocol identifier "
1291 "\"%.256s\"", __func__, cp);
1292 free(cp);
1293 goto invalid;
1294 }
1295 debug("%s: banner line %zu: %s", __func__, n, cp);
1296 free(cp);
1297 }
1298 peer_version_string = sshbuf_dup_string(peer_version);
1299 if (peer_version_string == NULL)
1300 error("%s: sshbuf_dup_string failed", __func__);
1301 /* XXX must be same size for sscanf */
1302 if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
1303 error("%s: calloc failed", __func__);
1304 r = SSH_ERR_ALLOC_FAIL;
1305 goto out;
1306 }
1307
1308 /*
1309 * Check that the versions match. In future this might accept
1310 * several versions and set appropriate flags to handle them.
1311 */
1312 if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1313 &remote_major, &remote_minor, remote_version) != 3) {
1314 error("Bad remote protocol version identification: '%.100s'",
1315 peer_version_string);
1316 invalid:
1317 send_error(ssh, "Invalid SSH identification string.");
1318 r = SSH_ERR_INVALID_FORMAT;
1319 goto out;
1320 }
1321 debug("Remote protocol version %d.%d, remote software version %.100s",
1322 remote_major, remote_minor, remote_version);
1323 ssh->compat = compat_datafellows(remote_version);
1324
1325 mismatch = 0;
1326 switch (remote_major) {
1327 case 2:
1328 break;
1329 case 1:
1330 if (remote_minor != 99)
1331 mismatch = 1;
1332 break;
1333 default:
1334 mismatch = 1;
1335 break;
1336 }
1337 if (mismatch) {
1338 error("Protocol major versions differ: %d vs. %d",
1339 PROTOCOL_MAJOR_2, remote_major);
1340 send_error(ssh, "Protocol major versions differ.");
1341 r = SSH_ERR_NO_PROTOCOL_VERSION;
1342 goto out;
1343 }
1344
1345 if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1346 logit("probed from %s port %d with %s. Don't panic.",
1347 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1348 peer_version_string);
1349 r = SSH_ERR_CONN_CLOSED; /* XXX */
1350 goto out;
1351 }
1352 if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1353 logit("scanned from %s port %d with %s. Don't panic.",
1354 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1355 peer_version_string);
1356 r = SSH_ERR_CONN_CLOSED; /* XXX */
1357 goto out;
1358 }
1359 if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1360 logit("Remote version \"%.100s\" uses unsafe RSA signature "
1361 "scheme; disabling use of RSA keys", remote_version);
1362 }
1363 /* success */
1364 r = 0;
1365 out:
1366 free(our_version_string);
1367 free(peer_version_string);
1368 free(remote_version);
1369 return r;
1370}
1371