blob: 5a8a03aad4578428e4cde7658d59fc546389a29d [file] [log] [blame]
naddy@openbsd.org91a21352019-09-06 14:45:34 +00001/* $OpenBSD: kex.c,v 1.154 2019/09/06 14:45:34 naddy Exp $ */
Damien Millera664e872000-04-16 11:52:47 +10002/*
Ben Lindstrom44697232001-07-04 03:32:30 +00003 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millera664e872000-04-16 11:52:47 +10004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
Damien Millera664e872000-04-16 11:52:47 +100013 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millera664e872000-04-16 11:52:47 +100027
djm@openbsd.org0a843d92018-12-27 03:25:24 +000028#include <sys/types.h>
29#include <errno.h>
Damien Millerd7834352006-08-05 12:39:39 +100030#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100031#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100032#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100033#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100034#include <string.h>
djm@openbsd.org0a843d92018-12-27 03:25:24 +000035#include <unistd.h>
36#include <poll.h>
Damien Millere3476ed2006-07-24 14:13:33 +100037
Damien Miller1f0311c2014-05-15 14:24:09 +100038#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100039#include <openssl/crypto.h>
Damien Millerbd5f2b72016-07-15 19:14:48 +100040#include <openssl/dh.h>
Damien Miller1f0311c2014-05-15 14:24:09 +100041#endif
Damien Millerd7834352006-08-05 12:39:39 +100042
djm@openbsd.org0a843d92018-12-27 03:25:24 +000043#include "ssh.h"
Damien Millerd7834352006-08-05 12:39:39 +100044#include "ssh2.h"
djm@openbsd.org0a843d92018-12-27 03:25:24 +000045#include "atomicio.h"
46#include "version.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000047#include "packet.h"
48#include "compat.h"
49#include "cipher.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000050#include "sshkey.h"
Damien Millerd7834352006-08-05 12:39:39 +100051#include "kex.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000052#include "log.h"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000053#include "mac.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000054#include "match.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000055#include "misc.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000056#include "dispatch.h"
Ben Lindstrom7a2073c2002-03-22 02:30:41 +000057#include "monitor.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000058
59#include "ssherr.h"
60#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110061#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100062
Ben Lindstrombba81212001-06-25 05:01:22 +000063/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000064static int kex_choose_conf(struct ssh *);
markus@openbsd.org2ae666a2017-05-30 14:23:52 +000065static int kex_input_newkeys(int, u_int32_t, struct ssh *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000066
djm@openbsd.org9690b782015-08-21 23:57:48 +000067static const char *proposal_names[PROPOSAL_MAX] = {
68 "KEX algorithms",
69 "host key algorithms",
70 "ciphers ctos",
71 "ciphers stoc",
72 "MACs ctos",
73 "MACs stoc",
74 "compression ctos",
75 "compression stoc",
76 "languages ctos",
77 "languages stoc",
78};
79
Damien Millerea111192013-04-23 19:24:32 +100080struct kexalg {
81 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000082 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100083 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110084 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100085};
86static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100087#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110088 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +000089 { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
90 { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
91 { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
92 { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
Damien Millerb3051d02014-01-10 10:58:53 +110093 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100094#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110095 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100096#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100097#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +110098 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
99 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
100 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
101 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +1100102# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +1100103 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
104 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +1000105# endif /* OPENSSL_HAS_NISTP521 */
106#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000107#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100108#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100109 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.org04937662016-09-22 17:52:53 +0000110 { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.orgdfd59162019-01-21 10:20:12 +0000111 { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0,
112 SSH_DIGEST_SHA512 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100113#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Damien Millerb3051d02014-01-10 10:58:53 +1100114 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000115};
116
117char *
Damien Miller690d9892013-11-08 12:16:49 +1100118kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000119{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000120 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000121 size_t nlen, rlen = 0;
122 const struct kexalg *k;
123
124 for (k = kexalgs; k->name != NULL; k++) {
125 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100126 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000127 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000128 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
129 free(ret);
130 return NULL;
131 }
132 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000133 memcpy(ret + rlen, k->name, nlen + 1);
134 rlen += nlen;
135 }
136 return ret;
137}
138
139static const struct kexalg *
140kex_alg_by_name(const char *name)
141{
142 const struct kexalg *k;
143
144 for (k = kexalgs; k->name != NULL; k++) {
145 if (strcmp(k->name, name) == 0)
146 return k;
147 }
148 return NULL;
149}
150
Damien Millerd5f62bf2010-09-24 22:11:14 +1000151/* Validate KEX method name list */
152int
153kex_names_valid(const char *names)
154{
155 char *s, *cp, *p;
156
157 if (names == NULL || strcmp(names, "") == 0)
158 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000159 if ((s = cp = strdup(names)) == NULL)
160 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000161 for ((p = strsep(&cp, ",")); p && *p != '\0';
162 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000163 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000164 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000165 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000166 return 0;
167 }
168 }
169 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000170 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000171 return 1;
172}
173
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000174/*
175 * Concatenate algorithm names, avoiding duplicates in the process.
176 * Caller must free returned string.
177 */
178char *
179kex_names_cat(const char *a, const char *b)
180{
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000181 char *ret = NULL, *tmp = NULL, *cp, *p, *m;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000182 size_t len;
183
184 if (a == NULL || *a == '\0')
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000185 return strdup(b);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000186 if (b == NULL || *b == '\0')
187 return strdup(a);
188 if (strlen(b) > 1024*1024)
189 return NULL;
190 len = strlen(a) + strlen(b) + 2;
191 if ((tmp = cp = strdup(b)) == NULL ||
192 (ret = calloc(1, len)) == NULL) {
193 free(tmp);
194 return NULL;
195 }
196 strlcpy(ret, a, len);
197 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000198 if ((m = match_list(ret, p, NULL)) != NULL) {
199 free(m);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000200 continue; /* Algorithm already present */
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000201 }
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000202 if (strlcat(ret, ",", len) >= len ||
203 strlcat(ret, p, len) >= len) {
204 free(tmp);
205 free(ret);
206 return NULL; /* Shouldn't happen */
207 }
208 }
209 free(tmp);
210 return ret;
211}
212
213/*
214 * Assemble a list of algorithms from a default list and a string from a
215 * configuration file. The user-provided string may begin with '+' to
naddy@openbsd.org91a21352019-09-06 14:45:34 +0000216 * indicate that it should be appended to the default, '-' that the
217 * specified names should be removed, or '^' that they should be placed
218 * at the head.
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000219 */
220int
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000221kex_assemble_names(char **listp, const char *def, const char *all)
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000222{
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000223 char *cp, *tmp, *patterns;
224 char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
225 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000226
djm@openbsd.org00865c22019-09-06 01:58:50 +0000227 if (listp == NULL || def == NULL || all == NULL)
228 return SSH_ERR_INVALID_ARGUMENT;
229
230 if (*listp == NULL || **listp == '\0') {
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000231 if ((*listp = strdup(def)) == NULL)
232 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000233 return 0;
234 }
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000235
236 list = *listp;
237 *listp = NULL;
238 if (*list == '+') {
239 /* Append names to default list */
240 if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
241 r = SSH_ERR_ALLOC_FAIL;
242 goto fail;
243 }
244 free(list);
245 list = tmp;
246 } else if (*list == '-') {
247 /* Remove names from default list */
248 if ((*listp = match_filter_blacklist(def, list + 1)) == NULL) {
249 r = SSH_ERR_ALLOC_FAIL;
250 goto fail;
251 }
252 free(list);
253 /* filtering has already been done */
254 return 0;
naddy@openbsd.org91a21352019-09-06 14:45:34 +0000255 } else if (*list == '^') {
256 /* Place names at head of default list */
257 if ((tmp = kex_names_cat(list + 1, def)) == NULL) {
258 r = SSH_ERR_ALLOC_FAIL;
259 goto fail;
260 }
261 free(list);
262 list = tmp;
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000263 } else {
264 /* Explicit list, overrides default - just use "list" as is */
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000265 }
266
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000267 /*
268 * The supplied names may be a pattern-list. For the -list case,
269 * the patterns are applied above. For the +list and explicit list
270 * cases we need to do it now.
271 */
272 ret = NULL;
273 if ((patterns = opatterns = strdup(list)) == NULL) {
274 r = SSH_ERR_ALLOC_FAIL;
275 goto fail;
276 }
277 /* Apply positive (i.e. non-negated) patterns from the list */
278 while ((cp = strsep(&patterns, ",")) != NULL) {
279 if (*cp == '!') {
280 /* negated matches are not supported here */
281 r = SSH_ERR_INVALID_ARGUMENT;
282 goto fail;
283 }
284 free(matching);
285 if ((matching = match_filter_whitelist(all, cp)) == NULL) {
286 r = SSH_ERR_ALLOC_FAIL;
287 goto fail;
288 }
289 if ((tmp = kex_names_cat(ret, matching)) == NULL) {
290 r = SSH_ERR_ALLOC_FAIL;
291 goto fail;
292 }
293 free(ret);
294 ret = tmp;
295 }
296 if (ret == NULL || *ret == '\0') {
297 /* An empty name-list is an error */
298 /* XXX better error code? */
299 r = SSH_ERR_INVALID_ARGUMENT;
300 goto fail;
301 }
302
303 /* success */
304 *listp = ret;
305 ret = NULL;
306 r = 0;
307
308 fail:
309 free(matching);
310 free(opatterns);
311 free(list);
312 free(ret);
313 return r;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000314}
315
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000316/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000317int
318kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000319{
Damien Millereccb9de2005-06-17 12:59:34 +1000320 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000321 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000322
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000323 sshbuf_reset(b);
324
Ben Lindstrom59971722002-03-27 17:42:57 +0000325 /*
326 * add a dummy cookie, the cookie will be overwritten by
327 * kex_send_kexinit(), each time a kexinit is set
328 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000329 for (i = 0; i < KEX_COOKIE_LEN; i++) {
330 if ((r = sshbuf_put_u8(b, 0)) != 0)
331 return r;
332 }
333 for (i = 0; i < PROPOSAL_MAX; i++) {
334 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
335 return r;
336 }
337 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
338 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
339 return r;
340 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000341}
342
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000343/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000344int
345kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000346{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000347 struct sshbuf *b = NULL;
348 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000349 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000350 char **proposal = NULL;
351 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000352
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000353 *propp = NULL;
354 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
355 return SSH_ERR_ALLOC_FAIL;
356 if ((b = sshbuf_fromb(raw)) == NULL) {
357 r = SSH_ERR_ALLOC_FAIL;
358 goto out;
359 }
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000360 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */
361 error("%s: consume cookie: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000362 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000363 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000364 /* extract kex init proposal strings */
365 for (i = 0; i < PROPOSAL_MAX; i++) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000366 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) {
367 error("%s: parse proposal %u: %s", __func__,
368 i, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000369 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000370 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000371 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000372 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000373 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000374 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000375 (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */
376 error("%s: parse: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000377 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000378 }
Damien Millerbabb47a2003-02-24 11:53:32 +1100379 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000380 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000381 debug2("first_kex_follows %d ", v);
382 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000383 r = 0;
384 *propp = proposal;
385 out:
386 if (r != 0 && proposal != NULL)
387 kex_prop_free(proposal);
388 sshbuf_free(b);
389 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000390}
391
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000392void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000393kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000394{
Damien Millereccb9de2005-06-17 12:59:34 +1000395 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000396
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000397 if (proposal == NULL)
398 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000399 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000400 free(proposal[i]);
401 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000402}
403
Darren Tucker0d0d1952007-06-05 18:23:28 +1000404/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000405static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000406kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
Damien Miller874d77b2000-10-14 16:23:11 +1100407{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000408 int r;
409
410 error("kex protocol error: type %d seq %u", type, seq);
411 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
412 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
413 (r = sshpkt_send(ssh)) != 0)
414 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000415 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100416}
417
Ben Lindstrombba81212001-06-25 05:01:22 +0000418static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000419kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000420{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000421 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100422 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000423}
424
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000425static int
426kex_send_ext_info(struct ssh *ssh)
427{
428 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000429 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000430
djm@openbsd.org0ea33242019-09-05 09:25:13 +0000431 debug("Sending SSH2_MSG_EXT_INFO");
djm@openbsd.org183ba552017-03-10 04:07:20 +0000432 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000433 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000434 /* XXX filter algs list by allowed pubkey/hostbased types */
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000435 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
436 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
437 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000438 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000439 (r = sshpkt_send(ssh)) != 0) {
440 error("%s: compose: %s", __func__, ssh_err(r));
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000441 goto out;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000442 }
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000443 /* success */
444 r = 0;
445 out:
446 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000447 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000448}
449
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000450int
451kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000452{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000453 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000454
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000455 kex_reset_dispatch(ssh);
456 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
457 (r = sshpkt_send(ssh)) != 0)
458 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000459 debug("SSH2_MSG_NEWKEYS sent");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000460 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
djm@openbsd.org0ea33242019-09-05 09:25:13 +0000461 if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000462 if ((r = kex_send_ext_info(ssh)) != 0)
463 return r;
djm@openbsd.org0ea33242019-09-05 09:25:13 +0000464 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000465 return 0;
466}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000467
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000468int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000469kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000470{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000471 struct kex *kex = ssh->kex;
472 u_int32_t i, ninfo;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000473 char *name;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000474 u_char *val;
475 size_t vlen;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000476 int r;
477
478 debug("SSH2_MSG_EXT_INFO received");
479 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
480 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
481 return r;
482 for (i = 0; i < ninfo; i++) {
483 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
484 return r;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000485 if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000486 free(name);
487 return r;
488 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000489 if (strcmp(name, "server-sig-algs") == 0) {
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000490 /* Ensure no \0 lurking in value */
491 if (memchr(val, '\0', vlen) != NULL) {
492 error("%s: nul byte in %s", __func__, name);
493 return SSH_ERR_INVALID_FORMAT;
494 }
495 debug("%s: %s=<%s>", __func__, name, val);
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000496 kex->server_sig_algs = val;
497 val = NULL;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000498 } else
499 debug("%s: %s (unrecognised)", __func__, name);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000500 free(name);
501 free(val);
502 }
503 return sshpkt_get_end(ssh);
504}
505
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000506static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000507kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000508{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000509 struct kex *kex = ssh->kex;
510 int r;
511
512 debug("SSH2_MSG_NEWKEYS received");
513 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000514 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000515 if ((r = sshpkt_get_end(ssh)) != 0)
516 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000517 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
518 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000519 kex->done = 1;
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000520 kex->flags &= ~KEX_INITIAL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000521 sshbuf_reset(kex->peer);
522 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000523 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000524 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000525 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000526 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000527}
Damien Millera664e872000-04-16 11:52:47 +1000528
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000529int
530kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000531{
Ben Lindstrom59971722002-03-27 17:42:57 +0000532 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000533 struct kex *kex = ssh->kex;
534 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000535
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000536 if (kex == NULL) {
537 error("%s: no hex", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000538 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000539 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000540 if (kex->flags & KEX_INIT_SENT)
541 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000542 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000543
544 /* generate a random cookie */
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000545 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) {
546 error("%s: bad kex length: %zu < %d", __func__,
547 sshbuf_len(kex->my), KEX_COOKIE_LEN);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000548 return SSH_ERR_INVALID_FORMAT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000549 }
550 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) {
551 error("%s: buffer error", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000552 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000553 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000554 arc4random_buf(cookie, KEX_COOKIE_LEN);
555
556 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
557 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000558 (r = sshpkt_send(ssh)) != 0) {
559 error("%s: compose reply: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000560 return r;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000561 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000562 debug("SSH2_MSG_KEXINIT sent");
563 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000564 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000565}
566
Darren Tucker0d0d1952007-06-05 18:23:28 +1000567/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000568int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000569kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000570{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000571 struct kex *kex = ssh->kex;
572 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000573 u_int i;
574 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000575 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000576
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000577 debug("SSH2_MSG_KEXINIT received");
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000578 if (kex == NULL) {
579 error("%s: no hex", __func__);
580 return SSH_ERR_INTERNAL_ERROR;
581 }
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000582 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000583 ptr = sshpkt_ptr(ssh, &dlen);
584 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
585 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000586
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000587 /* discard packet */
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000588 for (i = 0; i < KEX_COOKIE_LEN; i++) {
589 if ((r = sshpkt_get_u8(ssh, NULL)) != 0) {
590 error("%s: discard cookie: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000591 return r;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000592 }
593 }
594 for (i = 0; i < PROPOSAL_MAX; i++) {
595 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
596 error("%s: discard proposal: %s", __func__, ssh_err(r));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000597 return r;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000598 }
599 }
Darren Tuckerae608bd2012-09-06 21:19:51 +1000600 /*
601 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
602 * KEX method has the server move first, but a server might be using
603 * a custom method or one that we otherwise don't support. We should
604 * be prepared to remember first_kex_follows here so we can eat a
605 * packet later.
606 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
607 * for cases where the server *doesn't* go first. I guess we should
608 * ignore it when it is set for these cases, which is what we do now.
609 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000610 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
611 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
612 (r = sshpkt_get_end(ssh)) != 0)
613 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000614
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000615 if (!(kex->flags & KEX_INIT_SENT))
616 if ((r = kex_send_kexinit(ssh)) != 0)
617 return r;
618 if ((r = kex_choose_conf(ssh)) != 0)
619 return r;
620
621 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
622 return (kex->kex[kex->kex_type])(ssh);
623
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000624 error("%s: unknown kex type %u", __func__, kex->kex_type);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000625 return SSH_ERR_INTERNAL_ERROR;
626}
627
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000628struct kex *
629kex_new(void)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000630{
631 struct kex *kex;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000632
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000633 if ((kex = calloc(1, sizeof(*kex))) == NULL ||
634 (kex->peer = sshbuf_new()) == NULL ||
635 (kex->my = sshbuf_new()) == NULL ||
636 (kex->client_version = sshbuf_new()) == NULL ||
637 (kex->server_version = sshbuf_new()) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000638 kex_free(kex);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000639 return NULL;
640 }
641 return kex;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000642}
643
markus@openbsd.org091c3022015-01-19 19:52:16 +0000644void
645kex_free_newkeys(struct newkeys *newkeys)
646{
647 if (newkeys == NULL)
648 return;
649 if (newkeys->enc.key) {
650 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
651 free(newkeys->enc.key);
652 newkeys->enc.key = NULL;
653 }
654 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000655 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000656 free(newkeys->enc.iv);
657 newkeys->enc.iv = NULL;
658 }
659 free(newkeys->enc.name);
660 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
661 free(newkeys->comp.name);
662 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
663 mac_clear(&newkeys->mac);
664 if (newkeys->mac.key) {
665 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
666 free(newkeys->mac.key);
667 newkeys->mac.key = NULL;
668 }
669 free(newkeys->mac.name);
670 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
671 explicit_bzero(newkeys, sizeof(*newkeys));
672 free(newkeys);
673}
674
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000675void
676kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000677{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000678 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000679
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000680 if (kex == NULL)
681 return;
682
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000683#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000684 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100685#ifdef OPENSSL_HAS_ECC
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000686 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100687#endif /* OPENSSL_HAS_ECC */
688#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000689 for (mode = 0; mode < MODE_MAX; mode++) {
690 kex_free_newkeys(kex->newkeys[mode]);
691 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000692 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000693 sshbuf_free(kex->peer);
694 sshbuf_free(kex->my);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000695 sshbuf_free(kex->client_version);
696 sshbuf_free(kex->server_version);
djm@openbsd.orgaaca72d2019-01-21 10:40:11 +0000697 sshbuf_free(kex->client_pub);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000698 free(kex->session_id);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000699 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000700 free(kex->hostkey_alg);
701 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000702 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000703}
704
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000705int
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000706kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
707{
708 int r;
709
710 if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
711 return r;
712 ssh->kex->flags = KEX_INITIAL;
713 kex_reset_dispatch(ssh);
714 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
715 return 0;
716}
717
718int
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000720{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000721 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000722
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000723 if ((r = kex_ready(ssh, proposal)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000724 return r;
725 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
726 kex_free(ssh->kex);
727 ssh->kex = NULL;
728 return r;
Damien Millera664e872000-04-16 11:52:47 +1000729 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000730 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000731}
732
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000733/*
734 * Request key re-exchange, returns 0 on success or a ssherr.h error
735 * code otherwise. Must not be called if KEX is incomplete or in-progress.
736 */
737int
738kex_start_rekex(struct ssh *ssh)
739{
740 if (ssh->kex == NULL) {
741 error("%s: no kex", __func__);
742 return SSH_ERR_INTERNAL_ERROR;
743 }
744 if (ssh->kex->done == 0) {
745 error("%s: requested twice", __func__);
746 return SSH_ERR_INTERNAL_ERROR;
747 }
748 ssh->kex->done = 0;
749 return kex_send_kexinit(ssh);
750}
751
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000752static int
753choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000754{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000755 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000756
Damien Millera664e872000-04-16 11:52:47 +1000757 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000758 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000759 if ((enc->cipher = cipher_by_name(name)) == NULL) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000760 error("%s: unsupported cipher %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000761 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000762 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000763 }
Damien Millera664e872000-04-16 11:52:47 +1000764 enc->name = name;
765 enc->enabled = 0;
766 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100767 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000768 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100769 enc->key_len = cipher_keylen(enc->cipher);
770 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000771 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000772}
Damien Miller4f7becb2006-03-26 14:10:14 +1100773
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000774static int
775choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000776{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000777 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000778
Damien Millera664e872000-04-16 11:52:47 +1000779 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000780 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000781 if (mac_setup(mac, name) < 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000782 error("%s: unsupported MAC %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000783 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000784 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000785 }
Damien Millera664e872000-04-16 11:52:47 +1000786 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000787 mac->key = NULL;
788 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000789 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000790}
Damien Miller4f7becb2006-03-26 14:10:14 +1100791
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000792static int
793choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000794{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000795 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000796
Damien Millera664e872000-04-16 11:52:47 +1000797 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000798 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000799 if (strcmp(name, "zlib@openssh.com") == 0) {
sf@openbsd.org168b46f2018-07-09 13:37:10 +0000800 comp->type = COMP_DELAYED;
801 } else if (strcmp(name, "zlib") == 0) {
sf@openbsd.orgab392672018-07-06 09:06:14 +0000802 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000803 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000804 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000805 } else {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000806 error("%s: unsupported compression scheme %s", __func__, name);
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000807 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000808 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000809 }
810 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000811 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000812}
Damien Miller4f7becb2006-03-26 14:10:14 +1100813
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000814static int
815choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000816{
Damien Millerea111192013-04-23 19:24:32 +1000817 const struct kexalg *kexalg;
818
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000819 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000820
djm@openbsd.org9690b782015-08-21 23:57:48 +0000821 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000822 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000823 return SSH_ERR_NO_KEX_ALG_MATCH;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000824 if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
825 error("%s: unsupported KEX method %s", __func__, k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000826 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000827 }
Damien Millerea111192013-04-23 19:24:32 +1000828 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100829 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000830 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000831 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000832}
Damien Miller19bb3a52005-11-05 15:19:35 +1100833
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000834static int
835choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000836{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000837 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000838
djm@openbsd.org9690b782015-08-21 23:57:48 +0000839 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000840 k->hostkey_alg ? k->hostkey_alg : "(no match)");
841 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000842 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000843 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000844 if (k->hostkey_type == KEY_UNSPEC) {
845 error("%s: unsupported hostkey algorithm %s", __func__,
846 k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000847 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +0000848 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000849 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000850 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000851}
852
Damien Millera8e06ce2003-11-21 23:48:55 +1100853static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100854proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
855{
856 static int check[] = {
857 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
858 };
859 int *idx;
860 char *p;
861
862 for (idx = &check[0]; *idx != -1; idx++) {
863 if ((p = strchr(my[*idx], ',')) != NULL)
864 *p = '\0';
865 if ((p = strchr(peer[*idx], ',')) != NULL)
866 *p = '\0';
867 if (strcmp(my[*idx], peer[*idx]) != 0) {
868 debug2("proposal mismatch: my %s peer %s",
869 my[*idx], peer[*idx]);
870 return (0);
871 }
872 }
873 debug2("proposals match");
874 return (1);
875}
876
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000877static int
878kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000879{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000880 struct kex *kex = ssh->kex;
881 struct newkeys *newkeys;
882 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000883 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000884 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100885 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000886 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000887
djm@openbsd.org9690b782015-08-21 23:57:48 +0000888 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
889 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
890 goto out;
891 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
892 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000893 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000894
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000895 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000896 cprop=peer;
897 sprop=my;
898 } else {
899 cprop=my;
900 sprop=peer;
901 }
Damien Millera664e872000-04-16 11:52:47 +1000902
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000903 /* Check whether client supports ext_info_c */
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000904 if (kex->server && (kex->flags & KEX_INITIAL)) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000905 char *ext;
906
907 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000908 kex->ext_info_c = (ext != NULL);
909 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000910 }
911
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000912 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000913 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
914 sprop[PROPOSAL_KEX_ALGS])) != 0) {
915 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
916 peer[PROPOSAL_KEX_ALGS] = NULL;
917 goto out;
918 }
919 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
920 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
921 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
922 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
923 goto out;
924 }
Damien Millera664e872000-04-16 11:52:47 +1000925 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000926 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
927 r = SSH_ERR_ALLOC_FAIL;
928 goto out;
929 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000930 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000931 ctos = (!kex->server && mode == MODE_OUT) ||
932 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000933 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
934 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
935 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000936 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000937 sprop[nenc])) != 0) {
938 kex->failed_choice = peer[nenc];
939 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000940 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000941 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100942 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000943 /* ignore mac for authenticated encryption */
944 if (authlen == 0 &&
945 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000946 sprop[nmac])) != 0) {
947 kex->failed_choice = peer[nmac];
948 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000949 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000950 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000951 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000952 sprop[ncomp])) != 0) {
953 kex->failed_choice = peer[ncomp];
954 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000955 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000956 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000957 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000958 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000959 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100960 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000961 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000962 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100963 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000964 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000965 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000966 need = MAXIMUM(need, newkeys->enc.key_len);
967 need = MAXIMUM(need, newkeys->enc.block_size);
968 need = MAXIMUM(need, newkeys->enc.iv_len);
969 need = MAXIMUM(need, newkeys->mac.key_len);
970 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
971 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
972 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
973 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000974 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000975 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000976 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100977 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000978
Damien Millerbabb47a2003-02-24 11:53:32 +1100979 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000980 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000981 ssh->dispatch_skip_packets = 1;
982 r = 0;
983 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000984 kex_prop_free(my);
985 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000986 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000987}
988
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000989static int
990derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
991 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000992{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000993 struct kex *kex = ssh->kex;
994 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000995 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000996 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100997 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000998 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000999 int r;
Damien Miller46d38de2005-07-17 17:02:09 +10001000
Damien Millerb3051d02014-01-10 10:58:53 +11001001 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001002 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001003 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001004 r = SSH_ERR_ALLOC_FAIL;
1005 goto out;
1006 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001007
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001008 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001009 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1010 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001011 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1012 ssh_digest_update(hashctx, &c, 1) != 0 ||
1013 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001014 kex->session_id_len) != 0 ||
1015 ssh_digest_final(hashctx, digest, mdsz) != 0) {
1016 r = SSH_ERR_LIBCRYPTO_ERROR;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001017 error("%s: KEX hash failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001018 goto out;
1019 }
Damien Millerb3051d02014-01-10 10:58:53 +11001020 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001021 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001022
Ben Lindstrombe2cc432001-04-04 23:46:07 +00001023 /*
1024 * expand key:
1025 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
1026 * Key = K1 || K2 || ... || Kn
1027 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001028 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001029 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1030 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +11001031 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001032 ssh_digest_update(hashctx, digest, have) != 0 ||
1033 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001034 error("%s: KDF failed", __func__);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001035 r = SSH_ERR_LIBCRYPTO_ERROR;
1036 goto out;
1037 }
Damien Millerb3051d02014-01-10 10:58:53 +11001038 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001039 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001040 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001041#ifdef DEBUG_KEX
1042 fprintf(stderr, "key '%c'== ", c);
1043 dump_digest("key", digest, need);
1044#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001045 *keyp = digest;
1046 digest = NULL;
1047 r = 0;
1048 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00001049 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001050 ssh_digest_free(hashctx);
1051 return r;
Damien Millera664e872000-04-16 11:52:47 +10001052}
1053
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001054#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001055int
1056kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1057 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +10001058{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001059 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001060 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001061 u_int i, j, mode, ctos;
1062 int r;
Damien Millera664e872000-04-16 11:52:47 +10001063
djm@openbsd.org5ae3f6d2019-01-21 09:55:52 +00001064 /* save initial hash as session id */
1065 if (kex->session_id == NULL) {
1066 kex->session_id_len = hashlen;
1067 kex->session_id = malloc(kex->session_id_len);
1068 if (kex->session_id == NULL)
1069 return SSH_ERR_ALLOC_FAIL;
1070 memcpy(kex->session_id, hash, kex->session_id_len);
1071 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001072 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001073 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1074 shared_secret, &keys[i])) != 0) {
1075 for (j = 0; j < i; j++)
1076 free(keys[j]);
1077 return r;
1078 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001079 }
Damien Millera664e872000-04-16 11:52:47 +10001080 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001081 ctos = (!kex->server && mode == MODE_OUT) ||
1082 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001083 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1084 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1085 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001086 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001087 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001088}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001089
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001090int
djm@openbsd.org70edd732019-01-21 12:08:13 +00001091kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001092{
1093 struct kex *kex = ssh->kex;
1094
1095 *pubp = NULL;
1096 *prvp = NULL;
1097 if (kex->load_host_public_key == NULL ||
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001098 kex->load_host_private_key == NULL) {
1099 error("%s: missing hostkey loader", __func__);
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001100 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001101 }
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001102 *pubp = kex->load_host_public_key(kex->hostkey_type,
1103 kex->hostkey_nid, ssh);
1104 *prvp = kex->load_host_private_key(kex->hostkey_type,
1105 kex->hostkey_nid, ssh);
1106 if (*pubp == NULL)
1107 return SSH_ERR_NO_HOSTKEY_LOADED;
1108 return 0;
1109}
Darren Tuckere14e0052004-05-13 16:30:44 +10001110
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001111int
1112kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
1113{
1114 struct kex *kex = ssh->kex;
1115
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001116 if (kex->verify_host_key == NULL) {
1117 error("%s: missing hostkey verifier", __func__);
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001118 return SSH_ERR_INVALID_ARGUMENT;
djm@openbsd.org76f09bd2019-09-05 09:35:19 +00001119 }
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001120 if (server_host_key->type != kex->hostkey_type ||
1121 (kex->hostkey_type == KEY_ECDSA &&
1122 server_host_key->ecdsa_nid != kex->hostkey_nid))
1123 return SSH_ERR_KEY_TYPE_MISMATCH;
1124 if (kex->verify_host_key(server_host_key, ssh) == -1)
1125 return SSH_ERR_SIGNATURE_INVALID;
1126 return 0;
1127}
1128
Damien Millereb8b60e2010-08-31 22:41:14 +10001129#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001130void
djm@openbsd.orgdfd59162019-01-21 10:20:12 +00001131dump_digest(const char *msg, const u_char *digest, int len)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001132{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001133 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001134 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001135}
1136#endif
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001137
1138/*
1139 * Send a plaintext error message to the peer, suffixed by \r\n.
1140 * Only used during banner exchange, and there only for the server.
1141 */
1142static void
1143send_error(struct ssh *ssh, char *msg)
1144{
1145 char *crnl = "\r\n";
1146
1147 if (!ssh->kex->server)
1148 return;
1149
1150 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1151 msg, strlen(msg)) != strlen(msg) ||
1152 atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1153 crnl, strlen(crnl)) != strlen(crnl))
1154 error("%s: write: %.100s", __func__, strerror(errno));
1155}
1156
1157/*
1158 * Sends our identification string and waits for the peer's. Will block for
1159 * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1160 * Returns on 0 success or a ssherr.h code on failure.
1161 */
1162int
1163kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1164 const char *version_addendum)
1165{
1166 int remote_major, remote_minor, mismatch;
1167 size_t len, i, n;
1168 int r, expect_nl;
1169 u_char c;
1170 struct sshbuf *our_version = ssh->kex->server ?
1171 ssh->kex->server_version : ssh->kex->client_version;
1172 struct sshbuf *peer_version = ssh->kex->server ?
1173 ssh->kex->client_version : ssh->kex->server_version;
1174 char *our_version_string = NULL, *peer_version_string = NULL;
1175 char *cp, *remote_version = NULL;
1176
1177 /* Prepare and send our banner */
1178 sshbuf_reset(our_version);
1179 if (version_addendum != NULL && *version_addendum == '\0')
1180 version_addendum = NULL;
1181 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
1182 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1183 version_addendum == NULL ? "" : " ",
1184 version_addendum == NULL ? "" : version_addendum)) != 0) {
1185 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
1186 goto out;
1187 }
1188
1189 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1190 sshbuf_mutable_ptr(our_version),
1191 sshbuf_len(our_version)) != sshbuf_len(our_version)) {
1192 error("%s: write: %.100s", __func__, strerror(errno));
1193 r = SSH_ERR_SYSTEM_ERROR;
1194 goto out;
1195 }
1196 if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
1197 error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
1198 goto out;
1199 }
1200 our_version_string = sshbuf_dup_string(our_version);
1201 if (our_version_string == NULL) {
1202 error("%s: sshbuf_dup_string failed", __func__);
1203 r = SSH_ERR_ALLOC_FAIL;
1204 goto out;
1205 }
1206 debug("Local version string %.100s", our_version_string);
1207
1208 /* Read other side's version identification. */
1209 for (n = 0; ; n++) {
1210 if (n >= SSH_MAX_PRE_BANNER_LINES) {
1211 send_error(ssh, "No SSH identification string "
1212 "received.");
1213 error("%s: No SSH version received in first %u lines "
1214 "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
1215 r = SSH_ERR_INVALID_FORMAT;
1216 goto out;
1217 }
1218 sshbuf_reset(peer_version);
1219 expect_nl = 0;
1220 for (i = 0; ; i++) {
1221 if (timeout_ms > 0) {
1222 r = waitrfd(ssh_packet_get_connection_in(ssh),
1223 &timeout_ms);
1224 if (r == -1 && errno == ETIMEDOUT) {
1225 send_error(ssh, "Timed out waiting "
1226 "for SSH identification string.");
1227 error("Connection timed out during "
1228 "banner exchange");
1229 r = SSH_ERR_CONN_TIMEOUT;
1230 goto out;
1231 } else if (r == -1) {
1232 error("%s: %s",
1233 __func__, strerror(errno));
1234 r = SSH_ERR_SYSTEM_ERROR;
1235 goto out;
1236 }
1237 }
1238
1239 len = atomicio(read, ssh_packet_get_connection_in(ssh),
1240 &c, 1);
1241 if (len != 1 && errno == EPIPE) {
1242 error("%s: Connection closed by remote host",
1243 __func__);
1244 r = SSH_ERR_CONN_CLOSED;
1245 goto out;
1246 } else if (len != 1) {
1247 error("%s: read: %.100s",
1248 __func__, strerror(errno));
1249 r = SSH_ERR_SYSTEM_ERROR;
1250 goto out;
1251 }
1252 if (c == '\r') {
1253 expect_nl = 1;
1254 continue;
1255 }
1256 if (c == '\n')
1257 break;
1258 if (c == '\0' || expect_nl) {
1259 error("%s: banner line contains invalid "
1260 "characters", __func__);
1261 goto invalid;
1262 }
1263 if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
1264 error("%s: sshbuf_put: %s",
1265 __func__, ssh_err(r));
1266 goto out;
1267 }
1268 if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1269 error("%s: banner line too long", __func__);
1270 goto invalid;
1271 }
1272 }
1273 /* Is this an actual protocol banner? */
1274 if (sshbuf_len(peer_version) > 4 &&
1275 memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1276 break;
1277 /* If not, then just log the line and continue */
1278 if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
1279 error("%s: sshbuf_dup_string failed", __func__);
1280 r = SSH_ERR_ALLOC_FAIL;
1281 goto out;
1282 }
1283 /* Do not accept lines before the SSH ident from a client */
1284 if (ssh->kex->server) {
1285 error("%s: client sent invalid protocol identifier "
1286 "\"%.256s\"", __func__, cp);
1287 free(cp);
1288 goto invalid;
1289 }
1290 debug("%s: banner line %zu: %s", __func__, n, cp);
1291 free(cp);
1292 }
1293 peer_version_string = sshbuf_dup_string(peer_version);
1294 if (peer_version_string == NULL)
1295 error("%s: sshbuf_dup_string failed", __func__);
1296 /* XXX must be same size for sscanf */
1297 if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
1298 error("%s: calloc failed", __func__);
1299 r = SSH_ERR_ALLOC_FAIL;
1300 goto out;
1301 }
1302
1303 /*
1304 * Check that the versions match. In future this might accept
1305 * several versions and set appropriate flags to handle them.
1306 */
1307 if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1308 &remote_major, &remote_minor, remote_version) != 3) {
1309 error("Bad remote protocol version identification: '%.100s'",
1310 peer_version_string);
1311 invalid:
1312 send_error(ssh, "Invalid SSH identification string.");
1313 r = SSH_ERR_INVALID_FORMAT;
1314 goto out;
1315 }
1316 debug("Remote protocol version %d.%d, remote software version %.100s",
1317 remote_major, remote_minor, remote_version);
1318 ssh->compat = compat_datafellows(remote_version);
1319
1320 mismatch = 0;
1321 switch (remote_major) {
1322 case 2:
1323 break;
1324 case 1:
1325 if (remote_minor != 99)
1326 mismatch = 1;
1327 break;
1328 default:
1329 mismatch = 1;
1330 break;
1331 }
1332 if (mismatch) {
1333 error("Protocol major versions differ: %d vs. %d",
1334 PROTOCOL_MAJOR_2, remote_major);
1335 send_error(ssh, "Protocol major versions differ.");
1336 r = SSH_ERR_NO_PROTOCOL_VERSION;
1337 goto out;
1338 }
1339
1340 if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1341 logit("probed from %s port %d with %s. Don't panic.",
1342 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1343 peer_version_string);
1344 r = SSH_ERR_CONN_CLOSED; /* XXX */
1345 goto out;
1346 }
1347 if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1348 logit("scanned from %s port %d with %s. Don't panic.",
1349 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1350 peer_version_string);
1351 r = SSH_ERR_CONN_CLOSED; /* XXX */
1352 goto out;
1353 }
1354 if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1355 logit("Remote version \"%.100s\" uses unsafe RSA signature "
1356 "scheme; disabling use of RSA keys", remote_version);
1357 }
1358 /* success */
1359 r = 0;
1360 out:
1361 free(our_version_string);
1362 free(peer_version_string);
1363 free(remote_version);
1364 return r;
1365}
1366