blob: cf44fbc040e09ff0a8b013fd254254ab76e08ded [file] [log] [blame]
markus@openbsd.org2ae666a2017-05-30 14:23:52 +00001/* $OpenBSD: kex.c,v 1.133 2017/05/30 14:23:52 markus 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;
381 char *name, *val, *found;
382 int r;
383
384 debug("SSH2_MSG_EXT_INFO received");
385 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
386 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
387 return r;
388 for (i = 0; i < ninfo; i++) {
389 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
390 return r;
391 if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) {
392 free(name);
393 return r;
394 }
395 debug("%s: %s=<%s>", __func__, name, val);
396 if (strcmp(name, "server-sig-algs") == 0) {
397 found = match_list("rsa-sha2-256", val, NULL);
398 if (found) {
399 kex->rsa_sha2 = 256;
400 free(found);
401 }
402 found = match_list("rsa-sha2-512", val, NULL);
403 if (found) {
404 kex->rsa_sha2 = 512;
405 free(found);
406 }
407 }
408 free(name);
409 free(val);
410 }
411 return sshpkt_get_end(ssh);
412}
413
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000414static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000415kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000416{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000417 struct kex *kex = ssh->kex;
418 int r;
419
420 debug("SSH2_MSG_NEWKEYS received");
421 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000422 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000423 if ((r = sshpkt_get_end(ssh)) != 0)
424 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000425 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
426 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000427 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000428 sshbuf_reset(kex->peer);
429 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000430 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000431 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000432 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000433 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000434}
Damien Millera664e872000-04-16 11:52:47 +1000435
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000436int
437kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000438{
Ben Lindstrom59971722002-03-27 17:42:57 +0000439 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000440 struct kex *kex = ssh->kex;
441 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000442
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000443 if (kex == NULL)
444 return SSH_ERR_INTERNAL_ERROR;
445 if (kex->flags & KEX_INIT_SENT)
446 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000447 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000448
449 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000450 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
451 return SSH_ERR_INVALID_FORMAT;
452 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
453 return SSH_ERR_INTERNAL_ERROR;
454 arc4random_buf(cookie, KEX_COOKIE_LEN);
455
456 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
457 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
458 (r = sshpkt_send(ssh)) != 0)
459 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000460 debug("SSH2_MSG_KEXINIT sent");
461 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000462 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000463}
464
Darren Tucker0d0d1952007-06-05 18:23:28 +1000465/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000466int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000467kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000468{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000469 struct kex *kex = ssh->kex;
470 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000471 u_int i;
472 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000473 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000474
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000475 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000476 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000477 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000478
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000479 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000480 ptr = sshpkt_ptr(ssh, &dlen);
481 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
482 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000483
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000484 /* discard packet */
485 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000486 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
487 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000488 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000489 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
490 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000491 /*
492 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
493 * KEX method has the server move first, but a server might be using
494 * a custom method or one that we otherwise don't support. We should
495 * be prepared to remember first_kex_follows here so we can eat a
496 * packet later.
497 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
498 * for cases where the server *doesn't* go first. I guess we should
499 * ignore it when it is set for these cases, which is what we do now.
500 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000501 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
502 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
503 (r = sshpkt_get_end(ssh)) != 0)
504 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000505
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000506 if (!(kex->flags & KEX_INIT_SENT))
507 if ((r = kex_send_kexinit(ssh)) != 0)
508 return r;
509 if ((r = kex_choose_conf(ssh)) != 0)
510 return r;
511
512 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
513 return (kex->kex[kex->kex_type])(ssh);
514
515 return SSH_ERR_INTERNAL_ERROR;
516}
517
518int
519kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
520{
521 struct kex *kex;
522 int r;
523
524 *kexp = NULL;
525 if ((kex = calloc(1, sizeof(*kex))) == NULL)
526 return SSH_ERR_ALLOC_FAIL;
527 if ((kex->peer = sshbuf_new()) == NULL ||
528 (kex->my = sshbuf_new()) == NULL) {
529 r = SSH_ERR_ALLOC_FAIL;
530 goto out;
531 }
532 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
533 goto out;
534 kex->done = 0;
535 kex_reset_dispatch(ssh);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000536 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000537 r = 0;
538 *kexp = kex;
539 out:
540 if (r != 0)
541 kex_free(kex);
542 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000543}
544
markus@openbsd.org091c3022015-01-19 19:52:16 +0000545void
546kex_free_newkeys(struct newkeys *newkeys)
547{
548 if (newkeys == NULL)
549 return;
550 if (newkeys->enc.key) {
551 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
552 free(newkeys->enc.key);
553 newkeys->enc.key = NULL;
554 }
555 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000556 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000557 free(newkeys->enc.iv);
558 newkeys->enc.iv = NULL;
559 }
560 free(newkeys->enc.name);
561 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
562 free(newkeys->comp.name);
563 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
564 mac_clear(&newkeys->mac);
565 if (newkeys->mac.key) {
566 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
567 free(newkeys->mac.key);
568 newkeys->mac.key = NULL;
569 }
570 free(newkeys->mac.name);
571 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
572 explicit_bzero(newkeys, sizeof(*newkeys));
573 free(newkeys);
574}
575
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000576void
577kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000578{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000579 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000580
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000581#ifdef WITH_OPENSSL
582 if (kex->dh)
583 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100584#ifdef OPENSSL_HAS_ECC
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000585 if (kex->ec_client_key)
586 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100587#endif /* OPENSSL_HAS_ECC */
588#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000589 for (mode = 0; mode < MODE_MAX; mode++) {
590 kex_free_newkeys(kex->newkeys[mode]);
591 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000592 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000593 sshbuf_free(kex->peer);
594 sshbuf_free(kex->my);
595 free(kex->session_id);
596 free(kex->client_version_string);
597 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000598 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000599 free(kex->hostkey_alg);
600 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000601 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000602}
603
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000604int
605kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000606{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000607 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000608
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000609 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
610 return r;
611 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
612 kex_free(ssh->kex);
613 ssh->kex = NULL;
614 return r;
Damien Millera664e872000-04-16 11:52:47 +1000615 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000616 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000617}
618
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000619/*
620 * Request key re-exchange, returns 0 on success or a ssherr.h error
621 * code otherwise. Must not be called if KEX is incomplete or in-progress.
622 */
623int
624kex_start_rekex(struct ssh *ssh)
625{
626 if (ssh->kex == NULL) {
627 error("%s: no kex", __func__);
628 return SSH_ERR_INTERNAL_ERROR;
629 }
630 if (ssh->kex->done == 0) {
631 error("%s: requested twice", __func__);
632 return SSH_ERR_INTERNAL_ERROR;
633 }
634 ssh->kex->done = 0;
635 return kex_send_kexinit(ssh);
636}
637
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000638static int
639choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000640{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000641 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000642
Damien Millera664e872000-04-16 11:52:47 +1000643 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000644 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000645 if ((enc->cipher = cipher_by_name(name)) == NULL) {
646 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000647 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000648 }
Damien Millera664e872000-04-16 11:52:47 +1000649 enc->name = name;
650 enc->enabled = 0;
651 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100652 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000653 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100654 enc->key_len = cipher_keylen(enc->cipher);
655 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000656 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000657}
Damien Miller4f7becb2006-03-26 14:10:14 +1100658
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000659static int
660choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000661{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000662 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000663
Damien Millera664e872000-04-16 11:52:47 +1000664 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000665 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000666 if (mac_setup(mac, name) < 0) {
667 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000668 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000669 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000670 /* truncate the key */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000671 if (ssh->compat & SSH_BUG_HMAC)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000672 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000673 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000674 mac->key = NULL;
675 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000676 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000677}
Damien Miller4f7becb2006-03-26 14:10:14 +1100678
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000679static int
680choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000681{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000682 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000683
Damien Millera664e872000-04-16 11:52:47 +1000684 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000685 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000686 if (strcmp(name, "zlib@openssh.com") == 0) {
687 comp->type = COMP_DELAYED;
djm@openbsd.orgb7689152016-09-28 21:44:52 +0000688 } else if (strcmp(name, "zlib") == 0) {
689 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000690 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000691 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000692 } else {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000693 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000694 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000695 }
696 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000697 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000698}
Damien Miller4f7becb2006-03-26 14:10:14 +1100699
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000700static int
701choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000702{
Damien Millerea111192013-04-23 19:24:32 +1000703 const struct kexalg *kexalg;
704
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000705 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000706
djm@openbsd.org9690b782015-08-21 23:57:48 +0000707 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000708 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000709 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000710 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000711 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000712 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100713 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000714 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000715 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000716}
Damien Miller19bb3a52005-11-05 15:19:35 +1100717
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000718static int
719choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000720{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000721 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000722
djm@openbsd.org9690b782015-08-21 23:57:48 +0000723 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000724 k->hostkey_alg ? k->hostkey_alg : "(no match)");
725 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000726 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000727 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100728 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000729 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000730 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000731 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000732}
733
Damien Millera8e06ce2003-11-21 23:48:55 +1100734static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100735proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
736{
737 static int check[] = {
738 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
739 };
740 int *idx;
741 char *p;
742
743 for (idx = &check[0]; *idx != -1; idx++) {
744 if ((p = strchr(my[*idx], ',')) != NULL)
745 *p = '\0';
746 if ((p = strchr(peer[*idx], ',')) != NULL)
747 *p = '\0';
748 if (strcmp(my[*idx], peer[*idx]) != 0) {
749 debug2("proposal mismatch: my %s peer %s",
750 my[*idx], peer[*idx]);
751 return (0);
752 }
753 }
754 debug2("proposals match");
755 return (1);
756}
757
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000758static int
759kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000760{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000761 struct kex *kex = ssh->kex;
762 struct newkeys *newkeys;
763 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000764 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000765 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100766 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000767 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000768
djm@openbsd.org9690b782015-08-21 23:57:48 +0000769 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
770 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
771 goto out;
772 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
773 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000774 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000775
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000776 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000777 cprop=peer;
778 sprop=my;
779 } else {
780 cprop=my;
781 sprop=peer;
782 }
Damien Millera664e872000-04-16 11:52:47 +1000783
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000784 /* Check whether client supports ext_info_c */
785 if (kex->server) {
786 char *ext;
787
788 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000789 kex->ext_info_c = (ext != NULL);
790 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000791 }
792
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000793 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000794 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
795 sprop[PROPOSAL_KEX_ALGS])) != 0) {
796 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
797 peer[PROPOSAL_KEX_ALGS] = NULL;
798 goto out;
799 }
800 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
801 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
802 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
803 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
804 goto out;
805 }
Damien Millera664e872000-04-16 11:52:47 +1000806 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000807 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
808 r = SSH_ERR_ALLOC_FAIL;
809 goto out;
810 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000811 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000812 ctos = (!kex->server && mode == MODE_OUT) ||
813 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000814 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
815 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
816 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000817 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000818 sprop[nenc])) != 0) {
819 kex->failed_choice = peer[nenc];
820 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000821 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000822 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100823 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000824 /* ignore mac for authenticated encryption */
825 if (authlen == 0 &&
826 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000827 sprop[nmac])) != 0) {
828 kex->failed_choice = peer[nmac];
829 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000830 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000831 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000832 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000833 sprop[ncomp])) != 0) {
834 kex->failed_choice = peer[ncomp];
835 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000836 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000837 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000838 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000839 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000840 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100841 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000842 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000843 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100844 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000845 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000846 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000847 need = MAXIMUM(need, newkeys->enc.key_len);
848 need = MAXIMUM(need, newkeys->enc.block_size);
849 need = MAXIMUM(need, newkeys->enc.iv_len);
850 need = MAXIMUM(need, newkeys->mac.key_len);
851 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
852 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
853 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
854 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000855 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000856 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000857 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100858 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000859
Damien Millerbabb47a2003-02-24 11:53:32 +1100860 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100861 if (first_kex_follows && !proposals_match(my, peer) &&
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000862 !(ssh->compat & SSH_BUG_FIRSTKEX))
863 ssh->dispatch_skip_packets = 1;
864 r = 0;
865 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000866 kex_prop_free(my);
867 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000868 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000869}
870
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000871static int
872derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
873 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000874{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000875 struct kex *kex = ssh->kex;
876 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000877 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000878 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100879 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000880 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000881 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000882
Damien Millerb3051d02014-01-10 10:58:53 +1100883 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000884 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000885 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000886 r = SSH_ERR_ALLOC_FAIL;
887 goto out;
888 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000889
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000890 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000891 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
892 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100893 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
894 ssh_digest_update(hashctx, &c, 1) != 0 ||
895 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000896 kex->session_id_len) != 0 ||
897 ssh_digest_final(hashctx, digest, mdsz) != 0) {
898 r = SSH_ERR_LIBCRYPTO_ERROR;
899 goto out;
900 }
Damien Millerb3051d02014-01-10 10:58:53 +1100901 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000902 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000903
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000904 /*
905 * expand key:
906 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
907 * Key = K1 || K2 || ... || Kn
908 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000909 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000910 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
911 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100912 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000913 ssh_digest_update(hashctx, digest, have) != 0 ||
914 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
915 r = SSH_ERR_LIBCRYPTO_ERROR;
916 goto out;
917 }
Damien Millerb3051d02014-01-10 10:58:53 +1100918 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000919 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000920 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000921#ifdef DEBUG_KEX
922 fprintf(stderr, "key '%c'== ", c);
923 dump_digest("key", digest, need);
924#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000925 *keyp = digest;
926 digest = NULL;
927 r = 0;
928 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000929 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000930 ssh_digest_free(hashctx);
931 return r;
Damien Millera664e872000-04-16 11:52:47 +1000932}
933
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000934#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000935int
936kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
937 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000938{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000939 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000940 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000941 u_int i, j, mode, ctos;
942 int r;
Damien Millera664e872000-04-16 11:52:47 +1000943
Damien Miller19bb3a52005-11-05 15:19:35 +1100944 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000945 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
946 shared_secret, &keys[i])) != 0) {
947 for (j = 0; j < i; j++)
948 free(keys[j]);
949 return r;
950 }
Damien Miller19bb3a52005-11-05 15:19:35 +1100951 }
Damien Millera664e872000-04-16 11:52:47 +1000952 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +1100953 ctos = (!kex->server && mode == MODE_OUT) ||
954 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000955 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
956 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
957 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000958 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000959 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000960}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000961
Damien Miller1f0311c2014-05-15 14:24:09 +1000962#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000963int
964kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
965 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +1100966{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000967 struct sshbuf *shared_secret;
968 int r;
Damien Miller91b580e2014-01-12 19:21:22 +1100969
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000970 if ((shared_secret = sshbuf_new()) == NULL)
971 return SSH_ERR_ALLOC_FAIL;
972 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
973 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
974 sshbuf_free(shared_secret);
975 return r;
Damien Miller91b580e2014-01-12 19:21:22 +1100976}
Damien Miller1f0311c2014-05-15 14:24:09 +1000977#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100978
Darren Tuckere14e0052004-05-13 16:30:44 +1000979
Damien Millereb8b60e2010-08-31 22:41:14 +1000980#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000981void
982dump_digest(char *msg, u_char *digest, int len)
983{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000984 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000985 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000986}
987#endif