blob: 430cd8868347297722070d11d0f117c4de85a120 [file] [log] [blame]
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +00001/* $OpenBSD: kex.c,v 1.118 2016/05/02 10:26:04 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"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000052
53#include "ssherr.h"
54#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110055#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100056
Damien Millerb3092032006-03-16 18:22:18 +110057#if OPENSSL_VERSION_NUMBER >= 0x00907000L
58# if defined(HAVE_EVP_SHA256)
Damien Milleraf87af12006-03-15 13:02:28 +110059# define evp_ssh_sha256 EVP_sha256
Damien Millerb3092032006-03-16 18:22:18 +110060# else
Damien Millera63128d2006-03-15 12:08:28 +110061extern const EVP_MD *evp_ssh_sha256(void);
Damien Millerb3092032006-03-16 18:22:18 +110062# endif
Tim Rice425a6882006-03-15 20:17:05 -080063#endif
Damien Millera63128d2006-03-15 12:08:28 +110064
Ben Lindstrombba81212001-06-25 05:01:22 +000065/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000066static int kex_choose_conf(struct ssh *);
67static int kex_input_newkeys(int, u_int32_t, void *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000068
djm@openbsd.org9690b782015-08-21 23:57:48 +000069static const char *proposal_names[PROPOSAL_MAX] = {
70 "KEX algorithms",
71 "host key algorithms",
72 "ciphers ctos",
73 "ciphers stoc",
74 "MACs ctos",
75 "MACs stoc",
76 "compression ctos",
77 "compression stoc",
78 "languages ctos",
79 "languages stoc",
80};
81
Damien Millerea111192013-04-23 19:24:32 +100082struct kexalg {
83 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000084 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100085 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110086 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100087};
88static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100089#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110090 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +000091 { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
92 { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
93 { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
94 { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
Damien Millerb3051d02014-01-10 10:58:53 +110095 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100096#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110097 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100098#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100099#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +1100100 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
101 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
102 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
103 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +1100104# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +1100105 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
106 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +1000107# endif /* OPENSSL_HAS_NISTP521 */
108#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000109#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100110#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100111 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100112#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Damien Millerb3051d02014-01-10 10:58:53 +1100113 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000114};
115
116char *
Damien Miller690d9892013-11-08 12:16:49 +1100117kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000118{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000119 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000120 size_t nlen, rlen = 0;
121 const struct kexalg *k;
122
123 for (k = kexalgs; k->name != NULL; k++) {
124 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100125 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000126 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000127 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
128 free(ret);
129 return NULL;
130 }
131 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000132 memcpy(ret + rlen, k->name, nlen + 1);
133 rlen += nlen;
134 }
135 return ret;
136}
137
138static const struct kexalg *
139kex_alg_by_name(const char *name)
140{
141 const struct kexalg *k;
142
143 for (k = kexalgs; k->name != NULL; k++) {
144 if (strcmp(k->name, name) == 0)
145 return k;
146 }
147 return NULL;
148}
149
Damien Millerd5f62bf2010-09-24 22:11:14 +1000150/* Validate KEX method name list */
151int
152kex_names_valid(const char *names)
153{
154 char *s, *cp, *p;
155
156 if (names == NULL || strcmp(names, "") == 0)
157 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000158 if ((s = cp = strdup(names)) == NULL)
159 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000160 for ((p = strsep(&cp, ",")); p && *p != '\0';
161 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000162 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000163 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000164 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000165 return 0;
166 }
167 }
168 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000169 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000170 return 1;
171}
172
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000173/*
174 * Concatenate algorithm names, avoiding duplicates in the process.
175 * Caller must free returned string.
176 */
177char *
178kex_names_cat(const char *a, const char *b)
179{
180 char *ret = NULL, *tmp = NULL, *cp, *p;
181 size_t len;
182
183 if (a == NULL || *a == '\0')
184 return NULL;
185 if (b == NULL || *b == '\0')
186 return strdup(a);
187 if (strlen(b) > 1024*1024)
188 return NULL;
189 len = strlen(a) + strlen(b) + 2;
190 if ((tmp = cp = strdup(b)) == NULL ||
191 (ret = calloc(1, len)) == NULL) {
192 free(tmp);
193 return NULL;
194 }
195 strlcpy(ret, a, len);
196 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
197 if (match_list(ret, p, NULL) != NULL)
198 continue; /* Algorithm already present */
199 if (strlcat(ret, ",", len) >= len ||
200 strlcat(ret, p, len) >= len) {
201 free(tmp);
202 free(ret);
203 return NULL; /* Shouldn't happen */
204 }
205 }
206 free(tmp);
207 return ret;
208}
209
210/*
211 * Assemble a list of algorithms from a default list and a string from a
212 * configuration file. The user-provided string may begin with '+' to
213 * indicate that it should be appended to the default.
214 */
215int
216kex_assemble_names(const char *def, char **list)
217{
218 char *ret;
219
220 if (list == NULL || *list == NULL || **list == '\0') {
221 *list = strdup(def);
222 return 0;
223 }
224 if (**list != '+') {
225 return 0;
226 }
227
228 if ((ret = kex_names_cat(def, *list + 1)) == NULL)
229 return SSH_ERR_ALLOC_FAIL;
230 free(*list);
231 *list = ret;
232 return 0;
233}
234
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000235/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000236int
237kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000238{
Damien Millereccb9de2005-06-17 12:59:34 +1000239 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000240 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000241
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000242 sshbuf_reset(b);
243
Ben Lindstrom59971722002-03-27 17:42:57 +0000244 /*
245 * add a dummy cookie, the cookie will be overwritten by
246 * kex_send_kexinit(), each time a kexinit is set
247 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000248 for (i = 0; i < KEX_COOKIE_LEN; i++) {
249 if ((r = sshbuf_put_u8(b, 0)) != 0)
250 return r;
251 }
252 for (i = 0; i < PROPOSAL_MAX; i++) {
253 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
254 return r;
255 }
256 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
257 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
258 return r;
259 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000260}
261
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000262/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000263int
264kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000265{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000266 struct sshbuf *b = NULL;
267 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000268 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000269 char **proposal = NULL;
270 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000271
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000272 *propp = NULL;
273 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
274 return SSH_ERR_ALLOC_FAIL;
275 if ((b = sshbuf_fromb(raw)) == NULL) {
276 r = SSH_ERR_ALLOC_FAIL;
277 goto out;
278 }
279 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
280 goto out;
Damien Millerb1715dc2000-05-30 13:44:51 +1000281 /* extract kex init proposal strings */
282 for (i = 0; i < PROPOSAL_MAX; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000283 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
284 goto out;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000285 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000286 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000287 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000288 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
289 (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000290 goto out;
Damien Millerbabb47a2003-02-24 11:53:32 +1100291 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000292 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000293 debug2("first_kex_follows %d ", v);
294 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000295 r = 0;
296 *propp = proposal;
297 out:
298 if (r != 0 && proposal != NULL)
299 kex_prop_free(proposal);
300 sshbuf_free(b);
301 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000302}
303
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000304void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000305kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000306{
Damien Millereccb9de2005-06-17 12:59:34 +1000307 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000308
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000309 if (proposal == NULL)
310 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000311 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000312 free(proposal[i]);
313 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000314}
315
Darren Tucker0d0d1952007-06-05 18:23:28 +1000316/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000317static int
Damien Miller630d6f42002-01-22 23:17:30 +1100318kex_protocol_error(int type, u_int32_t seq, void *ctxt)
Damien Miller874d77b2000-10-14 16:23:11 +1100319{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000320 struct ssh *ssh = active_state; /* XXX */
321 int r;
322
323 error("kex protocol error: type %d seq %u", type, seq);
324 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
325 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
326 (r = sshpkt_send(ssh)) != 0)
327 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000328 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100329}
330
Ben Lindstrombba81212001-06-25 05:01:22 +0000331static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000332kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000333{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000334 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100335 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000336 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000337}
338
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000339static int
340kex_send_ext_info(struct ssh *ssh)
341{
342 int r;
343
344 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
345 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
346 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
347 (r = sshpkt_put_cstring(ssh, "rsa-sha2-256,rsa-sha2-512")) != 0 ||
348 (r = sshpkt_send(ssh)) != 0)
349 return r;
350 return 0;
351}
352
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000353int
354kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000355{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000356 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000357
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000358 kex_reset_dispatch(ssh);
359 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
360 (r = sshpkt_send(ssh)) != 0)
361 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000362 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000363 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000364 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000365 if (ssh->kex->ext_info_c)
366 if ((r = kex_send_ext_info(ssh)) != 0)
367 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000368 return 0;
369}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000370
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000371int
372kex_input_ext_info(int type, u_int32_t seq, void *ctxt)
373{
374 struct ssh *ssh = ctxt;
375 struct kex *kex = ssh->kex;
376 u_int32_t i, ninfo;
377 char *name, *val, *found;
378 int r;
379
380 debug("SSH2_MSG_EXT_INFO received");
381 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
382 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
383 return r;
384 for (i = 0; i < ninfo; i++) {
385 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
386 return r;
387 if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) {
388 free(name);
389 return r;
390 }
391 debug("%s: %s=<%s>", __func__, name, val);
392 if (strcmp(name, "server-sig-algs") == 0) {
393 found = match_list("rsa-sha2-256", val, NULL);
394 if (found) {
395 kex->rsa_sha2 = 256;
396 free(found);
397 }
398 found = match_list("rsa-sha2-512", val, NULL);
399 if (found) {
400 kex->rsa_sha2 = 512;
401 free(found);
402 }
403 }
404 free(name);
405 free(val);
406 }
407 return sshpkt_get_end(ssh);
408}
409
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000410static int
411kex_input_newkeys(int type, u_int32_t seq, void *ctxt)
412{
413 struct ssh *ssh = ctxt;
414 struct kex *kex = ssh->kex;
415 int r;
416
417 debug("SSH2_MSG_NEWKEYS received");
418 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
419 if ((r = sshpkt_get_end(ssh)) != 0)
420 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000421 kex->done = 1;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000422 sshbuf_reset(kex->peer);
423 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000424 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000425 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000426 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000427 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000428}
Damien Millera664e872000-04-16 11:52:47 +1000429
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000430int
431kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000432{
Ben Lindstrom59971722002-03-27 17:42:57 +0000433 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000434 struct kex *kex = ssh->kex;
435 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000436
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000437 if (kex == NULL)
438 return SSH_ERR_INTERNAL_ERROR;
439 if (kex->flags & KEX_INIT_SENT)
440 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000441 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000442
443 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000444 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
445 return SSH_ERR_INVALID_FORMAT;
446 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
447 return SSH_ERR_INTERNAL_ERROR;
448 arc4random_buf(cookie, KEX_COOKIE_LEN);
449
450 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
451 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
452 (r = sshpkt_send(ssh)) != 0)
453 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000454 debug("SSH2_MSG_KEXINIT sent");
455 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000456 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000457}
458
Darren Tucker0d0d1952007-06-05 18:23:28 +1000459/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000460int
Damien Miller630d6f42002-01-22 23:17:30 +1100461kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000462{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000463 struct ssh *ssh = ctxt;
464 struct kex *kex = ssh->kex;
465 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000466 u_int i;
467 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000468 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000469
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000470 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000471 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000472 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000473
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000474 ptr = sshpkt_ptr(ssh, &dlen);
475 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
476 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000477
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000478 /* discard packet */
479 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000480 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
481 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000482 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000483 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
484 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000485 /*
486 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
487 * KEX method has the server move first, but a server might be using
488 * a custom method or one that we otherwise don't support. We should
489 * be prepared to remember first_kex_follows here so we can eat a
490 * packet later.
491 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
492 * for cases where the server *doesn't* go first. I guess we should
493 * ignore it when it is set for these cases, which is what we do now.
494 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000495 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
496 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
497 (r = sshpkt_get_end(ssh)) != 0)
498 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000499
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000500 if (!(kex->flags & KEX_INIT_SENT))
501 if ((r = kex_send_kexinit(ssh)) != 0)
502 return r;
503 if ((r = kex_choose_conf(ssh)) != 0)
504 return r;
505
506 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
507 return (kex->kex[kex->kex_type])(ssh);
508
509 return SSH_ERR_INTERNAL_ERROR;
510}
511
512int
513kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
514{
515 struct kex *kex;
516 int r;
517
518 *kexp = NULL;
519 if ((kex = calloc(1, sizeof(*kex))) == NULL)
520 return SSH_ERR_ALLOC_FAIL;
521 if ((kex->peer = sshbuf_new()) == NULL ||
522 (kex->my = sshbuf_new()) == NULL) {
523 r = SSH_ERR_ALLOC_FAIL;
524 goto out;
525 }
526 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
527 goto out;
528 kex->done = 0;
529 kex_reset_dispatch(ssh);
530 r = 0;
531 *kexp = kex;
532 out:
533 if (r != 0)
534 kex_free(kex);
535 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000536}
537
markus@openbsd.org091c3022015-01-19 19:52:16 +0000538void
539kex_free_newkeys(struct newkeys *newkeys)
540{
541 if (newkeys == NULL)
542 return;
543 if (newkeys->enc.key) {
544 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
545 free(newkeys->enc.key);
546 newkeys->enc.key = NULL;
547 }
548 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000549 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000550 free(newkeys->enc.iv);
551 newkeys->enc.iv = NULL;
552 }
553 free(newkeys->enc.name);
554 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
555 free(newkeys->comp.name);
556 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
557 mac_clear(&newkeys->mac);
558 if (newkeys->mac.key) {
559 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
560 free(newkeys->mac.key);
561 newkeys->mac.key = NULL;
562 }
563 free(newkeys->mac.name);
564 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
565 explicit_bzero(newkeys, sizeof(*newkeys));
566 free(newkeys);
567}
568
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000569void
570kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000571{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000572 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000573
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000574#ifdef WITH_OPENSSL
575 if (kex->dh)
576 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100577#ifdef OPENSSL_HAS_ECC
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000578 if (kex->ec_client_key)
579 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100580#endif /* OPENSSL_HAS_ECC */
581#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000582 for (mode = 0; mode < MODE_MAX; mode++) {
583 kex_free_newkeys(kex->newkeys[mode]);
584 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000585 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000586 sshbuf_free(kex->peer);
587 sshbuf_free(kex->my);
588 free(kex->session_id);
589 free(kex->client_version_string);
590 free(kex->server_version_string);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000591 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000592 free(kex->hostkey_alg);
593 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000594 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000595}
596
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000597int
598kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000599{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000600 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000601
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000602 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
603 return r;
604 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
605 kex_free(ssh->kex);
606 ssh->kex = NULL;
607 return r;
Damien Millera664e872000-04-16 11:52:47 +1000608 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000609 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000610}
611
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000612/*
613 * Request key re-exchange, returns 0 on success or a ssherr.h error
614 * code otherwise. Must not be called if KEX is incomplete or in-progress.
615 */
616int
617kex_start_rekex(struct ssh *ssh)
618{
619 if (ssh->kex == NULL) {
620 error("%s: no kex", __func__);
621 return SSH_ERR_INTERNAL_ERROR;
622 }
623 if (ssh->kex->done == 0) {
624 error("%s: requested twice", __func__);
625 return SSH_ERR_INTERNAL_ERROR;
626 }
627 ssh->kex->done = 0;
628 return kex_send_kexinit(ssh);
629}
630
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000631static int
632choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000633{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000634 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000635
Damien Millera664e872000-04-16 11:52:47 +1000636 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000637 return SSH_ERR_NO_CIPHER_ALG_MATCH;
Damien Miller963f6b22002-02-19 15:21:23 +1100638 if ((enc->cipher = cipher_by_name(name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000639 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000640 enc->name = name;
641 enc->enabled = 0;
642 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100643 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000644 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100645 enc->key_len = cipher_keylen(enc->cipher);
646 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000647 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000648}
Damien Miller4f7becb2006-03-26 14:10:14 +1100649
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000650static int
651choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000652{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000653 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000654
Damien Millera664e872000-04-16 11:52:47 +1000655 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000656 return SSH_ERR_NO_MAC_ALG_MATCH;
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000657 if (mac_setup(mac, name) < 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000658 return SSH_ERR_INTERNAL_ERROR;
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000659 /* truncate the key */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000660 if (ssh->compat & SSH_BUG_HMAC)
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000661 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000662 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000663 mac->key = NULL;
664 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000665 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000666}
Damien Miller4f7becb2006-03-26 14:10:14 +1100667
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000668static int
669choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000670{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000671 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000672
Damien Millera664e872000-04-16 11:52:47 +1000673 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000674 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000675 if (strcmp(name, "zlib@openssh.com") == 0) {
676 comp->type = COMP_DELAYED;
677 } else if (strcmp(name, "zlib") == 0) {
678 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000679 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000680 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000681 } else {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000682 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000683 }
684 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000685 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000686}
Damien Miller4f7becb2006-03-26 14:10:14 +1100687
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000688static int
689choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000690{
Damien Millerea111192013-04-23 19:24:32 +1000691 const struct kexalg *kexalg;
692
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000693 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000694
djm@openbsd.org9690b782015-08-21 23:57:48 +0000695 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000696 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000697 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000698 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000699 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000700 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100701 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000702 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000703 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000704}
Damien Miller19bb3a52005-11-05 15:19:35 +1100705
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000706static int
707choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000708{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000709 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000710
djm@openbsd.org9690b782015-08-21 23:57:48 +0000711 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000712 k->hostkey_alg ? k->hostkey_alg : "(no match)");
713 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000714 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000715 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100716 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000717 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000718 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000720}
721
Damien Millera8e06ce2003-11-21 23:48:55 +1100722static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100723proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
724{
725 static int check[] = {
726 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
727 };
728 int *idx;
729 char *p;
730
731 for (idx = &check[0]; *idx != -1; idx++) {
732 if ((p = strchr(my[*idx], ',')) != NULL)
733 *p = '\0';
734 if ((p = strchr(peer[*idx], ',')) != NULL)
735 *p = '\0';
736 if (strcmp(my[*idx], peer[*idx]) != 0) {
737 debug2("proposal mismatch: my %s peer %s",
738 my[*idx], peer[*idx]);
739 return (0);
740 }
741 }
742 debug2("proposals match");
743 return (1);
744}
745
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000746static int
747kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000748{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000749 struct kex *kex = ssh->kex;
750 struct newkeys *newkeys;
751 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000752 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000753 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100754 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000755 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000756
djm@openbsd.org9690b782015-08-21 23:57:48 +0000757 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
758 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
759 goto out;
760 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
761 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000762 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000763
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000764 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000765 cprop=peer;
766 sprop=my;
767 } else {
768 cprop=my;
769 sprop=peer;
770 }
Damien Millera664e872000-04-16 11:52:47 +1000771
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000772 /* Check whether client supports ext_info_c */
773 if (kex->server) {
774 char *ext;
775
776 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
777 if (ext) {
778 kex->ext_info_c = 1;
779 free(ext);
780 }
781 }
782
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000783 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000784 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
785 sprop[PROPOSAL_KEX_ALGS])) != 0) {
786 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
787 peer[PROPOSAL_KEX_ALGS] = NULL;
788 goto out;
789 }
790 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
791 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
792 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
793 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
794 goto out;
795 }
Damien Millera664e872000-04-16 11:52:47 +1000796 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000797 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
798 r = SSH_ERR_ALLOC_FAIL;
799 goto out;
800 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000801 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000802 ctos = (!kex->server && mode == MODE_OUT) ||
803 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000804 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
805 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
806 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000807 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000808 sprop[nenc])) != 0) {
809 kex->failed_choice = peer[nenc];
810 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000811 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000812 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100813 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000814 /* ignore mac for authenticated encryption */
815 if (authlen == 0 &&
816 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000817 sprop[nmac])) != 0) {
818 kex->failed_choice = peer[nmac];
819 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000820 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000821 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000822 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000823 sprop[ncomp])) != 0) {
824 kex->failed_choice = peer[ncomp];
825 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000826 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000827 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000828 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000829 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000830 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100831 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000832 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000833 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100834 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000835 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000836 newkeys = kex->newkeys[mode];
Damien Millera92ac742014-01-26 09:38:03 +1100837 need = MAX(need, newkeys->enc.key_len);
838 need = MAX(need, newkeys->enc.block_size);
839 need = MAX(need, newkeys->enc.iv_len);
840 need = MAX(need, newkeys->mac.key_len);
841 dh_need = MAX(dh_need, cipher_seclen(newkeys->enc.cipher));
842 dh_need = MAX(dh_need, newkeys->enc.block_size);
843 dh_need = MAX(dh_need, newkeys->enc.iv_len);
844 dh_need = MAX(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000845 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000846 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000847 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100848 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000849
Damien Millerbabb47a2003-02-24 11:53:32 +1100850 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100851 if (first_kex_follows && !proposals_match(my, peer) &&
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000852 !(ssh->compat & SSH_BUG_FIRSTKEX))
853 ssh->dispatch_skip_packets = 1;
854 r = 0;
855 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000856 kex_prop_free(my);
857 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000858 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000859}
860
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000861static int
862derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
863 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000864{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000865 struct kex *kex = ssh->kex;
866 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000867 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000868 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100869 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000870 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000871 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000872
Damien Millerb3051d02014-01-10 10:58:53 +1100873 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000874 return SSH_ERR_INVALID_ARGUMENT;
875 if ((digest = calloc(1, roundup(need, mdsz))) == NULL) {
876 r = SSH_ERR_ALLOC_FAIL;
877 goto out;
878 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000879
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000880 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000881 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
882 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100883 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
884 ssh_digest_update(hashctx, &c, 1) != 0 ||
885 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000886 kex->session_id_len) != 0 ||
887 ssh_digest_final(hashctx, digest, mdsz) != 0) {
888 r = SSH_ERR_LIBCRYPTO_ERROR;
889 goto out;
890 }
Damien Millerb3051d02014-01-10 10:58:53 +1100891 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000892 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000893
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000894 /*
895 * expand key:
896 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
897 * Key = K1 || K2 || ... || Kn
898 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000899 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000900 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
901 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100902 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000903 ssh_digest_update(hashctx, digest, have) != 0 ||
904 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
905 r = SSH_ERR_LIBCRYPTO_ERROR;
906 goto out;
907 }
Damien Millerb3051d02014-01-10 10:58:53 +1100908 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000909 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000910 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000911#ifdef DEBUG_KEX
912 fprintf(stderr, "key '%c'== ", c);
913 dump_digest("key", digest, need);
914#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000915 *keyp = digest;
916 digest = NULL;
917 r = 0;
918 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +0000919 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000920 ssh_digest_free(hashctx);
921 return r;
Damien Millera664e872000-04-16 11:52:47 +1000922}
923
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000924#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000925int
926kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
927 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +1000928{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000929 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000930 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000931 u_int i, j, mode, ctos;
932 int r;
Damien Millera664e872000-04-16 11:52:47 +1000933
Damien Miller19bb3a52005-11-05 15:19:35 +1100934 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000935 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
936 shared_secret, &keys[i])) != 0) {
937 for (j = 0; j < i; j++)
938 free(keys[j]);
939 return r;
940 }
Damien Miller19bb3a52005-11-05 15:19:35 +1100941 }
Damien Millera664e872000-04-16 11:52:47 +1000942 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +1100943 ctos = (!kex->server && mode == MODE_OUT) ||
944 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000945 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
946 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
947 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000948 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000949 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000950}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000951
Damien Miller1f0311c2014-05-15 14:24:09 +1000952#ifdef WITH_OPENSSL
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000953int
954kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
955 const BIGNUM *secret)
Damien Miller91b580e2014-01-12 19:21:22 +1100956{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000957 struct sshbuf *shared_secret;
958 int r;
Damien Miller91b580e2014-01-12 19:21:22 +1100959
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000960 if ((shared_secret = sshbuf_new()) == NULL)
961 return SSH_ERR_ALLOC_FAIL;
962 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
963 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
964 sshbuf_free(shared_secret);
965 return r;
Damien Miller91b580e2014-01-12 19:21:22 +1100966}
Damien Miller1f0311c2014-05-15 14:24:09 +1000967#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100968
Damien Miller1f0311c2014-05-15 14:24:09 +1000969#ifdef WITH_SSH1
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000970int
Darren Tuckere14e0052004-05-13 16:30:44 +1000971derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
972 u_int8_t cookie[8], u_int8_t id[16])
973{
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000974 u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000975 struct ssh_digest_ctx *hashctx = NULL;
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000976 size_t hlen, slen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000977 int r;
Darren Tuckere14e0052004-05-13 16:30:44 +1000978
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000979 hlen = BN_num_bytes(host_modulus);
980 slen = BN_num_bytes(server_modulus);
981 if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) ||
982 slen < (512 / 8) || (u_int)slen > sizeof(sbuf))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000983 return SSH_ERR_KEY_BITS_MISMATCH;
djm@openbsd.org25f5f782015-01-30 00:22:25 +0000984 if (BN_bn2bin(host_modulus, hbuf) <= 0 ||
985 BN_bn2bin(server_modulus, sbuf) <= 0) {
986 r = SSH_ERR_LIBCRYPTO_ERROR;
987 goto out;
988 }
989 if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) {
990 r = SSH_ERR_ALLOC_FAIL;
991 goto out;
992 }
993 if (ssh_digest_update(hashctx, hbuf, hlen) != 0 ||
994 ssh_digest_update(hashctx, sbuf, slen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000995 ssh_digest_update(hashctx, cookie, 8) != 0 ||
996 ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) {
997 r = SSH_ERR_LIBCRYPTO_ERROR;
998 goto out;
999 }
Damien Millerb3051d02014-01-10 10:58:53 +11001000 memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001001 r = 0;
1002 out:
1003 ssh_digest_free(hashctx);
djm@openbsd.org25f5f782015-01-30 00:22:25 +00001004 explicit_bzero(hbuf, sizeof(hbuf));
1005 explicit_bzero(sbuf, sizeof(sbuf));
Damien Millera5103f42014-02-04 11:20:14 +11001006 explicit_bzero(obuf, sizeof(obuf));
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001007 return r;
Darren Tuckere14e0052004-05-13 16:30:44 +10001008}
Damien Miller1f0311c2014-05-15 14:24:09 +10001009#endif
Darren Tuckere14e0052004-05-13 16:30:44 +10001010
Damien Millereb8b60e2010-08-31 22:41:14 +10001011#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001012void
1013dump_digest(char *msg, u_char *digest, int len)
1014{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001015 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001016 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001017}
1018#endif