blob: cf4ac0dc574d138d8c42e26e8132dfad5b8a017c [file] [log] [blame]
Greg Hartman9768ca42017-06-22 20:49:52 -07001/* $OpenBSD: kex.c,v 1.131 2017/03/15 07:07:39 markus Exp $ */
Greg Hartmanbd77cf72015-02-25 13:21:06 -08002/*
3 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
4 *
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.
13 *
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"
27
Greg Hartmanbd77cf72015-02-25 13:21:06 -080028
29#include <signal.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
Adam Langleyd0592972015-03-30 14:49:51 -070035#ifdef WITH_OPENSSL
Greg Hartmanbd77cf72015-02-25 13:21:06 -080036#include <openssl/crypto.h>
Adam Langleyd0592972015-03-30 14:49:51 -070037#include <openssl/dh.h>
38#endif
Greg Hartmanbd77cf72015-02-25 13:21:06 -080039
Greg Hartmanbd77cf72015-02-25 13:21:06 -080040#include "ssh2.h"
Greg Hartmanbd77cf72015-02-25 13:21:06 -080041#include "packet.h"
42#include "compat.h"
43#include "cipher.h"
Adam Langleyd0592972015-03-30 14:49:51 -070044#include "sshkey.h"
Greg Hartmanbd77cf72015-02-25 13:21:06 -080045#include "kex.h"
46#include "log.h"
47#include "mac.h"
48#include "match.h"
Adam Langleyd0592972015-03-30 14:49:51 -070049#include "misc.h"
Greg Hartmanbd77cf72015-02-25 13:21:06 -080050#include "dispatch.h"
51#include "monitor.h"
Greg Hartmanbd77cf72015-02-25 13:21:06 -080052
Adam Langleyd0592972015-03-30 14:49:51 -070053#include "ssherr.h"
54#include "sshbuf.h"
55#include "digest.h"
56
Greg Hartmanbd77cf72015-02-25 13:21:06 -080057#if OPENSSL_VERSION_NUMBER >= 0x00907000L
58# if defined(HAVE_EVP_SHA256)
59# define evp_ssh_sha256 EVP_sha256
60# else
61extern const EVP_MD *evp_ssh_sha256(void);
62# endif
63#endif
64
65/* prototype */
Adam Langleyd0592972015-03-30 14:49:51 -070066static int kex_choose_conf(struct ssh *);
67static int kex_input_newkeys(int, u_int32_t, void *);
68
Greg Hartman9768ca42017-06-22 20:49:52 -070069static 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
Adam Langleyd0592972015-03-30 14:49:51 -070082struct kexalg {
83 char *name;
84 u_int type;
85 int ec_nid;
86 int hash_alg;
87};
88static const struct kexalg kexalgs[] = {
89#ifdef WITH_OPENSSL
90 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
Greg Hartman9768ca42017-06-22 20:49:52 -070091 { 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 },
Adam Langleyd0592972015-03-30 14:49:51 -070095 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
96#ifdef HAVE_EVP_SHA256
97 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
98#endif /* HAVE_EVP_SHA256 */
99#ifdef OPENSSL_HAS_ECC
100 { 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 },
104# ifdef OPENSSL_HAS_NISTP521
105 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
106 SSH_DIGEST_SHA512 },
107# endif /* OPENSSL_HAS_NISTP521 */
108#endif /* OPENSSL_HAS_ECC */
109#endif /* WITH_OPENSSL */
110#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
111 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Greg Hartman9768ca42017-06-22 20:49:52 -0700112 { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Adam Langleyd0592972015-03-30 14:49:51 -0700113#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
114 { NULL, -1, -1, -1},
115};
116
117char *
118kex_alg_list(char sep)
119{
120 char *ret = NULL, *tmp;
121 size_t nlen, rlen = 0;
122 const struct kexalg *k;
123
124 for (k = kexalgs; k->name != NULL; k++) {
125 if (ret != NULL)
126 ret[rlen++] = sep;
127 nlen = strlen(k->name);
128 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
129 free(ret);
130 return NULL;
131 }
132 ret = tmp;
133 memcpy(ret + rlen, k->name, nlen + 1);
134 rlen += nlen;
135 }
136 return ret;
137}
138
139static const struct kexalg *
140kex_alg_by_name(const char *name)
141{
142 const struct kexalg *k;
143
144 for (k = kexalgs; k->name != NULL; k++) {
145 if (strcmp(k->name, name) == 0)
146 return k;
147 }
148 return NULL;
149}
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800150
151/* Validate KEX method name list */
152int
153kex_names_valid(const char *names)
154{
155 char *s, *cp, *p;
156
157 if (names == NULL || strcmp(names, "") == 0)
158 return 0;
Adam Langleyd0592972015-03-30 14:49:51 -0700159 if ((s = cp = strdup(names)) == NULL)
160 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800161 for ((p = strsep(&cp, ",")); p && *p != '\0';
162 (p = strsep(&cp, ","))) {
Adam Langleyd0592972015-03-30 14:49:51 -0700163 if (kex_alg_by_name(p) == NULL) {
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800164 error("Unsupported KEX algorithm \"%.100s\"", p);
Adam Langleyd0592972015-03-30 14:49:51 -0700165 free(s);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800166 return 0;
167 }
168 }
169 debug3("kex names ok: [%s]", names);
Adam Langleyd0592972015-03-30 14:49:51 -0700170 free(s);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800171 return 1;
172}
173
Greg Hartmanccacbc92016-02-03 09:59:44 -0800174/*
175 * Concatenate algorithm names, avoiding duplicates in the process.
176 * Caller must free returned string.
177 */
178char *
179kex_names_cat(const char *a, const char *b)
180{
Greg Hartman9768ca42017-06-22 20:49:52 -0700181 char *ret = NULL, *tmp = NULL, *cp, *p, *m;
Greg Hartmanccacbc92016-02-03 09:59:44 -0800182 size_t len;
183
184 if (a == NULL || *a == '\0')
185 return NULL;
186 if (b == NULL || *b == '\0')
187 return strdup(a);
188 if (strlen(b) > 1024*1024)
189 return NULL;
190 len = strlen(a) + strlen(b) + 2;
191 if ((tmp = cp = strdup(b)) == NULL ||
192 (ret = calloc(1, len)) == NULL) {
193 free(tmp);
194 return NULL;
195 }
196 strlcpy(ret, a, len);
197 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
Greg Hartman9768ca42017-06-22 20:49:52 -0700198 if ((m = match_list(ret, p, NULL)) != NULL) {
199 free(m);
Greg Hartmanccacbc92016-02-03 09:59:44 -0800200 continue; /* Algorithm already present */
Greg Hartman9768ca42017-06-22 20:49:52 -0700201 }
Greg Hartmanccacbc92016-02-03 09:59:44 -0800202 if (strlcat(ret, ",", len) >= len ||
203 strlcat(ret, p, len) >= len) {
204 free(tmp);
205 free(ret);
206 return NULL; /* Shouldn't happen */
207 }
208 }
209 free(tmp);
210 return ret;
211}
212
213/*
214 * Assemble a list of algorithms from a default list and a string from a
215 * configuration file. The user-provided string may begin with '+' to
Greg Hartman9768ca42017-06-22 20:49:52 -0700216 * indicate that it should be appended to the default or '-' that the
217 * specified names should be removed.
Greg Hartmanccacbc92016-02-03 09:59:44 -0800218 */
219int
220kex_assemble_names(const char *def, char **list)
221{
222 char *ret;
223
224 if (list == NULL || *list == NULL || **list == '\0') {
225 *list = strdup(def);
226 return 0;
227 }
Greg Hartman9768ca42017-06-22 20:49:52 -0700228 if (**list == '+') {
229 if ((ret = kex_names_cat(def, *list + 1)) == NULL)
230 return SSH_ERR_ALLOC_FAIL;
231 free(*list);
232 *list = ret;
233 } else if (**list == '-') {
234 if ((ret = match_filter_list(def, *list + 1)) == NULL)
235 return SSH_ERR_ALLOC_FAIL;
236 free(*list);
237 *list = ret;
Greg Hartmanccacbc92016-02-03 09:59:44 -0800238 }
239
Greg Hartmanccacbc92016-02-03 09:59:44 -0800240 return 0;
241}
242
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800243/* put algorithm proposal into buffer */
Adam Langleyd0592972015-03-30 14:49:51 -0700244int
245kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800246{
247 u_int i;
Adam Langleyd0592972015-03-30 14:49:51 -0700248 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800249
Adam Langleyd0592972015-03-30 14:49:51 -0700250 sshbuf_reset(b);
251
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800252 /*
253 * add a dummy cookie, the cookie will be overwritten by
254 * kex_send_kexinit(), each time a kexinit is set
255 */
Adam Langleyd0592972015-03-30 14:49:51 -0700256 for (i = 0; i < KEX_COOKIE_LEN; i++) {
257 if ((r = sshbuf_put_u8(b, 0)) != 0)
258 return r;
259 }
260 for (i = 0; i < PROPOSAL_MAX; i++) {
261 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
262 return r;
263 }
264 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
265 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
266 return r;
267 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800268}
269
270/* parse buffer and return algorithm proposal */
Adam Langleyd0592972015-03-30 14:49:51 -0700271int
272kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800273{
Adam Langleyd0592972015-03-30 14:49:51 -0700274 struct sshbuf *b = NULL;
275 u_char v;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800276 u_int i;
Adam Langleyd0592972015-03-30 14:49:51 -0700277 char **proposal = NULL;
278 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800279
Adam Langleyd0592972015-03-30 14:49:51 -0700280 *propp = NULL;
281 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
282 return SSH_ERR_ALLOC_FAIL;
283 if ((b = sshbuf_fromb(raw)) == NULL) {
284 r = SSH_ERR_ALLOC_FAIL;
285 goto out;
286 }
287 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
288 goto out;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800289 /* extract kex init proposal strings */
290 for (i = 0; i < PROPOSAL_MAX; i++) {
Adam Langleyd0592972015-03-30 14:49:51 -0700291 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
292 goto out;
Greg Hartman9768ca42017-06-22 20:49:52 -0700293 debug2("%s: %s", proposal_names[i], proposal[i]);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800294 }
295 /* first kex follows / reserved */
Greg Hartmanccacbc92016-02-03 09:59:44 -0800296 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
297 (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */
Adam Langleyd0592972015-03-30 14:49:51 -0700298 goto out;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800299 if (first_kex_follows != NULL)
Greg Hartmanccacbc92016-02-03 09:59:44 -0800300 *first_kex_follows = v;
301 debug2("first_kex_follows %d ", v);
302 debug2("reserved %u ", i);
Adam Langleyd0592972015-03-30 14:49:51 -0700303 r = 0;
304 *propp = proposal;
305 out:
306 if (r != 0 && proposal != NULL)
307 kex_prop_free(proposal);
308 sshbuf_free(b);
309 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800310}
311
Adam Langleyd0592972015-03-30 14:49:51 -0700312void
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800313kex_prop_free(char **proposal)
314{
315 u_int i;
316
Greg Hartmanccacbc92016-02-03 09:59:44 -0800317 if (proposal == NULL)
318 return;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800319 for (i = 0; i < PROPOSAL_MAX; i++)
Adam Langleyd0592972015-03-30 14:49:51 -0700320 free(proposal[i]);
321 free(proposal);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800322}
323
324/* ARGSUSED */
Adam Langleyd0592972015-03-30 14:49:51 -0700325static int
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800326kex_protocol_error(int type, u_int32_t seq, void *ctxt)
327{
Greg Hartman9768ca42017-06-22 20:49:52 -0700328 struct ssh *ssh = active_state; /* XXX */
329 int r;
330
331 error("kex protocol error: type %d seq %u", type, seq);
332 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
333 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
334 (r = sshpkt_send(ssh)) != 0)
335 return r;
Adam Langleyd0592972015-03-30 14:49:51 -0700336 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800337}
338
339static void
Adam Langleyd0592972015-03-30 14:49:51 -0700340kex_reset_dispatch(struct ssh *ssh)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800341{
Adam Langleyd0592972015-03-30 14:49:51 -0700342 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800343 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Greg Hartman9768ca42017-06-22 20:49:52 -0700344}
345
346static int
347kex_send_ext_info(struct ssh *ssh)
348{
349 int r;
350 char *algs;
351
352 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
353 return SSH_ERR_ALLOC_FAIL;
354 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
355 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
356 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
357 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
358 (r = sshpkt_send(ssh)) != 0)
359 goto out;
360 /* success */
361 r = 0;
362 out:
363 free(algs);
364 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800365}
366
Adam Langleyd0592972015-03-30 14:49:51 -0700367int
368kex_send_newkeys(struct ssh *ssh)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800369{
Adam Langleyd0592972015-03-30 14:49:51 -0700370 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800371
Adam Langleyd0592972015-03-30 14:49:51 -0700372 kex_reset_dispatch(ssh);
373 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
374 (r = sshpkt_send(ssh)) != 0)
375 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800376 debug("SSH2_MSG_NEWKEYS sent");
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800377 debug("expecting SSH2_MSG_NEWKEYS");
Adam Langleyd0592972015-03-30 14:49:51 -0700378 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
Greg Hartman9768ca42017-06-22 20:49:52 -0700379 if (ssh->kex->ext_info_c)
380 if ((r = kex_send_ext_info(ssh)) != 0)
381 return r;
Adam Langleyd0592972015-03-30 14:49:51 -0700382 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800383}
384
Greg Hartman9768ca42017-06-22 20:49:52 -0700385int
386kex_input_ext_info(int type, u_int32_t seq, void *ctxt)
387{
388 struct ssh *ssh = ctxt;
389 struct kex *kex = ssh->kex;
390 u_int32_t i, ninfo;
391 char *name, *val, *found;
392 int r;
393
394 debug("SSH2_MSG_EXT_INFO received");
395 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
396 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
397 return r;
398 for (i = 0; i < ninfo; i++) {
399 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
400 return r;
401 if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) {
402 free(name);
403 return r;
404 }
405 debug("%s: %s=<%s>", __func__, name, val);
406 if (strcmp(name, "server-sig-algs") == 0) {
407 found = match_list("rsa-sha2-256", val, NULL);
408 if (found) {
409 kex->rsa_sha2 = 256;
410 free(found);
411 }
412 found = match_list("rsa-sha2-512", val, NULL);
413 if (found) {
414 kex->rsa_sha2 = 512;
415 free(found);
416 }
417 }
418 free(name);
419 free(val);
420 }
421 return sshpkt_get_end(ssh);
422}
423
Adam Langleyd0592972015-03-30 14:49:51 -0700424static int
425kex_input_newkeys(int type, u_int32_t seq, void *ctxt)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800426{
Adam Langleyd0592972015-03-30 14:49:51 -0700427 struct ssh *ssh = ctxt;
428 struct kex *kex = ssh->kex;
429 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800430
Adam Langleyd0592972015-03-30 14:49:51 -0700431 debug("SSH2_MSG_NEWKEYS received");
432 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
Greg Hartman9768ca42017-06-22 20:49:52 -0700433 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
Adam Langleyd0592972015-03-30 14:49:51 -0700434 if ((r = sshpkt_get_end(ssh)) != 0)
435 return r;
Greg Hartman9768ca42017-06-22 20:49:52 -0700436 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
437 return r;
Adam Langleyd0592972015-03-30 14:49:51 -0700438 kex->done = 1;
439 sshbuf_reset(kex->peer);
440 /* sshbuf_reset(kex->my); */
441 kex->flags &= ~KEX_INIT_SENT;
442 free(kex->name);
443 kex->name = NULL;
444 return 0;
445}
446
447int
448kex_send_kexinit(struct ssh *ssh)
449{
450 u_char *cookie;
451 struct kex *kex = ssh->kex;
452 int r;
453
454 if (kex == NULL)
455 return SSH_ERR_INTERNAL_ERROR;
456 if (kex->flags & KEX_INIT_SENT)
457 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800458 kex->done = 0;
459
460 /* generate a random cookie */
Adam Langleyd0592972015-03-30 14:49:51 -0700461 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
462 return SSH_ERR_INVALID_FORMAT;
463 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
464 return SSH_ERR_INTERNAL_ERROR;
465 arc4random_buf(cookie, KEX_COOKIE_LEN);
466
467 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
468 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
469 (r = sshpkt_send(ssh)) != 0)
470 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800471 debug("SSH2_MSG_KEXINIT sent");
472 kex->flags |= KEX_INIT_SENT;
Adam Langleyd0592972015-03-30 14:49:51 -0700473 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800474}
475
476/* ARGSUSED */
Adam Langleyd0592972015-03-30 14:49:51 -0700477int
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800478kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
479{
Adam Langleyd0592972015-03-30 14:49:51 -0700480 struct ssh *ssh = ctxt;
481 struct kex *kex = ssh->kex;
482 const u_char *ptr;
483 u_int i;
484 size_t dlen;
485 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800486
487 debug("SSH2_MSG_KEXINIT received");
488 if (kex == NULL)
Adam Langleyd0592972015-03-30 14:49:51 -0700489 return SSH_ERR_INVALID_ARGUMENT;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800490
Greg Hartman9768ca42017-06-22 20:49:52 -0700491 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
Adam Langleyd0592972015-03-30 14:49:51 -0700492 ptr = sshpkt_ptr(ssh, &dlen);
493 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
494 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800495
496 /* discard packet */
497 for (i = 0; i < KEX_COOKIE_LEN; i++)
Adam Langleyd0592972015-03-30 14:49:51 -0700498 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
499 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800500 for (i = 0; i < PROPOSAL_MAX; i++)
Adam Langleyd0592972015-03-30 14:49:51 -0700501 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
502 return r;
503 /*
504 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
505 * KEX method has the server move first, but a server might be using
506 * a custom method or one that we otherwise don't support. We should
507 * be prepared to remember first_kex_follows here so we can eat a
508 * packet later.
509 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
510 * for cases where the server *doesn't* go first. I guess we should
511 * ignore it when it is set for these cases, which is what we do now.
512 */
513 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
514 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
515 (r = sshpkt_get_end(ssh)) != 0)
516 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800517
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800518 if (!(kex->flags & KEX_INIT_SENT))
Adam Langleyd0592972015-03-30 14:49:51 -0700519 if ((r = kex_send_kexinit(ssh)) != 0)
520 return r;
521 if ((r = kex_choose_conf(ssh)) != 0)
522 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800523
Adam Langleyd0592972015-03-30 14:49:51 -0700524 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
525 return (kex->kex[kex->kex_type])(ssh);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800526
Adam Langleyd0592972015-03-30 14:49:51 -0700527 return SSH_ERR_INTERNAL_ERROR;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800528}
529
Adam Langleyd0592972015-03-30 14:49:51 -0700530int
531kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
532{
533 struct kex *kex;
534 int r;
535
536 *kexp = NULL;
537 if ((kex = calloc(1, sizeof(*kex))) == NULL)
538 return SSH_ERR_ALLOC_FAIL;
539 if ((kex->peer = sshbuf_new()) == NULL ||
540 (kex->my = sshbuf_new()) == NULL) {
541 r = SSH_ERR_ALLOC_FAIL;
542 goto out;
543 }
544 if ((r = kex_prop2buf(kex->my, proposal)) != 0)
545 goto out;
546 kex->done = 0;
547 kex_reset_dispatch(ssh);
Greg Hartman9768ca42017-06-22 20:49:52 -0700548 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
Adam Langleyd0592972015-03-30 14:49:51 -0700549 r = 0;
550 *kexp = kex;
551 out:
552 if (r != 0)
553 kex_free(kex);
554 return r;
555}
556
557void
558kex_free_newkeys(struct newkeys *newkeys)
559{
560 if (newkeys == NULL)
561 return;
562 if (newkeys->enc.key) {
563 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
564 free(newkeys->enc.key);
565 newkeys->enc.key = NULL;
566 }
567 if (newkeys->enc.iv) {
Greg Hartman9768ca42017-06-22 20:49:52 -0700568 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
Adam Langleyd0592972015-03-30 14:49:51 -0700569 free(newkeys->enc.iv);
570 newkeys->enc.iv = NULL;
571 }
572 free(newkeys->enc.name);
573 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
574 free(newkeys->comp.name);
575 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
576 mac_clear(&newkeys->mac);
577 if (newkeys->mac.key) {
578 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
579 free(newkeys->mac.key);
580 newkeys->mac.key = NULL;
581 }
582 free(newkeys->mac.name);
583 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
584 explicit_bzero(newkeys, sizeof(*newkeys));
585 free(newkeys);
586}
587
588void
589kex_free(struct kex *kex)
590{
591 u_int mode;
592
593#ifdef WITH_OPENSSL
594 if (kex->dh)
595 DH_free(kex->dh);
596#ifdef OPENSSL_HAS_ECC
597 if (kex->ec_client_key)
598 EC_KEY_free(kex->ec_client_key);
599#endif /* OPENSSL_HAS_ECC */
600#endif /* WITH_OPENSSL */
601 for (mode = 0; mode < MODE_MAX; mode++) {
602 kex_free_newkeys(kex->newkeys[mode]);
603 kex->newkeys[mode] = NULL;
604 }
605 sshbuf_free(kex->peer);
606 sshbuf_free(kex->my);
607 free(kex->session_id);
608 free(kex->client_version_string);
609 free(kex->server_version_string);
Greg Hartmanccacbc92016-02-03 09:59:44 -0800610 free(kex->failed_choice);
Greg Hartman9768ca42017-06-22 20:49:52 -0700611 free(kex->hostkey_alg);
612 free(kex->name);
Adam Langleyd0592972015-03-30 14:49:51 -0700613 free(kex);
614}
615
616int
617kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
618{
619 int r;
620
621 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
622 return r;
623 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
624 kex_free(ssh->kex);
625 ssh->kex = NULL;
626 return r;
627 }
628 return 0;
629}
630
Greg Hartman9768ca42017-06-22 20:49:52 -0700631/*
632 * Request key re-exchange, returns 0 on success or a ssherr.h error
633 * code otherwise. Must not be called if KEX is incomplete or in-progress.
634 */
635int
636kex_start_rekex(struct ssh *ssh)
637{
638 if (ssh->kex == NULL) {
639 error("%s: no kex", __func__);
640 return SSH_ERR_INTERNAL_ERROR;
641 }
642 if (ssh->kex->done == 0) {
643 error("%s: requested twice", __func__);
644 return SSH_ERR_INTERNAL_ERROR;
645 }
646 ssh->kex->done = 0;
647 return kex_send_kexinit(ssh);
648}
649
Adam Langleyd0592972015-03-30 14:49:51 -0700650static int
651choose_enc(struct sshenc *enc, char *client, char *server)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800652{
653 char *name = match_list(client, server, NULL);
Adam Langleyd0592972015-03-30 14:49:51 -0700654
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800655 if (name == NULL)
Adam Langleyd0592972015-03-30 14:49:51 -0700656 return SSH_ERR_NO_CIPHER_ALG_MATCH;
Greg Hartman9768ca42017-06-22 20:49:52 -0700657 if ((enc->cipher = cipher_by_name(name)) == NULL) {
658 free(name);
Adam Langleyd0592972015-03-30 14:49:51 -0700659 return SSH_ERR_INTERNAL_ERROR;
Greg Hartman9768ca42017-06-22 20:49:52 -0700660 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800661 enc->name = name;
662 enc->enabled = 0;
663 enc->iv = NULL;
Adam Langleyd0592972015-03-30 14:49:51 -0700664 enc->iv_len = cipher_ivlen(enc->cipher);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800665 enc->key = NULL;
666 enc->key_len = cipher_keylen(enc->cipher);
667 enc->block_size = cipher_blocksize(enc->cipher);
Adam Langleyd0592972015-03-30 14:49:51 -0700668 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800669}
670
Adam Langleyd0592972015-03-30 14:49:51 -0700671static int
672choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800673{
674 char *name = match_list(client, server, NULL);
Adam Langleyd0592972015-03-30 14:49:51 -0700675
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800676 if (name == NULL)
Adam Langleyd0592972015-03-30 14:49:51 -0700677 return SSH_ERR_NO_MAC_ALG_MATCH;
Greg Hartman9768ca42017-06-22 20:49:52 -0700678 if (mac_setup(mac, name) < 0) {
679 free(name);
Adam Langleyd0592972015-03-30 14:49:51 -0700680 return SSH_ERR_INTERNAL_ERROR;
Greg Hartman9768ca42017-06-22 20:49:52 -0700681 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800682 /* truncate the key */
Adam Langleyd0592972015-03-30 14:49:51 -0700683 if (ssh->compat & SSH_BUG_HMAC)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800684 mac->key_len = 16;
685 mac->name = name;
686 mac->key = NULL;
687 mac->enabled = 0;
Adam Langleyd0592972015-03-30 14:49:51 -0700688 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800689}
690
Adam Langleyd0592972015-03-30 14:49:51 -0700691static int
692choose_comp(struct sshcomp *comp, char *client, char *server)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800693{
694 char *name = match_list(client, server, NULL);
Adam Langleyd0592972015-03-30 14:49:51 -0700695
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800696 if (name == NULL)
Adam Langleyd0592972015-03-30 14:49:51 -0700697 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800698 if (strcmp(name, "zlib@openssh.com") == 0) {
699 comp->type = COMP_DELAYED;
700 } else if (strcmp(name, "zlib") == 0) {
701 comp->type = COMP_ZLIB;
702 } else if (strcmp(name, "none") == 0) {
703 comp->type = COMP_NONE;
704 } else {
Greg Hartman9768ca42017-06-22 20:49:52 -0700705 free(name);
Adam Langleyd0592972015-03-30 14:49:51 -0700706 return SSH_ERR_INTERNAL_ERROR;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800707 }
708 comp->name = name;
Adam Langleyd0592972015-03-30 14:49:51 -0700709 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800710}
711
Adam Langleyd0592972015-03-30 14:49:51 -0700712static int
713choose_kex(struct kex *k, char *client, char *server)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800714{
Adam Langleyd0592972015-03-30 14:49:51 -0700715 const struct kexalg *kexalg;
716
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800717 k->name = match_list(client, server, NULL);
Adam Langleyd0592972015-03-30 14:49:51 -0700718
Greg Hartman9768ca42017-06-22 20:49:52 -0700719 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800720 if (k->name == NULL)
Adam Langleyd0592972015-03-30 14:49:51 -0700721 return SSH_ERR_NO_KEX_ALG_MATCH;
722 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
723 return SSH_ERR_INTERNAL_ERROR;
724 k->kex_type = kexalg->type;
725 k->hash_alg = kexalg->hash_alg;
726 k->ec_nid = kexalg->ec_nid;
727 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800728}
729
Adam Langleyd0592972015-03-30 14:49:51 -0700730static int
731choose_hostkeyalg(struct kex *k, char *client, char *server)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800732{
Greg Hartman9768ca42017-06-22 20:49:52 -0700733 k->hostkey_alg = match_list(client, server, NULL);
Adam Langleyd0592972015-03-30 14:49:51 -0700734
Greg Hartman9768ca42017-06-22 20:49:52 -0700735 debug("kex: host key algorithm: %s",
736 k->hostkey_alg ? k->hostkey_alg : "(no match)");
737 if (k->hostkey_alg == NULL)
Adam Langleyd0592972015-03-30 14:49:51 -0700738 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
Greg Hartman9768ca42017-06-22 20:49:52 -0700739 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800740 if (k->hostkey_type == KEY_UNSPEC)
Adam Langleyd0592972015-03-30 14:49:51 -0700741 return SSH_ERR_INTERNAL_ERROR;
Greg Hartman9768ca42017-06-22 20:49:52 -0700742 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
Adam Langleyd0592972015-03-30 14:49:51 -0700743 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800744}
745
746static int
747proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
748{
749 static int check[] = {
750 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
751 };
752 int *idx;
753 char *p;
754
755 for (idx = &check[0]; *idx != -1; idx++) {
756 if ((p = strchr(my[*idx], ',')) != NULL)
757 *p = '\0';
758 if ((p = strchr(peer[*idx], ',')) != NULL)
759 *p = '\0';
760 if (strcmp(my[*idx], peer[*idx]) != 0) {
761 debug2("proposal mismatch: my %s peer %s",
762 my[*idx], peer[*idx]);
763 return (0);
764 }
765 }
766 debug2("proposals match");
767 return (1);
768}
769
Adam Langleyd0592972015-03-30 14:49:51 -0700770static int
771kex_choose_conf(struct ssh *ssh)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800772{
Adam Langleyd0592972015-03-30 14:49:51 -0700773 struct kex *kex = ssh->kex;
774 struct newkeys *newkeys;
775 char **my = NULL, **peer = NULL;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800776 char **cprop, **sprop;
777 int nenc, nmac, ncomp;
Adam Langleyd0592972015-03-30 14:49:51 -0700778 u_int mode, ctos, need, dh_need, authlen;
779 int r, first_kex_follows;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800780
Greg Hartman9768ca42017-06-22 20:49:52 -0700781 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
782 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
783 goto out;
784 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
785 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
Adam Langleyd0592972015-03-30 14:49:51 -0700786 goto out;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800787
788 if (kex->server) {
789 cprop=peer;
790 sprop=my;
791 } else {
792 cprop=my;
793 sprop=peer;
794 }
795
Greg Hartman9768ca42017-06-22 20:49:52 -0700796 /* Check whether client supports ext_info_c */
797 if (kex->server) {
798 char *ext;
Adam Langleyd0592972015-03-30 14:49:51 -0700799
Greg Hartman9768ca42017-06-22 20:49:52 -0700800 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
801 kex->ext_info_c = (ext != NULL);
802 free(ext);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800803 }
804
805 /* Algorithm Negotiation */
Greg Hartman9768ca42017-06-22 20:49:52 -0700806 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
807 sprop[PROPOSAL_KEX_ALGS])) != 0) {
808 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
809 peer[PROPOSAL_KEX_ALGS] = NULL;
810 goto out;
811 }
812 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
813 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
814 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
815 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
816 goto out;
817 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800818 for (mode = 0; mode < MODE_MAX; mode++) {
Adam Langleyd0592972015-03-30 14:49:51 -0700819 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
820 r = SSH_ERR_ALLOC_FAIL;
821 goto out;
822 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800823 kex->newkeys[mode] = newkeys;
824 ctos = (!kex->server && mode == MODE_OUT) ||
825 (kex->server && mode == MODE_IN);
826 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
827 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
828 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
Adam Langleyd0592972015-03-30 14:49:51 -0700829 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
Greg Hartmanccacbc92016-02-03 09:59:44 -0800830 sprop[nenc])) != 0) {
831 kex->failed_choice = peer[nenc];
832 peer[nenc] = NULL;
Adam Langleyd0592972015-03-30 14:49:51 -0700833 goto out;
Greg Hartmanccacbc92016-02-03 09:59:44 -0800834 }
Adam Langleyd0592972015-03-30 14:49:51 -0700835 authlen = cipher_authlen(newkeys->enc.cipher);
836 /* ignore mac for authenticated encryption */
837 if (authlen == 0 &&
838 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
Greg Hartmanccacbc92016-02-03 09:59:44 -0800839 sprop[nmac])) != 0) {
840 kex->failed_choice = peer[nmac];
841 peer[nmac] = NULL;
Adam Langleyd0592972015-03-30 14:49:51 -0700842 goto out;
Greg Hartmanccacbc92016-02-03 09:59:44 -0800843 }
Adam Langleyd0592972015-03-30 14:49:51 -0700844 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
Greg Hartmanccacbc92016-02-03 09:59:44 -0800845 sprop[ncomp])) != 0) {
846 kex->failed_choice = peer[ncomp];
847 peer[ncomp] = NULL;
Adam Langleyd0592972015-03-30 14:49:51 -0700848 goto out;
Greg Hartmanccacbc92016-02-03 09:59:44 -0800849 }
Greg Hartman9768ca42017-06-22 20:49:52 -0700850 debug("kex: %s cipher: %s MAC: %s compression: %s",
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800851 ctos ? "client->server" : "server->client",
852 newkeys->enc.name,
Adam Langleyd0592972015-03-30 14:49:51 -0700853 authlen == 0 ? newkeys->mac.name : "<implicit>",
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800854 newkeys->comp.name);
855 }
Adam Langleyd0592972015-03-30 14:49:51 -0700856 need = dh_need = 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800857 for (mode = 0; mode < MODE_MAX; mode++) {
858 newkeys = kex->newkeys[mode];
Greg Hartman9768ca42017-06-22 20:49:52 -0700859 need = MAXIMUM(need, newkeys->enc.key_len);
860 need = MAXIMUM(need, newkeys->enc.block_size);
861 need = MAXIMUM(need, newkeys->enc.iv_len);
862 need = MAXIMUM(need, newkeys->mac.key_len);
863 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
864 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
865 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
866 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800867 }
868 /* XXX need runden? */
869 kex->we_need = need;
Adam Langleyd0592972015-03-30 14:49:51 -0700870 kex->dh_need = dh_need;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800871
872 /* ignore the next message if the proposals do not match */
873 if (first_kex_follows && !proposals_match(my, peer) &&
Adam Langleyd0592972015-03-30 14:49:51 -0700874 !(ssh->compat & SSH_BUG_FIRSTKEX))
875 ssh->dispatch_skip_packets = 1;
876 r = 0;
877 out:
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800878 kex_prop_free(my);
879 kex_prop_free(peer);
Adam Langleyd0592972015-03-30 14:49:51 -0700880 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800881}
882
Adam Langleyd0592972015-03-30 14:49:51 -0700883static int
884derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
885 const struct sshbuf *shared_secret, u_char **keyp)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800886{
Adam Langleyd0592972015-03-30 14:49:51 -0700887 struct kex *kex = ssh->kex;
888 struct ssh_digest_ctx *hashctx = NULL;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800889 char c = id;
890 u_int have;
Adam Langleyd0592972015-03-30 14:49:51 -0700891 size_t mdsz;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800892 u_char *digest;
Adam Langleyd0592972015-03-30 14:49:51 -0700893 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800894
Adam Langleyd0592972015-03-30 14:49:51 -0700895 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
896 return SSH_ERR_INVALID_ARGUMENT;
Greg Hartman9768ca42017-06-22 20:49:52 -0700897 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
Adam Langleyd0592972015-03-30 14:49:51 -0700898 r = SSH_ERR_ALLOC_FAIL;
899 goto out;
900 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800901
902 /* K1 = HASH(K || H || "A" || session_id) */
Adam Langleyd0592972015-03-30 14:49:51 -0700903 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
904 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
905 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
906 ssh_digest_update(hashctx, &c, 1) != 0 ||
907 ssh_digest_update(hashctx, kex->session_id,
908 kex->session_id_len) != 0 ||
909 ssh_digest_final(hashctx, digest, mdsz) != 0) {
910 r = SSH_ERR_LIBCRYPTO_ERROR;
911 goto out;
912 }
913 ssh_digest_free(hashctx);
914 hashctx = NULL;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800915
916 /*
917 * expand key:
918 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
919 * Key = K1 || K2 || ... || Kn
920 */
921 for (have = mdsz; need > have; have += mdsz) {
Adam Langleyd0592972015-03-30 14:49:51 -0700922 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
923 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
924 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
925 ssh_digest_update(hashctx, digest, have) != 0 ||
926 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
927 r = SSH_ERR_LIBCRYPTO_ERROR;
928 goto out;
929 }
930 ssh_digest_free(hashctx);
931 hashctx = NULL;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800932 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800933#ifdef DEBUG_KEX
934 fprintf(stderr, "key '%c'== ", c);
935 dump_digest("key", digest, need);
936#endif
Adam Langleyd0592972015-03-30 14:49:51 -0700937 *keyp = digest;
938 digest = NULL;
939 r = 0;
940 out:
Greg Hartman9768ca42017-06-22 20:49:52 -0700941 free(digest);
Adam Langleyd0592972015-03-30 14:49:51 -0700942 ssh_digest_free(hashctx);
943 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800944}
945
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800946#define NKEYS 6
Adam Langleyd0592972015-03-30 14:49:51 -0700947int
948kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
949 const struct sshbuf *shared_secret)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800950{
Adam Langleyd0592972015-03-30 14:49:51 -0700951 struct kex *kex = ssh->kex;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800952 u_char *keys[NKEYS];
Adam Langleyd0592972015-03-30 14:49:51 -0700953 u_int i, j, mode, ctos;
954 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800955
956 for (i = 0; i < NKEYS; i++) {
Adam Langleyd0592972015-03-30 14:49:51 -0700957 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
958 shared_secret, &keys[i])) != 0) {
959 for (j = 0; j < i; j++)
960 free(keys[j]);
961 return r;
962 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800963 }
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800964 for (mode = 0; mode < MODE_MAX; mode++) {
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800965 ctos = (!kex->server && mode == MODE_OUT) ||
966 (kex->server && mode == MODE_IN);
Adam Langleyd0592972015-03-30 14:49:51 -0700967 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
968 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
969 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800970 }
Adam Langleyd0592972015-03-30 14:49:51 -0700971 return 0;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800972}
973
Adam Langleyd0592972015-03-30 14:49:51 -0700974#ifdef WITH_OPENSSL
975int
976kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
977 const BIGNUM *secret)
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800978{
Adam Langleyd0592972015-03-30 14:49:51 -0700979 struct sshbuf *shared_secret;
980 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800981
Adam Langleyd0592972015-03-30 14:49:51 -0700982 if ((shared_secret = sshbuf_new()) == NULL)
983 return SSH_ERR_ALLOC_FAIL;
984 if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
985 r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
986 sshbuf_free(shared_secret);
987 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800988}
Adam Langleyd0592972015-03-30 14:49:51 -0700989#endif
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800990
Adam Langleyd0592972015-03-30 14:49:51 -0700991#ifdef WITH_SSH1
992int
Greg Hartmanbd77cf72015-02-25 13:21:06 -0800993derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
994 u_int8_t cookie[8], u_int8_t id[16])
995{
Adam Langleyd0592972015-03-30 14:49:51 -0700996 u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
997 struct ssh_digest_ctx *hashctx = NULL;
998 size_t hlen, slen;
999 int r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -08001000
Adam Langleyd0592972015-03-30 14:49:51 -07001001 hlen = BN_num_bytes(host_modulus);
1002 slen = BN_num_bytes(server_modulus);
1003 if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) ||
1004 slen < (512 / 8) || (u_int)slen > sizeof(sbuf))
1005 return SSH_ERR_KEY_BITS_MISMATCH;
1006 if (BN_bn2bin(host_modulus, hbuf) <= 0 ||
1007 BN_bn2bin(server_modulus, sbuf) <= 0) {
1008 r = SSH_ERR_LIBCRYPTO_ERROR;
1009 goto out;
1010 }
1011 if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) {
1012 r = SSH_ERR_ALLOC_FAIL;
1013 goto out;
1014 }
1015 if (ssh_digest_update(hashctx, hbuf, hlen) != 0 ||
1016 ssh_digest_update(hashctx, sbuf, slen) != 0 ||
1017 ssh_digest_update(hashctx, cookie, 8) != 0 ||
1018 ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) {
1019 r = SSH_ERR_LIBCRYPTO_ERROR;
1020 goto out;
1021 }
1022 memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
1023 r = 0;
1024 out:
1025 ssh_digest_free(hashctx);
1026 explicit_bzero(hbuf, sizeof(hbuf));
1027 explicit_bzero(sbuf, sizeof(sbuf));
1028 explicit_bzero(obuf, sizeof(obuf));
1029 return r;
Greg Hartmanbd77cf72015-02-25 13:21:06 -08001030}
Adam Langleyd0592972015-03-30 14:49:51 -07001031#endif
Greg Hartmanbd77cf72015-02-25 13:21:06 -08001032
1033#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
1034void
1035dump_digest(char *msg, u_char *digest, int len)
1036{
Greg Hartmanbd77cf72015-02-25 13:21:06 -08001037 fprintf(stderr, "%s\n", msg);
Adam Langleyd0592972015-03-30 14:49:51 -07001038 sshbuf_dump_data(digest, len, stderr);
Greg Hartmanbd77cf72015-02-25 13:21:06 -08001039}
1040#endif