blob: 2195cea4edd053e6a8e80a0291d298832172ae98 [file] [log] [blame]
dtucker@openbsd.orgc2cc2542019-10-08 22:40:39 +00001/* $OpenBSD: kex.c,v 1.155 2019/10/08 22:40:39 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;
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;
Damien Millera664e872000-04-16 11:52:47 +1000805 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000806 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000807 } else {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000808 error("%s: unsupported compression scheme %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000809 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000810 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000811 }
812 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000813 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000814}
Damien Miller4f7becb2006-03-26 14:10:14 +1100815
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000816static int
817choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000818{
Damien Millerea111192013-04-23 19:24:32 +1000819 const struct kexalg *kexalg;
820
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000821 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000822
djm@openbsd.org9690b782015-08-21 23:57:48 +0000823 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000824 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000825 return SSH_ERR_NO_KEX_ALG_MATCH;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000826 if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
827 error("%s: unsupported KEX method %s", __func__, k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000828 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000829 }
Damien Millerea111192013-04-23 19:24:32 +1000830 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100831 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000832 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000833 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000834}
Damien Miller19bb3a52005-11-05 15:19:35 +1100835
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000836static int
837choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000838{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000839 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000840
djm@openbsd.org9690b782015-08-21 23:57:48 +0000841 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000842 k->hostkey_alg ? k->hostkey_alg : "(no match)");
843 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000844 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000845 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000846 if (k->hostkey_type == KEY_UNSPEC) {
847 error("%s: unsupported hostkey algorithm %s", __func__,
848 k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000849 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000850 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000851 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000852 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000853}
854
Damien Millera8e06ce2003-11-21 23:48:55 +1100855static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100856proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
857{
858 static int check[] = {
859 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
860 };
861 int *idx;
862 char *p;
863
864 for (idx = &check[0]; *idx != -1; idx++) {
865 if ((p = strchr(my[*idx], ',')) != NULL)
866 *p = '\0';
867 if ((p = strchr(peer[*idx], ',')) != NULL)
868 *p = '\0';
869 if (strcmp(my[*idx], peer[*idx]) != 0) {
870 debug2("proposal mismatch: my %s peer %s",
871 my[*idx], peer[*idx]);
872 return (0);
873 }
874 }
875 debug2("proposals match");
876 return (1);
877}
878
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000879static int
880kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000881{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000882 struct kex *kex = ssh->kex;
883 struct newkeys *newkeys;
884 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000885 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000886 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100887 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000888 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000889
djm@openbsd.org9690b782015-08-21 23:57:48 +0000890 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
891 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
892 goto out;
893 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
894 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000895 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000896
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000897 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000898 cprop=peer;
899 sprop=my;
900 } else {
901 cprop=my;
902 sprop=peer;
903 }
Damien Millera664e872000-04-16 11:52:47 +1000904
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000905 /* Check whether client supports ext_info_c */
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000906 if (kex->server && (kex->flags & KEX_INITIAL)) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000907 char *ext;
908
909 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000910 kex->ext_info_c = (ext != NULL);
911 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000912 }
913
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000914 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000915 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
916 sprop[PROPOSAL_KEX_ALGS])) != 0) {
917 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
918 peer[PROPOSAL_KEX_ALGS] = NULL;
919 goto out;
920 }
921 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
922 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
923 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
924 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
925 goto out;
926 }
Damien Millera664e872000-04-16 11:52:47 +1000927 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000928 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
929 r = SSH_ERR_ALLOC_FAIL;
930 goto out;
931 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000932 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000933 ctos = (!kex->server && mode == MODE_OUT) ||
934 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000935 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
936 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
937 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000938 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000939 sprop[nenc])) != 0) {
940 kex->failed_choice = peer[nenc];
941 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000942 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000943 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100944 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000945 /* ignore mac for authenticated encryption */
946 if (authlen == 0 &&
947 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000948 sprop[nmac])) != 0) {
949 kex->failed_choice = peer[nmac];
950 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000951 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000952 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000953 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000954 sprop[ncomp])) != 0) {
955 kex->failed_choice = peer[ncomp];
956 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000957 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000958 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000959 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000960 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000961 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100962 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000963 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000964 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100965 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000966 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000967 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000968 need = MAXIMUM(need, newkeys->enc.key_len);
969 need = MAXIMUM(need, newkeys->enc.block_size);
970 need = MAXIMUM(need, newkeys->enc.iv_len);
971 need = MAXIMUM(need, newkeys->mac.key_len);
972 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
973 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
974 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
975 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000976 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000977 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000978 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100979 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000980
Damien Millerbabb47a2003-02-24 11:53:32 +1100981 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000982 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000983 ssh->dispatch_skip_packets = 1;
984 r = 0;
985 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000986 kex_prop_free(my);
987 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000988 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000989}
990
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000991static int
992derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
993 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000994{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000995 struct kex *kex = ssh->kex;
996 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000997 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000998 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100999 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +10001000 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001001 int r;
Damien Miller46d38de2005-07-17 17:02:09 +10001002
Damien Millerb3051d02014-01-10 10:58:53 +11001003 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001004 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001005 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001006 r = SSH_ERR_ALLOC_FAIL;
1007 goto out;
1008 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001009
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001010 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001011 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1012 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001013 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1014 ssh_digest_update(hashctx, &c, 1) != 0 ||
1015 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001016 kex->session_id_len) != 0 ||
1017 ssh_digest_final(hashctx, digest, mdsz) != 0) {
1018 r = SSH_ERR_LIBCRYPTO_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001019 error("%s: KEX hash failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001020 goto out;
1021 }
Damien Millerb3051d02014-01-10 10:58:53 +11001022 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001023 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001024
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001025 /*
1026 * expand key:
1027 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
1028 * Key = K1 || K2 || ... || Kn
1029 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001030 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001031 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1032 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001033 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001034 ssh_digest_update(hashctx, digest, have) != 0 ||
1035 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001036 error("%s: KDF failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001037 r = SSH_ERR_LIBCRYPTO_ERROR;
1038 goto out;
1039 }
Damien Millerb3051d02014-01-10 10:58:53 +11001040 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001041 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001042 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001043#ifdef DEBUG_KEX
1044 fprintf(stderr, "key '%c'== ", c);
1045 dump_digest("key", digest, need);
1046#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001047 *keyp = digest;
1048 digest = NULL;
1049 r = 0;
1050 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00001051 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001052 ssh_digest_free(hashctx);
1053 return r;
Damien Millera664e872000-04-16 11:52:47 +10001054}
1055
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001056#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001057int
1058kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1059 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +10001060{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001061 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001062 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001063 u_int i, j, mode, ctos;
1064 int r;
Damien Millera664e872000-04-16 11:52:47 +10001065
djm@openbsd.org5ae3f6d2019-01-21 09:55:52 +00001066 /* save initial hash as session id */
1067 if (kex->session_id == NULL) {
1068 kex->session_id_len = hashlen;
1069 kex->session_id = malloc(kex->session_id_len);
1070 if (kex->session_id == NULL)
1071 return SSH_ERR_ALLOC_FAIL;
1072 memcpy(kex->session_id, hash, kex->session_id_len);
1073 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001074 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001075 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1076 shared_secret, &keys[i])) != 0) {
1077 for (j = 0; j < i; j++)
1078 free(keys[j]);
1079 return r;
1080 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001081 }
Damien Millera664e872000-04-16 11:52:47 +10001082 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001083 ctos = (!kex->server && mode == MODE_OUT) ||
1084 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001085 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1086 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1087 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001088 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001089 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001090}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001091
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001092int
djm@openbsd.org70edd732019-01-21 12:08:13 +00001093kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001094{
1095 struct kex *kex = ssh->kex;
1096
1097 *pubp = NULL;
1098 *prvp = NULL;
1099 if (kex->load_host_public_key == NULL ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001100 kex->load_host_private_key == NULL) {
1101 error("%s: missing hostkey loader", __func__);
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001102 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001103 }
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001104 *pubp = kex->load_host_public_key(kex->hostkey_type,
1105 kex->hostkey_nid, ssh);
1106 *prvp = kex->load_host_private_key(kex->hostkey_type,
1107 kex->hostkey_nid, ssh);
1108 if (*pubp == NULL)
1109 return SSH_ERR_NO_HOSTKEY_LOADED;
1110 return 0;
1111}
Darren Tuckere14e0052004-05-13 16:30:44 +10001112
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001113int
1114kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
1115{
1116 struct kex *kex = ssh->kex;
1117
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001118 if (kex->verify_host_key == NULL) {
1119 error("%s: missing hostkey verifier", __func__);
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001120 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001121 }
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001122 if (server_host_key->type != kex->hostkey_type ||
1123 (kex->hostkey_type == KEY_ECDSA &&
1124 server_host_key->ecdsa_nid != kex->hostkey_nid))
1125 return SSH_ERR_KEY_TYPE_MISMATCH;
1126 if (kex->verify_host_key(server_host_key, ssh) == -1)
1127 return SSH_ERR_SIGNATURE_INVALID;
1128 return 0;
1129}
1130
Damien Millereb8b60e2010-08-31 22:41:14 +10001131#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001132void
djm@openbsd.orgdfd59162019-01-21 10:20:12 +00001133dump_digest(const char *msg, const u_char *digest, int len)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001134{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001135 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001136 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001137}
1138#endif
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001139
1140/*
1141 * Send a plaintext error message to the peer, suffixed by \r\n.
1142 * Only used during banner exchange, and there only for the server.
1143 */
1144static void
1145send_error(struct ssh *ssh, char *msg)
1146{
1147 char *crnl = "\r\n";
1148
1149 if (!ssh->kex->server)
1150 return;
1151
1152 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1153 msg, strlen(msg)) != strlen(msg) ||
1154 atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1155 crnl, strlen(crnl)) != strlen(crnl))
1156 error("%s: write: %.100s", __func__, strerror(errno));
1157}
1158
1159/*
1160 * Sends our identification string and waits for the peer's. Will block for
1161 * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1162 * Returns on 0 success or a ssherr.h code on failure.
1163 */
1164int
1165kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1166 const char *version_addendum)
1167{
1168 int remote_major, remote_minor, mismatch;
1169 size_t len, i, n;
1170 int r, expect_nl;
1171 u_char c;
1172 struct sshbuf *our_version = ssh->kex->server ?
1173 ssh->kex->server_version : ssh->kex->client_version;
1174 struct sshbuf *peer_version = ssh->kex->server ?
1175 ssh->kex->client_version : ssh->kex->server_version;
1176 char *our_version_string = NULL, *peer_version_string = NULL;
1177 char *cp, *remote_version = NULL;
1178
1179 /* Prepare and send our banner */
1180 sshbuf_reset(our_version);
1181 if (version_addendum != NULL && *version_addendum == '\0')
1182 version_addendum = NULL;
1183 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
1184 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1185 version_addendum == NULL ? "" : " ",
1186 version_addendum == NULL ? "" : version_addendum)) != 0) {
1187 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
1188 goto out;
1189 }
1190
1191 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1192 sshbuf_mutable_ptr(our_version),
1193 sshbuf_len(our_version)) != sshbuf_len(our_version)) {
1194 error("%s: write: %.100s", __func__, strerror(errno));
1195 r = SSH_ERR_SYSTEM_ERROR;
1196 goto out;
1197 }
1198 if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
1199 error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
1200 goto out;
1201 }
1202 our_version_string = sshbuf_dup_string(our_version);
1203 if (our_version_string == NULL) {
1204 error("%s: sshbuf_dup_string failed", __func__);
1205 r = SSH_ERR_ALLOC_FAIL;
1206 goto out;
1207 }
1208 debug("Local version string %.100s", our_version_string);
1209
1210 /* Read other side's version identification. */
1211 for (n = 0; ; n++) {
1212 if (n >= SSH_MAX_PRE_BANNER_LINES) {
1213 send_error(ssh, "No SSH identification string "
1214 "received.");
1215 error("%s: No SSH version received in first %u lines "
1216 "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
1217 r = SSH_ERR_INVALID_FORMAT;
1218 goto out;
1219 }
1220 sshbuf_reset(peer_version);
1221 expect_nl = 0;
1222 for (i = 0; ; i++) {
1223 if (timeout_ms > 0) {
1224 r = waitrfd(ssh_packet_get_connection_in(ssh),
1225 &timeout_ms);
1226 if (r == -1 && errno == ETIMEDOUT) {
1227 send_error(ssh, "Timed out waiting "
1228 "for SSH identification string.");
1229 error("Connection timed out during "
1230 "banner exchange");
1231 r = SSH_ERR_CONN_TIMEOUT;
1232 goto out;
1233 } else if (r == -1) {
1234 error("%s: %s",
1235 __func__, strerror(errno));
1236 r = SSH_ERR_SYSTEM_ERROR;
1237 goto out;
1238 }
1239 }
1240
1241 len = atomicio(read, ssh_packet_get_connection_in(ssh),
1242 &c, 1);
1243 if (len != 1 && errno == EPIPE) {
1244 error("%s: Connection closed by remote host",
1245 __func__);
1246 r = SSH_ERR_CONN_CLOSED;
1247 goto out;
1248 } else if (len != 1) {
1249 error("%s: read: %.100s",
1250 __func__, strerror(errno));
1251 r = SSH_ERR_SYSTEM_ERROR;
1252 goto out;
1253 }
1254 if (c == '\r') {
1255 expect_nl = 1;
1256 continue;
1257 }
1258 if (c == '\n')
1259 break;
1260 if (c == '\0' || expect_nl) {
1261 error("%s: banner line contains invalid "
1262 "characters", __func__);
1263 goto invalid;
1264 }
1265 if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
1266 error("%s: sshbuf_put: %s",
1267 __func__, ssh_err(r));
1268 goto out;
1269 }
1270 if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1271 error("%s: banner line too long", __func__);
1272 goto invalid;
1273 }
1274 }
1275 /* Is this an actual protocol banner? */
1276 if (sshbuf_len(peer_version) > 4 &&
1277 memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1278 break;
1279 /* If not, then just log the line and continue */
1280 if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
1281 error("%s: sshbuf_dup_string failed", __func__);
1282 r = SSH_ERR_ALLOC_FAIL;
1283 goto out;
1284 }
1285 /* Do not accept lines before the SSH ident from a client */
1286 if (ssh->kex->server) {
1287 error("%s: client sent invalid protocol identifier "
1288 "\"%.256s\"", __func__, cp);
1289 free(cp);
1290 goto invalid;
1291 }
1292 debug("%s: banner line %zu: %s", __func__, n, cp);
1293 free(cp);
1294 }
1295 peer_version_string = sshbuf_dup_string(peer_version);
1296 if (peer_version_string == NULL)
1297 error("%s: sshbuf_dup_string failed", __func__);
1298 /* XXX must be same size for sscanf */
1299 if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
1300 error("%s: calloc failed", __func__);
1301 r = SSH_ERR_ALLOC_FAIL;
1302 goto out;
1303 }
1304
1305 /*
1306 * Check that the versions match. In future this might accept
1307 * several versions and set appropriate flags to handle them.
1308 */
1309 if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1310 &remote_major, &remote_minor, remote_version) != 3) {
1311 error("Bad remote protocol version identification: '%.100s'",
1312 peer_version_string);
1313 invalid:
1314 send_error(ssh, "Invalid SSH identification string.");
1315 r = SSH_ERR_INVALID_FORMAT;
1316 goto out;
1317 }
1318 debug("Remote protocol version %d.%d, remote software version %.100s",
1319 remote_major, remote_minor, remote_version);
1320 ssh->compat = compat_datafellows(remote_version);
1321
1322 mismatch = 0;
1323 switch (remote_major) {
1324 case 2:
1325 break;
1326 case 1:
1327 if (remote_minor != 99)
1328 mismatch = 1;
1329 break;
1330 default:
1331 mismatch = 1;
1332 break;
1333 }
1334 if (mismatch) {
1335 error("Protocol major versions differ: %d vs. %d",
1336 PROTOCOL_MAJOR_2, remote_major);
1337 send_error(ssh, "Protocol major versions differ.");
1338 r = SSH_ERR_NO_PROTOCOL_VERSION;
1339 goto out;
1340 }
1341
1342 if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1343 logit("probed from %s port %d with %s. Don't panic.",
1344 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1345 peer_version_string);
1346 r = SSH_ERR_CONN_CLOSED; /* XXX */
1347 goto out;
1348 }
1349 if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1350 logit("scanned from %s port %d with %s. Don't panic.",
1351 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1352 peer_version_string);
1353 r = SSH_ERR_CONN_CLOSED; /* XXX */
1354 goto out;
1355 }
1356 if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1357 logit("Remote version \"%.100s\" uses unsafe RSA signature "
1358 "scheme; disabling use of RSA keys", remote_version);
1359 }
1360 /* success */
1361 r = 0;
1362 out:
1363 free(our_version_string);
1364 free(peer_version_string);
1365 free(remote_version);
1366 return r;
1367}
1368