blob: 25f9f66f69af550e6ab8cb4ac8762c4ce17ed208 [file] [log] [blame]
sf@openbsd.org168b46f2018-07-09 13:37:10 +00001/* $OpenBSD: kex.c,v 1.141 2018/07/09 13:37:10 sf 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
Damien Miller8dbffe72006-08-05 11:02:17 +100028
Damien Millerd7834352006-08-05 12:39:39 +100029#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100030#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100031#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100032#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100033#include <string.h>
34
Damien Miller1f0311c2014-05-15 14:24:09 +100035#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100036#include <openssl/crypto.h>
Damien Millerbd5f2b72016-07-15 19:14:48 +100037#include <openssl/dh.h>
Damien Miller1f0311c2014-05-15 14:24:09 +100038#endif
Damien Millerd7834352006-08-05 12:39:39 +100039
Damien Millerd7834352006-08-05 12:39:39 +100040#include "ssh2.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000041#include "packet.h"
42#include "compat.h"
43#include "cipher.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000044#include "sshkey.h"
Damien Millerd7834352006-08-05 12:39:39 +100045#include "kex.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000046#include "log.h"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000047#include "mac.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000048#include "match.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000049#include "misc.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000050#include "dispatch.h"
Ben Lindstrom7a2073c2002-03-22 02:30:41 +000051#include "monitor.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000052
53#include "ssherr.h"
54#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110055#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100056
Ben Lindstrombba81212001-06-25 05:01:22 +000057/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000058static int kex_choose_conf(struct ssh *);
markus@openbsd.org2ae666a2017-05-30 14:23:52 +000059static int kex_input_newkeys(int, u_int32_t, struct ssh *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000060
djm@openbsd.org9690b782015-08-21 23:57:48 +000061static const char *proposal_names[PROPOSAL_MAX] = {
62 "KEX algorithms",
63 "host key algorithms",
64 "ciphers ctos",
65 "ciphers stoc",
66 "MACs ctos",
67 "MACs stoc",
68 "compression ctos",
69 "compression stoc",
70 "languages ctos",
71 "languages stoc",
72};
73
Damien Millerea111192013-04-23 19:24:32 +100074struct kexalg {
75 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000076 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100077 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110078 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100079};
80static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100081#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110082 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +000083 { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
84 { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
85 { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
86 { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
Damien Millerb3051d02014-01-10 10:58:53 +110087 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100088#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110089 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100090#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100091#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +110092 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
93 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
94 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
95 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +110096# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +110097 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
98 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +100099# endif /* OPENSSL_HAS_NISTP521 */
100#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000101#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100102#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100103 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.org04937662016-09-22 17:52:53 +0000104 { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100105#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Damien Millerb3051d02014-01-10 10:58:53 +1100106 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000107};
108
109char *
Damien Miller690d9892013-11-08 12:16:49 +1100110kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000111{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000112 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000113 size_t nlen, rlen = 0;
114 const struct kexalg *k;
115
116 for (k = kexalgs; k->name != NULL; k++) {
117 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100118 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000119 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000120 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
121 free(ret);
122 return NULL;
123 }
124 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000125 memcpy(ret + rlen, k->name, nlen + 1);
126 rlen += nlen;
127 }
128 return ret;
129}
130
131static const struct kexalg *
132kex_alg_by_name(const char *name)
133{
134 const struct kexalg *k;
135
136 for (k = kexalgs; k->name != NULL; k++) {
137 if (strcmp(k->name, name) == 0)
138 return k;
139 }
140 return NULL;
141}
142
Damien Millerd5f62bf2010-09-24 22:11:14 +1000143/* Validate KEX method name list */
144int
145kex_names_valid(const char *names)
146{
147 char *s, *cp, *p;
148
149 if (names == NULL || strcmp(names, "") == 0)
150 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000151 if ((s = cp = strdup(names)) == NULL)
152 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000153 for ((p = strsep(&cp, ",")); p && *p != '\0';
154 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000155 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000156 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000157 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000158 return 0;
159 }
160 }
161 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000162 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000163 return 1;
164}
165
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000166/*
167 * Concatenate algorithm names, avoiding duplicates in the process.
168 * Caller must free returned string.
169 */
170char *
171kex_names_cat(const char *a, const char *b)
172{
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000173 char *ret = NULL, *tmp = NULL, *cp, *p, *m;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000174 size_t len;
175
176 if (a == NULL || *a == '\0')
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000177 return strdup(b);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000178 if (b == NULL || *b == '\0')
179 return strdup(a);
180 if (strlen(b) > 1024*1024)
181 return NULL;
182 len = strlen(a) + strlen(b) + 2;
183 if ((tmp = cp = strdup(b)) == NULL ||
184 (ret = calloc(1, len)) == NULL) {
185 free(tmp);
186 return NULL;
187 }
188 strlcpy(ret, a, len);
189 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000190 if ((m = match_list(ret, p, NULL)) != NULL) {
191 free(m);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000192 continue; /* Algorithm already present */
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000193 }
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000194 if (strlcat(ret, ",", len) >= len ||
195 strlcat(ret, p, len) >= len) {
196 free(tmp);
197 free(ret);
198 return NULL; /* Shouldn't happen */
199 }
200 }
201 free(tmp);
202 return ret;
203}
204
205/*
206 * Assemble a list of algorithms from a default list and a string from a
207 * configuration file. The user-provided string may begin with '+' to
djm@openbsd.org68bc8cf2017-02-03 23:01:19 +0000208 * indicate that it should be appended to the default or '-' that the
209 * specified names should be removed.
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000210 */
211int
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000212kex_assemble_names(char **listp, const char *def, const char *all)
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000213{
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000214 char *cp, *tmp, *patterns;
215 char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
216 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000217
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000218 if (listp == NULL || *listp == NULL || **listp == '\0') {
219 if ((*listp = strdup(def)) == NULL)
220 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000221 return 0;
222 }
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000223
224 list = *listp;
225 *listp = NULL;
226 if (*list == '+') {
227 /* Append names to default list */
228 if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
229 r = SSH_ERR_ALLOC_FAIL;
230 goto fail;
231 }
232 free(list);
233 list = tmp;
234 } else if (*list == '-') {
235 /* Remove names from default list */
236 if ((*listp = match_filter_blacklist(def, list + 1)) == NULL) {
237 r = SSH_ERR_ALLOC_FAIL;
238 goto fail;
239 }
240 free(list);
241 /* filtering has already been done */
242 return 0;
243 } else {
244 /* Explicit list, overrides default - just use "list" as is */
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000245 }
246
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000247 /*
248 * The supplied names may be a pattern-list. For the -list case,
249 * the patterns are applied above. For the +list and explicit list
250 * cases we need to do it now.
251 */
252 ret = NULL;
253 if ((patterns = opatterns = strdup(list)) == NULL) {
254 r = SSH_ERR_ALLOC_FAIL;
255 goto fail;
256 }
257 /* Apply positive (i.e. non-negated) patterns from the list */
258 while ((cp = strsep(&patterns, ",")) != NULL) {
259 if (*cp == '!') {
260 /* negated matches are not supported here */
261 r = SSH_ERR_INVALID_ARGUMENT;
262 goto fail;
263 }
264 free(matching);
265 if ((matching = match_filter_whitelist(all, cp)) == NULL) {
266 r = SSH_ERR_ALLOC_FAIL;
267 goto fail;
268 }
269 if ((tmp = kex_names_cat(ret, matching)) == NULL) {
270 r = SSH_ERR_ALLOC_FAIL;
271 goto fail;
272 }
273 free(ret);
274 ret = tmp;
275 }
276 if (ret == NULL || *ret == '\0') {
277 /* An empty name-list is an error */
278 /* XXX better error code? */
279 r = SSH_ERR_INVALID_ARGUMENT;
280 goto fail;
281 }
282
283 /* success */
284 *listp = ret;
285 ret = NULL;
286 r = 0;
287
288 fail:
289 free(matching);
290 free(opatterns);
291 free(list);
292 free(ret);
293 return r;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000294}
295
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000296/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000297int
298kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000299{
Damien Millereccb9de2005-06-17 12:59:34 +1000300 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000301 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000302
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000303 sshbuf_reset(b);
304
Ben Lindstrom59971722002-03-27 17:42:57 +0000305 /*
306 * add a dummy cookie, the cookie will be overwritten by
307 * kex_send_kexinit(), each time a kexinit is set
308 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000309 for (i = 0; i < KEX_COOKIE_LEN; i++) {
310 if ((r = sshbuf_put_u8(b, 0)) != 0)
311 return r;
312 }
313 for (i = 0; i < PROPOSAL_MAX; i++) {
314 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
315 return r;
316 }
317 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
318 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
319 return r;
320 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000321}
322
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000323/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000324int
325kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000326{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000327 struct sshbuf *b = NULL;
328 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000329 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000330 char **proposal = NULL;
331 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000332
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000333 *propp = NULL;
334 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
335 return SSH_ERR_ALLOC_FAIL;
336 if ((b = sshbuf_fromb(raw)) == NULL) {
337 r = SSH_ERR_ALLOC_FAIL;
338 goto out;
339 }
340 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
341 goto out;
Damien Millerb1715dc2000-05-30 13:44:51 +1000342 /* extract kex init proposal strings */
343 for (i = 0; i < PROPOSAL_MAX; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000344 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
345 goto out;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000346 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000347 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000348 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000349 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
350 (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000351 goto out;
Damien Millerbabb47a2003-02-24 11:53:32 +1100352 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000353 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000354 debug2("first_kex_follows %d ", v);
355 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000356 r = 0;
357 *propp = proposal;
358 out:
359 if (r != 0 && proposal != NULL)
360 kex_prop_free(proposal);
361 sshbuf_free(b);
362 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000363}
364
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000365void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000366kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000367{
Damien Millereccb9de2005-06-17 12:59:34 +1000368 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000369
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000370 if (proposal == NULL)
371 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000372 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000373 free(proposal[i]);
374 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000375}
376
Darren Tucker0d0d1952007-06-05 18:23:28 +1000377/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000378static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000379kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
Damien Miller874d77b2000-10-14 16:23:11 +1100380{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000381 int r;
382
383 error("kex protocol error: type %d seq %u", type, seq);
384 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
385 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
386 (r = sshpkt_send(ssh)) != 0)
387 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000388 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100389}
390
Ben Lindstrombba81212001-06-25 05:01:22 +0000391static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000392kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000393{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000394 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100395 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000396}
397
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000398static int
399kex_send_ext_info(struct ssh *ssh)
400{
401 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000402 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000403
djm@openbsd.org183ba552017-03-10 04:07:20 +0000404 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000405 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000406 /* XXX filter algs list by allowed pubkey/hostbased types */
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000407 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
408 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
409 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000410 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000411 (r = sshpkt_send(ssh)) != 0)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000412 goto out;
413 /* success */
414 r = 0;
415 out:
416 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000417 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000418}
419
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000420int
421kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000422{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000423 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000424
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000425 kex_reset_dispatch(ssh);
426 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
427 (r = sshpkt_send(ssh)) != 0)
428 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000429 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000430 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000431 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000432 if (ssh->kex->ext_info_c)
433 if ((r = kex_send_ext_info(ssh)) != 0)
434 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000435 return 0;
436}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000437
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000438int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000439kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000440{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000441 struct kex *kex = ssh->kex;
442 u_int32_t i, ninfo;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000443 char *name;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000444 u_char *val;
445 size_t vlen;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000446 int r;
447
448 debug("SSH2_MSG_EXT_INFO received");
449 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
450 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
451 return r;
452 for (i = 0; i < ninfo; i++) {
453 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
454 return r;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000455 if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000456 free(name);
457 return r;
458 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000459 if (strcmp(name, "server-sig-algs") == 0) {
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000460 /* Ensure no \0 lurking in value */
461 if (memchr(val, '\0', vlen) != NULL) {
462 error("%s: nul byte in %s", __func__, name);
463 return SSH_ERR_INVALID_FORMAT;
464 }
465 debug("%s: %s=<%s>", __func__, name, val);
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000466 kex->server_sig_algs = val;
467 val = NULL;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000468 } else
469 debug("%s: %s (unrecognised)", __func__, name);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000470 free(name);
471 free(val);
472 }
473 return sshpkt_get_end(ssh);
474}
475
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000476static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000477kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000478{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000479 struct kex *kex = ssh->kex;
480 int r;
481
482 debug("SSH2_MSG_NEWKEYS received");
483 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000484 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000485 if ((r = sshpkt_get_end(ssh)) != 0)
486 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000487 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
488 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000489 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000490 sshbuf_reset(kex->peer);
491 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000492 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000493 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000494 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000495 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000496}
Damien Millera664e872000-04-16 11:52:47 +1000497
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000498int
499kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000500{
Ben Lindstrom59971722002-03-27 17:42:57 +0000501 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000502 struct kex *kex = ssh->kex;
503 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000504
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000505 if (kex == NULL)
506 return SSH_ERR_INTERNAL_ERROR;
507 if (kex->flags & KEX_INIT_SENT)
508 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000509 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000510
511 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000512 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
513 return SSH_ERR_INVALID_FORMAT;
514 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
515 return SSH_ERR_INTERNAL_ERROR;
516 arc4random_buf(cookie, KEX_COOKIE_LEN);
517
518 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
519 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
520 (r = sshpkt_send(ssh)) != 0)
521 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000522 debug("SSH2_MSG_KEXINIT sent");
523 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000524 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000525}
526
Darren Tucker0d0d1952007-06-05 18:23:28 +1000527/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000528int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000529kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000530{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000531 struct kex *kex = ssh->kex;
532 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000533 u_int i;
534 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000535 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000536
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000537 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000538 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000539 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000540
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000541 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000542 ptr = sshpkt_ptr(ssh, &dlen);
543 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
544 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000545
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000546 /* discard packet */
547 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000548 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
549 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000550 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000551 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
552 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000553 /*
554 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
555 * KEX method has the server move first, but a server might be using
556 * a custom method or one that we otherwise don't support. We should
557 * be prepared to remember first_kex_follows here so we can eat a
558 * packet later.
559 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
560 * for cases where the server *doesn't* go first. I guess we should
561 * ignore it when it is set for these cases, which is what we do now.
562 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000563 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
564 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
565 (r = sshpkt_get_end(ssh)) != 0)
566 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000567
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000568 if (!(kex->flags & KEX_INIT_SENT))
569 if ((r = kex_send_kexinit(ssh)) != 0)
570 return r;
571 if ((r = kex_choose_conf(ssh)) != 0)
572 return r;
573
574 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
575 return (kex->kex[kex->kex_type])(ssh);
576
577 return SSH_ERR_INTERNAL_ERROR;
578}
579
580int
581kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
582{
583 struct kex *kex;
584 int r;
585
586 *kexp = NULL;
587 if ((kex = calloc(1, sizeof(*kex))) == NULL)
588 return SSH_ERR_ALLOC_FAIL;
589 if ((kex->peer = sshbuf_new()) == NULL ||
590 (kex->my = sshbuf_new()) == NULL) {
591 r = SSH_ERR_ALLOC_FAIL;
592 goto out;
593 }
594 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
595 goto out;
596 kex->done = 0;
597 kex_reset_dispatch(ssh);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000598 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000599 r = 0;
600 *kexp = kex;
601 out:
602 if (r != 0)
603 kex_free(kex);
604 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000605}
606
markus@openbsd.org091c3022015-01-19 19:52:16 +0000607void
608kex_free_newkeys(struct newkeys *newkeys)
609{
610 if (newkeys == NULL)
611 return;
612 if (newkeys->enc.key) {
613 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
614 free(newkeys->enc.key);
615 newkeys->enc.key = NULL;
616 }
617 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000618 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000619 free(newkeys->enc.iv);
620 newkeys->enc.iv = NULL;
621 }
622 free(newkeys->enc.name);
623 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
624 free(newkeys->comp.name);
625 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
626 mac_clear(&newkeys->mac);
627 if (newkeys->mac.key) {
628 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
629 free(newkeys->mac.key);
630 newkeys->mac.key = NULL;
631 }
632 free(newkeys->mac.name);
633 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
634 explicit_bzero(newkeys, sizeof(*newkeys));
635 free(newkeys);
636}
637
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000638void
639kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000640{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000641 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000642
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000643#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000644 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100645#ifdef OPENSSL_HAS_ECC
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000646 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100647#endif /* OPENSSL_HAS_ECC */
648#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000649 for (mode = 0; mode < MODE_MAX; mode++) {
650 kex_free_newkeys(kex->newkeys[mode]);
651 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000652 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000653 sshbuf_free(kex->peer);
654 sshbuf_free(kex->my);
655 free(kex->session_id);
656 free(kex->client_version_string);
657 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000658 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000659 free(kex->hostkey_alg);
660 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000661 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000662}
663
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000664int
665kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000666{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000667 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000668
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000669 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
670 return r;
671 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
672 kex_free(ssh->kex);
673 ssh->kex = NULL;
674 return r;
Damien Millera664e872000-04-16 11:52:47 +1000675 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000676 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000677}
678
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000679/*
680 * Request key re-exchange, returns 0 on success or a ssherr.h error
681 * code otherwise. Must not be called if KEX is incomplete or in-progress.
682 */
683int
684kex_start_rekex(struct ssh *ssh)
685{
686 if (ssh->kex == NULL) {
687 error("%s: no kex", __func__);
688 return SSH_ERR_INTERNAL_ERROR;
689 }
690 if (ssh->kex->done == 0) {
691 error("%s: requested twice", __func__);
692 return SSH_ERR_INTERNAL_ERROR;
693 }
694 ssh->kex->done = 0;
695 return kex_send_kexinit(ssh);
696}
697
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000698static int
699choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000700{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000701 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000702
Damien Millera664e872000-04-16 11:52:47 +1000703 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000704 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000705 if ((enc->cipher = cipher_by_name(name)) == NULL) {
706 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000707 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000708 }
Damien Millera664e872000-04-16 11:52:47 +1000709 enc->name = name;
710 enc->enabled = 0;
711 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100712 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000713 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100714 enc->key_len = cipher_keylen(enc->cipher);
715 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000716 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000717}
Damien Miller4f7becb2006-03-26 14:10:14 +1100718
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719static int
720choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000721{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000722 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000723
Damien Millera664e872000-04-16 11:52:47 +1000724 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000725 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000726 if (mac_setup(mac, name) < 0) {
727 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000728 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000729 }
Damien Millera664e872000-04-16 11:52:47 +1000730 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000731 mac->key = NULL;
732 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000733 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000734}
Damien Miller4f7becb2006-03-26 14:10:14 +1100735
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000736static int
737choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000738{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000739 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000740
Damien Millera664e872000-04-16 11:52:47 +1000741 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000742 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000743 if (strcmp(name, "zlib@openssh.com") == 0) {
sf@openbsd.org168b46f2018-07-09 13:37:10 +0000744 comp->type = COMP_DELAYED;
745 } else if (strcmp(name, "zlib") == 0) {
sf@openbsd.orgab392672018-07-06 09:06:14 +0000746 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000747 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000748 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000749 } else {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000750 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000751 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000752 }
753 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000754 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000755}
Damien Miller4f7becb2006-03-26 14:10:14 +1100756
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000757static int
758choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000759{
Damien Millerea111192013-04-23 19:24:32 +1000760 const struct kexalg *kexalg;
761
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000762 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000763
djm@openbsd.org9690b782015-08-21 23:57:48 +0000764 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000765 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000766 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000767 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000768 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000769 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100770 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000771 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000772 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000773}
Damien Miller19bb3a52005-11-05 15:19:35 +1100774
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000775static int
776choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000777{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000778 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000779
djm@openbsd.org9690b782015-08-21 23:57:48 +0000780 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000781 k->hostkey_alg ? k->hostkey_alg : "(no match)");
782 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000783 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000784 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100785 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000786 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000787 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000788 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000789}
790
Damien Millera8e06ce2003-11-21 23:48:55 +1100791static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100792proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
793{
794 static int check[] = {
795 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
796 };
797 int *idx;
798 char *p;
799
800 for (idx = &check[0]; *idx != -1; idx++) {
801 if ((p = strchr(my[*idx], ',')) != NULL)
802 *p = '\0';
803 if ((p = strchr(peer[*idx], ',')) != NULL)
804 *p = '\0';
805 if (strcmp(my[*idx], peer[*idx]) != 0) {
806 debug2("proposal mismatch: my %s peer %s",
807 my[*idx], peer[*idx]);
808 return (0);
809 }
810 }
811 debug2("proposals match");
812 return (1);
813}
814
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000815static int
816kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000817{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000818 struct kex *kex = ssh->kex;
819 struct newkeys *newkeys;
820 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000821 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000822 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100823 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000824 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000825
djm@openbsd.org9690b782015-08-21 23:57:48 +0000826 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
827 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
828 goto out;
829 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
830 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000831 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000832
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000833 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000834 cprop=peer;
835 sprop=my;
836 } else {
837 cprop=my;
838 sprop=peer;
839 }
Damien Millera664e872000-04-16 11:52:47 +1000840
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000841 /* Check whether client supports ext_info_c */
842 if (kex->server) {
843 char *ext;
844
845 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000846 kex->ext_info_c = (ext != NULL);
847 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000848 }
849
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000850 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000851 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
852 sprop[PROPOSAL_KEX_ALGS])) != 0) {
853 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
854 peer[PROPOSAL_KEX_ALGS] = NULL;
855 goto out;
856 }
857 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
858 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
859 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
860 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
861 goto out;
862 }
Damien Millera664e872000-04-16 11:52:47 +1000863 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000864 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
865 r = SSH_ERR_ALLOC_FAIL;
866 goto out;
867 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000868 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000869 ctos = (!kex->server && mode == MODE_OUT) ||
870 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000871 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
872 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
873 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000874 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000875 sprop[nenc])) != 0) {
876 kex->failed_choice = peer[nenc];
877 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000878 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000879 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100880 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000881 /* ignore mac for authenticated encryption */
882 if (authlen == 0 &&
883 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000884 sprop[nmac])) != 0) {
885 kex->failed_choice = peer[nmac];
886 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000887 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000888 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000889 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000890 sprop[ncomp])) != 0) {
891 kex->failed_choice = peer[ncomp];
892 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000893 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000894 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000895 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000896 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000897 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100898 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000899 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000900 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100901 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000902 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000903 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000904 need = MAXIMUM(need, newkeys->enc.key_len);
905 need = MAXIMUM(need, newkeys->enc.block_size);
906 need = MAXIMUM(need, newkeys->enc.iv_len);
907 need = MAXIMUM(need, newkeys->mac.key_len);
908 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
909 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
910 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
911 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000912 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000913 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000914 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100915 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000916
Damien Millerbabb47a2003-02-24 11:53:32 +1100917 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000918 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000919 ssh->dispatch_skip_packets = 1;
920 r = 0;
921 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000922 kex_prop_free(my);
923 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000924 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000925}
926
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000927static int
928derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
929 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000930{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000931 struct kex *kex = ssh->kex;
932 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000933 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000934 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100935 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000936 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000937 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000938
Damien Millerb3051d02014-01-10 10:58:53 +1100939 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000940 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000941 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000942 r = SSH_ERR_ALLOC_FAIL;
943 goto out;
944 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000945
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000946 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000947 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
948 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100949 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
950 ssh_digest_update(hashctx, &c, 1) != 0 ||
951 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000952 kex->session_id_len) != 0 ||
953 ssh_digest_final(hashctx, digest, mdsz) != 0) {
954 r = SSH_ERR_LIBCRYPTO_ERROR;
955 goto out;
956 }
Damien Millerb3051d02014-01-10 10:58:53 +1100957 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000958 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000959
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000960 /*
961 * expand key:
962 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
963 * Key = K1 || K2 || ... || Kn
964 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000965 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000966 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
967 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100968 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000969 ssh_digest_update(hashctx, digest, have) != 0 ||
970 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
971 r = SSH_ERR_LIBCRYPTO_ERROR;
972 goto out;
973 }
Damien Millerb3051d02014-01-10 10:58:53 +1100974 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000975 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000976 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000977#ifdef DEBUG_KEX
978 fprintf(stderr, "key '%c'== ", c);
979 dump_digest("key", digest, need);
980#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000981 *keyp = digest;
982 digest = NULL;
983 r = 0;
984 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000985 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000986 ssh_digest_free(hashctx);
987 return r;
Damien Millera664e872000-04-16 11:52:47 +1000988}
989
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000990#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000991int
992kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
993 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000994{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000995 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000996 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000997 u_int i, j, mode, ctos;
998 int r;
Damien Millera664e872000-04-16 11:52:47 +1000999
Damien Miller19bb3a52005-11-05 15:19:35 +11001000 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001001 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1002 shared_secret, &keys[i])) != 0) {
1003 for (j = 0; j < i; j++)
1004 free(keys[j]);
1005 return r;
1006 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001007 }
Damien Millera664e872000-04-16 11:52:47 +10001008 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001009 ctos = (!kex->server && mode == MODE_OUT) ||
1010 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001011 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1012 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1013 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001014 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001015 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001016}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001017
Damien Miller1f0311c2014-05-15 14:24:09 +10001018#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001019int
1020kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
1021 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +11001022{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001023 struct sshbuf *shared_secret;
1024 int r;
Damien Miller91b580e2014-01-12 19:21:22 +11001025
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001026 if ((shared_secret = sshbuf_new()) == NULL)
1027 return SSH_ERR_ALLOC_FAIL;
1028 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
1029 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
1030 sshbuf_free(shared_secret);
1031 return r;
Damien Miller91b580e2014-01-12 19:21:22 +11001032}
Damien Miller1f0311c2014-05-15 14:24:09 +10001033#endif
Damien Miller91b580e2014-01-12 19:21:22 +11001034
Darren Tuckere14e0052004-05-13 16:30:44 +10001035
Damien Millereb8b60e2010-08-31 22:41:14 +10001036#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001037void
1038dump_digest(char *msg, u_char *digest, int len)
1039{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001040 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001041 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001042}
1043#endif