blob: d5d5a9dae996072e779597ccfa805b5e0bc8fc83 [file] [log] [blame]
djm@openbsd.orgc9480302017-06-13 12:13:59 +00001/* $OpenBSD: kex.c,v 1.134 2017/06/13 12:13:59 djm Exp $ */
Damien Millera664e872000-04-16 11:52:47 +10002/*
Ben Lindstrom44697232001-07-04 03:32:30 +00003 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millera664e872000-04-16 11:52:47 +10004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
Damien Millera664e872000-04-16 11:52:47 +100013 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millera664e872000-04-16 11:52:47 +100027
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')
177 return NULL;
178 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
212kex_assemble_names(const char *def, char **list)
213{
214 char *ret;
215
216 if (list == NULL || *list == NULL || **list == '\0') {
217 *list = strdup(def);
218 return 0;
219 }
djm@openbsd.org68bc8cf2017-02-03 23:01:19 +0000220 if (**list == '+') {
221 if ((ret = kex_names_cat(def, *list + 1)) == NULL)
222 return SSH_ERR_ALLOC_FAIL;
223 free(*list);
224 *list = ret;
225 } else if (**list == '-') {
226 if ((ret = match_filter_list(def, *list + 1)) == NULL)
227 return SSH_ERR_ALLOC_FAIL;
228 free(*list);
229 *list = ret;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000230 }
231
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000232 return 0;
233}
234
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000235/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000236int
237kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000238{
Damien Millereccb9de2005-06-17 12:59:34 +1000239 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000240 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000241
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000242 sshbuf_reset(b);
243
Ben Lindstrom59971722002-03-27 17:42:57 +0000244 /*
245 * add a dummy cookie, the cookie will be overwritten by
246 * kex_send_kexinit(), each time a kexinit is set
247 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000248 for (i = 0; i < KEX_COOKIE_LEN; i++) {
249 if ((r = sshbuf_put_u8(b, 0)) != 0)
250 return r;
251 }
252 for (i = 0; i < PROPOSAL_MAX; i++) {
253 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
254 return r;
255 }
256 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
257 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
258 return r;
259 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000260}
261
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000262/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000263int
264kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000265{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000266 struct sshbuf *b = NULL;
267 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000268 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000269 char **proposal = NULL;
270 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000271
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000272 *propp = NULL;
273 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
274 return SSH_ERR_ALLOC_FAIL;
275 if ((b = sshbuf_fromb(raw)) == NULL) {
276 r = SSH_ERR_ALLOC_FAIL;
277 goto out;
278 }
279 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
280 goto out;
Damien Millerb1715dc2000-05-30 13:44:51 +1000281 /* extract kex init proposal strings */
282 for (i = 0; i < PROPOSAL_MAX; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000283 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
284 goto out;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000285 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000286 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000287 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000288 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
289 (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000290 goto out;
Damien Millerbabb47a2003-02-24 11:53:32 +1100291 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000292 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000293 debug2("first_kex_follows %d ", v);
294 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000295 r = 0;
296 *propp = proposal;
297 out:
298 if (r != 0 && proposal != NULL)
299 kex_prop_free(proposal);
300 sshbuf_free(b);
301 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000302}
303
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000304void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000305kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000306{
Damien Millereccb9de2005-06-17 12:59:34 +1000307 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000308
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000309 if (proposal == NULL)
310 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000311 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000312 free(proposal[i]);
313 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000314}
315
Darren Tucker0d0d1952007-06-05 18:23:28 +1000316/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000317static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000318kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
Damien Miller874d77b2000-10-14 16:23:11 +1100319{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000320 int r;
321
322 error("kex protocol error: type %d seq %u", type, seq);
323 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
324 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
325 (r = sshpkt_send(ssh)) != 0)
326 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000327 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100328}
329
Ben Lindstrombba81212001-06-25 05:01:22 +0000330static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000331kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000332{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000333 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100334 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000335}
336
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000337static int
338kex_send_ext_info(struct ssh *ssh)
339{
340 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000341 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000342
djm@openbsd.org183ba552017-03-10 04:07:20 +0000343 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000344 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000345 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
346 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
347 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000348 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000349 (r = sshpkt_send(ssh)) != 0)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000350 goto out;
351 /* success */
352 r = 0;
353 out:
354 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000355 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000356}
357
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000358int
359kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000360{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000361 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000362
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000363 kex_reset_dispatch(ssh);
364 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
365 (r = sshpkt_send(ssh)) != 0)
366 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000367 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000368 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000369 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000370 if (ssh->kex->ext_info_c)
371 if ((r = kex_send_ext_info(ssh)) != 0)
372 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000373 return 0;
374}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000375
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000376int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000377kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000378{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000379 struct kex *kex = ssh->kex;
380 u_int32_t i, ninfo;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000381 char *name, *found;
382 u_char *val;
383 size_t vlen;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000384 int r;
385
386 debug("SSH2_MSG_EXT_INFO received");
387 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
388 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
389 return r;
390 for (i = 0; i < ninfo; i++) {
391 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
392 return r;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000393 if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000394 free(name);
395 return r;
396 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000397 if (strcmp(name, "server-sig-algs") == 0) {
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000398 /* Ensure no \0 lurking in value */
399 if (memchr(val, '\0', vlen) != NULL) {
400 error("%s: nul byte in %s", __func__, name);
401 return SSH_ERR_INVALID_FORMAT;
402 }
403 debug("%s: %s=<%s>", __func__, name, val);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000404 found = match_list("rsa-sha2-256", val, NULL);
405 if (found) {
406 kex->rsa_sha2 = 256;
407 free(found);
408 }
409 found = match_list("rsa-sha2-512", val, NULL);
410 if (found) {
411 kex->rsa_sha2 = 512;
412 free(found);
413 }
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000414 } else
415 debug("%s: %s (unrecognised)", __func__, name);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000416 free(name);
417 free(val);
418 }
419 return sshpkt_get_end(ssh);
420}
421
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000422static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000423kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000424{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000425 struct kex *kex = ssh->kex;
426 int r;
427
428 debug("SSH2_MSG_NEWKEYS received");
429 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000430 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000431 if ((r = sshpkt_get_end(ssh)) != 0)
432 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000433 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
434 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000435 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000436 sshbuf_reset(kex->peer);
437 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000438 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000439 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000440 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000441 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000442}
Damien Millera664e872000-04-16 11:52:47 +1000443
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000444int
445kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000446{
Ben Lindstrom59971722002-03-27 17:42:57 +0000447 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000448 struct kex *kex = ssh->kex;
449 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000450
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000451 if (kex == NULL)
452 return SSH_ERR_INTERNAL_ERROR;
453 if (kex->flags & KEX_INIT_SENT)
454 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000455 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000456
457 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000458 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
459 return SSH_ERR_INVALID_FORMAT;
460 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
461 return SSH_ERR_INTERNAL_ERROR;
462 arc4random_buf(cookie, KEX_COOKIE_LEN);
463
464 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
465 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
466 (r = sshpkt_send(ssh)) != 0)
467 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000468 debug("SSH2_MSG_KEXINIT sent");
469 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000470 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000471}
472
Darren Tucker0d0d1952007-06-05 18:23:28 +1000473/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000474int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000475kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000476{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000477 struct kex *kex = ssh->kex;
478 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000479 u_int i;
480 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000481 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000482
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000483 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000484 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000485 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000486
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000487 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000488 ptr = sshpkt_ptr(ssh, &dlen);
489 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
490 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000491
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000492 /* discard packet */
493 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000494 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
495 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000496 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000497 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
498 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000499 /*
500 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
501 * KEX method has the server move first, but a server might be using
502 * a custom method or one that we otherwise don't support. We should
503 * be prepared to remember first_kex_follows here so we can eat a
504 * packet later.
505 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
506 * for cases where the server *doesn't* go first. I guess we should
507 * ignore it when it is set for these cases, which is what we do now.
508 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000509 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
510 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
511 (r = sshpkt_get_end(ssh)) != 0)
512 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000513
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000514 if (!(kex->flags & KEX_INIT_SENT))
515 if ((r = kex_send_kexinit(ssh)) != 0)
516 return r;
517 if ((r = kex_choose_conf(ssh)) != 0)
518 return r;
519
520 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
521 return (kex->kex[kex->kex_type])(ssh);
522
523 return SSH_ERR_INTERNAL_ERROR;
524}
525
526int
527kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
528{
529 struct kex *kex;
530 int r;
531
532 *kexp = NULL;
533 if ((kex = calloc(1, sizeof(*kex))) == NULL)
534 return SSH_ERR_ALLOC_FAIL;
535 if ((kex->peer = sshbuf_new()) == NULL ||
536 (kex->my = sshbuf_new()) == NULL) {
537 r = SSH_ERR_ALLOC_FAIL;
538 goto out;
539 }
540 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
541 goto out;
542 kex->done = 0;
543 kex_reset_dispatch(ssh);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000544 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000545 r = 0;
546 *kexp = kex;
547 out:
548 if (r != 0)
549 kex_free(kex);
550 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000551}
552
markus@openbsd.org091c3022015-01-19 19:52:16 +0000553void
554kex_free_newkeys(struct newkeys *newkeys)
555{
556 if (newkeys == NULL)
557 return;
558 if (newkeys->enc.key) {
559 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
560 free(newkeys->enc.key);
561 newkeys->enc.key = NULL;
562 }
563 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000564 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000565 free(newkeys->enc.iv);
566 newkeys->enc.iv = NULL;
567 }
568 free(newkeys->enc.name);
569 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
570 free(newkeys->comp.name);
571 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
572 mac_clear(&newkeys->mac);
573 if (newkeys->mac.key) {
574 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
575 free(newkeys->mac.key);
576 newkeys->mac.key = NULL;
577 }
578 free(newkeys->mac.name);
579 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
580 explicit_bzero(newkeys, sizeof(*newkeys));
581 free(newkeys);
582}
583
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000584void
585kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000586{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000587 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000588
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000589#ifdef WITH_OPENSSL
590 if (kex->dh)
591 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100592#ifdef OPENSSL_HAS_ECC
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000593 if (kex->ec_client_key)
594 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100595#endif /* OPENSSL_HAS_ECC */
596#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000597 for (mode = 0; mode < MODE_MAX; mode++) {
598 kex_free_newkeys(kex->newkeys[mode]);
599 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000600 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000601 sshbuf_free(kex->peer);
602 sshbuf_free(kex->my);
603 free(kex->session_id);
604 free(kex->client_version_string);
605 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000606 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000607 free(kex->hostkey_alg);
608 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000609 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000610}
611
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000612int
613kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000614{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000615 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000616
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000617 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
618 return r;
619 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
620 kex_free(ssh->kex);
621 ssh->kex = NULL;
622 return r;
Damien Millera664e872000-04-16 11:52:47 +1000623 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000624 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000625}
626
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000627/*
628 * Request key re-exchange, returns 0 on success or a ssherr.h error
629 * code otherwise. Must not be called if KEX is incomplete or in-progress.
630 */
631int
632kex_start_rekex(struct ssh *ssh)
633{
634 if (ssh->kex == NULL) {
635 error("%s: no kex", __func__);
636 return SSH_ERR_INTERNAL_ERROR;
637 }
638 if (ssh->kex->done == 0) {
639 error("%s: requested twice", __func__);
640 return SSH_ERR_INTERNAL_ERROR;
641 }
642 ssh->kex->done = 0;
643 return kex_send_kexinit(ssh);
644}
645
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000646static int
647choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000648{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000649 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000650
Damien Millera664e872000-04-16 11:52:47 +1000651 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000652 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000653 if ((enc->cipher = cipher_by_name(name)) == NULL) {
654 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000655 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000656 }
Damien Millera664e872000-04-16 11:52:47 +1000657 enc->name = name;
658 enc->enabled = 0;
659 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100660 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000661 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100662 enc->key_len = cipher_keylen(enc->cipher);
663 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000664 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000665}
Damien Miller4f7becb2006-03-26 14:10:14 +1100666
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000667static int
668choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000669{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000670 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000671
Damien Millera664e872000-04-16 11:52:47 +1000672 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000673 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000674 if (mac_setup(mac, name) < 0) {
675 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000676 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000677 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000678 /* truncate the key */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000679 if (ssh->compat & SSH_BUG_HMAC)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000680 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000681 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000682 mac->key = NULL;
683 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000684 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000685}
Damien Miller4f7becb2006-03-26 14:10:14 +1100686
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000687static int
688choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000689{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000690 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000691
Damien Millera664e872000-04-16 11:52:47 +1000692 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000693 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000694 if (strcmp(name, "zlib@openssh.com") == 0) {
695 comp->type = COMP_DELAYED;
djm@openbsd.orgb7689152016-09-28 21:44:52 +0000696 } else if (strcmp(name, "zlib") == 0) {
697 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000698 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000699 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000700 } else {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000701 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000702 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000703 }
704 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000705 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000706}
Damien Miller4f7becb2006-03-26 14:10:14 +1100707
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000708static int
709choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000710{
Damien Millerea111192013-04-23 19:24:32 +1000711 const struct kexalg *kexalg;
712
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000713 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000714
djm@openbsd.org9690b782015-08-21 23:57:48 +0000715 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000716 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000717 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000718 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000720 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100721 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000722 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000723 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000724}
Damien Miller19bb3a52005-11-05 15:19:35 +1100725
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000726static int
727choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000728{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000729 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000730
djm@openbsd.org9690b782015-08-21 23:57:48 +0000731 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000732 k->hostkey_alg ? k->hostkey_alg : "(no match)");
733 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000734 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000735 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100736 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000737 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000738 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000739 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000740}
741
Damien Millera8e06ce2003-11-21 23:48:55 +1100742static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100743proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
744{
745 static int check[] = {
746 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
747 };
748 int *idx;
749 char *p;
750
751 for (idx = &check[0]; *idx != -1; idx++) {
752 if ((p = strchr(my[*idx], ',')) != NULL)
753 *p = '\0';
754 if ((p = strchr(peer[*idx], ',')) != NULL)
755 *p = '\0';
756 if (strcmp(my[*idx], peer[*idx]) != 0) {
757 debug2("proposal mismatch: my %s peer %s",
758 my[*idx], peer[*idx]);
759 return (0);
760 }
761 }
762 debug2("proposals match");
763 return (1);
764}
765
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000766static int
767kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000768{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000769 struct kex *kex = ssh->kex;
770 struct newkeys *newkeys;
771 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000772 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000773 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100774 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000775 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000776
djm@openbsd.org9690b782015-08-21 23:57:48 +0000777 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
778 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
779 goto out;
780 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
781 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000782 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000783
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000784 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000785 cprop=peer;
786 sprop=my;
787 } else {
788 cprop=my;
789 sprop=peer;
790 }
Damien Millera664e872000-04-16 11:52:47 +1000791
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000792 /* Check whether client supports ext_info_c */
793 if (kex->server) {
794 char *ext;
795
796 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000797 kex->ext_info_c = (ext != NULL);
798 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000799 }
800
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000801 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000802 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
803 sprop[PROPOSAL_KEX_ALGS])) != 0) {
804 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
805 peer[PROPOSAL_KEX_ALGS] = NULL;
806 goto out;
807 }
808 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
809 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
810 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
811 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
812 goto out;
813 }
Damien Millera664e872000-04-16 11:52:47 +1000814 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000815 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
816 r = SSH_ERR_ALLOC_FAIL;
817 goto out;
818 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000819 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000820 ctos = (!kex->server && mode == MODE_OUT) ||
821 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000822 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
823 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
824 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000825 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000826 sprop[nenc])) != 0) {
827 kex->failed_choice = peer[nenc];
828 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000829 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000830 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100831 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000832 /* ignore mac for authenticated encryption */
833 if (authlen == 0 &&
834 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000835 sprop[nmac])) != 0) {
836 kex->failed_choice = peer[nmac];
837 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000838 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000839 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000840 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000841 sprop[ncomp])) != 0) {
842 kex->failed_choice = peer[ncomp];
843 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000844 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000845 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000846 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000847 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000848 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100849 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000850 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000851 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100852 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000853 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000854 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000855 need = MAXIMUM(need, newkeys->enc.key_len);
856 need = MAXIMUM(need, newkeys->enc.block_size);
857 need = MAXIMUM(need, newkeys->enc.iv_len);
858 need = MAXIMUM(need, newkeys->mac.key_len);
859 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
860 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
861 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
862 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000863 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000864 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000865 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100866 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000867
Damien Millerbabb47a2003-02-24 11:53:32 +1100868 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100869 if (first_kex_follows && !proposals_match(my, peer) &&
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000870 !(ssh->compat & SSH_BUG_FIRSTKEX))
871 ssh->dispatch_skip_packets = 1;
872 r = 0;
873 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000874 kex_prop_free(my);
875 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000876 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000877}
878
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000879static int
880derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
881 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000882{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000883 struct kex *kex = ssh->kex;
884 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000885 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000886 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100887 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000888 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000889 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000890
Damien Millerb3051d02014-01-10 10:58:53 +1100891 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000892 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000893 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000894 r = SSH_ERR_ALLOC_FAIL;
895 goto out;
896 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000897
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000898 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000899 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
900 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100901 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
902 ssh_digest_update(hashctx, &c, 1) != 0 ||
903 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000904 kex->session_id_len) != 0 ||
905 ssh_digest_final(hashctx, digest, mdsz) != 0) {
906 r = SSH_ERR_LIBCRYPTO_ERROR;
907 goto out;
908 }
Damien Millerb3051d02014-01-10 10:58:53 +1100909 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000910 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000911
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000912 /*
913 * expand key:
914 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
915 * Key = K1 || K2 || ... || Kn
916 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000917 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000918 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
919 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100920 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000921 ssh_digest_update(hashctx, digest, have) != 0 ||
922 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
923 r = SSH_ERR_LIBCRYPTO_ERROR;
924 goto out;
925 }
Damien Millerb3051d02014-01-10 10:58:53 +1100926 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000927 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000928 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000929#ifdef DEBUG_KEX
930 fprintf(stderr, "key '%c'== ", c);
931 dump_digest("key", digest, need);
932#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000933 *keyp = digest;
934 digest = NULL;
935 r = 0;
936 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000937 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000938 ssh_digest_free(hashctx);
939 return r;
Damien Millera664e872000-04-16 11:52:47 +1000940}
941
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000942#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000943int
944kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
945 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000946{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000947 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000948 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000949 u_int i, j, mode, ctos;
950 int r;
Damien Millera664e872000-04-16 11:52:47 +1000951
Damien Miller19bb3a52005-11-05 15:19:35 +1100952 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000953 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
954 shared_secret, &keys[i])) != 0) {
955 for (j = 0; j < i; j++)
956 free(keys[j]);
957 return r;
958 }
Damien Miller19bb3a52005-11-05 15:19:35 +1100959 }
Damien Millera664e872000-04-16 11:52:47 +1000960 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +1100961 ctos = (!kex->server && mode == MODE_OUT) ||
962 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000963 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
964 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
965 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000966 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000967 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000968}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000969
Damien Miller1f0311c2014-05-15 14:24:09 +1000970#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000971int
972kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
973 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +1100974{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000975 struct sshbuf *shared_secret;
976 int r;
Damien Miller91b580e2014-01-12 19:21:22 +1100977
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000978 if ((shared_secret = sshbuf_new()) == NULL)
979 return SSH_ERR_ALLOC_FAIL;
980 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
981 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
982 sshbuf_free(shared_secret);
983 return r;
Damien Miller91b580e2014-01-12 19:21:22 +1100984}
Damien Miller1f0311c2014-05-15 14:24:09 +1000985#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100986
Darren Tuckere14e0052004-05-13 16:30:44 +1000987
Damien Millereb8b60e2010-08-31 22:41:14 +1000988#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000989void
990dump_digest(char *msg, u_char *digest, int len)
991{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000992 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000993 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000994}
995#endif