xref: /netbsd-src/crypto/external/bsd/openssh/dist/kex.c (revision 195d6bf6173f7fc3dac511ecb082e722f8fcd656)
1*195d6bf6Srin /*	$NetBSD: kex.c,v 1.38 2024/10/03 08:14:13 rin Exp $	*/
29469f4f1Schristos /* $OpenBSD: kex.c,v 1.187 2024/08/23 04:51:00 deraadt Exp $ */
3a629fefcSchristos 
4ca32bd8dSchristos /*
5ca32bd8dSchristos  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
6ca32bd8dSchristos  *
7ca32bd8dSchristos  * Redistribution and use in source and binary forms, with or without
8ca32bd8dSchristos  * modification, are permitted provided that the following conditions
9ca32bd8dSchristos  * are met:
10ca32bd8dSchristos  * 1. Redistributions of source code must retain the above copyright
11ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer.
12ca32bd8dSchristos  * 2. Redistributions in binary form must reproduce the above copyright
13ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer in the
14ca32bd8dSchristos  *    documentation and/or other materials provided with the distribution.
15ca32bd8dSchristos  *
16ca32bd8dSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17ca32bd8dSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18ca32bd8dSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19ca32bd8dSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20ca32bd8dSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21ca32bd8dSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22ca32bd8dSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23ca32bd8dSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24ca32bd8dSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25ca32bd8dSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26ca32bd8dSchristos  */
27ca32bd8dSchristos 
28313c6c94Schristos #include "includes.h"
29*195d6bf6Srin __RCSID("$NetBSD: kex.c,v 1.38 2024/10/03 08:14:13 rin Exp $");
30ca32bd8dSchristos 
31ee85abc4Schristos #include <sys/param.h>	/* MAX roundup */
32aa36fcacSchristos #include <sys/types.h>
33aa36fcacSchristos #include <errno.h>
34ca32bd8dSchristos #include <signal.h>
35ca32bd8dSchristos #include <stdio.h>
36ca32bd8dSchristos #include <stdlib.h>
37ca32bd8dSchristos #include <string.h>
38aa36fcacSchristos #include <unistd.h>
39aa36fcacSchristos #include <poll.h>
40ca32bd8dSchristos 
418a4530f9Schristos #ifdef WITH_OPENSSL
42ca32bd8dSchristos #include <openssl/crypto.h>
438df81648Schristos #include <openssl/dh.h>
448a4530f9Schristos #endif
45ca32bd8dSchristos 
46aa36fcacSchristos #include "ssh.h"
47ca32bd8dSchristos #include "ssh2.h"
48aa36fcacSchristos #include "atomicio.h"
49aa36fcacSchristos #include "version.h"
50ca32bd8dSchristos #include "packet.h"
51ca32bd8dSchristos #include "compat.h"
52ca32bd8dSchristos #include "cipher.h"
53e4d43b82Schristos #include "sshkey.h"
54ca32bd8dSchristos #include "kex.h"
55ca32bd8dSchristos #include "log.h"
56ca32bd8dSchristos #include "mac.h"
57ca32bd8dSchristos #include "match.h"
58e4d43b82Schristos #include "misc.h"
59ca32bd8dSchristos #include "dispatch.h"
605101d403Schristos #include "packet.h"
61ca32bd8dSchristos #include "monitor.h"
62b1066cf3Schristos #include "myproposal.h"
63e4d43b82Schristos 
64e4d43b82Schristos #include "ssherr.h"
65e4d43b82Schristos #include "sshbuf.h"
668a4530f9Schristos #include "digest.h"
67b1066cf3Schristos #include "xmalloc.h"
68ca32bd8dSchristos 
69ca32bd8dSchristos /* prototype */
70514b5d45Schristos static int kex_choose_conf(struct ssh *, uint32_t seq);
717a183406Schristos static int kex_input_newkeys(int, u_int32_t, struct ssh *);
72ca32bd8dSchristos 
73a03ec00cSchristos static const char * const proposal_names[PROPOSAL_MAX] = {
7479976551Schristos 	"KEX algorithms",
7579976551Schristos 	"host key algorithms",
7679976551Schristos 	"ciphers ctos",
7779976551Schristos 	"ciphers stoc",
7879976551Schristos 	"MACs ctos",
7979976551Schristos 	"MACs stoc",
8079976551Schristos 	"compression ctos",
8179976551Schristos 	"compression stoc",
8279976551Schristos 	"languages ctos",
8379976551Schristos 	"languages stoc",
8479976551Schristos };
8579976551Schristos 
86b1066cf3Schristos /*
87b1066cf3Schristos  * Fill out a proposal array with dynamically allocated values, which may
88b1066cf3Schristos  * be modified as required for compatibility reasons.
89b1066cf3Schristos  * Any of the options may be NULL, in which case the default is used.
90b1066cf3Schristos  * Array contents must be freed by calling kex_proposal_free_entries.
91b1066cf3Schristos  */
92b1066cf3Schristos void
93b1066cf3Schristos kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX],
94b1066cf3Schristos     const char *kexalgos, const char *ciphers, const char *macs,
95b1066cf3Schristos     const char *comp, const char *hkalgs)
96b1066cf3Schristos {
97b1066cf3Schristos 	const char *defpropserver[PROPOSAL_MAX] = { KEX_SERVER };
98b1066cf3Schristos 	const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT };
99b1066cf3Schristos 	const char **defprop = ssh->kex->server ? defpropserver : defpropclient;
100b1066cf3Schristos 	u_int i;
101514b5d45Schristos 	char *cp;
102b1066cf3Schristos 
103b1066cf3Schristos 	if (prop == NULL)
104b1066cf3Schristos 		fatal_f("proposal missing");
105b1066cf3Schristos 
106514b5d45Schristos 	/* Append EXT_INFO signalling to KexAlgorithms */
107514b5d45Schristos 	if (kexalgos == NULL)
108514b5d45Schristos 		kexalgos = defprop[PROPOSAL_KEX_ALGS];
109514b5d45Schristos 	if ((cp = kex_names_cat(kexalgos, ssh->kex->server ?
110514b5d45Schristos 	    "ext-info-s,kex-strict-s-v00@openssh.com" :
111514b5d45Schristos 	    "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL)
112514b5d45Schristos 		fatal_f("kex_names_cat");
113514b5d45Schristos 
114b1066cf3Schristos 	for (i = 0; i < PROPOSAL_MAX; i++) {
115b1066cf3Schristos 		switch(i) {
116b1066cf3Schristos 		case PROPOSAL_KEX_ALGS:
117514b5d45Schristos 			prop[i] = compat_kex_proposal(ssh, cp);
118b1066cf3Schristos 			break;
119b1066cf3Schristos 		case PROPOSAL_ENC_ALGS_CTOS:
120b1066cf3Schristos 		case PROPOSAL_ENC_ALGS_STOC:
121b1066cf3Schristos 			prop[i] = xstrdup(ciphers ? ciphers : defprop[i]);
122b1066cf3Schristos 			break;
123b1066cf3Schristos 		case PROPOSAL_MAC_ALGS_CTOS:
124b1066cf3Schristos 		case PROPOSAL_MAC_ALGS_STOC:
125b1066cf3Schristos 			prop[i]  = xstrdup(macs ? macs : defprop[i]);
126b1066cf3Schristos 			break;
127b1066cf3Schristos 		case PROPOSAL_COMP_ALGS_CTOS:
128b1066cf3Schristos 		case PROPOSAL_COMP_ALGS_STOC:
129b1066cf3Schristos 			prop[i] = xstrdup(comp ? comp : defprop[i]);
130b1066cf3Schristos 			break;
131b1066cf3Schristos 		case PROPOSAL_SERVER_HOST_KEY_ALGS:
132b1066cf3Schristos 			prop[i] = xstrdup(hkalgs ? hkalgs : defprop[i]);
133b1066cf3Schristos 			break;
134b1066cf3Schristos 		default:
135b1066cf3Schristos 			prop[i] = xstrdup(defprop[i]);
136b1066cf3Schristos 		}
137b1066cf3Schristos 	}
138514b5d45Schristos 	free(cp);
139b1066cf3Schristos }
140b1066cf3Schristos 
141b1066cf3Schristos void
142b1066cf3Schristos kex_proposal_free_entries(char *prop[PROPOSAL_MAX])
143b1066cf3Schristos {
144b1066cf3Schristos 	u_int i;
145b1066cf3Schristos 
146b1066cf3Schristos 	for (i = 0; i < PROPOSAL_MAX; i++)
147b1066cf3Schristos 		free(prop[i]);
148b1066cf3Schristos }
149b1066cf3Schristos 
150ca32bd8dSchristos /* put algorithm proposal into buffer */
151e4d43b82Schristos int
152b1066cf3Schristos kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
153ca32bd8dSchristos {
154ca32bd8dSchristos 	u_int i;
155e4d43b82Schristos 	int r;
156ca32bd8dSchristos 
157e4d43b82Schristos 	sshbuf_reset(b);
158e4d43b82Schristos 
159ca32bd8dSchristos 	/*
160ca32bd8dSchristos 	 * add a dummy cookie, the cookie will be overwritten by
161ca32bd8dSchristos 	 * kex_send_kexinit(), each time a kexinit is set
162ca32bd8dSchristos 	 */
163e4d43b82Schristos 	for (i = 0; i < KEX_COOKIE_LEN; i++) {
164e4d43b82Schristos 		if ((r = sshbuf_put_u8(b, 0)) != 0)
165e4d43b82Schristos 			return r;
166e4d43b82Schristos 	}
167e4d43b82Schristos 	for (i = 0; i < PROPOSAL_MAX; i++) {
168e4d43b82Schristos 		if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
169e4d43b82Schristos 			return r;
170e4d43b82Schristos 	}
171e4d43b82Schristos 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* first_kex_packet_follows */
172e4d43b82Schristos 	    (r = sshbuf_put_u32(b, 0)) != 0)	/* uint32 reserved */
173e4d43b82Schristos 		return r;
174e4d43b82Schristos 	return 0;
175ca32bd8dSchristos }
176ca32bd8dSchristos 
177ca32bd8dSchristos /* parse buffer and return algorithm proposal */
178e4d43b82Schristos int
179e4d43b82Schristos kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
180ca32bd8dSchristos {
181e4d43b82Schristos 	struct sshbuf *b = NULL;
182e4d43b82Schristos 	u_char v;
183ca32bd8dSchristos 	u_int i;
184e4d43b82Schristos 	char **proposal = NULL;
185e4d43b82Schristos 	int r;
186ca32bd8dSchristos 
187e4d43b82Schristos 	*propp = NULL;
188e4d43b82Schristos 	if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
189e4d43b82Schristos 		return SSH_ERR_ALLOC_FAIL;
190e4d43b82Schristos 	if ((b = sshbuf_fromb(raw)) == NULL) {
191e4d43b82Schristos 		r = SSH_ERR_ALLOC_FAIL;
192e4d43b82Schristos 		goto out;
193e4d43b82Schristos 	}
194cd4ada6aSchristos 	if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */
19517418e98Schristos 		error_fr(r, "consume cookie");
196e4d43b82Schristos 		goto out;
197cd4ada6aSchristos 	}
198ca32bd8dSchristos 	/* extract kex init proposal strings */
199ca32bd8dSchristos 	for (i = 0; i < PROPOSAL_MAX; i++) {
200cd4ada6aSchristos 		if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) {
20117418e98Schristos 			error_fr(r, "parse proposal %u", i);
202e4d43b82Schristos 			goto out;
203cd4ada6aSchristos 		}
20479976551Schristos 		debug2("%s: %s", proposal_names[i], proposal[i]);
205ca32bd8dSchristos 	}
206ca32bd8dSchristos 	/* first kex follows / reserved */
20779976551Schristos 	if ((r = sshbuf_get_u8(b, &v)) != 0 ||	/* first_kex_follows */
208cd4ada6aSchristos 	    (r = sshbuf_get_u32(b, &i)) != 0) {	/* reserved */
20917418e98Schristos 		error_fr(r, "parse");
210e4d43b82Schristos 		goto out;
211cd4ada6aSchristos 	}
212ca32bd8dSchristos 	if (first_kex_follows != NULL)
21379976551Schristos 		*first_kex_follows = v;
21479976551Schristos 	debug2("first_kex_follows %d ", v);
21579976551Schristos 	debug2("reserved %u ", i);
216e4d43b82Schristos 	r = 0;
217e4d43b82Schristos 	*propp = proposal;
218e4d43b82Schristos  out:
219e4d43b82Schristos 	if (r != 0 && proposal != NULL)
220e4d43b82Schristos 		kex_prop_free(proposal);
221e4d43b82Schristos 	sshbuf_free(b);
222e4d43b82Schristos 	return r;
223ca32bd8dSchristos }
224ca32bd8dSchristos 
225e4d43b82Schristos void
226ca32bd8dSchristos kex_prop_free(char **proposal)
227ca32bd8dSchristos {
228ca32bd8dSchristos 	u_int i;
229ca32bd8dSchristos 
2304054ffb0Schristos 	if (proposal == NULL)
2314054ffb0Schristos 		return;
232ca32bd8dSchristos 	for (i = 0; i < PROPOSAL_MAX; i++)
23300a838c4Schristos 		free(proposal[i]);
23400a838c4Schristos 	free(proposal);
235ca32bd8dSchristos }
236ca32bd8dSchristos 
23717418e98Schristos int
2387a183406Schristos kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
239ca32bd8dSchristos {
24079976551Schristos 	int r;
24179976551Schristos 
242514b5d45Schristos 	/* If in strict mode, any unexpected message is an error */
243514b5d45Schristos 	if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) {
244514b5d45Schristos 		ssh_packet_disconnect(ssh, "strict KEX violation: "
245514b5d45Schristos 		    "unexpected packet type %u (seqnr %u)", type, seq);
246514b5d45Schristos 	}
247514b5d45Schristos 	error_f("type %u seq %u", type, seq);
24879976551Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
24979976551Schristos 	    (r = sshpkt_put_u32(ssh, seq)) != 0 ||
25079976551Schristos 	    (r = sshpkt_send(ssh)) != 0)
25179976551Schristos 		return r;
252e4d43b82Schristos 	return 0;
253ca32bd8dSchristos }
254ca32bd8dSchristos 
255ca32bd8dSchristos static void
256e4d43b82Schristos kex_reset_dispatch(struct ssh *ssh)
257ca32bd8dSchristos {
258e4d43b82Schristos 	ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
259ca32bd8dSchristos 	    SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
260ca32bd8dSchristos }
261ca32bd8dSchristos 
262514b5d45Schristos void
263514b5d45Schristos kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs)
264514b5d45Schristos {
265514b5d45Schristos 	char *alg, *oalgs, *algs, *sigalgs;
266514b5d45Schristos 	const char *sigalg;
267514b5d45Schristos 
268514b5d45Schristos 	/*
269514b5d45Schristos 	 * NB. allowed algorithms may contain certificate algorithms that
270514b5d45Schristos 	 * map to a specific plain signature type, e.g.
271514b5d45Schristos 	 * rsa-sha2-512-cert-v01@openssh.com => rsa-sha2-512
272514b5d45Schristos 	 * We need to be careful here to match these, retain the mapping
273514b5d45Schristos 	 * and only add each signature algorithm once.
274514b5d45Schristos 	 */
275514b5d45Schristos 	if ((sigalgs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
276514b5d45Schristos 		fatal_f("sshkey_alg_list failed");
277514b5d45Schristos 	oalgs = algs = xstrdup(allowed_algs);
278514b5d45Schristos 	free(ssh->kex->server_sig_algs);
279514b5d45Schristos 	ssh->kex->server_sig_algs = NULL;
280514b5d45Schristos 	for ((alg = strsep(&algs, ",")); alg != NULL && *alg != '\0';
281514b5d45Schristos 	    (alg = strsep(&algs, ","))) {
282514b5d45Schristos 		if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL)
283514b5d45Schristos 			continue;
2841c7715ddSchristos 		if (!kex_has_any_alg(sigalg, sigalgs))
285514b5d45Schristos 			continue;
286514b5d45Schristos 		/* Don't add an algorithm twice. */
287514b5d45Schristos 		if (ssh->kex->server_sig_algs != NULL &&
2881c7715ddSchristos 		    kex_has_any_alg(sigalg, ssh->kex->server_sig_algs))
289514b5d45Schristos 			continue;
290514b5d45Schristos 		xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg);
291514b5d45Schristos 	}
292514b5d45Schristos 	free(oalgs);
293514b5d45Schristos 	free(sigalgs);
294514b5d45Schristos 	if (ssh->kex->server_sig_algs == NULL)
295514b5d45Schristos 		ssh->kex->server_sig_algs = xstrdup("");
296514b5d45Schristos }
297514b5d45Schristos 
29879976551Schristos static int
299514b5d45Schristos kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m)
30079976551Schristos {
30179976551Schristos 	int r;
30279976551Schristos 
303514b5d45Schristos 	if (ssh->kex->server_sig_algs == NULL &&
304514b5d45Schristos 	    (ssh->kex->server_sig_algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
305ee85abc4Schristos 		return SSH_ERR_ALLOC_FAIL;
306514b5d45Schristos 	if ((r = sshbuf_put_u32(m, 3)) != 0 ||
307514b5d45Schristos 	    (r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 ||
308514b5d45Schristos 	    (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 ||
309514b5d45Schristos 	    (r = sshbuf_put_cstring(m,
310a03ec00cSchristos 	    "publickey-hostbound@openssh.com")) != 0 ||
311514b5d45Schristos 	    (r = sshbuf_put_cstring(m, "0")) != 0 ||
312514b5d45Schristos 	    (r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 ||
313514b5d45Schristos 	    (r = sshbuf_put_cstring(m, "0")) != 0) {
314514b5d45Schristos 		error_fr(r, "compose");
315514b5d45Schristos 		return r;
316514b5d45Schristos 	}
317514b5d45Schristos 	return 0;
318514b5d45Schristos }
319514b5d45Schristos 
320514b5d45Schristos static int
321514b5d45Schristos kex_compose_ext_info_client(struct ssh *ssh, struct sshbuf *m)
322514b5d45Schristos {
323514b5d45Schristos 	int r;
324514b5d45Schristos 
325514b5d45Schristos 	if ((r = sshbuf_put_u32(m, 1)) != 0 ||
326514b5d45Schristos 	    (r = sshbuf_put_cstring(m, "ext-info-in-auth@openssh.com")) != 0 ||
327514b5d45Schristos 	    (r = sshbuf_put_cstring(m, "0")) != 0) {
32817418e98Schristos 		error_fr(r, "compose");
329ee85abc4Schristos 		goto out;
330cd4ada6aSchristos 	}
331ee85abc4Schristos 	/* success */
332ee85abc4Schristos 	r = 0;
333ee85abc4Schristos  out:
33479976551Schristos 	return r;
33579976551Schristos }
33679976551Schristos 
337514b5d45Schristos static int
338514b5d45Schristos kex_maybe_send_ext_info(struct ssh *ssh)
339514b5d45Schristos {
340514b5d45Schristos 	int r;
341514b5d45Schristos 	struct sshbuf *m = NULL;
342514b5d45Schristos 
343514b5d45Schristos 	if ((ssh->kex->flags & KEX_INITIAL) == 0)
344514b5d45Schristos 		return 0;
345514b5d45Schristos 	if (!ssh->kex->ext_info_c && !ssh->kex->ext_info_s)
346514b5d45Schristos 		return 0;
347514b5d45Schristos 
348514b5d45Schristos 	/* Compose EXT_INFO packet. */
349514b5d45Schristos 	if ((m = sshbuf_new()) == NULL)
350514b5d45Schristos 		fatal_f("sshbuf_new failed");
351514b5d45Schristos 	if (ssh->kex->ext_info_c &&
352514b5d45Schristos 	    (r = kex_compose_ext_info_server(ssh, m)) != 0)
353514b5d45Schristos 		goto fail;
354514b5d45Schristos 	if (ssh->kex->ext_info_s &&
355514b5d45Schristos 	    (r = kex_compose_ext_info_client(ssh, m)) != 0)
356514b5d45Schristos 		goto fail;
357514b5d45Schristos 
358514b5d45Schristos 	/* Send the actual KEX_INFO packet */
359514b5d45Schristos 	debug("Sending SSH2_MSG_EXT_INFO");
360514b5d45Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
361514b5d45Schristos 	    (r = sshpkt_putb(ssh, m)) != 0 ||
362514b5d45Schristos 	    (r = sshpkt_send(ssh)) != 0) {
363514b5d45Schristos 		error_f("send EXT_INFO");
364514b5d45Schristos 		goto fail;
365514b5d45Schristos 	}
366514b5d45Schristos 
367514b5d45Schristos 	r = 0;
368514b5d45Schristos 
369514b5d45Schristos  fail:
370514b5d45Schristos 	sshbuf_free(m);
371514b5d45Schristos 	return r;
372514b5d45Schristos }
373514b5d45Schristos 
374514b5d45Schristos int
375514b5d45Schristos kex_server_update_ext_info(struct ssh *ssh)
376514b5d45Schristos {
377514b5d45Schristos 	int r;
378514b5d45Schristos 
379514b5d45Schristos 	if ((ssh->kex->flags & KEX_HAS_EXT_INFO_IN_AUTH) == 0)
380514b5d45Schristos 		return 0;
381514b5d45Schristos 
382514b5d45Schristos 	debug_f("Sending SSH2_MSG_EXT_INFO");
383514b5d45Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
384514b5d45Schristos 	    (r = sshpkt_put_u32(ssh, 1)) != 0 ||
385514b5d45Schristos 	    (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
386514b5d45Schristos 	    (r = sshpkt_put_cstring(ssh, ssh->kex->server_sig_algs)) != 0 ||
387514b5d45Schristos 	    (r = sshpkt_send(ssh)) != 0) {
388514b5d45Schristos 		error_f("send EXT_INFO");
389514b5d45Schristos 		return r;
390514b5d45Schristos 	}
391514b5d45Schristos 	return 0;
392514b5d45Schristos }
393514b5d45Schristos 
394e4d43b82Schristos int
395e4d43b82Schristos kex_send_newkeys(struct ssh *ssh)
396ca32bd8dSchristos {
397e4d43b82Schristos 	int r;
398ca32bd8dSchristos 
399e4d43b82Schristos 	kex_reset_dispatch(ssh);
400e4d43b82Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
401e4d43b82Schristos 	    (r = sshpkt_send(ssh)) != 0)
402e4d43b82Schristos 		return r;
403ca32bd8dSchristos 	debug("SSH2_MSG_NEWKEYS sent");
404e4d43b82Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
405514b5d45Schristos 	if ((r = kex_maybe_send_ext_info(ssh)) != 0)
40679976551Schristos 		return r;
407cd4ada6aSchristos 	debug("expecting SSH2_MSG_NEWKEYS");
408e4d43b82Schristos 	return 0;
409e4d43b82Schristos }
410ca32bd8dSchristos 
411a629fefcSchristos /* Check whether an ext_info value contains the expected version string */
412a629fefcSchristos static int
413a629fefcSchristos kex_ext_info_check_ver(struct kex *kex, const char *name,
414a629fefcSchristos     const u_char *val, size_t len, const char *want_ver, u_int flag)
415a629fefcSchristos {
416a629fefcSchristos 	if (memchr(val, '\0', len) != NULL) {
417a629fefcSchristos 		error("SSH2_MSG_EXT_INFO: %s value contains nul byte", name);
418a629fefcSchristos 		return SSH_ERR_INVALID_FORMAT;
419a629fefcSchristos 	}
420a629fefcSchristos 	debug_f("%s=<%s>", name, val);
421a629fefcSchristos 	if (strcmp((const char *)val, want_ver) == 0)
422a629fefcSchristos 		kex->flags |= flag;
423a629fefcSchristos 	else
424a629fefcSchristos 		debug_f("unsupported version of %s extension", name);
425a629fefcSchristos 	return 0;
426a629fefcSchristos }
427a629fefcSchristos 
428514b5d45Schristos static int
429514b5d45Schristos kex_ext_info_client_parse(struct ssh *ssh, const char *name,
430514b5d45Schristos     const u_char *value, size_t vlen)
431514b5d45Schristos {
432514b5d45Schristos 	int r;
433514b5d45Schristos 
434514b5d45Schristos 	/* NB. some messages are only accepted in the initial EXT_INFO */
435514b5d45Schristos 	if (strcmp(name, "server-sig-algs") == 0) {
436514b5d45Schristos 		/* Ensure no \0 lurking in value */
437514b5d45Schristos 		if (memchr(value, '\0', vlen) != NULL) {
438514b5d45Schristos 			error_f("nul byte in %s", name);
439514b5d45Schristos 			return SSH_ERR_INVALID_FORMAT;
440514b5d45Schristos 		}
441514b5d45Schristos 		debug_f("%s=<%s>", name, value);
442514b5d45Schristos 		free(ssh->kex->server_sig_algs);
443514b5d45Schristos 		ssh->kex->server_sig_algs = xstrdup((const char *)value);
444514b5d45Schristos 	} else if (ssh->kex->ext_info_received == 1 &&
445514b5d45Schristos 	    strcmp(name, "publickey-hostbound@openssh.com") == 0) {
446514b5d45Schristos 		if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen,
447514b5d45Schristos 		    "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) {
448514b5d45Schristos 			return r;
449514b5d45Schristos 		}
450514b5d45Schristos 	} else if (ssh->kex->ext_info_received == 1 &&
451514b5d45Schristos 	    strcmp(name, "ping@openssh.com") == 0) {
452514b5d45Schristos 		if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen,
453514b5d45Schristos 		    "0", KEX_HAS_PING)) != 0) {
454514b5d45Schristos 			return r;
455514b5d45Schristos 		}
456514b5d45Schristos 	} else
457514b5d45Schristos 		debug_f("%s (unrecognised)", name);
458514b5d45Schristos 
459514b5d45Schristos 	return 0;
460514b5d45Schristos }
461514b5d45Schristos 
462514b5d45Schristos static int
463514b5d45Schristos kex_ext_info_server_parse(struct ssh *ssh, const char *name,
464514b5d45Schristos     const u_char *value, size_t vlen)
465514b5d45Schristos {
466514b5d45Schristos 	int r;
467514b5d45Schristos 
468514b5d45Schristos 	if (strcmp(name, "ext-info-in-auth@openssh.com") == 0) {
469514b5d45Schristos 		if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen,
470514b5d45Schristos 		    "0", KEX_HAS_EXT_INFO_IN_AUTH)) != 0) {
471514b5d45Schristos 			return r;
472514b5d45Schristos 		}
473514b5d45Schristos 	} else
474514b5d45Schristos 		debug_f("%s (unrecognised)", name);
475514b5d45Schristos 	return 0;
476514b5d45Schristos }
477514b5d45Schristos 
47879976551Schristos int
4797a183406Schristos kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
48079976551Schristos {
48179976551Schristos 	struct kex *kex = ssh->kex;
482514b5d45Schristos 	const int max_ext_info = kex->server ? 1 : 2;
48379976551Schristos 	u_int32_t i, ninfo;
48455a4608bSchristos 	char *name;
4857a183406Schristos 	u_char *val;
4867a183406Schristos 	size_t vlen;
48779976551Schristos 	int r;
48879976551Schristos 
48979976551Schristos 	debug("SSH2_MSG_EXT_INFO received");
490514b5d45Schristos 	if (++kex->ext_info_received > max_ext_info) {
491514b5d45Schristos 		error("too many SSH2_MSG_EXT_INFO messages sent by peer");
492514b5d45Schristos 		return dispatch_protocol_error(type, seq, ssh);
493514b5d45Schristos 	}
49479976551Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
49579976551Schristos 	if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
49679976551Schristos 		return r;
497b1066cf3Schristos 	if (ninfo >= 1024) {
498b1066cf3Schristos 		error("SSH2_MSG_EXT_INFO with too many entries, expected "
499b1066cf3Schristos 		    "<=1024, received %u", ninfo);
500514b5d45Schristos 		return dispatch_protocol_error(type, seq, ssh);
50147690f35Schristos 	}
50279976551Schristos 	for (i = 0; i < ninfo; i++) {
50379976551Schristos 		if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
50479976551Schristos 			return r;
5057a183406Schristos 		if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
50679976551Schristos 			free(name);
50779976551Schristos 			return r;
50879976551Schristos 		}
509514b5d45Schristos 		debug3_f("extension %s", name);
510514b5d45Schristos 		if (kex->server) {
511514b5d45Schristos 			if ((r = kex_ext_info_server_parse(ssh, name,
512514b5d45Schristos 			    val, vlen)) != 0)
513514b5d45Schristos 				return r;
514514b5d45Schristos 		} else {
515514b5d45Schristos 			if ((r = kex_ext_info_client_parse(ssh, name,
516514b5d45Schristos 			    val, vlen)) != 0)
517a629fefcSchristos 				return r;
518a03ec00cSchristos 		}
51979976551Schristos 		free(name);
52079976551Schristos 		free(val);
52179976551Schristos 	}
52279976551Schristos 	return sshpkt_get_end(ssh);
52379976551Schristos }
52479976551Schristos 
525e4d43b82Schristos static int
5267a183406Schristos kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
527e4d43b82Schristos {
528e4d43b82Schristos 	struct kex *kex = ssh->kex;
529c5555919Schristos 	int r, initial = (kex->flags & KEX_INITIAL) != 0;
530c5555919Schristos 	char *cp, **prop;
531e4d43b82Schristos 
532e4d43b82Schristos 	debug("SSH2_MSG_NEWKEYS received");
533c5555919Schristos 	if (kex->ext_info_c && initial)
534514b5d45Schristos 		ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info);
535e4d43b82Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
53641768fc1Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
537e4d43b82Schristos 	if ((r = sshpkt_get_end(ssh)) != 0)
538e4d43b82Schristos 		return r;
539ee85abc4Schristos 	if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
540ee85abc4Schristos 		return r;
541c5555919Schristos 	if (initial) {
542c5555919Schristos 		/* Remove initial KEX signalling from proposal for rekeying */
543c5555919Schristos 		if ((r = kex_buf2prop(kex->my, NULL, &prop)) != 0)
544c5555919Schristos 			return r;
545c5555919Schristos 		if ((cp = match_filter_denylist(prop[PROPOSAL_KEX_ALGS],
546c5555919Schristos 		    kex->server ?
547c5555919Schristos 		    "ext-info-s,kex-strict-s-v00@openssh.com" :
548c5555919Schristos 		    "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) {
549c5555919Schristos 			error_f("match_filter_denylist failed");
550c5555919Schristos 			goto fail;
551c5555919Schristos 		}
552c5555919Schristos 		free(prop[PROPOSAL_KEX_ALGS]);
553c5555919Schristos 		prop[PROPOSAL_KEX_ALGS] = cp;
554c5555919Schristos 		if ((r = kex_prop2buf(ssh->kex->my, prop)) != 0) {
555c5555919Schristos 			error_f("kex_prop2buf failed");
556c5555919Schristos  fail:
557c5555919Schristos 			kex_proposal_free_entries(prop);
558c5555919Schristos 			free(prop);
559c5555919Schristos 			return SSH_ERR_INTERNAL_ERROR;
560c5555919Schristos 		}
561c5555919Schristos 		kex_proposal_free_entries(prop);
562c5555919Schristos 		free(prop);
563c5555919Schristos 	}
564ca32bd8dSchristos 	kex->done = 1;
565aa36fcacSchristos 	kex->flags &= ~KEX_INITIAL;
566e4d43b82Schristos 	sshbuf_reset(kex->peer);
567ca32bd8dSchristos 	kex->flags &= ~KEX_INIT_SENT;
56800a838c4Schristos 	free(kex->name);
569ca32bd8dSchristos 	kex->name = NULL;
570e4d43b82Schristos 	return 0;
571ca32bd8dSchristos }
572ca32bd8dSchristos 
573e4d43b82Schristos int
574e4d43b82Schristos kex_send_kexinit(struct ssh *ssh)
575ca32bd8dSchristos {
576ca32bd8dSchristos 	u_char *cookie;
577e4d43b82Schristos 	struct kex *kex = ssh->kex;
578e4d43b82Schristos 	int r;
579ca32bd8dSchristos 
580cd4ada6aSchristos 	if (kex == NULL) {
58117418e98Schristos 		error_f("no kex");
582e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
583cd4ada6aSchristos 	}
584e4d43b82Schristos 	if (kex->flags & KEX_INIT_SENT)
585e4d43b82Schristos 		return 0;
586ca32bd8dSchristos 	kex->done = 0;
587ca32bd8dSchristos 
588ca32bd8dSchristos 	/* generate a random cookie */
589cd4ada6aSchristos 	if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) {
59017418e98Schristos 		error_f("bad kex length: %zu < %d",
591cd4ada6aSchristos 		    sshbuf_len(kex->my), KEX_COOKIE_LEN);
592e4d43b82Schristos 		return SSH_ERR_INVALID_FORMAT;
593cd4ada6aSchristos 	}
594cd4ada6aSchristos 	if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) {
59517418e98Schristos 		error_f("buffer error");
596e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
597cd4ada6aSchristos 	}
598e4d43b82Schristos 	arc4random_buf(cookie, KEX_COOKIE_LEN);
599e4d43b82Schristos 
600e4d43b82Schristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
601e4d43b82Schristos 	    (r = sshpkt_putb(ssh, kex->my)) != 0 ||
602cd4ada6aSchristos 	    (r = sshpkt_send(ssh)) != 0) {
60317418e98Schristos 		error_fr(r, "compose reply");
604e4d43b82Schristos 		return r;
605cd4ada6aSchristos 	}
606ca32bd8dSchristos 	debug("SSH2_MSG_KEXINIT sent");
607ca32bd8dSchristos 	kex->flags |= KEX_INIT_SENT;
608e4d43b82Schristos 	return 0;
609ca32bd8dSchristos }
610ca32bd8dSchristos 
611e4d43b82Schristos int
6127a183406Schristos kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
613ca32bd8dSchristos {
614e4d43b82Schristos 	struct kex *kex = ssh->kex;
615e4d43b82Schristos 	const u_char *ptr;
616e4d43b82Schristos 	u_int i;
617e4d43b82Schristos 	size_t dlen;
618e4d43b82Schristos 	int r;
619ca32bd8dSchristos 
620ca32bd8dSchristos 	debug("SSH2_MSG_KEXINIT received");
621cd4ada6aSchristos 	if (kex == NULL) {
62217418e98Schristos 		error_f("no kex");
623cd4ada6aSchristos 		return SSH_ERR_INTERNAL_ERROR;
624cd4ada6aSchristos 	}
625514b5d45Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error);
626e4d43b82Schristos 	ptr = sshpkt_ptr(ssh, &dlen);
627e4d43b82Schristos 	if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
628e4d43b82Schristos 		return r;
629ca32bd8dSchristos 
630ca32bd8dSchristos 	/* discard packet */
631cd4ada6aSchristos 	for (i = 0; i < KEX_COOKIE_LEN; i++) {
632cd4ada6aSchristos 		if ((r = sshpkt_get_u8(ssh, NULL)) != 0) {
63317418e98Schristos 			error_fr(r, "discard cookie");
634e4d43b82Schristos 			return r;
635cd4ada6aSchristos 		}
636cd4ada6aSchristos 	}
637cd4ada6aSchristos 	for (i = 0; i < PROPOSAL_MAX; i++) {
638cd4ada6aSchristos 		if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
63917418e98Schristos 			error_fr(r, "discard proposal");
640e4d43b82Schristos 			return r;
641cd4ada6aSchristos 		}
642cd4ada6aSchristos 	}
643ce11a51fSchristos 	/*
644ce11a51fSchristos 	 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
645ce11a51fSchristos 	 * KEX method has the server move first, but a server might be using
646ce11a51fSchristos 	 * a custom method or one that we otherwise don't support. We should
647ce11a51fSchristos 	 * be prepared to remember first_kex_follows here so we can eat a
648ce11a51fSchristos 	 * packet later.
649ce11a51fSchristos 	 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
650ce11a51fSchristos 	 * for cases where the server *doesn't* go first. I guess we should
651ce11a51fSchristos 	 * ignore it when it is set for these cases, which is what we do now.
652ce11a51fSchristos 	 */
653e4d43b82Schristos 	if ((r = sshpkt_get_u8(ssh, NULL)) != 0 ||	/* first_kex_follows */
654e4d43b82Schristos 	    (r = sshpkt_get_u32(ssh, NULL)) != 0 ||	/* reserved */
655e4d43b82Schristos 	    (r = sshpkt_get_end(ssh)) != 0)
656e4d43b82Schristos 			return r;
657ca32bd8dSchristos 
658ca32bd8dSchristos 	if (!(kex->flags & KEX_INIT_SENT))
659e4d43b82Schristos 		if ((r = kex_send_kexinit(ssh)) != 0)
660e4d43b82Schristos 			return r;
661514b5d45Schristos 	if ((r = kex_choose_conf(ssh, seq)) != 0)
662e4d43b82Schristos 		return r;
663ca32bd8dSchristos 
664e4d43b82Schristos 	if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
665e4d43b82Schristos 		return (kex->kex[kex->kex_type])(ssh);
666ca32bd8dSchristos 
66717418e98Schristos 	error_f("unknown kex type %u", kex->kex_type);
668e4d43b82Schristos 	return SSH_ERR_INTERNAL_ERROR;
669ca32bd8dSchristos }
670ca32bd8dSchristos 
671aa36fcacSchristos struct kex *
672aa36fcacSchristos kex_new(void)
673e4d43b82Schristos {
674e4d43b82Schristos 	struct kex *kex;
675e4d43b82Schristos 
676aa36fcacSchristos 	if ((kex = calloc(1, sizeof(*kex))) == NULL ||
677aa36fcacSchristos 	    (kex->peer = sshbuf_new()) == NULL ||
678aa36fcacSchristos 	    (kex->my = sshbuf_new()) == NULL ||
679aa36fcacSchristos 	    (kex->client_version = sshbuf_new()) == NULL ||
68017418e98Schristos 	    (kex->server_version = sshbuf_new()) == NULL ||
68117418e98Schristos 	    (kex->session_id = sshbuf_new()) == NULL) {
682e4d43b82Schristos 		kex_free(kex);
683aa36fcacSchristos 		return NULL;
684aa36fcacSchristos 	}
685aa36fcacSchristos 	return kex;
686e4d43b82Schristos }
687e4d43b82Schristos 
688e4d43b82Schristos void
689e4d43b82Schristos kex_free_newkeys(struct newkeys *newkeys)
690e4d43b82Schristos {
691e4d43b82Schristos 	if (newkeys == NULL)
692e4d43b82Schristos 		return;
693e4d43b82Schristos 	if (newkeys->enc.key) {
694e4d43b82Schristos 		explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
695e4d43b82Schristos 		free(newkeys->enc.key);
696e4d43b82Schristos 		newkeys->enc.key = NULL;
697e4d43b82Schristos 	}
698e4d43b82Schristos 	if (newkeys->enc.iv) {
69979976551Schristos 		explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
700e4d43b82Schristos 		free(newkeys->enc.iv);
701e4d43b82Schristos 		newkeys->enc.iv = NULL;
702e4d43b82Schristos 	}
703e4d43b82Schristos 	free(newkeys->enc.name);
704e4d43b82Schristos 	explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
705e4d43b82Schristos 	free(newkeys->comp.name);
706e4d43b82Schristos 	explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
707e4d43b82Schristos 	mac_clear(&newkeys->mac);
708e4d43b82Schristos 	if (newkeys->mac.key) {
709e4d43b82Schristos 		explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
710e4d43b82Schristos 		free(newkeys->mac.key);
711e4d43b82Schristos 		newkeys->mac.key = NULL;
712e4d43b82Schristos 	}
713e4d43b82Schristos 	free(newkeys->mac.name);
714e4d43b82Schristos 	explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
7158db691beSchristos 	freezero(newkeys, sizeof(*newkeys));
716e4d43b82Schristos }
717e4d43b82Schristos 
718e4d43b82Schristos void
719e4d43b82Schristos kex_free(struct kex *kex)
720e4d43b82Schristos {
721e4d43b82Schristos 	u_int mode;
722e4d43b82Schristos 
723aa36fcacSchristos 	if (kex == NULL)
724aa36fcacSchristos 		return;
725aa36fcacSchristos 
726e4d43b82Schristos #ifdef WITH_OPENSSL
727e4d43b82Schristos 	DH_free(kex->dh);
728e4d43b82Schristos 	EC_KEY_free(kex->ec_client_key);
729e4d43b82Schristos #endif
730e4d43b82Schristos 	for (mode = 0; mode < MODE_MAX; mode++) {
731e4d43b82Schristos 		kex_free_newkeys(kex->newkeys[mode]);
732e4d43b82Schristos 		kex->newkeys[mode] = NULL;
733e4d43b82Schristos 	}
734e4d43b82Schristos 	sshbuf_free(kex->peer);
735e4d43b82Schristos 	sshbuf_free(kex->my);
736aa36fcacSchristos 	sshbuf_free(kex->client_version);
737aa36fcacSchristos 	sshbuf_free(kex->server_version);
738aa36fcacSchristos 	sshbuf_free(kex->client_pub);
73917418e98Schristos 	sshbuf_free(kex->session_id);
740a03ec00cSchristos 	sshbuf_free(kex->initial_sig);
741a03ec00cSchristos 	sshkey_free(kex->initial_hostkey);
7428395c133Schristos 	free(kex->failed_choice);
74379976551Schristos 	free(kex->hostkey_alg);
74479976551Schristos 	free(kex->name);
745e4d43b82Schristos 	free(kex);
746e4d43b82Schristos }
747e4d43b82Schristos 
748e4d43b82Schristos int
749b1066cf3Schristos kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
750aa36fcacSchristos {
751aa36fcacSchristos 	int r;
752aa36fcacSchristos 
753aa36fcacSchristos 	if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
754aa36fcacSchristos 		return r;
755aa36fcacSchristos 	ssh->kex->flags = KEX_INITIAL;
756aa36fcacSchristos 	kex_reset_dispatch(ssh);
757aa36fcacSchristos 	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
758aa36fcacSchristos 	return 0;
759aa36fcacSchristos }
760aa36fcacSchristos 
761aa36fcacSchristos int
762b1066cf3Schristos kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
763e4d43b82Schristos {
764e4d43b82Schristos 	int r;
765e4d43b82Schristos 
766aa36fcacSchristos 	if ((r = kex_ready(ssh, proposal)) != 0)
767e4d43b82Schristos 		return r;
768e4d43b82Schristos 	if ((r = kex_send_kexinit(ssh)) != 0) {		/* we start */
769e4d43b82Schristos 		kex_free(ssh->kex);
770e4d43b82Schristos 		ssh->kex = NULL;
771e4d43b82Schristos 		return r;
772e4d43b82Schristos 	}
773e4d43b82Schristos 	return 0;
774e4d43b82Schristos }
775e4d43b82Schristos 
77679976551Schristos /*
77779976551Schristos  * Request key re-exchange, returns 0 on success or a ssherr.h error
77879976551Schristos  * code otherwise. Must not be called if KEX is incomplete or in-progress.
77979976551Schristos  */
78079976551Schristos int
78179976551Schristos kex_start_rekex(struct ssh *ssh)
78279976551Schristos {
78379976551Schristos 	if (ssh->kex == NULL) {
78417418e98Schristos 		error_f("no kex");
78579976551Schristos 		return SSH_ERR_INTERNAL_ERROR;
78679976551Schristos 	}
78779976551Schristos 	if (ssh->kex->done == 0) {
78817418e98Schristos 		error_f("requested twice");
78979976551Schristos 		return SSH_ERR_INTERNAL_ERROR;
79079976551Schristos 	}
79179976551Schristos 	ssh->kex->done = 0;
79279976551Schristos 	return kex_send_kexinit(ssh);
79379976551Schristos }
79479976551Schristos 
795e4d43b82Schristos static int
796e4d43b82Schristos choose_enc(struct sshenc *enc, char *client, char *server)
797ca32bd8dSchristos {
798ca32bd8dSchristos 	char *name = match_list(client, server, NULL);
799e4d43b82Schristos 
800ca32bd8dSchristos 	if (name == NULL)
801e4d43b82Schristos 		return SSH_ERR_NO_CIPHER_ALG_MATCH;
80241768fc1Schristos 	if ((enc->cipher = cipher_by_name(name)) == NULL) {
80317418e98Schristos 		error_f("unsupported cipher %s", name);
80441768fc1Schristos 		free(name);
805e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
80641768fc1Schristos 	}
807ca32bd8dSchristos 	enc->name = name;
808ca32bd8dSchristos 	enc->enabled = 0;
809ca32bd8dSchristos 	enc->iv = NULL;
810ce11a51fSchristos 	enc->iv_len = cipher_ivlen(enc->cipher);
811ca32bd8dSchristos 	enc->key = NULL;
812ca32bd8dSchristos 	enc->key_len = cipher_keylen(enc->cipher);
813ca32bd8dSchristos 	enc->block_size = cipher_blocksize(enc->cipher);
814e4d43b82Schristos 	return 0;
815ca32bd8dSchristos }
816ca32bd8dSchristos 
817e4d43b82Schristos static int
818e4d43b82Schristos choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
819ca32bd8dSchristos {
820ca32bd8dSchristos 	char *name = match_list(client, server, NULL);
821e4d43b82Schristos 
822ca32bd8dSchristos 	if (name == NULL)
823e4d43b82Schristos 		return SSH_ERR_NO_MAC_ALG_MATCH;
82441768fc1Schristos 	if (mac_setup(mac, name) < 0) {
82517418e98Schristos 		error_f("unsupported MAC %s", name);
82641768fc1Schristos 		free(name);
827e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
82841768fc1Schristos 	}
829ca32bd8dSchristos 	mac->name = name;
830ca32bd8dSchristos 	mac->key = NULL;
831ca32bd8dSchristos 	mac->enabled = 0;
832e4d43b82Schristos 	return 0;
833ca32bd8dSchristos }
834ca32bd8dSchristos 
835e4d43b82Schristos static int
836e4d43b82Schristos choose_comp(struct sshcomp *comp, char *client, char *server)
837ca32bd8dSchristos {
838ca32bd8dSchristos 	char *name = match_list(client, server, NULL);
839e4d43b82Schristos 
840ca32bd8dSchristos 	if (name == NULL)
841e4d43b82Schristos 		return SSH_ERR_NO_COMPRESS_ALG_MATCH;
842ed75d7a8Schristos #ifdef WITH_ZLIB
843ca32bd8dSchristos 	if (strcmp(name, "zlib@openssh.com") == 0) {
844ca32bd8dSchristos 		comp->type = COMP_DELAYED;
845ed75d7a8Schristos 	} else
846ed75d7a8Schristos #endif	/* WITH_ZLIB */
847ed75d7a8Schristos 	if (strcmp(name, "none") == 0) {
848ca32bd8dSchristos 		comp->type = COMP_NONE;
849ca32bd8dSchristos 	} else {
85017418e98Schristos 		error_f("unsupported compression scheme %s", name);
85141768fc1Schristos 		free(name);
852e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
853ca32bd8dSchristos 	}
854ca32bd8dSchristos 	comp->name = name;
855e4d43b82Schristos 	return 0;
856ca32bd8dSchristos }
857ca32bd8dSchristos 
858e4d43b82Schristos static int
859e4d43b82Schristos choose_kex(struct kex *k, char *client, char *server)
860ca32bd8dSchristos {
861ca32bd8dSchristos 	k->name = match_list(client, server, NULL);
862e4d43b82Schristos 
86379976551Schristos 	debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
864ca32bd8dSchristos 	if (k->name == NULL)
865e4d43b82Schristos 		return SSH_ERR_NO_KEX_ALG_MATCH;
8661c7715ddSchristos 	if (!kex_name_valid(k->name)) {
86717418e98Schristos 		error_f("unsupported KEX method %s", k->name);
868e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
869cd4ada6aSchristos 	}
8701c7715ddSchristos 	k->kex_type = kex_type_from_name(k->name);
8711c7715ddSchristos 	k->hash_alg = kex_hash_from_name(k->name);
8721c7715ddSchristos 	k->ec_nid = kex_nid_from_name(k->name);
873e4d43b82Schristos 	return 0;
874ca32bd8dSchristos }
875ca32bd8dSchristos 
876e4d43b82Schristos static int
877e4d43b82Schristos choose_hostkeyalg(struct kex *k, char *client, char *server)
878ca32bd8dSchristos {
87917418e98Schristos 	free(k->hostkey_alg);
88079976551Schristos 	k->hostkey_alg = match_list(client, server, NULL);
881e4d43b82Schristos 
88279976551Schristos 	debug("kex: host key algorithm: %s",
88379976551Schristos 	    k->hostkey_alg ? k->hostkey_alg : "(no match)");
88479976551Schristos 	if (k->hostkey_alg == NULL)
885e4d43b82Schristos 		return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
88679976551Schristos 	k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
887cd4ada6aSchristos 	if (k->hostkey_type == KEY_UNSPEC) {
88817418e98Schristos 		error_f("unsupported hostkey algorithm %s", k->hostkey_alg);
889e4d43b82Schristos 		return SSH_ERR_INTERNAL_ERROR;
890cd4ada6aSchristos 	}
89179976551Schristos 	k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
892e4d43b82Schristos 	return 0;
893ca32bd8dSchristos }
894ca32bd8dSchristos 
895ca32bd8dSchristos static int
896ca32bd8dSchristos proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
897ca32bd8dSchristos {
898ca32bd8dSchristos 	static int check[] = {
899ca32bd8dSchristos 		PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
900ca32bd8dSchristos 	};
901ca32bd8dSchristos 	int *idx;
902ca32bd8dSchristos 	char *p;
903ca32bd8dSchristos 
904ca32bd8dSchristos 	for (idx = &check[0]; *idx != -1; idx++) {
905ca32bd8dSchristos 		if ((p = strchr(my[*idx], ',')) != NULL)
906ca32bd8dSchristos 			*p = '\0';
907ca32bd8dSchristos 		if ((p = strchr(peer[*idx], ',')) != NULL)
908ca32bd8dSchristos 			*p = '\0';
909ca32bd8dSchristos 		if (strcmp(my[*idx], peer[*idx]) != 0) {
910ca32bd8dSchristos 			debug2("proposal mismatch: my %s peer %s",
911ca32bd8dSchristos 			    my[*idx], peer[*idx]);
912ca32bd8dSchristos 			return (0);
913ca32bd8dSchristos 		}
914ca32bd8dSchristos 	}
915ca32bd8dSchristos 	debug2("proposals match");
916ca32bd8dSchristos 	return (1);
917ca32bd8dSchristos }
918ca32bd8dSchristos 
919a03ec00cSchristos static int
920514b5d45Schristos kexalgs_contains(char **peer, const char *ext)
921a03ec00cSchristos {
9221c7715ddSchristos 	return kex_has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
923a03ec00cSchristos }
924a03ec00cSchristos 
925e4d43b82Schristos static int
926514b5d45Schristos kex_choose_conf(struct ssh *ssh, uint32_t seq)
927ca32bd8dSchristos {
928e4d43b82Schristos 	struct kex *kex = ssh->kex;
929e4d43b82Schristos 	struct newkeys *newkeys;
930e4d43b82Schristos 	char **my = NULL, **peer = NULL;
931ca32bd8dSchristos 	char **cprop, **sprop;
932ca32bd8dSchristos 	int nenc, nmac, ncomp;
9338a4530f9Schristos 	u_int mode, ctos, need, dh_need, authlen;
934313c6c94Schristos 	int log_flag = 0;
935e4d43b82Schristos 	int r, first_kex_follows;
936313c6c94Schristos 
93779976551Schristos 	debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
93879976551Schristos 	if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
93979976551Schristos 		goto out;
94079976551Schristos 	debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
94179976551Schristos 	if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
942e4d43b82Schristos 		goto out;
943ca32bd8dSchristos 
944ca32bd8dSchristos 	if (kex->server) {
945ca32bd8dSchristos 		cprop=peer;
946ca32bd8dSchristos 		sprop=my;
947ca32bd8dSchristos 	} else {
948ca32bd8dSchristos 		cprop=my;
949ca32bd8dSchristos 		sprop=peer;
950ca32bd8dSchristos 	}
951ca32bd8dSchristos 
952514b5d45Schristos 	/* Check whether peer supports ext_info/kex_strict */
953514b5d45Schristos 	if ((kex->flags & KEX_INITIAL) != 0) {
954514b5d45Schristos 		if (kex->server) {
955514b5d45Schristos 			kex->ext_info_c = kexalgs_contains(peer, "ext-info-c");
956514b5d45Schristos 			kex->kex_strict = kexalgs_contains(peer,
957514b5d45Schristos 			    "kex-strict-c-v00@openssh.com");
958514b5d45Schristos 		} else {
959514b5d45Schristos 			kex->ext_info_s = kexalgs_contains(peer, "ext-info-s");
960514b5d45Schristos 			kex->kex_strict = kexalgs_contains(peer,
961514b5d45Schristos 			    "kex-strict-s-v00@openssh.com");
962514b5d45Schristos 		}
963514b5d45Schristos 		if (kex->kex_strict) {
964514b5d45Schristos 			debug3_f("will use strict KEX ordering");
965514b5d45Schristos 			if (seq != 0)
966514b5d45Schristos 				ssh_packet_disconnect(ssh,
967514b5d45Schristos 				    "strict KEX violation: "
968514b5d45Schristos 				    "KEXINIT was not the first packet");
969514b5d45Schristos 		}
97079976551Schristos 	}
97179976551Schristos 
972a03ec00cSchristos 	/* Check whether client supports rsa-sha2 algorithms */
973a03ec00cSchristos 	if (kex->server && (kex->flags & KEX_INITIAL)) {
9741c7715ddSchristos 		if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
975a03ec00cSchristos 		    "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com"))
976a03ec00cSchristos 			kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
9771c7715ddSchristos 		if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
978a03ec00cSchristos 		    "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com"))
979a03ec00cSchristos 			kex->flags |= KEX_RSA_SHA2_512_SUPPORTED;
980a03ec00cSchristos 	}
981a03ec00cSchristos 
982ca32bd8dSchristos 	/* Algorithm Negotiation */
98379976551Schristos 	if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
98479976551Schristos 	    sprop[PROPOSAL_KEX_ALGS])) != 0) {
98579976551Schristos 		kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
98679976551Schristos 		peer[PROPOSAL_KEX_ALGS] = NULL;
98779976551Schristos 		goto out;
98879976551Schristos 	}
98979976551Schristos 	if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
99079976551Schristos 	    sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
99179976551Schristos 		kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
99279976551Schristos 		peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
99379976551Schristos 		goto out;
99479976551Schristos 	}
995ca32bd8dSchristos 	for (mode = 0; mode < MODE_MAX; mode++) {
996e4d43b82Schristos 		if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
997e4d43b82Schristos 			r = SSH_ERR_ALLOC_FAIL;
998e4d43b82Schristos 			goto out;
999e4d43b82Schristos 		}
1000ca32bd8dSchristos 		kex->newkeys[mode] = newkeys;
1001ca32bd8dSchristos 		ctos = (!kex->server && mode == MODE_OUT) ||
1002ca32bd8dSchristos 		    (kex->server && mode == MODE_IN);
1003ca32bd8dSchristos 		nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
1004ca32bd8dSchristos 		nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
1005ca32bd8dSchristos 		ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
1006e4d43b82Schristos 		if ((r = choose_enc(&newkeys->enc, cprop[nenc],
10078395c133Schristos 		    sprop[nenc])) != 0) {
10088395c133Schristos 			kex->failed_choice = peer[nenc];
10098395c133Schristos 			peer[nenc] = NULL;
1010e4d43b82Schristos 			goto out;
10118395c133Schristos 		}
1012ce11a51fSchristos 		authlen = cipher_authlen(newkeys->enc.cipher);
1013e4d43b82Schristos 		/* ignore mac for authenticated encryption */
1014e4d43b82Schristos 		if (authlen == 0 &&
1015e4d43b82Schristos 		    (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
10168395c133Schristos 		    sprop[nmac])) != 0) {
10178395c133Schristos 			kex->failed_choice = peer[nmac];
10188395c133Schristos 			peer[nmac] = NULL;
1019e4d43b82Schristos 			goto out;
10208395c133Schristos 		}
1021e4d43b82Schristos 		if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
10228395c133Schristos 		    sprop[ncomp])) != 0) {
10238395c133Schristos 			kex->failed_choice = peer[ncomp];
10248395c133Schristos 			peer[ncomp] = NULL;
1025e4d43b82Schristos 			goto out;
10268395c133Schristos 		}
1027313c6c94Schristos 		debug("REQUESTED ENC.NAME is '%s'", newkeys->enc.name);
1028313c6c94Schristos 		if (strcmp(newkeys->enc.name, "none") == 0) {
1029e4d43b82Schristos 			int auth_flag;
1030e4d43b82Schristos 
1031aa36fcacSchristos 			auth_flag = ssh_packet_authentication_state(ssh);
1032313c6c94Schristos 			debug("Requesting NONE. Authflag is %d", auth_flag);
1033313c6c94Schristos 			if (auth_flag == 1) {
1034313c6c94Schristos 				debug("None requested post authentication.");
1035313c6c94Schristos 			} else {
1036313c6c94Schristos 				fatal("Pre-authentication none cipher requests are not allowed.");
1037313c6c94Schristos 			}
1038313c6c94Schristos 		}
103979976551Schristos 		debug("kex: %s cipher: %s MAC: %s compression: %s",
1040ca32bd8dSchristos 		    ctos ? "client->server" : "server->client",
1041ca32bd8dSchristos 		    newkeys->enc.name,
1042ce11a51fSchristos 		    authlen == 0 ? newkeys->mac.name : "<implicit>",
1043ca32bd8dSchristos 		    newkeys->comp.name);
1044313c6c94Schristos 		/* client starts withctos = 0 && log flag = 0 and no log*/
1045313c6c94Schristos 		/* 2nd client pass ctos=1 and flag = 1 so no log*/
1046313c6c94Schristos 		/* server starts with ctos =1 && log_flag = 0 so log */
1047313c6c94Schristos 		/* 2nd sever pass ctos = 1 && log flag = 1 so no log*/
1048313c6c94Schristos 		/* -cjr*/
1049313c6c94Schristos 		if (ctos && !log_flag) {
1050313c6c94Schristos 			logit("SSH: Server;Ltype: Kex;Remote: %s-%d;Enc: %s;MAC: %s;Comp: %s",
10515101d403Schristos 			      ssh_remote_ipaddr(ssh),
10525101d403Schristos 			      ssh_remote_port(ssh),
1053313c6c94Schristos 			      newkeys->enc.name,
1054*195d6bf6Srin 			      authlen == 0 ? newkeys->mac.name : "<implicit>",
1055313c6c94Schristos 			      newkeys->comp.name);
1056313c6c94Schristos 		}
1057313c6c94Schristos 		log_flag = 1;
1058ca32bd8dSchristos 	}
10598a4530f9Schristos 	need = dh_need = 0;
1060ca32bd8dSchristos 	for (mode = 0; mode < MODE_MAX; mode++) {
1061ca32bd8dSchristos 		newkeys = kex->newkeys[mode];
1062ee85abc4Schristos 		need = MAXIMUM(need, newkeys->enc.key_len);
1063ee85abc4Schristos 		need = MAXIMUM(need, newkeys->enc.block_size);
1064ee85abc4Schristos 		need = MAXIMUM(need, newkeys->enc.iv_len);
1065ee85abc4Schristos 		need = MAXIMUM(need, newkeys->mac.key_len);
1066ee85abc4Schristos 		dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
1067ee85abc4Schristos 		dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
1068ee85abc4Schristos 		dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
1069ee85abc4Schristos 		dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
1070ca32bd8dSchristos 	}
1071ca32bd8dSchristos 	/* XXX need runden? */
1072ca32bd8dSchristos 	kex->we_need = need;
10738a4530f9Schristos 	kex->dh_need = dh_need;
1074ca32bd8dSchristos 
1075ca32bd8dSchristos 	/* ignore the next message if the proposals do not match */
1076ffae97bbSchristos 	if (first_kex_follows && !proposals_match(my, peer))
1077e4d43b82Schristos 		ssh->dispatch_skip_packets = 1;
1078e4d43b82Schristos 	r = 0;
1079e4d43b82Schristos  out:
1080ca32bd8dSchristos 	kex_prop_free(my);
1081ca32bd8dSchristos 	kex_prop_free(peer);
1082e4d43b82Schristos 	return r;
1083ca32bd8dSchristos }
1084ca32bd8dSchristos 
1085e4d43b82Schristos static int
1086e4d43b82Schristos derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
1087e4d43b82Schristos     const struct sshbuf *shared_secret, u_char **keyp)
1088ca32bd8dSchristos {
1089e4d43b82Schristos 	struct kex *kex = ssh->kex;
1090e4d43b82Schristos 	struct ssh_digest_ctx *hashctx = NULL;
1091ca32bd8dSchristos 	char c = id;
1092ca32bd8dSchristos 	u_int have;
10938a4530f9Schristos 	size_t mdsz;
1094ca32bd8dSchristos 	u_char *digest;
1095e4d43b82Schristos 	int r;
1096ca32bd8dSchristos 
10978a4530f9Schristos 	if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
1098e4d43b82Schristos 		return SSH_ERR_INVALID_ARGUMENT;
1099ee85abc4Schristos 	if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
1100e4d43b82Schristos 		r = SSH_ERR_ALLOC_FAIL;
1101e4d43b82Schristos 		goto out;
1102e4d43b82Schristos 	}
1103ca32bd8dSchristos 
1104ca32bd8dSchristos 	/* K1 = HASH(K || H || "A" || session_id) */
1105e4d43b82Schristos 	if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1106e4d43b82Schristos 	    ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
11078a4530f9Schristos 	    ssh_digest_update(hashctx, hash, hashlen) != 0 ||
11088a4530f9Schristos 	    ssh_digest_update(hashctx, &c, 1) != 0 ||
110917418e98Schristos 	    ssh_digest_update_buffer(hashctx, kex->session_id) != 0 ||
1110e4d43b82Schristos 	    ssh_digest_final(hashctx, digest, mdsz) != 0) {
1111e4d43b82Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
111217418e98Schristos 		error_f("KEX hash failed");
1113e4d43b82Schristos 		goto out;
1114e4d43b82Schristos 	}
11158a4530f9Schristos 	ssh_digest_free(hashctx);
1116e4d43b82Schristos 	hashctx = NULL;
1117ca32bd8dSchristos 
1118ca32bd8dSchristos 	/*
1119ca32bd8dSchristos 	 * expand key:
1120ca32bd8dSchristos 	 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
1121ca32bd8dSchristos 	 * Key = K1 || K2 || ... || Kn
1122ca32bd8dSchristos 	 */
1123ca32bd8dSchristos 	for (have = mdsz; need > have; have += mdsz) {
1124e4d43b82Schristos 		if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1125e4d43b82Schristos 		    ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
11268a4530f9Schristos 		    ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1127e4d43b82Schristos 		    ssh_digest_update(hashctx, digest, have) != 0 ||
1128e4d43b82Schristos 		    ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
112917418e98Schristos 			error_f("KDF failed");
1130e4d43b82Schristos 			r = SSH_ERR_LIBCRYPTO_ERROR;
1131e4d43b82Schristos 			goto out;
1132ca32bd8dSchristos 		}
1133e4d43b82Schristos 		ssh_digest_free(hashctx);
1134e4d43b82Schristos 		hashctx = NULL;
1135e4d43b82Schristos 	}
1136ca32bd8dSchristos #ifdef DEBUG_KEX
1137ca32bd8dSchristos 	fprintf(stderr, "key '%c'== ", c);
1138ca32bd8dSchristos 	dump_digest("key", digest, need);
1139ca32bd8dSchristos #endif
1140e4d43b82Schristos 	*keyp = digest;
1141e4d43b82Schristos 	digest = NULL;
1142e4d43b82Schristos 	r = 0;
1143e4d43b82Schristos  out:
1144e4d43b82Schristos 	free(digest);
1145e4d43b82Schristos 	ssh_digest_free(hashctx);
1146e4d43b82Schristos 	return r;
1147ca32bd8dSchristos }
1148ca32bd8dSchristos 
1149ca32bd8dSchristos #define NKEYS	6
1150e4d43b82Schristos int
1151e4d43b82Schristos kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1152e4d43b82Schristos     const struct sshbuf *shared_secret)
1153ca32bd8dSchristos {
1154e4d43b82Schristos 	struct kex *kex = ssh->kex;
1155ca32bd8dSchristos 	u_char *keys[NKEYS];
1156e4d43b82Schristos 	u_int i, j, mode, ctos;
1157e4d43b82Schristos 	int r;
1158ca32bd8dSchristos 
1159aa36fcacSchristos 	/* save initial hash as session id */
116017418e98Schristos 	if ((kex->flags & KEX_INITIAL) != 0) {
116117418e98Schristos 		if (sshbuf_len(kex->session_id) != 0) {
116217418e98Schristos 			error_f("already have session ID at kex");
116317418e98Schristos 			return SSH_ERR_INTERNAL_ERROR;
116417418e98Schristos 		}
116517418e98Schristos 		if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0)
116617418e98Schristos 			return r;
116717418e98Schristos 	} else if (sshbuf_len(kex->session_id) == 0) {
116817418e98Schristos 		error_f("no session ID in rekex");
116917418e98Schristos 		return SSH_ERR_INTERNAL_ERROR;
1170aa36fcacSchristos 	}
1171ca32bd8dSchristos 	for (i = 0; i < NKEYS; i++) {
1172e4d43b82Schristos 		if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1173e4d43b82Schristos 		    shared_secret, &keys[i])) != 0) {
1174e4d43b82Schristos 			for (j = 0; j < i; j++)
1175e4d43b82Schristos 				free(keys[j]);
1176e4d43b82Schristos 			return r;
1177ca32bd8dSchristos 		}
1178e4d43b82Schristos 	}
1179ca32bd8dSchristos 	for (mode = 0; mode < MODE_MAX; mode++) {
1180ca32bd8dSchristos 		ctos = (!kex->server && mode == MODE_OUT) ||
1181ca32bd8dSchristos 		    (kex->server && mode == MODE_IN);
1182e4d43b82Schristos 		kex->newkeys[mode]->enc.iv  = keys[ctos ? 0 : 1];
1183e4d43b82Schristos 		kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1184e4d43b82Schristos 		kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
1185ca32bd8dSchristos 	}
1186e4d43b82Schristos 	return 0;
1187ca32bd8dSchristos }
1188ca32bd8dSchristos 
1189e4d43b82Schristos int
1190aa36fcacSchristos kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
11918a4530f9Schristos {
1192aa36fcacSchristos 	struct kex *kex = ssh->kex;
11938a4530f9Schristos 
1194aa36fcacSchristos 	*pubp = NULL;
1195aa36fcacSchristos 	*prvp = NULL;
1196aa36fcacSchristos 	if (kex->load_host_public_key == NULL ||
1197cd4ada6aSchristos 	    kex->load_host_private_key == NULL) {
119817418e98Schristos 		error_f("missing hostkey loader");
1199aa36fcacSchristos 		return SSH_ERR_INVALID_ARGUMENT;
1200cd4ada6aSchristos 	}
1201aa36fcacSchristos 	*pubp = kex->load_host_public_key(kex->hostkey_type,
1202aa36fcacSchristos 	    kex->hostkey_nid, ssh);
1203aa36fcacSchristos 	*prvp = kex->load_host_private_key(kex->hostkey_type,
1204aa36fcacSchristos 	    kex->hostkey_nid, ssh);
1205aa36fcacSchristos 	if (*pubp == NULL)
1206aa36fcacSchristos 		return SSH_ERR_NO_HOSTKEY_LOADED;
1207aa36fcacSchristos 	return 0;
12088a4530f9Schristos }
12098a4530f9Schristos 
1210aa36fcacSchristos int
1211aa36fcacSchristos kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
1212aa36fcacSchristos {
1213aa36fcacSchristos 	struct kex *kex = ssh->kex;
1214aa36fcacSchristos 
1215cd4ada6aSchristos 	if (kex->verify_host_key == NULL) {
121617418e98Schristos 		error_f("missing hostkey verifier");
1217aa36fcacSchristos 		return SSH_ERR_INVALID_ARGUMENT;
1218cd4ada6aSchristos 	}
1219aa36fcacSchristos 	if (server_host_key->type != kex->hostkey_type ||
1220aa36fcacSchristos 	    (kex->hostkey_type == KEY_ECDSA &&
1221aa36fcacSchristos 	    server_host_key->ecdsa_nid != kex->hostkey_nid))
1222aa36fcacSchristos 		return SSH_ERR_KEY_TYPE_MISMATCH;
1223aa36fcacSchristos 	if (kex->verify_host_key(server_host_key, ssh) == -1)
1224aa36fcacSchristos 		return  SSH_ERR_SIGNATURE_INVALID;
1225aa36fcacSchristos 	return 0;
1226aa36fcacSchristos }
1227ca32bd8dSchristos 
1228185c8f97Schristos #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
1229ca32bd8dSchristos void
1230aa36fcacSchristos dump_digest(const char *msg, const u_char *digest, int len)
1231ca32bd8dSchristos {
1232ca32bd8dSchristos 	fprintf(stderr, "%s\n", msg);
1233e4d43b82Schristos 	sshbuf_dump_data(digest, len, stderr);
1234ca32bd8dSchristos }
1235ca32bd8dSchristos #endif
1236aa36fcacSchristos 
1237aa36fcacSchristos /*
1238aa36fcacSchristos  * Send a plaintext error message to the peer, suffixed by \r\n.
1239aa36fcacSchristos  * Only used during banner exchange, and there only for the server.
1240aa36fcacSchristos  */
1241aa36fcacSchristos static void
1242aa36fcacSchristos send_error(struct ssh *ssh, const char *msg)
1243aa36fcacSchristos {
1244aa36fcacSchristos 	const char *crnl = "\r\n";
1245aa36fcacSchristos 
1246aa36fcacSchristos 	if (!ssh->kex->server)
1247aa36fcacSchristos 		return;
1248aa36fcacSchristos 
1249aa36fcacSchristos 	if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1250aa36fcacSchristos 	    __UNCONST(msg), strlen(msg)) != strlen(msg) ||
1251aa36fcacSchristos 	    atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1252aa36fcacSchristos 	    __UNCONST(crnl), strlen(crnl)) != strlen(crnl))
125317418e98Schristos 		error_f("write: %.100s", strerror(errno));
1254aa36fcacSchristos }
1255aa36fcacSchristos 
1256aa36fcacSchristos /*
1257aa36fcacSchristos  * Sends our identification string and waits for the peer's. Will block for
1258aa36fcacSchristos  * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1259aa36fcacSchristos  * Returns on 0 success or a ssherr.h code on failure.
1260aa36fcacSchristos  */
1261aa36fcacSchristos int
1262aa36fcacSchristos kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1263aa36fcacSchristos     const char *version_addendum)
1264aa36fcacSchristos {
12658db691beSchristos 	int remote_major, remote_minor, mismatch, oerrno = 0;
1266b1066cf3Schristos 	size_t len, n;
1267aa36fcacSchristos 	int r, expect_nl;
1268aa36fcacSchristos 	u_char c;
1269aa36fcacSchristos 	struct sshbuf *our_version = ssh->kex->server ?
1270aa36fcacSchristos 	    ssh->kex->server_version : ssh->kex->client_version;
1271aa36fcacSchristos 	struct sshbuf *peer_version = ssh->kex->server ?
1272aa36fcacSchristos 	    ssh->kex->client_version : ssh->kex->server_version;
1273aa36fcacSchristos 	char *our_version_string = NULL, *peer_version_string = NULL;
1274aa36fcacSchristos 	char *cp, *remote_version = NULL;
1275aa36fcacSchristos 
1276aa36fcacSchristos 	/* Prepare and send our banner */
1277aa36fcacSchristos 	sshbuf_reset(our_version);
1278aa36fcacSchristos 	if (version_addendum != NULL && *version_addendum == '\0')
1279aa36fcacSchristos 		version_addendum = NULL;
1280514b5d45Schristos 	if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%s%s%s\r\n",
1281aa36fcacSchristos 	    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1282aa36fcacSchristos 	    version_addendum == NULL ? "" : " ",
1283aa36fcacSchristos 	    version_addendum == NULL ? "" : version_addendum)) != 0) {
12848db691beSchristos 		oerrno = errno;
128517418e98Schristos 		error_fr(r, "sshbuf_putf");
1286aa36fcacSchristos 		goto out;
1287aa36fcacSchristos 	}
1288aa36fcacSchristos 
1289aa36fcacSchristos 	if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1290aa36fcacSchristos 	    sshbuf_mutable_ptr(our_version),
1291aa36fcacSchristos 	    sshbuf_len(our_version)) != sshbuf_len(our_version)) {
12928db691beSchristos 		oerrno = errno;
129317418e98Schristos 		debug_f("write: %.100s", strerror(errno));
1294aa36fcacSchristos 		r = SSH_ERR_SYSTEM_ERROR;
1295aa36fcacSchristos 		goto out;
1296aa36fcacSchristos 	}
1297aa36fcacSchristos 	if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
12988db691beSchristos 		oerrno = errno;
129917418e98Schristos 		error_fr(r, "sshbuf_consume_end");
1300aa36fcacSchristos 		goto out;
1301aa36fcacSchristos 	}
1302aa36fcacSchristos 	our_version_string = sshbuf_dup_string(our_version);
1303aa36fcacSchristos 	if (our_version_string == NULL) {
130417418e98Schristos 		error_f("sshbuf_dup_string failed");
1305aa36fcacSchristos 		r = SSH_ERR_ALLOC_FAIL;
1306aa36fcacSchristos 		goto out;
1307aa36fcacSchristos 	}
1308aa36fcacSchristos 	debug("Local version string %.100s", our_version_string);
1309aa36fcacSchristos 
1310aa36fcacSchristos 	/* Read other side's version identification. */
1311aa36fcacSchristos 	for (n = 0; ; n++) {
1312aa36fcacSchristos 		if (n >= SSH_MAX_PRE_BANNER_LINES) {
1313aa36fcacSchristos 			send_error(ssh, "No SSH identification string "
1314aa36fcacSchristos 			    "received.");
131517418e98Schristos 			error_f("No SSH version received in first %u lines "
131617418e98Schristos 			    "from server", SSH_MAX_PRE_BANNER_LINES);
1317aa36fcacSchristos 			r = SSH_ERR_INVALID_FORMAT;
1318aa36fcacSchristos 			goto out;
1319aa36fcacSchristos 		}
1320aa36fcacSchristos 		sshbuf_reset(peer_version);
1321aa36fcacSchristos 		expect_nl = 0;
1322b1066cf3Schristos 		for (;;) {
1323aa36fcacSchristos 			if (timeout_ms > 0) {
1324aa36fcacSchristos 				r = waitrfd(ssh_packet_get_connection_in(ssh),
1325a629fefcSchristos 				    &timeout_ms, NULL);
1326aa36fcacSchristos 				if (r == -1 && errno == ETIMEDOUT) {
1327aa36fcacSchristos 					send_error(ssh, "Timed out waiting "
1328aa36fcacSchristos 					    "for SSH identification string.");
1329aa36fcacSchristos 					error("Connection timed out during "
1330aa36fcacSchristos 					    "banner exchange");
1331aa36fcacSchristos 					r = SSH_ERR_CONN_TIMEOUT;
1332aa36fcacSchristos 					goto out;
1333aa36fcacSchristos 				} else if (r == -1) {
13348db691beSchristos 					oerrno = errno;
133517418e98Schristos 					error_f("%s", strerror(errno));
1336aa36fcacSchristos 					r = SSH_ERR_SYSTEM_ERROR;
1337aa36fcacSchristos 					goto out;
1338aa36fcacSchristos 				}
1339aa36fcacSchristos 			}
1340aa36fcacSchristos 
1341aa36fcacSchristos 			len = atomicio(read, ssh_packet_get_connection_in(ssh),
1342aa36fcacSchristos 			    &c, 1);
1343aa36fcacSchristos 			if (len != 1 && errno == EPIPE) {
1344a629fefcSchristos 				verbose_f("Connection closed by remote host");
1345aa36fcacSchristos 				r = SSH_ERR_CONN_CLOSED;
1346aa36fcacSchristos 				goto out;
1347aa36fcacSchristos 			} else if (len != 1) {
13488db691beSchristos 				oerrno = errno;
134917418e98Schristos 				error_f("read: %.100s", strerror(errno));
1350aa36fcacSchristos 				r = SSH_ERR_SYSTEM_ERROR;
1351aa36fcacSchristos 				goto out;
1352aa36fcacSchristos 			}
1353aa36fcacSchristos 			if (c == '\r') {
1354aa36fcacSchristos 				expect_nl = 1;
1355aa36fcacSchristos 				continue;
1356aa36fcacSchristos 			}
1357aa36fcacSchristos 			if (c == '\n')
1358aa36fcacSchristos 				break;
1359aa36fcacSchristos 			if (c == '\0' || expect_nl) {
1360a629fefcSchristos 				verbose_f("banner line contains invalid "
136117418e98Schristos 				    "characters");
1362aa36fcacSchristos 				goto invalid;
1363aa36fcacSchristos 			}
1364aa36fcacSchristos 			if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
13658db691beSchristos 				oerrno = errno;
136617418e98Schristos 				error_fr(r, "sshbuf_put");
1367aa36fcacSchristos 				goto out;
1368aa36fcacSchristos 			}
1369aa36fcacSchristos 			if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1370a629fefcSchristos 				verbose_f("banner line too long");
1371aa36fcacSchristos 				goto invalid;
1372aa36fcacSchristos 			}
1373aa36fcacSchristos 		}
1374aa36fcacSchristos 		/* Is this an actual protocol banner? */
1375aa36fcacSchristos 		if (sshbuf_len(peer_version) > 4 &&
1376aa36fcacSchristos 		    memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1377aa36fcacSchristos 			break;
1378aa36fcacSchristos 		/* If not, then just log the line and continue */
1379aa36fcacSchristos 		if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
138017418e98Schristos 			error_f("sshbuf_dup_string failed");
1381aa36fcacSchristos 			r = SSH_ERR_ALLOC_FAIL;
1382aa36fcacSchristos 			goto out;
1383aa36fcacSchristos 		}
1384aa36fcacSchristos 		/* Do not accept lines before the SSH ident from a client */
1385aa36fcacSchristos 		if (ssh->kex->server) {
1386a629fefcSchristos 			verbose_f("client sent invalid protocol identifier "
138717418e98Schristos 			    "\"%.256s\"", cp);
1388aa36fcacSchristos 			free(cp);
1389aa36fcacSchristos 			goto invalid;
1390aa36fcacSchristos 		}
139117418e98Schristos 		debug_f("banner line %zu: %s", n, cp);
1392aa36fcacSchristos 		free(cp);
1393aa36fcacSchristos 	}
1394aa36fcacSchristos 	peer_version_string = sshbuf_dup_string(peer_version);
1395aa36fcacSchristos 	if (peer_version_string == NULL)
1396b1066cf3Schristos 		fatal_f("sshbuf_dup_string failed");
1397aa36fcacSchristos 	/* XXX must be same size for sscanf */
1398aa36fcacSchristos 	if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
139917418e98Schristos 		error_f("calloc failed");
1400aa36fcacSchristos 		r = SSH_ERR_ALLOC_FAIL;
1401aa36fcacSchristos 		goto out;
1402aa36fcacSchristos 	}
1403aa36fcacSchristos 
1404aa36fcacSchristos 	/*
1405aa36fcacSchristos 	 * Check that the versions match.  In future this might accept
1406aa36fcacSchristos 	 * several versions and set appropriate flags to handle them.
1407aa36fcacSchristos 	 */
1408aa36fcacSchristos 	if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1409aa36fcacSchristos 	    &remote_major, &remote_minor, remote_version) != 3) {
1410aa36fcacSchristos 		error("Bad remote protocol version identification: '%.100s'",
1411aa36fcacSchristos 		    peer_version_string);
1412aa36fcacSchristos  invalid:
1413aa36fcacSchristos 		send_error(ssh, "Invalid SSH identification string.");
1414aa36fcacSchristos 		r = SSH_ERR_INVALID_FORMAT;
1415aa36fcacSchristos 		goto out;
1416aa36fcacSchristos 	}
1417aa36fcacSchristos 	debug("Remote protocol version %d.%d, remote software version %.100s",
1418aa36fcacSchristos 	    remote_major, remote_minor, remote_version);
141917418e98Schristos 	compat_banner(ssh, remote_version);
1420aa36fcacSchristos 
1421aa36fcacSchristos 	mismatch = 0;
1422aa36fcacSchristos 	switch (remote_major) {
1423aa36fcacSchristos 	case 2:
1424aa36fcacSchristos 		break;
1425aa36fcacSchristos 	case 1:
1426aa36fcacSchristos 		if (remote_minor != 99)
1427aa36fcacSchristos 			mismatch = 1;
1428aa36fcacSchristos 		break;
1429aa36fcacSchristos 	default:
1430aa36fcacSchristos 		mismatch = 1;
1431aa36fcacSchristos 		break;
1432aa36fcacSchristos 	}
1433aa36fcacSchristos 	if (mismatch) {
1434aa36fcacSchristos 		error("Protocol major versions differ: %d vs. %d",
1435aa36fcacSchristos 		    PROTOCOL_MAJOR_2, remote_major);
1436aa36fcacSchristos 		send_error(ssh, "Protocol major versions differ.");
1437aa36fcacSchristos 		r = SSH_ERR_NO_PROTOCOL_VERSION;
1438aa36fcacSchristos 		goto out;
1439aa36fcacSchristos 	}
1440aa36fcacSchristos 
1441aa36fcacSchristos 	if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1442aa36fcacSchristos 		logit("probed from %s port %d with %s.  Don't panic.",
1443aa36fcacSchristos 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1444aa36fcacSchristos 		    peer_version_string);
1445aa36fcacSchristos 		r = SSH_ERR_CONN_CLOSED; /* XXX */
1446aa36fcacSchristos 		goto out;
1447aa36fcacSchristos 	}
1448aa36fcacSchristos 	if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1449aa36fcacSchristos 		logit("scanned from %s port %d with %s.  Don't panic.",
1450aa36fcacSchristos 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1451aa36fcacSchristos 		    peer_version_string);
1452aa36fcacSchristos 		r = SSH_ERR_CONN_CLOSED; /* XXX */
1453aa36fcacSchristos 		goto out;
1454aa36fcacSchristos 	}
1455aa36fcacSchristos 	/* success */
1456aa36fcacSchristos 	r = 0;
1457aa36fcacSchristos  out:
1458aa36fcacSchristos 	free(our_version_string);
1459aa36fcacSchristos 	free(peer_version_string);
1460aa36fcacSchristos 	free(remote_version);
14618db691beSchristos 	if (r == SSH_ERR_SYSTEM_ERROR)
14628db691beSchristos 		errno = oerrno;
1463aa36fcacSchristos 	return r;
1464aa36fcacSchristos }
1465aa36fcacSchristos 
1466