blob: 09c7258e05bdf16e77c00469d835b194de60d3a5 [file] [log] [blame]
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001/* $OpenBSD: kex.c,v 1.158 2020/03/13 04:01:56 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>
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));
jsg@openbsd.orgd5ba1c02020-02-26 13:40:09 +0000673 freezero(newkeys, sizeof(*newkeys));
markus@openbsd.org091c3022015-01-19 19:52:16 +0000674}
675
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000676void
677kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000678{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000679 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000680
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000681 if (kex == NULL)
682 return;
683
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000684#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000685 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100686#ifdef OPENSSL_HAS_ECC
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000687 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100688#endif /* OPENSSL_HAS_ECC */
689#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000690 for (mode = 0; mode < MODE_MAX; mode++) {
691 kex_free_newkeys(kex->newkeys[mode]);
692 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000693 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000694 sshbuf_free(kex->peer);
695 sshbuf_free(kex->my);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000696 sshbuf_free(kex->client_version);
697 sshbuf_free(kex->server_version);
djm@openbsd.orgaaca72d2019-01-21 10:40:11 +0000698 sshbuf_free(kex->client_pub);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000699 free(kex->session_id);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000700 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000701 free(kex->hostkey_alg);
702 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000703 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000704}
705
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000706int
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000707kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
708{
709 int r;
710
711 if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
712 return r;
713 ssh->kex->flags = KEX_INITIAL;
714 kex_reset_dispatch(ssh);
715 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
716 return 0;
717}
718
719int
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000720kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000721{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000722 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000723
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000724 if ((r = kex_ready(ssh, proposal)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000725 return r;
726 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
727 kex_free(ssh->kex);
728 ssh->kex = NULL;
729 return r;
Damien Millera664e872000-04-16 11:52:47 +1000730 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000731 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000732}
733
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000734/*
735 * Request key re-exchange, returns 0 on success or a ssherr.h error
736 * code otherwise. Must not be called if KEX is incomplete or in-progress.
737 */
738int
739kex_start_rekex(struct ssh *ssh)
740{
741 if (ssh->kex == NULL) {
742 error("%s: no kex", __func__);
743 return SSH_ERR_INTERNAL_ERROR;
744 }
745 if (ssh->kex->done == 0) {
746 error("%s: requested twice", __func__);
747 return SSH_ERR_INTERNAL_ERROR;
748 }
749 ssh->kex->done = 0;
750 return kex_send_kexinit(ssh);
751}
752
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000753static int
754choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000755{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000756 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000757
Damien Millera664e872000-04-16 11:52:47 +1000758 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000759 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000760 if ((enc->cipher = cipher_by_name(name)) == NULL) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000761 error("%s: unsupported cipher %s", __func__, name);
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;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000764 }
Damien Millera664e872000-04-16 11:52:47 +1000765 enc->name = name;
766 enc->enabled = 0;
767 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100768 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000769 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100770 enc->key_len = cipher_keylen(enc->cipher);
771 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000772 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000773}
Damien Miller4f7becb2006-03-26 14:10:14 +1100774
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000775static int
776choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000777{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000778 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000779
Damien Millera664e872000-04-16 11:52:47 +1000780 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000781 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000782 if (mac_setup(mac, name) < 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000783 error("%s: unsupported MAC %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000784 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000785 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000786 }
Damien Millera664e872000-04-16 11:52:47 +1000787 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000788 mac->key = NULL;
789 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000790 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000791}
Damien Miller4f7becb2006-03-26 14:10:14 +1100792
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000793static int
794choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000795{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000796 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000797
Damien Millera664e872000-04-16 11:52:47 +1000798 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000799 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
dtucker@openbsd.org7f8e66f2020-01-23 10:24:29 +0000800#ifdef WITH_ZLIB
Damien Miller9786e6e2005-07-26 21:54:56 +1000801 if (strcmp(name, "zlib@openssh.com") == 0) {
sf@openbsd.org168b46f2018-07-09 13:37:10 +0000802 comp->type = COMP_DELAYED;
803 } else if (strcmp(name, "zlib") == 0) {
sf@openbsd.orgab392672018-07-06 09:06:14 +0000804 comp->type = COMP_ZLIB;
dtucker@openbsd.org7f8e66f2020-01-23 10:24:29 +0000805 } else
806#endif /* WITH_ZLIB */
807 if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000808 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000809 } else {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000810 error("%s: unsupported compression scheme %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000811 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000812 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000813 }
814 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000815 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000816}
Damien Miller4f7becb2006-03-26 14:10:14 +1100817
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000818static int
819choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000820{
Damien Millerea111192013-04-23 19:24:32 +1000821 const struct kexalg *kexalg;
822
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000823 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000824
djm@openbsd.org9690b782015-08-21 23:57:48 +0000825 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000826 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000827 return SSH_ERR_NO_KEX_ALG_MATCH;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000828 if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
829 error("%s: unsupported KEX method %s", __func__, k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000830 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000831 }
Damien Millerea111192013-04-23 19:24:32 +1000832 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100833 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000834 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000835 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000836}
Damien Miller19bb3a52005-11-05 15:19:35 +1100837
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000838static int
839choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000840{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000841 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000842
djm@openbsd.org9690b782015-08-21 23:57:48 +0000843 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000844 k->hostkey_alg ? k->hostkey_alg : "(no match)");
845 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000846 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000847 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000848 if (k->hostkey_type == KEY_UNSPEC) {
849 error("%s: unsupported hostkey algorithm %s", __func__,
850 k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000851 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000852 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000853 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000854 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000855}
856
Damien Millera8e06ce2003-11-21 23:48:55 +1100857static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100858proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
859{
860 static int check[] = {
861 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
862 };
863 int *idx;
864 char *p;
865
866 for (idx = &check[0]; *idx != -1; idx++) {
867 if ((p = strchr(my[*idx], ',')) != NULL)
868 *p = '\0';
869 if ((p = strchr(peer[*idx], ',')) != NULL)
870 *p = '\0';
871 if (strcmp(my[*idx], peer[*idx]) != 0) {
872 debug2("proposal mismatch: my %s peer %s",
873 my[*idx], peer[*idx]);
874 return (0);
875 }
876 }
877 debug2("proposals match");
878 return (1);
879}
880
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000881static int
882kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000883{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000884 struct kex *kex = ssh->kex;
885 struct newkeys *newkeys;
886 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000887 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000888 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100889 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000890 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000891
djm@openbsd.org9690b782015-08-21 23:57:48 +0000892 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
893 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
894 goto out;
895 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
896 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000897 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000898
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000899 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000900 cprop=peer;
901 sprop=my;
902 } else {
903 cprop=my;
904 sprop=peer;
905 }
Damien Millera664e872000-04-16 11:52:47 +1000906
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000907 /* Check whether client supports ext_info_c */
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000908 if (kex->server && (kex->flags & KEX_INITIAL)) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000909 char *ext;
910
911 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000912 kex->ext_info_c = (ext != NULL);
913 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000914 }
915
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000916 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000917 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
918 sprop[PROPOSAL_KEX_ALGS])) != 0) {
919 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
920 peer[PROPOSAL_KEX_ALGS] = NULL;
921 goto out;
922 }
923 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
924 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
925 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
926 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
927 goto out;
928 }
Damien Millera664e872000-04-16 11:52:47 +1000929 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000930 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
931 r = SSH_ERR_ALLOC_FAIL;
932 goto out;
933 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000934 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000935 ctos = (!kex->server && mode == MODE_OUT) ||
936 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000937 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
938 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
939 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000940 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000941 sprop[nenc])) != 0) {
942 kex->failed_choice = peer[nenc];
943 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000944 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000945 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100946 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000947 /* ignore mac for authenticated encryption */
948 if (authlen == 0 &&
949 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000950 sprop[nmac])) != 0) {
951 kex->failed_choice = peer[nmac];
952 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000953 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000954 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000955 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000956 sprop[ncomp])) != 0) {
957 kex->failed_choice = peer[ncomp];
958 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000959 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000960 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000961 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000962 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000963 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100964 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000965 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000966 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100967 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000968 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000969 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000970 need = MAXIMUM(need, newkeys->enc.key_len);
971 need = MAXIMUM(need, newkeys->enc.block_size);
972 need = MAXIMUM(need, newkeys->enc.iv_len);
973 need = MAXIMUM(need, newkeys->mac.key_len);
974 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
975 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
976 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
977 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000978 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000979 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000980 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100981 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000982
Damien Millerbabb47a2003-02-24 11:53:32 +1100983 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000984 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000985 ssh->dispatch_skip_packets = 1;
986 r = 0;
987 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000988 kex_prop_free(my);
989 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000990 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000991}
992
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000993static int
994derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
995 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000996{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000997 struct kex *kex = ssh->kex;
998 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000999 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +10001000 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +11001001 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +10001002 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001003 int r;
Damien Miller46d38de2005-07-17 17:02:09 +10001004
Damien Millerb3051d02014-01-10 10:58:53 +11001005 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001006 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001007 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001008 r = SSH_ERR_ALLOC_FAIL;
1009 goto out;
1010 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001011
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001012 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001013 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1014 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001015 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1016 ssh_digest_update(hashctx, &c, 1) != 0 ||
1017 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001018 kex->session_id_len) != 0 ||
1019 ssh_digest_final(hashctx, digest, mdsz) != 0) {
1020 r = SSH_ERR_LIBCRYPTO_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001021 error("%s: KEX hash failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001022 goto out;
1023 }
Damien Millerb3051d02014-01-10 10:58:53 +11001024 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001025 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001026
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001027 /*
1028 * expand key:
1029 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
1030 * Key = K1 || K2 || ... || Kn
1031 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001032 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001033 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1034 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001035 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001036 ssh_digest_update(hashctx, digest, have) != 0 ||
1037 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001038 error("%s: KDF failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001039 r = SSH_ERR_LIBCRYPTO_ERROR;
1040 goto out;
1041 }
Damien Millerb3051d02014-01-10 10:58:53 +11001042 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001043 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001044 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001045#ifdef DEBUG_KEX
1046 fprintf(stderr, "key '%c'== ", c);
1047 dump_digest("key", digest, need);
1048#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001049 *keyp = digest;
1050 digest = NULL;
1051 r = 0;
1052 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00001053 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001054 ssh_digest_free(hashctx);
1055 return r;
Damien Millera664e872000-04-16 11:52:47 +10001056}
1057
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001058#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001059int
1060kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1061 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +10001062{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001063 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001064 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001065 u_int i, j, mode, ctos;
1066 int r;
Damien Millera664e872000-04-16 11:52:47 +10001067
djm@openbsd.org5ae3f6d2019-01-21 09:55:52 +00001068 /* save initial hash as session id */
1069 if (kex->session_id == NULL) {
1070 kex->session_id_len = hashlen;
1071 kex->session_id = malloc(kex->session_id_len);
1072 if (kex->session_id == NULL)
1073 return SSH_ERR_ALLOC_FAIL;
1074 memcpy(kex->session_id, hash, kex->session_id_len);
1075 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001076 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001077 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1078 shared_secret, &keys[i])) != 0) {
1079 for (j = 0; j < i; j++)
1080 free(keys[j]);
1081 return r;
1082 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001083 }
Damien Millera664e872000-04-16 11:52:47 +10001084 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001085 ctos = (!kex->server && mode == MODE_OUT) ||
1086 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001087 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1088 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1089 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001090 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001091 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001092}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001093
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001094int
djm@openbsd.org70edd732019-01-21 12:08:13 +00001095kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001096{
1097 struct kex *kex = ssh->kex;
1098
1099 *pubp = NULL;
1100 *prvp = NULL;
1101 if (kex->load_host_public_key == NULL ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001102 kex->load_host_private_key == NULL) {
1103 error("%s: missing hostkey loader", __func__);
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001104 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001105 }
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001106 *pubp = kex->load_host_public_key(kex->hostkey_type,
1107 kex->hostkey_nid, ssh);
1108 *prvp = kex->load_host_private_key(kex->hostkey_type,
1109 kex->hostkey_nid, ssh);
1110 if (*pubp == NULL)
1111 return SSH_ERR_NO_HOSTKEY_LOADED;
1112 return 0;
1113}
Darren Tuckere14e0052004-05-13 16:30:44 +10001114
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001115int
1116kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
1117{
1118 struct kex *kex = ssh->kex;
1119
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001120 if (kex->verify_host_key == NULL) {
1121 error("%s: missing hostkey verifier", __func__);
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001122 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001123 }
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001124 if (server_host_key->type != kex->hostkey_type ||
1125 (kex->hostkey_type == KEY_ECDSA &&
1126 server_host_key->ecdsa_nid != kex->hostkey_nid))
1127 return SSH_ERR_KEY_TYPE_MISMATCH;
1128 if (kex->verify_host_key(server_host_key, ssh) == -1)
1129 return SSH_ERR_SIGNATURE_INVALID;
1130 return 0;
1131}
1132
Damien Millereb8b60e2010-08-31 22:41:14 +10001133#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001134void
djm@openbsd.orgdfd59162019-01-21 10:20:12 +00001135dump_digest(const char *msg, const u_char *digest, int len)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001136{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001137 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001138 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001139}
1140#endif
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001141
1142/*
1143 * Send a plaintext error message to the peer, suffixed by \r\n.
1144 * Only used during banner exchange, and there only for the server.
1145 */
1146static void
1147send_error(struct ssh *ssh, char *msg)
1148{
1149 char *crnl = "\r\n";
1150
1151 if (!ssh->kex->server)
1152 return;
1153
1154 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1155 msg, strlen(msg)) != strlen(msg) ||
1156 atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1157 crnl, strlen(crnl)) != strlen(crnl))
1158 error("%s: write: %.100s", __func__, strerror(errno));
1159}
1160
1161/*
1162 * Sends our identification string and waits for the peer's. Will block for
1163 * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1164 * Returns on 0 success or a ssherr.h code on failure.
1165 */
1166int
1167kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1168 const char *version_addendum)
1169{
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001170 int remote_major, remote_minor, mismatch, oerrno = 0;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001171 size_t len, i, n;
1172 int r, expect_nl;
1173 u_char c;
1174 struct sshbuf *our_version = ssh->kex->server ?
1175 ssh->kex->server_version : ssh->kex->client_version;
1176 struct sshbuf *peer_version = ssh->kex->server ?
1177 ssh->kex->client_version : ssh->kex->server_version;
1178 char *our_version_string = NULL, *peer_version_string = NULL;
1179 char *cp, *remote_version = NULL;
1180
1181 /* Prepare and send our banner */
1182 sshbuf_reset(our_version);
1183 if (version_addendum != NULL && *version_addendum == '\0')
1184 version_addendum = NULL;
1185 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
1186 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1187 version_addendum == NULL ? "" : " ",
1188 version_addendum == NULL ? "" : version_addendum)) != 0) {
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001189 oerrno = errno;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001190 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)) {
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001197 oerrno = errno;
1198 debug("%s: write: %.100s", __func__, strerror(errno));
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001199 r = SSH_ERR_SYSTEM_ERROR;
1200 goto out;
1201 }
1202 if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001203 oerrno = errno;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001204 error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
1205 goto out;
1206 }
1207 our_version_string = sshbuf_dup_string(our_version);
1208 if (our_version_string == NULL) {
1209 error("%s: sshbuf_dup_string failed", __func__);
1210 r = SSH_ERR_ALLOC_FAIL;
1211 goto out;
1212 }
1213 debug("Local version string %.100s", our_version_string);
1214
1215 /* Read other side's version identification. */
1216 for (n = 0; ; n++) {
1217 if (n >= SSH_MAX_PRE_BANNER_LINES) {
1218 send_error(ssh, "No SSH identification string "
1219 "received.");
1220 error("%s: No SSH version received in first %u lines "
1221 "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
1222 r = SSH_ERR_INVALID_FORMAT;
1223 goto out;
1224 }
1225 sshbuf_reset(peer_version);
1226 expect_nl = 0;
1227 for (i = 0; ; i++) {
1228 if (timeout_ms > 0) {
1229 r = waitrfd(ssh_packet_get_connection_in(ssh),
1230 &timeout_ms);
1231 if (r == -1 && errno == ETIMEDOUT) {
1232 send_error(ssh, "Timed out waiting "
1233 "for SSH identification string.");
1234 error("Connection timed out during "
1235 "banner exchange");
1236 r = SSH_ERR_CONN_TIMEOUT;
1237 goto out;
1238 } else if (r == -1) {
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001239 oerrno = errno;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001240 error("%s: %s",
1241 __func__, strerror(errno));
1242 r = SSH_ERR_SYSTEM_ERROR;
1243 goto out;
1244 }
1245 }
1246
1247 len = atomicio(read, ssh_packet_get_connection_in(ssh),
1248 &c, 1);
1249 if (len != 1 && errno == EPIPE) {
1250 error("%s: Connection closed by remote host",
1251 __func__);
1252 r = SSH_ERR_CONN_CLOSED;
1253 goto out;
1254 } else if (len != 1) {
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001255 oerrno = errno;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001256 error("%s: read: %.100s",
1257 __func__, strerror(errno));
1258 r = SSH_ERR_SYSTEM_ERROR;
1259 goto out;
1260 }
1261 if (c == '\r') {
1262 expect_nl = 1;
1263 continue;
1264 }
1265 if (c == '\n')
1266 break;
1267 if (c == '\0' || expect_nl) {
1268 error("%s: banner line contains invalid "
1269 "characters", __func__);
1270 goto invalid;
1271 }
1272 if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001273 oerrno = errno;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001274 error("%s: sshbuf_put: %s",
1275 __func__, ssh_err(r));
1276 goto out;
1277 }
1278 if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1279 error("%s: banner line too long", __func__);
1280 goto invalid;
1281 }
1282 }
1283 /* Is this an actual protocol banner? */
1284 if (sshbuf_len(peer_version) > 4 &&
1285 memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1286 break;
1287 /* If not, then just log the line and continue */
1288 if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
1289 error("%s: sshbuf_dup_string failed", __func__);
1290 r = SSH_ERR_ALLOC_FAIL;
1291 goto out;
1292 }
1293 /* Do not accept lines before the SSH ident from a client */
1294 if (ssh->kex->server) {
1295 error("%s: client sent invalid protocol identifier "
1296 "\"%.256s\"", __func__, cp);
1297 free(cp);
1298 goto invalid;
1299 }
1300 debug("%s: banner line %zu: %s", __func__, n, cp);
1301 free(cp);
1302 }
1303 peer_version_string = sshbuf_dup_string(peer_version);
1304 if (peer_version_string == NULL)
1305 error("%s: sshbuf_dup_string failed", __func__);
1306 /* XXX must be same size for sscanf */
1307 if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
1308 error("%s: calloc failed", __func__);
1309 r = SSH_ERR_ALLOC_FAIL;
1310 goto out;
1311 }
1312
1313 /*
1314 * Check that the versions match. In future this might accept
1315 * several versions and set appropriate flags to handle them.
1316 */
1317 if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1318 &remote_major, &remote_minor, remote_version) != 3) {
1319 error("Bad remote protocol version identification: '%.100s'",
1320 peer_version_string);
1321 invalid:
1322 send_error(ssh, "Invalid SSH identification string.");
1323 r = SSH_ERR_INVALID_FORMAT;
1324 goto out;
1325 }
1326 debug("Remote protocol version %d.%d, remote software version %.100s",
1327 remote_major, remote_minor, remote_version);
1328 ssh->compat = compat_datafellows(remote_version);
1329
1330 mismatch = 0;
1331 switch (remote_major) {
1332 case 2:
1333 break;
1334 case 1:
1335 if (remote_minor != 99)
1336 mismatch = 1;
1337 break;
1338 default:
1339 mismatch = 1;
1340 break;
1341 }
1342 if (mismatch) {
1343 error("Protocol major versions differ: %d vs. %d",
1344 PROTOCOL_MAJOR_2, remote_major);
1345 send_error(ssh, "Protocol major versions differ.");
1346 r = SSH_ERR_NO_PROTOCOL_VERSION;
1347 goto out;
1348 }
1349
1350 if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1351 logit("probed from %s port %d with %s. Don't panic.",
1352 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1353 peer_version_string);
1354 r = SSH_ERR_CONN_CLOSED; /* XXX */
1355 goto out;
1356 }
1357 if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1358 logit("scanned from %s port %d with %s. Don't panic.",
1359 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1360 peer_version_string);
1361 r = SSH_ERR_CONN_CLOSED; /* XXX */
1362 goto out;
1363 }
1364 if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1365 logit("Remote version \"%.100s\" uses unsafe RSA signature "
1366 "scheme; disabling use of RSA keys", remote_version);
1367 }
1368 /* success */
1369 r = 0;
1370 out:
1371 free(our_version_string);
1372 free(peer_version_string);
1373 free(remote_version);
djm@openbsd.org5becbec2020-03-13 04:01:56 +00001374 if (r == SSH_ERR_SYSTEM_ERROR)
1375 errno = oerrno;
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001376 return r;
1377}
1378