blob: 9a40759c19e16d1407cecabbd1b5fd4d99bf1717 [file] [log] [blame]
djm@openbsd.org56912de2017-04-30 23:10:43 +00001/* $OpenBSD: kex.c,v 1.132 2017/04/30 23:10:43 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 *);
59static int kex_input_newkeys(int, u_int32_t, void *);
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
Damien Miller630d6f42002-01-22 23:17:30 +1100318kex_protocol_error(int type, u_int32_t seq, void *ctxt)
Damien Miller874d77b2000-10-14 16:23:11 +1100319{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000320 struct ssh *ssh = active_state; /* XXX */
321 int r;
322
323 error("kex protocol error: type %d seq %u", type, seq);
324 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
325 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
326 (r = sshpkt_send(ssh)) != 0)
327 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000328 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100329}
330
Ben Lindstrombba81212001-06-25 05:01:22 +0000331static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000332kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000333{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000334 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100335 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000336}
337
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000338static int
339kex_send_ext_info(struct ssh *ssh)
340{
341 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000342 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000343
djm@openbsd.org183ba552017-03-10 04:07:20 +0000344 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000345 return SSH_ERR_ALLOC_FAIL;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000346 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
347 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
348 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000349 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000350 (r = sshpkt_send(ssh)) != 0)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000351 goto out;
352 /* success */
353 r = 0;
354 out:
355 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000356 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000357}
358
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000359int
360kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000361{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000362 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000363
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000364 kex_reset_dispatch(ssh);
365 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
366 (r = sshpkt_send(ssh)) != 0)
367 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000368 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000369 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000370 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000371 if (ssh->kex->ext_info_c)
372 if ((r = kex_send_ext_info(ssh)) != 0)
373 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000374 return 0;
375}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000376
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000377int
378kex_input_ext_info(int type, u_int32_t seq, void *ctxt)
379{
380 struct ssh *ssh = ctxt;
381 struct kex *kex = ssh->kex;
382 u_int32_t i, ninfo;
383 char *name, *val, *found;
384 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;
393 if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) {
394 free(name);
395 return r;
396 }
397 debug("%s: %s=<%s>", __func__, name, val);
398 if (strcmp(name, "server-sig-algs") == 0) {
399 found = match_list("rsa-sha2-256", val, NULL);
400 if (found) {
401 kex->rsa_sha2 = 256;
402 free(found);
403 }
404 found = match_list("rsa-sha2-512", val, NULL);
405 if (found) {
406 kex->rsa_sha2 = 512;
407 free(found);
408 }
409 }
410 free(name);
411 free(val);
412 }
413 return sshpkt_get_end(ssh);
414}
415
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000416static int
417kex_input_newkeys(int type, u_int32_t seq, void *ctxt)
418{
419 struct ssh *ssh = ctxt;
420 struct kex *kex = ssh->kex;
421 int r;
422
423 debug("SSH2_MSG_NEWKEYS received");
424 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000425 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000426 if ((r = sshpkt_get_end(ssh)) != 0)
427 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000428 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
429 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000430 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000431 sshbuf_reset(kex->peer);
432 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000433 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000434 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000435 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000436 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000437}
Damien Millera664e872000-04-16 11:52:47 +1000438
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000439int
440kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000441{
Ben Lindstrom59971722002-03-27 17:42:57 +0000442 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000443 struct kex *kex = ssh->kex;
444 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000445
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000446 if (kex == NULL)
447 return SSH_ERR_INTERNAL_ERROR;
448 if (kex->flags & KEX_INIT_SENT)
449 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000450 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000451
452 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000453 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
454 return SSH_ERR_INVALID_FORMAT;
455 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
456 return SSH_ERR_INTERNAL_ERROR;
457 arc4random_buf(cookie, KEX_COOKIE_LEN);
458
459 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
460 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
461 (r = sshpkt_send(ssh)) != 0)
462 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000463 debug("SSH2_MSG_KEXINIT sent");
464 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000465 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000466}
467
Darren Tucker0d0d1952007-06-05 18:23:28 +1000468/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000469int
Damien Miller630d6f42002-01-22 23:17:30 +1100470kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000471{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000472 struct ssh *ssh = ctxt;
473 struct kex *kex = ssh->kex;
474 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000475 u_int i;
476 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000477 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000478
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000479 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000480 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000481 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000482
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000483 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000484 ptr = sshpkt_ptr(ssh, &dlen);
485 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
486 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000487
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000488 /* discard packet */
489 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000490 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
491 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000492 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000493 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
494 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000495 /*
496 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
497 * KEX method has the server move first, but a server might be using
498 * a custom method or one that we otherwise don't support. We should
499 * be prepared to remember first_kex_follows here so we can eat a
500 * packet later.
501 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
502 * for cases where the server *doesn't* go first. I guess we should
503 * ignore it when it is set for these cases, which is what we do now.
504 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000505 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
506 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
507 (r = sshpkt_get_end(ssh)) != 0)
508 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000509
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000510 if (!(kex->flags & KEX_INIT_SENT))
511 if ((r = kex_send_kexinit(ssh)) != 0)
512 return r;
513 if ((r = kex_choose_conf(ssh)) != 0)
514 return r;
515
516 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
517 return (kex->kex[kex->kex_type])(ssh);
518
519 return SSH_ERR_INTERNAL_ERROR;
520}
521
522int
523kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
524{
525 struct kex *kex;
526 int r;
527
528 *kexp = NULL;
529 if ((kex = calloc(1, sizeof(*kex))) == NULL)
530 return SSH_ERR_ALLOC_FAIL;
531 if ((kex->peer = sshbuf_new()) == NULL ||
532 (kex->my = sshbuf_new()) == NULL) {
533 r = SSH_ERR_ALLOC_FAIL;
534 goto out;
535 }
536 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
537 goto out;
538 kex->done = 0;
539 kex_reset_dispatch(ssh);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000540 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000541 r = 0;
542 *kexp = kex;
543 out:
544 if (r != 0)
545 kex_free(kex);
546 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000547}
548
markus@openbsd.org091c3022015-01-19 19:52:16 +0000549void
550kex_free_newkeys(struct newkeys *newkeys)
551{
552 if (newkeys == NULL)
553 return;
554 if (newkeys->enc.key) {
555 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
556 free(newkeys->enc.key);
557 newkeys->enc.key = NULL;
558 }
559 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000560 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000561 free(newkeys->enc.iv);
562 newkeys->enc.iv = NULL;
563 }
564 free(newkeys->enc.name);
565 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
566 free(newkeys->comp.name);
567 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
568 mac_clear(&newkeys->mac);
569 if (newkeys->mac.key) {
570 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
571 free(newkeys->mac.key);
572 newkeys->mac.key = NULL;
573 }
574 free(newkeys->mac.name);
575 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
576 explicit_bzero(newkeys, sizeof(*newkeys));
577 free(newkeys);
578}
579
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000580void
581kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000582{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000583 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000584
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000585#ifdef WITH_OPENSSL
586 if (kex->dh)
587 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100588#ifdef OPENSSL_HAS_ECC
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000589 if (kex->ec_client_key)
590 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100591#endif /* OPENSSL_HAS_ECC */
592#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000593 for (mode = 0; mode < MODE_MAX; mode++) {
594 kex_free_newkeys(kex->newkeys[mode]);
595 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000596 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000597 sshbuf_free(kex->peer);
598 sshbuf_free(kex->my);
599 free(kex->session_id);
600 free(kex->client_version_string);
601 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000602 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000603 free(kex->hostkey_alg);
604 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000605 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000606}
607
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000608int
609kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000610{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000611 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000612
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000613 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
614 return r;
615 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
616 kex_free(ssh->kex);
617 ssh->kex = NULL;
618 return r;
Damien Millera664e872000-04-16 11:52:47 +1000619 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000620 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000621}
622
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000623/*
624 * Request key re-exchange, returns 0 on success or a ssherr.h error
625 * code otherwise. Must not be called if KEX is incomplete or in-progress.
626 */
627int
628kex_start_rekex(struct ssh *ssh)
629{
630 if (ssh->kex == NULL) {
631 error("%s: no kex", __func__);
632 return SSH_ERR_INTERNAL_ERROR;
633 }
634 if (ssh->kex->done == 0) {
635 error("%s: requested twice", __func__);
636 return SSH_ERR_INTERNAL_ERROR;
637 }
638 ssh->kex->done = 0;
639 return kex_send_kexinit(ssh);
640}
641
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000642static int
643choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000644{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000645 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000646
Damien Millera664e872000-04-16 11:52:47 +1000647 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000648 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000649 if ((enc->cipher = cipher_by_name(name)) == NULL) {
650 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000651 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000652 }
Damien Millera664e872000-04-16 11:52:47 +1000653 enc->name = name;
654 enc->enabled = 0;
655 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100656 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000657 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100658 enc->key_len = cipher_keylen(enc->cipher);
659 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000660 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000661}
Damien Miller4f7becb2006-03-26 14:10:14 +1100662
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000663static int
664choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000665{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000666 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000667
Damien Millera664e872000-04-16 11:52:47 +1000668 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000669 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000670 if (mac_setup(mac, name) < 0) {
671 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000672 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000673 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000674 /* truncate the key */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000675 if (ssh->compat & SSH_BUG_HMAC)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000676 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000677 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000678 mac->key = NULL;
679 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000680 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000681}
Damien Miller4f7becb2006-03-26 14:10:14 +1100682
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000683static int
684choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000685{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000686 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000687
Damien Millera664e872000-04-16 11:52:47 +1000688 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000689 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000690 if (strcmp(name, "zlib@openssh.com") == 0) {
691 comp->type = COMP_DELAYED;
djm@openbsd.orgb7689152016-09-28 21:44:52 +0000692 } else if (strcmp(name, "zlib") == 0) {
693 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000694 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000695 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000696 } else {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000697 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000698 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000699 }
700 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000701 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000702}
Damien Miller4f7becb2006-03-26 14:10:14 +1100703
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000704static int
705choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000706{
Damien Millerea111192013-04-23 19:24:32 +1000707 const struct kexalg *kexalg;
708
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000709 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000710
djm@openbsd.org9690b782015-08-21 23:57:48 +0000711 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000712 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000713 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000714 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000715 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000716 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100717 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000718 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000720}
Damien Miller19bb3a52005-11-05 15:19:35 +1100721
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000722static int
723choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000724{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000725 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000726
djm@openbsd.org9690b782015-08-21 23:57:48 +0000727 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000728 k->hostkey_alg ? k->hostkey_alg : "(no match)");
729 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000730 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000731 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100732 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000733 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000734 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000735 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000736}
737
Damien Millera8e06ce2003-11-21 23:48:55 +1100738static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100739proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
740{
741 static int check[] = {
742 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
743 };
744 int *idx;
745 char *p;
746
747 for (idx = &check[0]; *idx != -1; idx++) {
748 if ((p = strchr(my[*idx], ',')) != NULL)
749 *p = '\0';
750 if ((p = strchr(peer[*idx], ',')) != NULL)
751 *p = '\0';
752 if (strcmp(my[*idx], peer[*idx]) != 0) {
753 debug2("proposal mismatch: my %s peer %s",
754 my[*idx], peer[*idx]);
755 return (0);
756 }
757 }
758 debug2("proposals match");
759 return (1);
760}
761
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000762static int
763kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000764{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000765 struct kex *kex = ssh->kex;
766 struct newkeys *newkeys;
767 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000768 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000769 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100770 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000771 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000772
djm@openbsd.org9690b782015-08-21 23:57:48 +0000773 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
774 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
775 goto out;
776 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
777 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000778 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000779
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000780 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000781 cprop=peer;
782 sprop=my;
783 } else {
784 cprop=my;
785 sprop=peer;
786 }
Damien Millera664e872000-04-16 11:52:47 +1000787
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000788 /* Check whether client supports ext_info_c */
789 if (kex->server) {
790 char *ext;
791
792 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000793 kex->ext_info_c = (ext != NULL);
794 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000795 }
796
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000797 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000798 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
799 sprop[PROPOSAL_KEX_ALGS])) != 0) {
800 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
801 peer[PROPOSAL_KEX_ALGS] = NULL;
802 goto out;
803 }
804 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
805 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
806 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
807 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
808 goto out;
809 }
Damien Millera664e872000-04-16 11:52:47 +1000810 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000811 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
812 r = SSH_ERR_ALLOC_FAIL;
813 goto out;
814 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000815 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000816 ctos = (!kex->server && mode == MODE_OUT) ||
817 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000818 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
819 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
820 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000821 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000822 sprop[nenc])) != 0) {
823 kex->failed_choice = peer[nenc];
824 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000825 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000826 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100827 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000828 /* ignore mac for authenticated encryption */
829 if (authlen == 0 &&
830 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000831 sprop[nmac])) != 0) {
832 kex->failed_choice = peer[nmac];
833 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000834 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000835 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000836 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000837 sprop[ncomp])) != 0) {
838 kex->failed_choice = peer[ncomp];
839 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000840 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000841 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000842 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000843 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000844 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100845 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000846 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000847 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100848 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000849 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000850 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000851 need = MAXIMUM(need, newkeys->enc.key_len);
852 need = MAXIMUM(need, newkeys->enc.block_size);
853 need = MAXIMUM(need, newkeys->enc.iv_len);
854 need = MAXIMUM(need, newkeys->mac.key_len);
855 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
856 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
857 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
858 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000859 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000860 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000861 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100862 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000863
Damien Millerbabb47a2003-02-24 11:53:32 +1100864 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100865 if (first_kex_follows && !proposals_match(my, peer) &&
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000866 !(ssh->compat & SSH_BUG_FIRSTKEX))
867 ssh->dispatch_skip_packets = 1;
868 r = 0;
869 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000870 kex_prop_free(my);
871 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000872 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000873}
874
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000875static int
876derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
877 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000878{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000879 struct kex *kex = ssh->kex;
880 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000881 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000882 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100883 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000884 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000885 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000886
Damien Millerb3051d02014-01-10 10:58:53 +1100887 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000888 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000889 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000890 r = SSH_ERR_ALLOC_FAIL;
891 goto out;
892 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000893
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000894 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000895 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
896 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100897 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
898 ssh_digest_update(hashctx, &c, 1) != 0 ||
899 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000900 kex->session_id_len) != 0 ||
901 ssh_digest_final(hashctx, digest, mdsz) != 0) {
902 r = SSH_ERR_LIBCRYPTO_ERROR;
903 goto out;
904 }
Damien Millerb3051d02014-01-10 10:58:53 +1100905 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000906 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000907
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000908 /*
909 * expand key:
910 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
911 * Key = K1 || K2 || ... || Kn
912 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000913 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000914 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
915 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100916 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000917 ssh_digest_update(hashctx, digest, have) != 0 ||
918 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
919 r = SSH_ERR_LIBCRYPTO_ERROR;
920 goto out;
921 }
Damien Millerb3051d02014-01-10 10:58:53 +1100922 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000923 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000924 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000925#ifdef DEBUG_KEX
926 fprintf(stderr, "key '%c'== ", c);
927 dump_digest("key", digest, need);
928#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000929 *keyp = digest;
930 digest = NULL;
931 r = 0;
932 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000933 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000934 ssh_digest_free(hashctx);
935 return r;
Damien Millera664e872000-04-16 11:52:47 +1000936}
937
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000938#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000939int
940kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
941 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000942{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000943 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000944 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000945 u_int i, j, mode, ctos;
946 int r;
Damien Millera664e872000-04-16 11:52:47 +1000947
Damien Miller19bb3a52005-11-05 15:19:35 +1100948 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000949 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
950 shared_secret, &keys[i])) != 0) {
951 for (j = 0; j < i; j++)
952 free(keys[j]);
953 return r;
954 }
Damien Miller19bb3a52005-11-05 15:19:35 +1100955 }
Damien Millera664e872000-04-16 11:52:47 +1000956 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +1100957 ctos = (!kex->server && mode == MODE_OUT) ||
958 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000959 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
960 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
961 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000962 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000963 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000964}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000965
Damien Miller1f0311c2014-05-15 14:24:09 +1000966#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000967int
968kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
969 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +1100970{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000971 struct sshbuf *shared_secret;
972 int r;
Damien Miller91b580e2014-01-12 19:21:22 +1100973
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000974 if ((shared_secret = sshbuf_new()) == NULL)
975 return SSH_ERR_ALLOC_FAIL;
976 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
977 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
978 sshbuf_free(shared_secret);
979 return r;
Damien Miller91b580e2014-01-12 19:21:22 +1100980}
Damien Miller1f0311c2014-05-15 14:24:09 +1000981#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100982
Darren Tuckere14e0052004-05-13 16:30:44 +1000983
Damien Millereb8b60e2010-08-31 22:41:14 +1000984#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000985void
986dump_digest(char *msg, u_char *digest, int len)
987{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000988 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000989 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000990}
991#endif