blob: d8793b91959d1cd8ffc9934909d5f7051db0b279 [file] [log] [blame]
djm@openbsd.org179c3532015-10-13 00:21:27 +00001/* $OpenBSD: kex.c,v 1.111 2015/10/13 00:21:27 djm Exp $ */
Damien Millera664e872000-04-16 11:52:47 +10002/*
Ben Lindstrom44697232001-07-04 03:32:30 +00003 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millera664e872000-04-16 11:52:47 +10004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
Damien Millera664e872000-04-16 11:52:47 +100013 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millera664e872000-04-16 11:52:47 +100027
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{
Damien Miller630d6f42002-01-22 23:17:30 +1100318 error("Hm, kex protocol error: type %d seq %u", type, seq);
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000319 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100320}
321
Ben Lindstrombba81212001-06-25 05:01:22 +0000322static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000323kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000324{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000325 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100326 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000327 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000328}
329
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000330int
331kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000332{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000333 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000334
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000335 kex_reset_dispatch(ssh);
336 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
337 (r = sshpkt_send(ssh)) != 0)
338 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000339 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000340 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000341 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
342 return 0;
343}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000344
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000345static int
346kex_input_newkeys(int type, u_int32_t seq, void *ctxt)
347{
348 struct ssh *ssh = ctxt;
349 struct kex *kex = ssh->kex;
350 int r;
351
352 debug("SSH2_MSG_NEWKEYS received");
353 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
354 if ((r = sshpkt_get_end(ssh)) != 0)
355 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000356 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000357 sshbuf_reset(kex->peer);
358 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000359 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000360 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000361 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000362 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000363}
Damien Millera664e872000-04-16 11:52:47 +1000364
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000365int
366kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000367{
Ben Lindstrom59971722002-03-27 17:42:57 +0000368 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000369 struct kex *kex = ssh->kex;
370 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000371
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000372 if (kex == NULL)
373 return SSH_ERR_INTERNAL_ERROR;
374 if (kex->flags & KEX_INIT_SENT)
375 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000376 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000377
378 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000379 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
380 return SSH_ERR_INVALID_FORMAT;
381 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
382 return SSH_ERR_INTERNAL_ERROR;
383 arc4random_buf(cookie, KEX_COOKIE_LEN);
384
385 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
386 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
387 (r = sshpkt_send(ssh)) != 0)
388 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000389 debug("SSH2_MSG_KEXINIT sent");
390 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000391 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000392}
393
Darren Tucker0d0d1952007-06-05 18:23:28 +1000394/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000395int
Damien Miller630d6f42002-01-22 23:17:30 +1100396kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000397{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000398 struct ssh *ssh = ctxt;
399 struct kex *kex = ssh->kex;
400 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000401 u_int i;
402 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000403 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000404
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000405 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000406 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000407 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000408
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000409 ptr = sshpkt_ptr(ssh, &dlen);
410 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
411 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000412
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000413 /* discard packet */
414 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000415 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
416 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000417 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000418 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
419 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000420 /*
421 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
422 * KEX method has the server move first, but a server might be using
423 * a custom method or one that we otherwise don't support. We should
424 * be prepared to remember first_kex_follows here so we can eat a
425 * packet later.
426 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
427 * for cases where the server *doesn't* go first. I guess we should
428 * ignore it when it is set for these cases, which is what we do now.
429 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000430 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
431 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
432 (r = sshpkt_get_end(ssh)) != 0)
433 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000434
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000435 if (!(kex->flags & KEX_INIT_SENT))
436 if ((r = kex_send_kexinit(ssh)) != 0)
437 return r;
438 if ((r = kex_choose_conf(ssh)) != 0)
439 return r;
440
441 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
442 return (kex->kex[kex->kex_type])(ssh);
443
444 return SSH_ERR_INTERNAL_ERROR;
445}
446
447int
448kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
449{
450 struct kex *kex;
451 int r;
452
453 *kexp = NULL;
454 if ((kex = calloc(1, sizeof(*kex))) == NULL)
455 return SSH_ERR_ALLOC_FAIL;
456 if ((kex->peer = sshbuf_new()) == NULL ||
457 (kex->my = sshbuf_new()) == NULL) {
458 r = SSH_ERR_ALLOC_FAIL;
459 goto out;
460 }
461 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
462 goto out;
463 kex->done = 0;
464 kex_reset_dispatch(ssh);
465 r = 0;
466 *kexp = kex;
467 out:
468 if (r != 0)
469 kex_free(kex);
470 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000471}
472
markus@openbsd.org091c3022015-01-19 19:52:16 +0000473void
474kex_free_newkeys(struct newkeys *newkeys)
475{
476 if (newkeys == NULL)
477 return;
478 if (newkeys->enc.key) {
479 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
480 free(newkeys->enc.key);
481 newkeys->enc.key = NULL;
482 }
483 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000484 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000485 free(newkeys->enc.iv);
486 newkeys->enc.iv = NULL;
487 }
488 free(newkeys->enc.name);
489 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
490 free(newkeys->comp.name);
491 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
492 mac_clear(&newkeys->mac);
493 if (newkeys->mac.key) {
494 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
495 free(newkeys->mac.key);
496 newkeys->mac.key = NULL;
497 }
498 free(newkeys->mac.name);
499 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
500 explicit_bzero(newkeys, sizeof(*newkeys));
501 free(newkeys);
502}
503
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000504void
505kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000506{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000507 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000508
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000509#ifdef WITH_OPENSSL
510 if (kex->dh)
511 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100512#ifdef OPENSSL_HAS_ECC
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000513 if (kex->ec_client_key)
514 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100515#endif /* OPENSSL_HAS_ECC */
516#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000517 for (mode = 0; mode < MODE_MAX; mode++) {
518 kex_free_newkeys(kex->newkeys[mode]);
519 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000520 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000521 sshbuf_free(kex->peer);
522 sshbuf_free(kex->my);
523 free(kex->session_id);
524 free(kex->client_version_string);
525 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000526 free(kex->failed_choice);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000527 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000528}
529
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000530int
531kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000532{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000533 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000534
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000535 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
536 return r;
537 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
538 kex_free(ssh->kex);
539 ssh->kex = NULL;
540 return r;
Damien Millera664e872000-04-16 11:52:47 +1000541 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000542 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000543}
544
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000545static int
546choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000547{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000548 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000549
Damien Millera664e872000-04-16 11:52:47 +1000550 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000551 return SSH_ERR_NO_CIPHER_ALG_MATCH;
Damien Miller963f6b22002-02-19 15:21:23 +1100552 if ((enc->cipher = cipher_by_name(name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000553 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000554 enc->name = name;
555 enc->enabled = 0;
556 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100557 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000558 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100559 enc->key_len = cipher_keylen(enc->cipher);
560 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000561 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000562}
Damien Miller4f7becb2006-03-26 14:10:14 +1100563
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000564static int
565choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000566{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000567 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000568
Damien Millera664e872000-04-16 11:52:47 +1000569 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000570 return SSH_ERR_NO_MAC_ALG_MATCH;
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000571 if (mac_setup(mac, name) < 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000572 return SSH_ERR_INTERNAL_ERROR;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000573 /* truncate the key */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000574 if (ssh->compat & SSH_BUG_HMAC)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000575 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000576 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000577 mac->key = NULL;
578 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000579 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000580}
Damien Miller4f7becb2006-03-26 14:10:14 +1100581
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000582static int
583choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000584{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000585 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000586
Damien Millera664e872000-04-16 11:52:47 +1000587 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000588 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000589 if (strcmp(name, "zlib@openssh.com") == 0) {
590 comp->type = COMP_DELAYED;
591 } else if (strcmp(name, "zlib") == 0) {
592 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000593 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000594 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000595 } else {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000596 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000597 }
598 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000599 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000600}
Damien Miller4f7becb2006-03-26 14:10:14 +1100601
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000602static int
603choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000604{
Damien Millerea111192013-04-23 19:24:32 +1000605 const struct kexalg *kexalg;
606
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000607 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000608
djm@openbsd.org9690b782015-08-21 23:57:48 +0000609 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000610 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000611 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000612 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000613 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000614 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100615 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000616 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000617 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000618}
Damien Miller19bb3a52005-11-05 15:19:35 +1100619
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000620static int
621choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000622{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000623 char *hostkeyalg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000624
djm@openbsd.org9690b782015-08-21 23:57:48 +0000625 debug("kex: host key algorithm: %s",
626 hostkeyalg ? hostkeyalg : "(no match)");
Damien Miller0bc1bd82000-11-13 22:57:25 +1100627 if (hostkeyalg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000628 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
629 k->hostkey_type = sshkey_type_from_name(hostkeyalg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100630 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000631 return SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org5104db72015-01-26 06:10:03 +0000632 k->hostkey_nid = sshkey_ecdsa_nid_from_name(hostkeyalg);
Darren Tuckera627d422013-06-02 07:31:17 +1000633 free(hostkeyalg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000634 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000635}
636
Damien Millera8e06ce2003-11-21 23:48:55 +1100637static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100638proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
639{
640 static int check[] = {
641 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
642 };
643 int *idx;
644 char *p;
645
646 for (idx = &check[0]; *idx != -1; idx++) {
647 if ((p = strchr(my[*idx], ',')) != NULL)
648 *p = '\0';
649 if ((p = strchr(peer[*idx], ',')) != NULL)
650 *p = '\0';
651 if (strcmp(my[*idx], peer[*idx]) != 0) {
652 debug2("proposal mismatch: my %s peer %s",
653 my[*idx], peer[*idx]);
654 return (0);
655 }
656 }
657 debug2("proposals match");
658 return (1);
659}
660
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000661static int
662kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000663{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000664 struct kex *kex = ssh->kex;
665 struct newkeys *newkeys;
666 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000667 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000668 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100669 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000670 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000671
djm@openbsd.org9690b782015-08-21 23:57:48 +0000672 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
673 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
674 goto out;
675 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
676 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000677 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000678
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000679 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000680 cprop=peer;
681 sprop=my;
682 } else {
683 cprop=my;
684 sprop=peer;
685 }
Damien Millera664e872000-04-16 11:52:47 +1000686
Darren Tucker36331b52010-01-08 16:50:41 +1100687 /* Check whether server offers roaming */
688 if (!kex->server) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000689 char *roaming = match_list(KEX_RESUME,
690 peer[PROPOSAL_KEX_ALGS], NULL);
691
Darren Tucker36331b52010-01-08 16:50:41 +1100692 if (roaming) {
693 kex->roaming = 1;
Darren Tuckera627d422013-06-02 07:31:17 +1000694 free(roaming);
Darren Tucker36331b52010-01-08 16:50:41 +1100695 }
696 }
697
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000698 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000699 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
700 sprop[PROPOSAL_KEX_ALGS])) != 0) {
701 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
702 peer[PROPOSAL_KEX_ALGS] = NULL;
703 goto out;
704 }
705 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
706 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
707 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
708 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
709 goto out;
710 }
Damien Millera664e872000-04-16 11:52:47 +1000711 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000712 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
713 r = SSH_ERR_ALLOC_FAIL;
714 goto out;
715 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000716 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000717 ctos = (!kex->server && mode == MODE_OUT) ||
718 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000719 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
720 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
721 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000722 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000723 sprop[nenc])) != 0) {
724 kex->failed_choice = peer[nenc];
725 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000726 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000727 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100728 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000729 /* ignore mac for authenticated encryption */
730 if (authlen == 0 &&
731 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000732 sprop[nmac])) != 0) {
733 kex->failed_choice = peer[nmac];
734 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000735 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000736 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000737 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000738 sprop[ncomp])) != 0) {
739 kex->failed_choice = peer[ncomp];
740 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000741 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000742 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000743 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000744 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000745 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100746 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000747 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000748 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100749 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000750 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000751 newkeys = kex->newkeys[mode];
Damien Millera92ac742014-01-26 09:38:03 +1100752 need = MAX(need, newkeys->enc.key_len);
753 need = MAX(need, newkeys->enc.block_size);
754 need = MAX(need, newkeys->enc.iv_len);
755 need = MAX(need, newkeys->mac.key_len);
756 dh_need = MAX(dh_need, cipher_seclen(newkeys->enc.cipher));
757 dh_need = MAX(dh_need, newkeys->enc.block_size);
758 dh_need = MAX(dh_need, newkeys->enc.iv_len);
759 dh_need = MAX(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000760 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000761 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000762 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100763 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000764
Damien Millerbabb47a2003-02-24 11:53:32 +1100765 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100766 if (first_kex_follows && !proposals_match(my, peer) &&
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000767 !(ssh->compat & SSH_BUG_FIRSTKEX))
768 ssh->dispatch_skip_packets = 1;
769 r = 0;
770 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000771 kex_prop_free(my);
772 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000773 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000774}
775
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000776static int
777derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
778 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000779{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000780 struct kex *kex = ssh->kex;
781 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000782 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000783 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100784 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000785 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000786 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000787
Damien Millerb3051d02014-01-10 10:58:53 +1100788 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000789 return SSH_ERR_INVALID_ARGUMENT;
790 if ((digest = calloc(1, roundup(need, mdsz))) == NULL) {
791 r = SSH_ERR_ALLOC_FAIL;
792 goto out;
793 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000794
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000795 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000796 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
797 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100798 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
799 ssh_digest_update(hashctx, &c, 1) != 0 ||
800 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000801 kex->session_id_len) != 0 ||
802 ssh_digest_final(hashctx, digest, mdsz) != 0) {
803 r = SSH_ERR_LIBCRYPTO_ERROR;
804 goto out;
805 }
Damien Millerb3051d02014-01-10 10:58:53 +1100806 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000807 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000808
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000809 /*
810 * expand key:
811 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
812 * Key = K1 || K2 || ... || Kn
813 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000814 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000815 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
816 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100817 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000818 ssh_digest_update(hashctx, digest, have) != 0 ||
819 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
820 r = SSH_ERR_LIBCRYPTO_ERROR;
821 goto out;
822 }
Damien Millerb3051d02014-01-10 10:58:53 +1100823 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000824 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000825 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000826#ifdef DEBUG_KEX
827 fprintf(stderr, "key '%c'== ", c);
828 dump_digest("key", digest, need);
829#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000830 *keyp = digest;
831 digest = NULL;
832 r = 0;
833 out:
834 if (digest)
835 free(digest);
836 ssh_digest_free(hashctx);
837 return r;
Damien Millera664e872000-04-16 11:52:47 +1000838}
839
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000840#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000841int
842kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
843 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000844{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000845 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000846 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000847 u_int i, j, mode, ctos;
848 int r;
Damien Millera664e872000-04-16 11:52:47 +1000849
Damien Miller19bb3a52005-11-05 15:19:35 +1100850 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000851 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
852 shared_secret, &keys[i])) != 0) {
853 for (j = 0; j < i; j++)
854 free(keys[j]);
855 return r;
856 }
Damien Miller19bb3a52005-11-05 15:19:35 +1100857 }
Damien Millera664e872000-04-16 11:52:47 +1000858 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +1100859 ctos = (!kex->server && mode == MODE_OUT) ||
860 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000861 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
862 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
863 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000864 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000865 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000866}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000867
Damien Miller1f0311c2014-05-15 14:24:09 +1000868#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000869int
870kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
871 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +1100872{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000873 struct sshbuf *shared_secret;
874 int r;
Damien Miller91b580e2014-01-12 19:21:22 +1100875
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000876 if ((shared_secret = sshbuf_new()) == NULL)
877 return SSH_ERR_ALLOC_FAIL;
878 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
879 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
880 sshbuf_free(shared_secret);
881 return r;
Damien Miller91b580e2014-01-12 19:21:22 +1100882}
Damien Miller1f0311c2014-05-15 14:24:09 +1000883#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100884
Damien Miller1f0311c2014-05-15 14:24:09 +1000885#ifdef WITH_SSH1
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000886int
Darren Tuckere14e0052004-05-13 16:30:44 +1000887derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
888 u_int8_t cookie[8], u_int8_t id[16])
889{
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000890 u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000891 struct ssh_digest_ctx *hashctx = NULL;
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000892 size_t hlen, slen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000893 int r;
Darren Tuckere14e0052004-05-13 16:30:44 +1000894
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000895 hlen = BN_num_bytes(host_modulus);
896 slen = BN_num_bytes(server_modulus);
897 if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) ||
898 slen < (512 / 8) || (u_int)slen > sizeof(sbuf))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000899 return SSH_ERR_KEY_BITS_MISMATCH;
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000900 if (BN_bn2bin(host_modulus, hbuf) <= 0 ||
901 BN_bn2bin(server_modulus, sbuf) <= 0) {
902 r = SSH_ERR_LIBCRYPTO_ERROR;
903 goto out;
904 }
905 if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) {
906 r = SSH_ERR_ALLOC_FAIL;
907 goto out;
908 }
909 if (ssh_digest_update(hashctx, hbuf, hlen) != 0 ||
910 ssh_digest_update(hashctx, sbuf, slen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000911 ssh_digest_update(hashctx, cookie, 8) != 0 ||
912 ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) {
913 r = SSH_ERR_LIBCRYPTO_ERROR;
914 goto out;
915 }
Damien Millerb3051d02014-01-10 10:58:53 +1100916 memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000917 r = 0;
918 out:
919 ssh_digest_free(hashctx);
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000920 explicit_bzero(hbuf, sizeof(hbuf));
921 explicit_bzero(sbuf, sizeof(sbuf));
Damien Millera5103f42014-02-04 11:20:14 +1100922 explicit_bzero(obuf, sizeof(obuf));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000923 return r;
Darren Tuckere14e0052004-05-13 16:30:44 +1000924}
Damien Miller1f0311c2014-05-15 14:24:09 +1000925#endif
Darren Tuckere14e0052004-05-13 16:30:44 +1000926
Damien Millereb8b60e2010-08-31 22:41:14 +1000927#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000928void
929dump_digest(char *msg, u_char *digest, int len)
930{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000931 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000932 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000933}
934#endif