blob: 8243164f4d9ca9ddae587230087d1a0df0c8e476 [file] [log] [blame]
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00001/* $OpenBSD: kex.c,v 1.114 2015/12/10 17:08:40 mmcc 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
deraadt@openbsd.org087266e2015-01-20 23:14:00 +000028#include <sys/param.h> /* MAX roundup */
Damien Miller8dbffe72006-08-05 11:02:17 +100029
Damien Millerd7834352006-08-05 12:39:39 +100030#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100031#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100032#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100033#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100034#include <string.h>
35
Damien Miller1f0311c2014-05-15 14:24:09 +100036#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100037#include <openssl/crypto.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"
Darren Tucker36331b52010-01-08 16:50:41 +110052#include "roaming.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000053
54#include "ssherr.h"
55#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110056#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100057
Damien Millerb3092032006-03-16 18:22:18 +110058#if OPENSSL_VERSION_NUMBER >= 0x00907000L
59# if defined(HAVE_EVP_SHA256)
Damien Milleraf87af12006-03-15 13:02:28 +110060# define evp_ssh_sha256 EVP_sha256
Damien Millerb3092032006-03-16 18:22:18 +110061# else
Damien Millera63128d2006-03-15 12:08:28 +110062extern const EVP_MD *evp_ssh_sha256(void);
Damien Millerb3092032006-03-16 18:22:18 +110063# endif
Tim Rice425a6882006-03-15 20:17:05 -080064#endif
Damien Millera63128d2006-03-15 12:08:28 +110065
Ben Lindstrombba81212001-06-25 05:01:22 +000066/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000067static int kex_choose_conf(struct ssh *);
68static int kex_input_newkeys(int, u_int32_t, void *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000069
djm@openbsd.org9690b782015-08-21 23:57:48 +000070static const char *proposal_names[PROPOSAL_MAX] = {
71 "KEX algorithms",
72 "host key algorithms",
73 "ciphers ctos",
74 "ciphers stoc",
75 "MACs ctos",
76 "MACs stoc",
77 "compression ctos",
78 "compression stoc",
79 "languages ctos",
80 "languages stoc",
81};
82
Damien Millerea111192013-04-23 19:24:32 +100083struct kexalg {
84 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000085 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100086 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110087 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100088};
89static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100090#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110091 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
92 { KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
93 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100094#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110095 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100096#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100097#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +110098 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
99 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
100 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
101 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +1100102# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +1100103 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
104 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +1000105# endif /* OPENSSL_HAS_NISTP521 */
106#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000107#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100108#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100109 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100110#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Damien Millerb3051d02014-01-10 10:58:53 +1100111 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000112};
113
114char *
Damien Miller690d9892013-11-08 12:16:49 +1100115kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000116{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000117 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000118 size_t nlen, rlen = 0;
119 const struct kexalg *k;
120
121 for (k = kexalgs; k->name != NULL; k++) {
122 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100123 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000124 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000125 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
126 free(ret);
127 return NULL;
128 }
129 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000130 memcpy(ret + rlen, k->name, nlen + 1);
131 rlen += nlen;
132 }
133 return ret;
134}
135
136static const struct kexalg *
137kex_alg_by_name(const char *name)
138{
139 const struct kexalg *k;
140
141 for (k = kexalgs; k->name != NULL; k++) {
142 if (strcmp(k->name, name) == 0)
143 return k;
144 }
145 return NULL;
146}
147
Damien Millerd5f62bf2010-09-24 22:11:14 +1000148/* Validate KEX method name list */
149int
150kex_names_valid(const char *names)
151{
152 char *s, *cp, *p;
153
154 if (names == NULL || strcmp(names, "") == 0)
155 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000156 if ((s = cp = strdup(names)) == NULL)
157 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000158 for ((p = strsep(&cp, ",")); p && *p != '\0';
159 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000160 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000161 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000162 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000163 return 0;
164 }
165 }
166 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000167 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000168 return 1;
169}
170
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000171/*
172 * Concatenate algorithm names, avoiding duplicates in the process.
173 * Caller must free returned string.
174 */
175char *
176kex_names_cat(const char *a, const char *b)
177{
178 char *ret = NULL, *tmp = NULL, *cp, *p;
179 size_t len;
180
181 if (a == NULL || *a == '\0')
182 return NULL;
183 if (b == NULL || *b == '\0')
184 return strdup(a);
185 if (strlen(b) > 1024*1024)
186 return NULL;
187 len = strlen(a) + strlen(b) + 2;
188 if ((tmp = cp = strdup(b)) == NULL ||
189 (ret = calloc(1, len)) == NULL) {
190 free(tmp);
191 return NULL;
192 }
193 strlcpy(ret, a, len);
194 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
195 if (match_list(ret, p, NULL) != NULL)
196 continue; /* Algorithm already present */
197 if (strlcat(ret, ",", len) >= len ||
198 strlcat(ret, p, len) >= len) {
199 free(tmp);
200 free(ret);
201 return NULL; /* Shouldn't happen */
202 }
203 }
204 free(tmp);
205 return ret;
206}
207
208/*
209 * Assemble a list of algorithms from a default list and a string from a
210 * configuration file. The user-provided string may begin with '+' to
211 * indicate that it should be appended to the default.
212 */
213int
214kex_assemble_names(const char *def, char **list)
215{
216 char *ret;
217
218 if (list == NULL || *list == NULL || **list == '\0') {
219 *list = strdup(def);
220 return 0;
221 }
222 if (**list != '+') {
223 return 0;
224 }
225
226 if ((ret = kex_names_cat(def, *list + 1)) == NULL)
227 return SSH_ERR_ALLOC_FAIL;
228 free(*list);
229 *list = ret;
230 return 0;
231}
232
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000233/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000234int
235kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000236{
Damien Millereccb9de2005-06-17 12:59:34 +1000237 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000238 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000239
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000240 sshbuf_reset(b);
241
Ben Lindstrom59971722002-03-27 17:42:57 +0000242 /*
243 * add a dummy cookie, the cookie will be overwritten by
244 * kex_send_kexinit(), each time a kexinit is set
245 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000246 for (i = 0; i < KEX_COOKIE_LEN; i++) {
247 if ((r = sshbuf_put_u8(b, 0)) != 0)
248 return r;
249 }
250 for (i = 0; i < PROPOSAL_MAX; i++) {
251 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
252 return r;
253 }
254 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
255 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
256 return r;
257 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000258}
259
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000260/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000261int
262kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000263{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000264 struct sshbuf *b = NULL;
265 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000266 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000267 char **proposal = NULL;
268 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000269
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000270 *propp = NULL;
271 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
272 return SSH_ERR_ALLOC_FAIL;
273 if ((b = sshbuf_fromb(raw)) == NULL) {
274 r = SSH_ERR_ALLOC_FAIL;
275 goto out;
276 }
277 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
278 goto out;
Damien Millerb1715dc2000-05-30 13:44:51 +1000279 /* extract kex init proposal strings */
280 for (i = 0; i < PROPOSAL_MAX; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000281 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
282 goto out;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000283 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000284 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000285 /* first kex follows / reserved */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000286 if ((r = sshbuf_get_u8(b, &v)) != 0 ||
287 (r = sshbuf_get_u32(b, &i)) != 0)
288 goto out;
Damien Millerbabb47a2003-02-24 11:53:32 +1100289 if (first_kex_follows != NULL)
290 *first_kex_follows = i;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000291 debug2("first_kex_follows %d ", v);
292 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000293 r = 0;
294 *propp = proposal;
295 out:
296 if (r != 0 && proposal != NULL)
297 kex_prop_free(proposal);
298 sshbuf_free(b);
299 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000300}
301
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000302void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000303kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000304{
Damien Millereccb9de2005-06-17 12:59:34 +1000305 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000306
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000307 if (proposal == NULL)
308 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000309 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000310 free(proposal[i]);
311 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000312}
313
Darren Tucker0d0d1952007-06-05 18:23:28 +1000314/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000315static int
Damien Miller630d6f42002-01-22 23:17:30 +1100316kex_protocol_error(int type, u_int32_t seq, void *ctxt)
Damien Miller874d77b2000-10-14 16:23:11 +1100317{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000318 struct ssh *ssh = active_state; /* XXX */
319 int r;
320
321 error("kex protocol error: type %d seq %u", type, seq);
322 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
323 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
324 (r = sshpkt_send(ssh)) != 0)
325 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000326 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100327}
328
Ben Lindstrombba81212001-06-25 05:01:22 +0000329static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000330kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000331{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000332 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100333 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000334 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
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;
341
342 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
343 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
344 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
345 (r = sshpkt_put_cstring(ssh, "rsa-sha2-256,rsa-sha2-512")) != 0 ||
346 (r = sshpkt_send(ssh)) != 0)
347 return r;
348 return 0;
349}
350
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000351int
352kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000353{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000354 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000355
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000356 kex_reset_dispatch(ssh);
357 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
358 (r = sshpkt_send(ssh)) != 0)
359 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000360 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000361 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000362 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000363 if (ssh->kex->ext_info_c)
364 if ((r = kex_send_ext_info(ssh)) != 0)
365 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000366 return 0;
367}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000368
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000369int
370kex_input_ext_info(int type, u_int32_t seq, void *ctxt)
371{
372 struct ssh *ssh = ctxt;
373 struct kex *kex = ssh->kex;
374 u_int32_t i, ninfo;
375 char *name, *val, *found;
376 int r;
377
378 debug("SSH2_MSG_EXT_INFO received");
379 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
380 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
381 return r;
382 for (i = 0; i < ninfo; i++) {
383 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
384 return r;
385 if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) {
386 free(name);
387 return r;
388 }
389 debug("%s: %s=<%s>", __func__, name, val);
390 if (strcmp(name, "server-sig-algs") == 0) {
391 found = match_list("rsa-sha2-256", val, NULL);
392 if (found) {
393 kex->rsa_sha2 = 256;
394 free(found);
395 }
396 found = match_list("rsa-sha2-512", val, NULL);
397 if (found) {
398 kex->rsa_sha2 = 512;
399 free(found);
400 }
401 }
402 free(name);
403 free(val);
404 }
405 return sshpkt_get_end(ssh);
406}
407
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000408static int
409kex_input_newkeys(int type, u_int32_t seq, void *ctxt)
410{
411 struct ssh *ssh = ctxt;
412 struct kex *kex = ssh->kex;
413 int r;
414
415 debug("SSH2_MSG_NEWKEYS received");
416 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
417 if ((r = sshpkt_get_end(ssh)) != 0)
418 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000419 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000420 sshbuf_reset(kex->peer);
421 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000422 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000423 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000424 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000425 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000426}
Damien Millera664e872000-04-16 11:52:47 +1000427
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000428int
429kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000430{
Ben Lindstrom59971722002-03-27 17:42:57 +0000431 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000432 struct kex *kex = ssh->kex;
433 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000434
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000435 if (kex == NULL)
436 return SSH_ERR_INTERNAL_ERROR;
437 if (kex->flags & KEX_INIT_SENT)
438 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000439 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000440
441 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000442 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
443 return SSH_ERR_INVALID_FORMAT;
444 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
445 return SSH_ERR_INTERNAL_ERROR;
446 arc4random_buf(cookie, KEX_COOKIE_LEN);
447
448 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
449 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
450 (r = sshpkt_send(ssh)) != 0)
451 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000452 debug("SSH2_MSG_KEXINIT sent");
453 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000454 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000455}
456
Darren Tucker0d0d1952007-06-05 18:23:28 +1000457/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000458int
Damien Miller630d6f42002-01-22 23:17:30 +1100459kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000460{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000461 struct ssh *ssh = ctxt;
462 struct kex *kex = ssh->kex;
463 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000464 u_int i;
465 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000466 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000467
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000468 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000469 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000470 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000471
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000472 ptr = sshpkt_ptr(ssh, &dlen);
473 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
474 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000475
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000476 /* discard packet */
477 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000478 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
479 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000480 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000481 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
482 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000483 /*
484 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
485 * KEX method has the server move first, but a server might be using
486 * a custom method or one that we otherwise don't support. We should
487 * be prepared to remember first_kex_follows here so we can eat a
488 * packet later.
489 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
490 * for cases where the server *doesn't* go first. I guess we should
491 * ignore it when it is set for these cases, which is what we do now.
492 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000493 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
494 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
495 (r = sshpkt_get_end(ssh)) != 0)
496 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000497
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000498 if (!(kex->flags & KEX_INIT_SENT))
499 if ((r = kex_send_kexinit(ssh)) != 0)
500 return r;
501 if ((r = kex_choose_conf(ssh)) != 0)
502 return r;
503
504 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
505 return (kex->kex[kex->kex_type])(ssh);
506
507 return SSH_ERR_INTERNAL_ERROR;
508}
509
510int
511kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
512{
513 struct kex *kex;
514 int r;
515
516 *kexp = NULL;
517 if ((kex = calloc(1, sizeof(*kex))) == NULL)
518 return SSH_ERR_ALLOC_FAIL;
519 if ((kex->peer = sshbuf_new()) == NULL ||
520 (kex->my = sshbuf_new()) == NULL) {
521 r = SSH_ERR_ALLOC_FAIL;
522 goto out;
523 }
524 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
525 goto out;
526 kex->done = 0;
527 kex_reset_dispatch(ssh);
528 r = 0;
529 *kexp = kex;
530 out:
531 if (r != 0)
532 kex_free(kex);
533 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000534}
535
markus@openbsd.org091c3022015-01-19 19:52:16 +0000536void
537kex_free_newkeys(struct newkeys *newkeys)
538{
539 if (newkeys == NULL)
540 return;
541 if (newkeys->enc.key) {
542 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
543 free(newkeys->enc.key);
544 newkeys->enc.key = NULL;
545 }
546 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000547 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000548 free(newkeys->enc.iv);
549 newkeys->enc.iv = NULL;
550 }
551 free(newkeys->enc.name);
552 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
553 free(newkeys->comp.name);
554 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
555 mac_clear(&newkeys->mac);
556 if (newkeys->mac.key) {
557 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
558 free(newkeys->mac.key);
559 newkeys->mac.key = NULL;
560 }
561 free(newkeys->mac.name);
562 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
563 explicit_bzero(newkeys, sizeof(*newkeys));
564 free(newkeys);
565}
566
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000567void
568kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000569{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000570 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000571
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000572#ifdef WITH_OPENSSL
573 if (kex->dh)
574 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100575#ifdef OPENSSL_HAS_ECC
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000576 if (kex->ec_client_key)
577 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100578#endif /* OPENSSL_HAS_ECC */
579#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000580 for (mode = 0; mode < MODE_MAX; mode++) {
581 kex_free_newkeys(kex->newkeys[mode]);
582 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000583 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000584 sshbuf_free(kex->peer);
585 sshbuf_free(kex->my);
586 free(kex->session_id);
587 free(kex->client_version_string);
588 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000589 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000590 free(kex->hostkey_alg);
591 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000592 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000593}
594
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000595int
596kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000597{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000598 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000599
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000600 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
601 return r;
602 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
603 kex_free(ssh->kex);
604 ssh->kex = NULL;
605 return r;
Damien Millera664e872000-04-16 11:52:47 +1000606 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000607 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000608}
609
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000610static int
611choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000612{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000613 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000614
Damien Millera664e872000-04-16 11:52:47 +1000615 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000616 return SSH_ERR_NO_CIPHER_ALG_MATCH;
Damien Miller963f6b22002-02-19 15:21:23 +1100617 if ((enc->cipher = cipher_by_name(name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000618 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000619 enc->name = name;
620 enc->enabled = 0;
621 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100622 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000623 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100624 enc->key_len = cipher_keylen(enc->cipher);
625 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000626 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000627}
Damien Miller4f7becb2006-03-26 14:10:14 +1100628
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000629static int
630choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000631{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000632 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000633
Damien Millera664e872000-04-16 11:52:47 +1000634 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000635 return SSH_ERR_NO_MAC_ALG_MATCH;
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000636 if (mac_setup(mac, name) < 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000637 return SSH_ERR_INTERNAL_ERROR;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000638 /* truncate the key */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000639 if (ssh->compat & SSH_BUG_HMAC)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000640 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000641 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000642 mac->key = NULL;
643 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000644 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000645}
Damien Miller4f7becb2006-03-26 14:10:14 +1100646
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000647static int
648choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000649{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000650 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000651
Damien Millera664e872000-04-16 11:52:47 +1000652 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000653 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000654 if (strcmp(name, "zlib@openssh.com") == 0) {
655 comp->type = COMP_DELAYED;
656 } else if (strcmp(name, "zlib") == 0) {
657 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000658 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000659 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000660 } else {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000661 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000662 }
663 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000664 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000665}
Damien Miller4f7becb2006-03-26 14:10:14 +1100666
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000667static int
668choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000669{
Damien Millerea111192013-04-23 19:24:32 +1000670 const struct kexalg *kexalg;
671
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000672 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000673
djm@openbsd.org9690b782015-08-21 23:57:48 +0000674 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000675 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000676 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000677 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000678 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000679 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100680 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000681 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000682 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000683}
Damien Miller19bb3a52005-11-05 15:19:35 +1100684
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000685static int
686choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000687{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000688 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000689
djm@openbsd.org9690b782015-08-21 23:57:48 +0000690 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000691 k->hostkey_alg ? k->hostkey_alg : "(no match)");
692 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000693 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000694 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100695 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000696 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000697 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000698 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000699}
700
Damien Millera8e06ce2003-11-21 23:48:55 +1100701static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100702proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
703{
704 static int check[] = {
705 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
706 };
707 int *idx;
708 char *p;
709
710 for (idx = &check[0]; *idx != -1; idx++) {
711 if ((p = strchr(my[*idx], ',')) != NULL)
712 *p = '\0';
713 if ((p = strchr(peer[*idx], ',')) != NULL)
714 *p = '\0';
715 if (strcmp(my[*idx], peer[*idx]) != 0) {
716 debug2("proposal mismatch: my %s peer %s",
717 my[*idx], peer[*idx]);
718 return (0);
719 }
720 }
721 debug2("proposals match");
722 return (1);
723}
724
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000725static int
726kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000727{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000728 struct kex *kex = ssh->kex;
729 struct newkeys *newkeys;
730 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000731 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000732 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100733 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000734 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000735
djm@openbsd.org9690b782015-08-21 23:57:48 +0000736 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
737 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
738 goto out;
739 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
740 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000741 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000742
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000743 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000744 cprop=peer;
745 sprop=my;
746 } else {
747 cprop=my;
748 sprop=peer;
749 }
Damien Millera664e872000-04-16 11:52:47 +1000750
Darren Tucker36331b52010-01-08 16:50:41 +1100751 /* Check whether server offers roaming */
752 if (!kex->server) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000753 char *roaming = match_list(KEX_RESUME,
754 peer[PROPOSAL_KEX_ALGS], NULL);
755
Darren Tucker36331b52010-01-08 16:50:41 +1100756 if (roaming) {
757 kex->roaming = 1;
Darren Tuckera627d422013-06-02 07:31:17 +1000758 free(roaming);
Darren Tucker36331b52010-01-08 16:50:41 +1100759 }
760 }
761
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000762 /* Check whether client supports ext_info_c */
763 if (kex->server) {
764 char *ext;
765
766 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
767 if (ext) {
768 kex->ext_info_c = 1;
769 free(ext);
770 }
771 }
772
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000773 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000774 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
775 sprop[PROPOSAL_KEX_ALGS])) != 0) {
776 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
777 peer[PROPOSAL_KEX_ALGS] = NULL;
778 goto out;
779 }
780 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
781 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
782 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
783 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
784 goto out;
785 }
Damien Millera664e872000-04-16 11:52:47 +1000786 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000787 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
788 r = SSH_ERR_ALLOC_FAIL;
789 goto out;
790 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000791 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000792 ctos = (!kex->server && mode == MODE_OUT) ||
793 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000794 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
795 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
796 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000797 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000798 sprop[nenc])) != 0) {
799 kex->failed_choice = peer[nenc];
800 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000801 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000802 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100803 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000804 /* ignore mac for authenticated encryption */
805 if (authlen == 0 &&
806 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000807 sprop[nmac])) != 0) {
808 kex->failed_choice = peer[nmac];
809 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000810 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000811 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000812 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000813 sprop[ncomp])) != 0) {
814 kex->failed_choice = peer[ncomp];
815 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000816 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000817 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000818 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000819 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000820 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100821 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000822 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000823 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100824 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000825 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000826 newkeys = kex->newkeys[mode];
Damien Millera92ac742014-01-26 09:38:03 +1100827 need = MAX(need, newkeys->enc.key_len);
828 need = MAX(need, newkeys->enc.block_size);
829 need = MAX(need, newkeys->enc.iv_len);
830 need = MAX(need, newkeys->mac.key_len);
831 dh_need = MAX(dh_need, cipher_seclen(newkeys->enc.cipher));
832 dh_need = MAX(dh_need, newkeys->enc.block_size);
833 dh_need = MAX(dh_need, newkeys->enc.iv_len);
834 dh_need = MAX(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000835 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000836 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000837 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100838 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000839
Damien Millerbabb47a2003-02-24 11:53:32 +1100840 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100841 if (first_kex_follows && !proposals_match(my, peer) &&
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000842 !(ssh->compat & SSH_BUG_FIRSTKEX))
843 ssh->dispatch_skip_packets = 1;
844 r = 0;
845 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000846 kex_prop_free(my);
847 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000848 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000849}
850
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000851static int
852derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
853 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000854{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000855 struct kex *kex = ssh->kex;
856 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000857 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000858 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100859 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000860 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000861 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000862
Damien Millerb3051d02014-01-10 10:58:53 +1100863 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000864 return SSH_ERR_INVALID_ARGUMENT;
865 if ((digest = calloc(1, roundup(need, mdsz))) == NULL) {
866 r = SSH_ERR_ALLOC_FAIL;
867 goto out;
868 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000869
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000870 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000871 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
872 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100873 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
874 ssh_digest_update(hashctx, &c, 1) != 0 ||
875 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000876 kex->session_id_len) != 0 ||
877 ssh_digest_final(hashctx, digest, mdsz) != 0) {
878 r = SSH_ERR_LIBCRYPTO_ERROR;
879 goto out;
880 }
Damien Millerb3051d02014-01-10 10:58:53 +1100881 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000882 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000883
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000884 /*
885 * expand key:
886 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
887 * Key = K1 || K2 || ... || Kn
888 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000889 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000890 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
891 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100892 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000893 ssh_digest_update(hashctx, digest, have) != 0 ||
894 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
895 r = SSH_ERR_LIBCRYPTO_ERROR;
896 goto out;
897 }
Damien Millerb3051d02014-01-10 10:58:53 +1100898 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000899 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000900 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000901#ifdef DEBUG_KEX
902 fprintf(stderr, "key '%c'== ", c);
903 dump_digest("key", digest, need);
904#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000905 *keyp = digest;
906 digest = NULL;
907 r = 0;
908 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000909 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000910 ssh_digest_free(hashctx);
911 return r;
Damien Millera664e872000-04-16 11:52:47 +1000912}
913
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000914#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000915int
916kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
917 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000918{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000919 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000920 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000921 u_int i, j, mode, ctos;
922 int r;
Damien Millera664e872000-04-16 11:52:47 +1000923
Damien Miller19bb3a52005-11-05 15:19:35 +1100924 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000925 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
926 shared_secret, &keys[i])) != 0) {
927 for (j = 0; j < i; j++)
928 free(keys[j]);
929 return r;
930 }
Damien Miller19bb3a52005-11-05 15:19:35 +1100931 }
Damien Millera664e872000-04-16 11:52:47 +1000932 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +1100933 ctos = (!kex->server && mode == MODE_OUT) ||
934 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000935 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
936 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
937 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000938 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000939 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000940}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000941
Damien Miller1f0311c2014-05-15 14:24:09 +1000942#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000943int
944kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
945 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +1100946{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000947 struct sshbuf *shared_secret;
948 int r;
Damien Miller91b580e2014-01-12 19:21:22 +1100949
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000950 if ((shared_secret = sshbuf_new()) == NULL)
951 return SSH_ERR_ALLOC_FAIL;
952 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
953 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
954 sshbuf_free(shared_secret);
955 return r;
Damien Miller91b580e2014-01-12 19:21:22 +1100956}
Damien Miller1f0311c2014-05-15 14:24:09 +1000957#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100958
Damien Miller1f0311c2014-05-15 14:24:09 +1000959#ifdef WITH_SSH1
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000960int
Darren Tuckere14e0052004-05-13 16:30:44 +1000961derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
962 u_int8_t cookie[8], u_int8_t id[16])
963{
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000964 u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000965 struct ssh_digest_ctx *hashctx = NULL;
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000966 size_t hlen, slen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000967 int r;
Darren Tuckere14e0052004-05-13 16:30:44 +1000968
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000969 hlen = BN_num_bytes(host_modulus);
970 slen = BN_num_bytes(server_modulus);
971 if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) ||
972 slen < (512 / 8) || (u_int)slen > sizeof(sbuf))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000973 return SSH_ERR_KEY_BITS_MISMATCH;
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000974 if (BN_bn2bin(host_modulus, hbuf) <= 0 ||
975 BN_bn2bin(server_modulus, sbuf) <= 0) {
976 r = SSH_ERR_LIBCRYPTO_ERROR;
977 goto out;
978 }
979 if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) {
980 r = SSH_ERR_ALLOC_FAIL;
981 goto out;
982 }
983 if (ssh_digest_update(hashctx, hbuf, hlen) != 0 ||
984 ssh_digest_update(hashctx, sbuf, slen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000985 ssh_digest_update(hashctx, cookie, 8) != 0 ||
986 ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) {
987 r = SSH_ERR_LIBCRYPTO_ERROR;
988 goto out;
989 }
Damien Millerb3051d02014-01-10 10:58:53 +1100990 memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000991 r = 0;
992 out:
993 ssh_digest_free(hashctx);
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000994 explicit_bzero(hbuf, sizeof(hbuf));
995 explicit_bzero(sbuf, sizeof(sbuf));
Damien Millera5103f42014-02-04 11:20:14 +1100996 explicit_bzero(obuf, sizeof(obuf));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000997 return r;
Darren Tuckere14e0052004-05-13 16:30:44 +1000998}
Damien Miller1f0311c2014-05-15 14:24:09 +1000999#endif
Darren Tuckere14e0052004-05-13 16:30:44 +10001000
Damien Millereb8b60e2010-08-31 22:41:14 +10001001#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001002void
1003dump_digest(char *msg, u_char *digest, int len)
1004{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001005 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001006 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001007}
1008#endif