xref: /freebsd/crypto/openssh/kex.c (revision f374ba41f55c1a127303d92d830dd58eef2f5243)
1*f374ba41SEd Maste /* $OpenBSD: kex.c,v 1.173 2022/11/07 10:05:38 dtucker Exp $ */
2a04a10f8SKris Kennaway /*
3ae1f160dSDag-Erling Smørgrav  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
4a04a10f8SKris Kennaway  *
5a04a10f8SKris Kennaway  * Redistribution and use in source and binary forms, with or without
6a04a10f8SKris Kennaway  * modification, are permitted provided that the following conditions
7a04a10f8SKris Kennaway  * are met:
8a04a10f8SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
9a04a10f8SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
10a04a10f8SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
11a04a10f8SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
12a04a10f8SKris Kennaway  *    documentation and/or other materials provided with the distribution.
13a04a10f8SKris Kennaway  *
14a04a10f8SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15a04a10f8SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16a04a10f8SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17a04a10f8SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18a04a10f8SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19a04a10f8SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20a04a10f8SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21a04a10f8SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22a04a10f8SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23a04a10f8SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24a04a10f8SKris Kennaway  */
25a04a10f8SKris Kennaway 
26a04a10f8SKris Kennaway #include "includes.h"
27761efaa7SDag-Erling Smørgrav 
2819261079SEd Maste #include <sys/types.h>
2919261079SEd Maste #include <errno.h>
30761efaa7SDag-Erling Smørgrav #include <signal.h>
31761efaa7SDag-Erling Smørgrav #include <stdarg.h>
32761efaa7SDag-Erling Smørgrav #include <stdio.h>
33761efaa7SDag-Erling Smørgrav #include <stdlib.h>
34761efaa7SDag-Erling Smørgrav #include <string.h>
3519261079SEd Maste #include <unistd.h>
3619261079SEd Maste #ifdef HAVE_POLL_H
3719261079SEd Maste #include <poll.h>
3819261079SEd Maste #endif
39a04a10f8SKris Kennaway 
40a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL
411e8db6e2SBrian Feldman #include <openssl/crypto.h>
42076ad2f8SDag-Erling Smørgrav #include <openssl/dh.h>
43a0ee8cc6SDag-Erling Smørgrav #endif
441e8db6e2SBrian Feldman 
4519261079SEd Maste #include "ssh.h"
46761efaa7SDag-Erling Smørgrav #include "ssh2.h"
4719261079SEd Maste #include "atomicio.h"
4819261079SEd Maste #include "version.h"
492632b0c8SKris Kennaway #include "packet.h"
50a04a10f8SKris Kennaway #include "compat.h"
511e8db6e2SBrian Feldman #include "cipher.h"
52bc5531deSDag-Erling Smørgrav #include "sshkey.h"
53761efaa7SDag-Erling Smørgrav #include "kex.h"
541e8db6e2SBrian Feldman #include "log.h"
551e8db6e2SBrian Feldman #include "mac.h"
561e8db6e2SBrian Feldman #include "match.h"
57bc5531deSDag-Erling Smørgrav #include "misc.h"
581e8db6e2SBrian Feldman #include "dispatch.h"
59545d5ecaSDag-Erling Smørgrav #include "monitor.h"
60bc5531deSDag-Erling Smørgrav 
61bc5531deSDag-Erling Smørgrav #include "ssherr.h"
62bc5531deSDag-Erling Smørgrav #include "sshbuf.h"
63f7167e0eSDag-Erling Smørgrav #include "digest.h"
64a04a10f8SKris Kennaway 
65ae1f160dSDag-Erling Smørgrav /* prototype */
66bc5531deSDag-Erling Smørgrav static int kex_choose_conf(struct ssh *);
674f52dfbbSDag-Erling Smørgrav static int kex_input_newkeys(int, u_int32_t, struct ssh *);
681e8db6e2SBrian Feldman 
691323ec57SEd Maste static const char * const proposal_names[PROPOSAL_MAX] = {
70acc1a9efSDag-Erling Smørgrav 	"KEX algorithms",
71acc1a9efSDag-Erling Smørgrav 	"host key algorithms",
72acc1a9efSDag-Erling Smørgrav 	"ciphers ctos",
73acc1a9efSDag-Erling Smørgrav 	"ciphers stoc",
74acc1a9efSDag-Erling Smørgrav 	"MACs ctos",
75acc1a9efSDag-Erling Smørgrav 	"MACs stoc",
76acc1a9efSDag-Erling Smørgrav 	"compression ctos",
77acc1a9efSDag-Erling Smørgrav 	"compression stoc",
78acc1a9efSDag-Erling Smørgrav 	"languages ctos",
79acc1a9efSDag-Erling Smørgrav 	"languages stoc",
80acc1a9efSDag-Erling Smørgrav };
81acc1a9efSDag-Erling Smørgrav 
82e4a9863fSDag-Erling Smørgrav struct kexalg {
83e4a9863fSDag-Erling Smørgrav 	char *name;
84bc5531deSDag-Erling Smørgrav 	u_int type;
85e4a9863fSDag-Erling Smørgrav 	int ec_nid;
86f7167e0eSDag-Erling Smørgrav 	int hash_alg;
87e4a9863fSDag-Erling Smørgrav };
88e4a9863fSDag-Erling Smørgrav static const struct kexalg kexalgs[] = {
89a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL
90f7167e0eSDag-Erling Smørgrav 	{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
91076ad2f8SDag-Erling Smørgrav 	{ KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
92076ad2f8SDag-Erling Smørgrav 	{ KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
93076ad2f8SDag-Erling Smørgrav 	{ KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
94076ad2f8SDag-Erling Smørgrav 	{ KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
95f7167e0eSDag-Erling Smørgrav 	{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
96e4a9863fSDag-Erling Smørgrav #ifdef HAVE_EVP_SHA256
97f7167e0eSDag-Erling Smørgrav 	{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
98a0ee8cc6SDag-Erling Smørgrav #endif /* HAVE_EVP_SHA256 */
99e4a9863fSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
100f7167e0eSDag-Erling Smørgrav 	{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
101f7167e0eSDag-Erling Smørgrav 	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
102f7167e0eSDag-Erling Smørgrav 	{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
103f7167e0eSDag-Erling Smørgrav 	    SSH_DIGEST_SHA384 },
104f7167e0eSDag-Erling Smørgrav # ifdef OPENSSL_HAS_NISTP521
105f7167e0eSDag-Erling Smørgrav 	{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
106f7167e0eSDag-Erling Smørgrav 	    SSH_DIGEST_SHA512 },
107a0ee8cc6SDag-Erling Smørgrav # endif /* OPENSSL_HAS_NISTP521 */
108a0ee8cc6SDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */
109a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_OPENSSL */
110bc5531deSDag-Erling Smørgrav #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
111f7167e0eSDag-Erling Smørgrav 	{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
112ca86bcf2SDag-Erling Smørgrav 	{ KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
11319261079SEd Maste #ifdef USE_SNTRUP761X25519
11419261079SEd Maste 	{ KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0,
11519261079SEd Maste 	    SSH_DIGEST_SHA512 },
11619261079SEd Maste #endif
117bc5531deSDag-Erling Smørgrav #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
11819261079SEd Maste 	{ NULL, 0, -1, -1},
119e4a9863fSDag-Erling Smørgrav };
120e4a9863fSDag-Erling Smørgrav 
121e4a9863fSDag-Erling Smørgrav char *
122f7167e0eSDag-Erling Smørgrav kex_alg_list(char sep)
123e4a9863fSDag-Erling Smørgrav {
124bc5531deSDag-Erling Smørgrav 	char *ret = NULL, *tmp;
125e4a9863fSDag-Erling Smørgrav 	size_t nlen, rlen = 0;
126e4a9863fSDag-Erling Smørgrav 	const struct kexalg *k;
127e4a9863fSDag-Erling Smørgrav 
128e4a9863fSDag-Erling Smørgrav 	for (k = kexalgs; k->name != NULL; k++) {
129e4a9863fSDag-Erling Smørgrav 		if (ret != NULL)
130f7167e0eSDag-Erling Smørgrav 			ret[rlen++] = sep;
131e4a9863fSDag-Erling Smørgrav 		nlen = strlen(k->name);
132bc5531deSDag-Erling Smørgrav 		if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
133bc5531deSDag-Erling Smørgrav 			free(ret);
134bc5531deSDag-Erling Smørgrav 			return NULL;
135bc5531deSDag-Erling Smørgrav 		}
136bc5531deSDag-Erling Smørgrav 		ret = tmp;
137e4a9863fSDag-Erling Smørgrav 		memcpy(ret + rlen, k->name, nlen + 1);
138e4a9863fSDag-Erling Smørgrav 		rlen += nlen;
139e4a9863fSDag-Erling Smørgrav 	}
140e4a9863fSDag-Erling Smørgrav 	return ret;
141e4a9863fSDag-Erling Smørgrav }
142e4a9863fSDag-Erling Smørgrav 
143e4a9863fSDag-Erling Smørgrav static const struct kexalg *
144e4a9863fSDag-Erling Smørgrav kex_alg_by_name(const char *name)
145e4a9863fSDag-Erling Smørgrav {
146e4a9863fSDag-Erling Smørgrav 	const struct kexalg *k;
147e4a9863fSDag-Erling Smørgrav 
148e4a9863fSDag-Erling Smørgrav 	for (k = kexalgs; k->name != NULL; k++) {
149e4a9863fSDag-Erling Smørgrav 		if (strcmp(k->name, name) == 0)
150e4a9863fSDag-Erling Smørgrav 			return k;
151e4a9863fSDag-Erling Smørgrav 	}
152e4a9863fSDag-Erling Smørgrav 	return NULL;
153e4a9863fSDag-Erling Smørgrav }
154e4a9863fSDag-Erling Smørgrav 
1554a421b63SDag-Erling Smørgrav /* Validate KEX method name list */
1564a421b63SDag-Erling Smørgrav int
1574a421b63SDag-Erling Smørgrav kex_names_valid(const char *names)
1584a421b63SDag-Erling Smørgrav {
1594a421b63SDag-Erling Smørgrav 	char *s, *cp, *p;
1604a421b63SDag-Erling Smørgrav 
1614a421b63SDag-Erling Smørgrav 	if (names == NULL || strcmp(names, "") == 0)
1624a421b63SDag-Erling Smørgrav 		return 0;
163bc5531deSDag-Erling Smørgrav 	if ((s = cp = strdup(names)) == NULL)
164bc5531deSDag-Erling Smørgrav 		return 0;
1654a421b63SDag-Erling Smørgrav 	for ((p = strsep(&cp, ",")); p && *p != '\0';
1664a421b63SDag-Erling Smørgrav 	    (p = strsep(&cp, ","))) {
167e4a9863fSDag-Erling Smørgrav 		if (kex_alg_by_name(p) == NULL) {
1684a421b63SDag-Erling Smørgrav 			error("Unsupported KEX algorithm \"%.100s\"", p);
169e4a9863fSDag-Erling Smørgrav 			free(s);
1704a421b63SDag-Erling Smørgrav 			return 0;
1714a421b63SDag-Erling Smørgrav 		}
1724a421b63SDag-Erling Smørgrav 	}
1734a421b63SDag-Erling Smørgrav 	debug3("kex names ok: [%s]", names);
174e4a9863fSDag-Erling Smørgrav 	free(s);
1754a421b63SDag-Erling Smørgrav 	return 1;
1764a421b63SDag-Erling Smørgrav }
1774a421b63SDag-Erling Smørgrav 
178eccfee6eSDag-Erling Smørgrav /*
179eccfee6eSDag-Erling Smørgrav  * Concatenate algorithm names, avoiding duplicates in the process.
180eccfee6eSDag-Erling Smørgrav  * Caller must free returned string.
181eccfee6eSDag-Erling Smørgrav  */
182eccfee6eSDag-Erling Smørgrav char *
183eccfee6eSDag-Erling Smørgrav kex_names_cat(const char *a, const char *b)
184eccfee6eSDag-Erling Smørgrav {
185d93a896eSDag-Erling Smørgrav 	char *ret = NULL, *tmp = NULL, *cp, *p, *m;
186eccfee6eSDag-Erling Smørgrav 	size_t len;
187eccfee6eSDag-Erling Smørgrav 
188eccfee6eSDag-Erling Smørgrav 	if (a == NULL || *a == '\0')
189190cef3dSDag-Erling Smørgrav 		return strdup(b);
190eccfee6eSDag-Erling Smørgrav 	if (b == NULL || *b == '\0')
191eccfee6eSDag-Erling Smørgrav 		return strdup(a);
192eccfee6eSDag-Erling Smørgrav 	if (strlen(b) > 1024*1024)
193eccfee6eSDag-Erling Smørgrav 		return NULL;
194eccfee6eSDag-Erling Smørgrav 	len = strlen(a) + strlen(b) + 2;
195eccfee6eSDag-Erling Smørgrav 	if ((tmp = cp = strdup(b)) == NULL ||
196eccfee6eSDag-Erling Smørgrav 	    (ret = calloc(1, len)) == NULL) {
197eccfee6eSDag-Erling Smørgrav 		free(tmp);
198eccfee6eSDag-Erling Smørgrav 		return NULL;
199eccfee6eSDag-Erling Smørgrav 	}
200eccfee6eSDag-Erling Smørgrav 	strlcpy(ret, a, len);
201eccfee6eSDag-Erling Smørgrav 	for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
202d93a896eSDag-Erling Smørgrav 		if ((m = match_list(ret, p, NULL)) != NULL) {
203d93a896eSDag-Erling Smørgrav 			free(m);
204eccfee6eSDag-Erling Smørgrav 			continue; /* Algorithm already present */
205d93a896eSDag-Erling Smørgrav 		}
206eccfee6eSDag-Erling Smørgrav 		if (strlcat(ret, ",", len) >= len ||
207eccfee6eSDag-Erling Smørgrav 		    strlcat(ret, p, len) >= len) {
208eccfee6eSDag-Erling Smørgrav 			free(tmp);
209eccfee6eSDag-Erling Smørgrav 			free(ret);
210eccfee6eSDag-Erling Smørgrav 			return NULL; /* Shouldn't happen */
211eccfee6eSDag-Erling Smørgrav 		}
212eccfee6eSDag-Erling Smørgrav 	}
213eccfee6eSDag-Erling Smørgrav 	free(tmp);
214eccfee6eSDag-Erling Smørgrav 	return ret;
215eccfee6eSDag-Erling Smørgrav }
216eccfee6eSDag-Erling Smørgrav 
217eccfee6eSDag-Erling Smørgrav /*
218eccfee6eSDag-Erling Smørgrav  * Assemble a list of algorithms from a default list and a string from a
219eccfee6eSDag-Erling Smørgrav  * configuration file. The user-provided string may begin with '+' to
22019261079SEd Maste  * indicate that it should be appended to the default, '-' that the
22119261079SEd Maste  * specified names should be removed, or '^' that they should be placed
22219261079SEd Maste  * at the head.
223eccfee6eSDag-Erling Smørgrav  */
224eccfee6eSDag-Erling Smørgrav int
225190cef3dSDag-Erling Smørgrav kex_assemble_names(char **listp, const char *def, const char *all)
226eccfee6eSDag-Erling Smørgrav {
227190cef3dSDag-Erling Smørgrav 	char *cp, *tmp, *patterns;
228190cef3dSDag-Erling Smørgrav 	char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
229190cef3dSDag-Erling Smørgrav 	int r = SSH_ERR_INTERNAL_ERROR;
230eccfee6eSDag-Erling Smørgrav 
23119261079SEd Maste 	if (listp == NULL || def == NULL || all == NULL)
23219261079SEd Maste 		return SSH_ERR_INVALID_ARGUMENT;
23319261079SEd Maste 
23419261079SEd Maste 	if (*listp == NULL || **listp == '\0') {
235190cef3dSDag-Erling Smørgrav 		if ((*listp = strdup(def)) == NULL)
236190cef3dSDag-Erling Smørgrav 			return SSH_ERR_ALLOC_FAIL;
237eccfee6eSDag-Erling Smørgrav 		return 0;
238eccfee6eSDag-Erling Smørgrav 	}
239190cef3dSDag-Erling Smørgrav 
240190cef3dSDag-Erling Smørgrav 	list = *listp;
241190cef3dSDag-Erling Smørgrav 	*listp = NULL;
242190cef3dSDag-Erling Smørgrav 	if (*list == '+') {
243190cef3dSDag-Erling Smørgrav 		/* Append names to default list */
244190cef3dSDag-Erling Smørgrav 		if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
245190cef3dSDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
246190cef3dSDag-Erling Smørgrav 			goto fail;
247190cef3dSDag-Erling Smørgrav 		}
248190cef3dSDag-Erling Smørgrav 		free(list);
249190cef3dSDag-Erling Smørgrav 		list = tmp;
250190cef3dSDag-Erling Smørgrav 	} else if (*list == '-') {
251190cef3dSDag-Erling Smørgrav 		/* Remove names from default list */
25219261079SEd Maste 		if ((*listp = match_filter_denylist(def, list + 1)) == NULL) {
253190cef3dSDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
254190cef3dSDag-Erling Smørgrav 			goto fail;
255190cef3dSDag-Erling Smørgrav 		}
256190cef3dSDag-Erling Smørgrav 		free(list);
257190cef3dSDag-Erling Smørgrav 		/* filtering has already been done */
258190cef3dSDag-Erling Smørgrav 		return 0;
25919261079SEd Maste 	} else if (*list == '^') {
26019261079SEd Maste 		/* Place names at head of default list */
26119261079SEd Maste 		if ((tmp = kex_names_cat(list + 1, def)) == NULL) {
26219261079SEd Maste 			r = SSH_ERR_ALLOC_FAIL;
26319261079SEd Maste 			goto fail;
26419261079SEd Maste 		}
26519261079SEd Maste 		free(list);
26619261079SEd Maste 		list = tmp;
267190cef3dSDag-Erling Smørgrav 	} else {
268190cef3dSDag-Erling Smørgrav 		/* Explicit list, overrides default - just use "list" as is */
269d93a896eSDag-Erling Smørgrav 	}
270d93a896eSDag-Erling Smørgrav 
271190cef3dSDag-Erling Smørgrav 	/*
272190cef3dSDag-Erling Smørgrav 	 * The supplied names may be a pattern-list. For the -list case,
273190cef3dSDag-Erling Smørgrav 	 * the patterns are applied above. For the +list and explicit list
274190cef3dSDag-Erling Smørgrav 	 * cases we need to do it now.
275190cef3dSDag-Erling Smørgrav 	 */
276190cef3dSDag-Erling Smørgrav 	ret = NULL;
277190cef3dSDag-Erling Smørgrav 	if ((patterns = opatterns = strdup(list)) == NULL) {
278190cef3dSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
279190cef3dSDag-Erling Smørgrav 		goto fail;
280190cef3dSDag-Erling Smørgrav 	}
281190cef3dSDag-Erling Smørgrav 	/* Apply positive (i.e. non-negated) patterns from the list */
282190cef3dSDag-Erling Smørgrav 	while ((cp = strsep(&patterns, ",")) != NULL) {
283190cef3dSDag-Erling Smørgrav 		if (*cp == '!') {
284190cef3dSDag-Erling Smørgrav 			/* negated matches are not supported here */
285190cef3dSDag-Erling Smørgrav 			r = SSH_ERR_INVALID_ARGUMENT;
286190cef3dSDag-Erling Smørgrav 			goto fail;
287190cef3dSDag-Erling Smørgrav 		}
288190cef3dSDag-Erling Smørgrav 		free(matching);
28919261079SEd Maste 		if ((matching = match_filter_allowlist(all, cp)) == NULL) {
290190cef3dSDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
291190cef3dSDag-Erling Smørgrav 			goto fail;
292190cef3dSDag-Erling Smørgrav 		}
293190cef3dSDag-Erling Smørgrav 		if ((tmp = kex_names_cat(ret, matching)) == NULL) {
294190cef3dSDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
295190cef3dSDag-Erling Smørgrav 			goto fail;
296190cef3dSDag-Erling Smørgrav 		}
297190cef3dSDag-Erling Smørgrav 		free(ret);
298190cef3dSDag-Erling Smørgrav 		ret = tmp;
299190cef3dSDag-Erling Smørgrav 	}
300190cef3dSDag-Erling Smørgrav 	if (ret == NULL || *ret == '\0') {
301190cef3dSDag-Erling Smørgrav 		/* An empty name-list is an error */
302190cef3dSDag-Erling Smørgrav 		/* XXX better error code? */
303190cef3dSDag-Erling Smørgrav 		r = SSH_ERR_INVALID_ARGUMENT;
304190cef3dSDag-Erling Smørgrav 		goto fail;
305190cef3dSDag-Erling Smørgrav 	}
306190cef3dSDag-Erling Smørgrav 
307190cef3dSDag-Erling Smørgrav 	/* success */
308190cef3dSDag-Erling Smørgrav 	*listp = ret;
309190cef3dSDag-Erling Smørgrav 	ret = NULL;
310190cef3dSDag-Erling Smørgrav 	r = 0;
311190cef3dSDag-Erling Smørgrav 
312190cef3dSDag-Erling Smørgrav  fail:
313190cef3dSDag-Erling Smørgrav 	free(matching);
314190cef3dSDag-Erling Smørgrav 	free(opatterns);
315190cef3dSDag-Erling Smørgrav 	free(list);
316190cef3dSDag-Erling Smørgrav 	free(ret);
317190cef3dSDag-Erling Smørgrav 	return r;
318eccfee6eSDag-Erling Smørgrav }
319eccfee6eSDag-Erling Smørgrav 
3201765946bSDag-Erling Smørgrav /* put algorithm proposal into buffer */
321bc5531deSDag-Erling Smørgrav int
322bc5531deSDag-Erling Smørgrav kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
323a04a10f8SKris Kennaway {
324043840dfSDag-Erling Smørgrav 	u_int i;
325bc5531deSDag-Erling Smørgrav 	int r;
3261e8db6e2SBrian Feldman 
327bc5531deSDag-Erling Smørgrav 	sshbuf_reset(b);
328bc5531deSDag-Erling Smørgrav 
329545d5ecaSDag-Erling Smørgrav 	/*
330545d5ecaSDag-Erling Smørgrav 	 * add a dummy cookie, the cookie will be overwritten by
331545d5ecaSDag-Erling Smørgrav 	 * kex_send_kexinit(), each time a kexinit is set
332545d5ecaSDag-Erling Smørgrav 	 */
333bc5531deSDag-Erling Smørgrav 	for (i = 0; i < KEX_COOKIE_LEN; i++) {
334bc5531deSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(b, 0)) != 0)
335bc5531deSDag-Erling Smørgrav 			return r;
336bc5531deSDag-Erling Smørgrav 	}
337bc5531deSDag-Erling Smørgrav 	for (i = 0; i < PROPOSAL_MAX; i++) {
338bc5531deSDag-Erling Smørgrav 		if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
339bc5531deSDag-Erling Smørgrav 			return r;
340bc5531deSDag-Erling Smørgrav 	}
341bc5531deSDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* first_kex_packet_follows */
342bc5531deSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, 0)) != 0)	/* uint32 reserved */
343bc5531deSDag-Erling Smørgrav 		return r;
344bc5531deSDag-Erling Smørgrav 	return 0;
345a04a10f8SKris Kennaway }
346a04a10f8SKris Kennaway 
3471e8db6e2SBrian Feldman /* parse buffer and return algorithm proposal */
348bc5531deSDag-Erling Smørgrav int
349bc5531deSDag-Erling Smørgrav kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
3502632b0c8SKris Kennaway {
351bc5531deSDag-Erling Smørgrav 	struct sshbuf *b = NULL;
352bc5531deSDag-Erling Smørgrav 	u_char v;
353d4af9e69SDag-Erling Smørgrav 	u_int i;
354bc5531deSDag-Erling Smørgrav 	char **proposal = NULL;
355bc5531deSDag-Erling Smørgrav 	int r;
3562632b0c8SKris Kennaway 
357bc5531deSDag-Erling Smørgrav 	*propp = NULL;
358bc5531deSDag-Erling Smørgrav 	if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
359bc5531deSDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
360bc5531deSDag-Erling Smørgrav 	if ((b = sshbuf_fromb(raw)) == NULL) {
361bc5531deSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
362bc5531deSDag-Erling Smørgrav 		goto out;
363bc5531deSDag-Erling Smørgrav 	}
36419261079SEd Maste 	if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */
36519261079SEd Maste 		error_fr(r, "consume cookie");
366bc5531deSDag-Erling Smørgrav 		goto out;
36719261079SEd Maste 	}
3682632b0c8SKris Kennaway 	/* extract kex init proposal strings */
3692632b0c8SKris Kennaway 	for (i = 0; i < PROPOSAL_MAX; i++) {
37019261079SEd Maste 		if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) {
37119261079SEd Maste 			error_fr(r, "parse proposal %u", i);
372bc5531deSDag-Erling Smørgrav 			goto out;
37319261079SEd Maste 		}
374acc1a9efSDag-Erling Smørgrav 		debug2("%s: %s", proposal_names[i], proposal[i]);
3752632b0c8SKris Kennaway 	}
3761e8db6e2SBrian Feldman 	/* first kex follows / reserved */
377fc1ba28aSDag-Erling Smørgrav 	if ((r = sshbuf_get_u8(b, &v)) != 0 ||	/* first_kex_follows */
37819261079SEd Maste 	    (r = sshbuf_get_u32(b, &i)) != 0) {	/* reserved */
37919261079SEd Maste 		error_fr(r, "parse");
380bc5531deSDag-Erling Smørgrav 		goto out;
38119261079SEd Maste 	}
382d0c8c0bcSDag-Erling Smørgrav 	if (first_kex_follows != NULL)
383fc1ba28aSDag-Erling Smørgrav 		*first_kex_follows = v;
384fc1ba28aSDag-Erling Smørgrav 	debug2("first_kex_follows %d ", v);
385fc1ba28aSDag-Erling Smørgrav 	debug2("reserved %u ", i);
386bc5531deSDag-Erling Smørgrav 	r = 0;
387bc5531deSDag-Erling Smørgrav 	*propp = proposal;
388bc5531deSDag-Erling Smørgrav  out:
389bc5531deSDag-Erling Smørgrav 	if (r != 0 && proposal != NULL)
390bc5531deSDag-Erling Smørgrav 		kex_prop_free(proposal);
391bc5531deSDag-Erling Smørgrav 	sshbuf_free(b);
392bc5531deSDag-Erling Smørgrav 	return r;
3935b9b2fafSBrian Feldman }
3945b9b2fafSBrian Feldman 
395bc5531deSDag-Erling Smørgrav void
3961e8db6e2SBrian Feldman kex_prop_free(char **proposal)
397a04a10f8SKris Kennaway {
398043840dfSDag-Erling Smørgrav 	u_int i;
3991e8db6e2SBrian Feldman 
400557f75e5SDag-Erling Smørgrav 	if (proposal == NULL)
401557f75e5SDag-Erling Smørgrav 		return;
4021e8db6e2SBrian Feldman 	for (i = 0; i < PROPOSAL_MAX; i++)
403e4a9863fSDag-Erling Smørgrav 		free(proposal[i]);
404e4a9863fSDag-Erling Smørgrav 	free(proposal);
405a04a10f8SKris Kennaway }
406a04a10f8SKris Kennaway 
407d4af9e69SDag-Erling Smørgrav /* ARGSUSED */
40819261079SEd Maste int
4094f52dfbbSDag-Erling Smørgrav kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
410a04a10f8SKris Kennaway {
411acc1a9efSDag-Erling Smørgrav 	int r;
412acc1a9efSDag-Erling Smørgrav 
413acc1a9efSDag-Erling Smørgrav 	error("kex protocol error: type %d seq %u", type, seq);
414acc1a9efSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
415acc1a9efSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, seq)) != 0 ||
416acc1a9efSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
417acc1a9efSDag-Erling Smørgrav 		return r;
418bc5531deSDag-Erling Smørgrav 	return 0;
419a04a10f8SKris Kennaway }
420a04a10f8SKris Kennaway 
421ae1f160dSDag-Erling Smørgrav static void
422bc5531deSDag-Erling Smørgrav kex_reset_dispatch(struct ssh *ssh)
4235b9b2fafSBrian Feldman {
424bc5531deSDag-Erling Smørgrav 	ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
425ae1f160dSDag-Erling Smørgrav 	    SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
4265b9b2fafSBrian Feldman }
4275b9b2fafSBrian Feldman 
428acc1a9efSDag-Erling Smørgrav static int
429acc1a9efSDag-Erling Smørgrav kex_send_ext_info(struct ssh *ssh)
430acc1a9efSDag-Erling Smørgrav {
431acc1a9efSDag-Erling Smørgrav 	int r;
432ca86bcf2SDag-Erling Smørgrav 	char *algs;
433acc1a9efSDag-Erling Smørgrav 
43419261079SEd Maste 	debug("Sending SSH2_MSG_EXT_INFO");
435d93a896eSDag-Erling Smørgrav 	if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
436ca86bcf2SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
437190cef3dSDag-Erling Smørgrav 	/* XXX filter algs list by allowed pubkey/hostbased types */
438acc1a9efSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
4391323ec57SEd Maste 	    (r = sshpkt_put_u32(ssh, 2)) != 0 ||
440acc1a9efSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
441ca86bcf2SDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
4421323ec57SEd Maste 	    (r = sshpkt_put_cstring(ssh,
4431323ec57SEd Maste 	    "publickey-hostbound@openssh.com")) != 0 ||
4441323ec57SEd Maste 	    (r = sshpkt_put_cstring(ssh, "0")) != 0 ||
44519261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0) {
44619261079SEd Maste 		error_fr(r, "compose");
447ca86bcf2SDag-Erling Smørgrav 		goto out;
44819261079SEd Maste 	}
449ca86bcf2SDag-Erling Smørgrav 	/* success */
450ca86bcf2SDag-Erling Smørgrav 	r = 0;
451ca86bcf2SDag-Erling Smørgrav  out:
452ca86bcf2SDag-Erling Smørgrav 	free(algs);
453acc1a9efSDag-Erling Smørgrav 	return r;
454acc1a9efSDag-Erling Smørgrav }
455acc1a9efSDag-Erling Smørgrav 
456bc5531deSDag-Erling Smørgrav int
457bc5531deSDag-Erling Smørgrav kex_send_newkeys(struct ssh *ssh)
458a04a10f8SKris Kennaway {
459bc5531deSDag-Erling Smørgrav 	int r;
460a04a10f8SKris Kennaway 
461bc5531deSDag-Erling Smørgrav 	kex_reset_dispatch(ssh);
462bc5531deSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
463bc5531deSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
464bc5531deSDag-Erling Smørgrav 		return r;
4651e8db6e2SBrian Feldman 	debug("SSH2_MSG_NEWKEYS sent");
466bc5531deSDag-Erling Smørgrav 	ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
46719261079SEd Maste 	if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0)
468acc1a9efSDag-Erling Smørgrav 		if ((r = kex_send_ext_info(ssh)) != 0)
469acc1a9efSDag-Erling Smørgrav 			return r;
47019261079SEd Maste 	debug("expecting SSH2_MSG_NEWKEYS");
471bc5531deSDag-Erling Smørgrav 	return 0;
472bc5531deSDag-Erling Smørgrav }
4731e8db6e2SBrian Feldman 
474acc1a9efSDag-Erling Smørgrav int
4754f52dfbbSDag-Erling Smørgrav kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
476acc1a9efSDag-Erling Smørgrav {
477acc1a9efSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
478acc1a9efSDag-Erling Smørgrav 	u_int32_t i, ninfo;
479190cef3dSDag-Erling Smørgrav 	char *name;
4804f52dfbbSDag-Erling Smørgrav 	u_char *val;
4814f52dfbbSDag-Erling Smørgrav 	size_t vlen;
482acc1a9efSDag-Erling Smørgrav 	int r;
483acc1a9efSDag-Erling Smørgrav 
484acc1a9efSDag-Erling Smørgrav 	debug("SSH2_MSG_EXT_INFO received");
485acc1a9efSDag-Erling Smørgrav 	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
486acc1a9efSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
487acc1a9efSDag-Erling Smørgrav 		return r;
488acc1a9efSDag-Erling Smørgrav 	for (i = 0; i < ninfo; i++) {
489acc1a9efSDag-Erling Smørgrav 		if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
490acc1a9efSDag-Erling Smørgrav 			return r;
4914f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
492acc1a9efSDag-Erling Smørgrav 			free(name);
493acc1a9efSDag-Erling Smørgrav 			return r;
494acc1a9efSDag-Erling Smørgrav 		}
495acc1a9efSDag-Erling Smørgrav 		if (strcmp(name, "server-sig-algs") == 0) {
4964f52dfbbSDag-Erling Smørgrav 			/* Ensure no \0 lurking in value */
4974f52dfbbSDag-Erling Smørgrav 			if (memchr(val, '\0', vlen) != NULL) {
49819261079SEd Maste 				error_f("nul byte in %s", name);
4994f52dfbbSDag-Erling Smørgrav 				return SSH_ERR_INVALID_FORMAT;
5004f52dfbbSDag-Erling Smørgrav 			}
50119261079SEd Maste 			debug_f("%s=<%s>", name, val);
502190cef3dSDag-Erling Smørgrav 			kex->server_sig_algs = val;
503190cef3dSDag-Erling Smørgrav 			val = NULL;
5041323ec57SEd Maste 		} else if (strcmp(name,
5051323ec57SEd Maste 		    "publickey-hostbound@openssh.com") == 0) {
5061323ec57SEd Maste 			/* XXX refactor */
5071323ec57SEd Maste 			/* Ensure no \0 lurking in value */
5081323ec57SEd Maste 			if (memchr(val, '\0', vlen) != NULL) {
5091323ec57SEd Maste 				error_f("nul byte in %s", name);
5101323ec57SEd Maste 				return SSH_ERR_INVALID_FORMAT;
5111323ec57SEd Maste 			}
5121323ec57SEd Maste 			debug_f("%s=<%s>", name, val);
5131323ec57SEd Maste 			if (strcmp(val, "0") == 0)
5141323ec57SEd Maste 				kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND;
5151323ec57SEd Maste 			else {
5161323ec57SEd Maste 				debug_f("unsupported version of %s extension",
5171323ec57SEd Maste 				    name);
5181323ec57SEd Maste 			}
5194f52dfbbSDag-Erling Smørgrav 		} else
52019261079SEd Maste 			debug_f("%s (unrecognised)", name);
521acc1a9efSDag-Erling Smørgrav 		free(name);
522acc1a9efSDag-Erling Smørgrav 		free(val);
523acc1a9efSDag-Erling Smørgrav 	}
524acc1a9efSDag-Erling Smørgrav 	return sshpkt_get_end(ssh);
525acc1a9efSDag-Erling Smørgrav }
526acc1a9efSDag-Erling Smørgrav 
527bc5531deSDag-Erling Smørgrav static int
5284f52dfbbSDag-Erling Smørgrav kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
529bc5531deSDag-Erling Smørgrav {
530bc5531deSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
531bc5531deSDag-Erling Smørgrav 	int r;
532bc5531deSDag-Erling Smørgrav 
533bc5531deSDag-Erling Smørgrav 	debug("SSH2_MSG_NEWKEYS received");
534bc5531deSDag-Erling Smørgrav 	ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
535d93a896eSDag-Erling Smørgrav 	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
536bc5531deSDag-Erling Smørgrav 	if ((r = sshpkt_get_end(ssh)) != 0)
537bc5531deSDag-Erling Smørgrav 		return r;
538ca86bcf2SDag-Erling Smørgrav 	if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
539ca86bcf2SDag-Erling Smørgrav 		return r;
5401e8db6e2SBrian Feldman 	kex->done = 1;
54119261079SEd Maste 	kex->flags &= ~KEX_INITIAL;
542bc5531deSDag-Erling Smørgrav 	sshbuf_reset(kex->peer);
543bc5531deSDag-Erling Smørgrav 	/* sshbuf_reset(kex->my); */
5441e8db6e2SBrian Feldman 	kex->flags &= ~KEX_INIT_SENT;
545e4a9863fSDag-Erling Smørgrav 	free(kex->name);
5461e8db6e2SBrian Feldman 	kex->name = NULL;
547bc5531deSDag-Erling Smørgrav 	return 0;
548a04a10f8SKris Kennaway }
549a04a10f8SKris Kennaway 
550bc5531deSDag-Erling Smørgrav int
551bc5531deSDag-Erling Smørgrav kex_send_kexinit(struct ssh *ssh)
552a04a10f8SKris Kennaway {
553545d5ecaSDag-Erling Smørgrav 	u_char *cookie;
554bc5531deSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
555bc5531deSDag-Erling Smørgrav 	int r;
556545d5ecaSDag-Erling Smørgrav 
55719261079SEd Maste 	if (kex == NULL) {
55819261079SEd Maste 		error_f("no kex");
559bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
56019261079SEd Maste 	}
561bc5531deSDag-Erling Smørgrav 	if (kex->flags & KEX_INIT_SENT)
562bc5531deSDag-Erling Smørgrav 		return 0;
5631e8db6e2SBrian Feldman 	kex->done = 0;
564545d5ecaSDag-Erling Smørgrav 
565545d5ecaSDag-Erling Smørgrav 	/* generate a random cookie */
56619261079SEd Maste 	if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) {
56719261079SEd Maste 		error_f("bad kex length: %zu < %d",
56819261079SEd Maste 		    sshbuf_len(kex->my), KEX_COOKIE_LEN);
569bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INVALID_FORMAT;
57019261079SEd Maste 	}
57119261079SEd Maste 	if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) {
57219261079SEd Maste 		error_f("buffer error");
573bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
57419261079SEd Maste 	}
575bc5531deSDag-Erling Smørgrav 	arc4random_buf(cookie, KEX_COOKIE_LEN);
576bc5531deSDag-Erling Smørgrav 
577bc5531deSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
578bc5531deSDag-Erling Smørgrav 	    (r = sshpkt_putb(ssh, kex->my)) != 0 ||
57919261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0) {
58019261079SEd Maste 		error_fr(r, "compose reply");
581bc5531deSDag-Erling Smørgrav 		return r;
58219261079SEd Maste 	}
5831e8db6e2SBrian Feldman 	debug("SSH2_MSG_KEXINIT sent");
5841e8db6e2SBrian Feldman 	kex->flags |= KEX_INIT_SENT;
585bc5531deSDag-Erling Smørgrav 	return 0;
5861e8db6e2SBrian Feldman }
587a04a10f8SKris Kennaway 
588d4af9e69SDag-Erling Smørgrav /* ARGSUSED */
589bc5531deSDag-Erling Smørgrav int
5904f52dfbbSDag-Erling Smørgrav kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
5911e8db6e2SBrian Feldman {
592bc5531deSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
593bc5531deSDag-Erling Smørgrav 	const u_char *ptr;
594bc5531deSDag-Erling Smørgrav 	u_int i;
595bc5531deSDag-Erling Smørgrav 	size_t dlen;
596bc5531deSDag-Erling Smørgrav 	int r;
5972632b0c8SKris Kennaway 
5981e8db6e2SBrian Feldman 	debug("SSH2_MSG_KEXINIT received");
59919261079SEd Maste 	if (kex == NULL) {
60019261079SEd Maste 		error_f("no kex");
60119261079SEd Maste 		return SSH_ERR_INTERNAL_ERROR;
60219261079SEd Maste 	}
603ca86bcf2SDag-Erling Smørgrav 	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
604bc5531deSDag-Erling Smørgrav 	ptr = sshpkt_ptr(ssh, &dlen);
605bc5531deSDag-Erling Smørgrav 	if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
606bc5531deSDag-Erling Smørgrav 		return r;
6071e8db6e2SBrian Feldman 
6081e8db6e2SBrian Feldman 	/* discard packet */
60919261079SEd Maste 	for (i = 0; i < KEX_COOKIE_LEN; i++) {
61019261079SEd Maste 		if ((r = sshpkt_get_u8(ssh, NULL)) != 0) {
61119261079SEd Maste 			error_fr(r, "discard cookie");
612bc5531deSDag-Erling Smørgrav 			return r;
61319261079SEd Maste 		}
61419261079SEd Maste 	}
61519261079SEd Maste 	for (i = 0; i < PROPOSAL_MAX; i++) {
61619261079SEd Maste 		if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
61719261079SEd Maste 			error_fr(r, "discard proposal");
618bc5531deSDag-Erling Smørgrav 			return r;
61919261079SEd Maste 		}
62019261079SEd Maste 	}
6216888a9beSDag-Erling Smørgrav 	/*
6226888a9beSDag-Erling Smørgrav 	 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
6236888a9beSDag-Erling Smørgrav 	 * KEX method has the server move first, but a server might be using
6246888a9beSDag-Erling Smørgrav 	 * a custom method or one that we otherwise don't support. We should
6256888a9beSDag-Erling Smørgrav 	 * be prepared to remember first_kex_follows here so we can eat a
6266888a9beSDag-Erling Smørgrav 	 * packet later.
6276888a9beSDag-Erling Smørgrav 	 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
6286888a9beSDag-Erling Smørgrav 	 * for cases where the server *doesn't* go first. I guess we should
6296888a9beSDag-Erling Smørgrav 	 * ignore it when it is set for these cases, which is what we do now.
6306888a9beSDag-Erling Smørgrav 	 */
631bc5531deSDag-Erling Smørgrav 	if ((r = sshpkt_get_u8(ssh, NULL)) != 0 ||	/* first_kex_follows */
632bc5531deSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, NULL)) != 0 ||	/* reserved */
633bc5531deSDag-Erling Smørgrav 	    (r = sshpkt_get_end(ssh)) != 0)
634bc5531deSDag-Erling Smørgrav 			return r;
6351e8db6e2SBrian Feldman 
6361e8db6e2SBrian Feldman 	if (!(kex->flags & KEX_INIT_SENT))
637bc5531deSDag-Erling Smørgrav 		if ((r = kex_send_kexinit(ssh)) != 0)
638bc5531deSDag-Erling Smørgrav 			return r;
639bc5531deSDag-Erling Smørgrav 	if ((r = kex_choose_conf(ssh)) != 0)
640bc5531deSDag-Erling Smørgrav 		return r;
6411e8db6e2SBrian Feldman 
642bc5531deSDag-Erling Smørgrav 	if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
643bc5531deSDag-Erling Smørgrav 		return (kex->kex[kex->kex_type])(ssh);
6441e8db6e2SBrian Feldman 
64519261079SEd Maste 	error_f("unknown kex type %u", kex->kex_type);
646bc5531deSDag-Erling Smørgrav 	return SSH_ERR_INTERNAL_ERROR;
6471e8db6e2SBrian Feldman }
648a04a10f8SKris Kennaway 
64919261079SEd Maste struct kex *
65019261079SEd Maste kex_new(void)
651bc5531deSDag-Erling Smørgrav {
652bc5531deSDag-Erling Smørgrav 	struct kex *kex;
653bc5531deSDag-Erling Smørgrav 
65419261079SEd Maste 	if ((kex = calloc(1, sizeof(*kex))) == NULL ||
65519261079SEd Maste 	    (kex->peer = sshbuf_new()) == NULL ||
65619261079SEd Maste 	    (kex->my = sshbuf_new()) == NULL ||
65719261079SEd Maste 	    (kex->client_version = sshbuf_new()) == NULL ||
65819261079SEd Maste 	    (kex->server_version = sshbuf_new()) == NULL ||
65919261079SEd Maste 	    (kex->session_id = sshbuf_new()) == NULL) {
660bc5531deSDag-Erling Smørgrav 		kex_free(kex);
66119261079SEd Maste 		return NULL;
66219261079SEd Maste 	}
66319261079SEd Maste 	return kex;
664bc5531deSDag-Erling Smørgrav }
665bc5531deSDag-Erling Smørgrav 
666bc5531deSDag-Erling Smørgrav void
667bc5531deSDag-Erling Smørgrav kex_free_newkeys(struct newkeys *newkeys)
668bc5531deSDag-Erling Smørgrav {
669bc5531deSDag-Erling Smørgrav 	if (newkeys == NULL)
670bc5531deSDag-Erling Smørgrav 		return;
671bc5531deSDag-Erling Smørgrav 	if (newkeys->enc.key) {
672bc5531deSDag-Erling Smørgrav 		explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
673bc5531deSDag-Erling Smørgrav 		free(newkeys->enc.key);
674bc5531deSDag-Erling Smørgrav 		newkeys->enc.key = NULL;
675bc5531deSDag-Erling Smørgrav 	}
676bc5531deSDag-Erling Smørgrav 	if (newkeys->enc.iv) {
677acc1a9efSDag-Erling Smørgrav 		explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
678bc5531deSDag-Erling Smørgrav 		free(newkeys->enc.iv);
679bc5531deSDag-Erling Smørgrav 		newkeys->enc.iv = NULL;
680bc5531deSDag-Erling Smørgrav 	}
681bc5531deSDag-Erling Smørgrav 	free(newkeys->enc.name);
682bc5531deSDag-Erling Smørgrav 	explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
683bc5531deSDag-Erling Smørgrav 	free(newkeys->comp.name);
684bc5531deSDag-Erling Smørgrav 	explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
685bc5531deSDag-Erling Smørgrav 	mac_clear(&newkeys->mac);
686bc5531deSDag-Erling Smørgrav 	if (newkeys->mac.key) {
687bc5531deSDag-Erling Smørgrav 		explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
688bc5531deSDag-Erling Smørgrav 		free(newkeys->mac.key);
689bc5531deSDag-Erling Smørgrav 		newkeys->mac.key = NULL;
690bc5531deSDag-Erling Smørgrav 	}
691bc5531deSDag-Erling Smørgrav 	free(newkeys->mac.name);
692bc5531deSDag-Erling Smørgrav 	explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
69319261079SEd Maste 	freezero(newkeys, sizeof(*newkeys));
694bc5531deSDag-Erling Smørgrav }
695bc5531deSDag-Erling Smørgrav 
696bc5531deSDag-Erling Smørgrav void
697bc5531deSDag-Erling Smørgrav kex_free(struct kex *kex)
698bc5531deSDag-Erling Smørgrav {
699bc5531deSDag-Erling Smørgrav 	u_int mode;
700bc5531deSDag-Erling Smørgrav 
70119261079SEd Maste 	if (kex == NULL)
70219261079SEd Maste 		return;
70319261079SEd Maste 
704bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL
705bc5531deSDag-Erling Smørgrav 	DH_free(kex->dh);
706bc5531deSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
707bc5531deSDag-Erling Smørgrav 	EC_KEY_free(kex->ec_client_key);
708bc5531deSDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */
709bc5531deSDag-Erling Smørgrav #endif /* WITH_OPENSSL */
710bc5531deSDag-Erling Smørgrav 	for (mode = 0; mode < MODE_MAX; mode++) {
711bc5531deSDag-Erling Smørgrav 		kex_free_newkeys(kex->newkeys[mode]);
712bc5531deSDag-Erling Smørgrav 		kex->newkeys[mode] = NULL;
713bc5531deSDag-Erling Smørgrav 	}
714bc5531deSDag-Erling Smørgrav 	sshbuf_free(kex->peer);
715bc5531deSDag-Erling Smørgrav 	sshbuf_free(kex->my);
71619261079SEd Maste 	sshbuf_free(kex->client_version);
71719261079SEd Maste 	sshbuf_free(kex->server_version);
71819261079SEd Maste 	sshbuf_free(kex->client_pub);
71919261079SEd Maste 	sshbuf_free(kex->session_id);
7201323ec57SEd Maste 	sshbuf_free(kex->initial_sig);
7211323ec57SEd Maste 	sshkey_free(kex->initial_hostkey);
722eccfee6eSDag-Erling Smørgrav 	free(kex->failed_choice);
723acc1a9efSDag-Erling Smørgrav 	free(kex->hostkey_alg);
724acc1a9efSDag-Erling Smørgrav 	free(kex->name);
725bc5531deSDag-Erling Smørgrav 	free(kex);
726bc5531deSDag-Erling Smørgrav }
727bc5531deSDag-Erling Smørgrav 
728bc5531deSDag-Erling Smørgrav int
72919261079SEd Maste kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
73019261079SEd Maste {
73119261079SEd Maste 	int r;
73219261079SEd Maste 
73319261079SEd Maste 	if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
73419261079SEd Maste 		return r;
73519261079SEd Maste 	ssh->kex->flags = KEX_INITIAL;
73619261079SEd Maste 	kex_reset_dispatch(ssh);
73719261079SEd Maste 	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
73819261079SEd Maste 	return 0;
73919261079SEd Maste }
74019261079SEd Maste 
74119261079SEd Maste int
742bc5531deSDag-Erling Smørgrav kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
743bc5531deSDag-Erling Smørgrav {
744bc5531deSDag-Erling Smørgrav 	int r;
745bc5531deSDag-Erling Smørgrav 
74619261079SEd Maste 	if ((r = kex_ready(ssh, proposal)) != 0)
747bc5531deSDag-Erling Smørgrav 		return r;
748bc5531deSDag-Erling Smørgrav 	if ((r = kex_send_kexinit(ssh)) != 0) {		/* we start */
749bc5531deSDag-Erling Smørgrav 		kex_free(ssh->kex);
750bc5531deSDag-Erling Smørgrav 		ssh->kex = NULL;
751bc5531deSDag-Erling Smørgrav 		return r;
752bc5531deSDag-Erling Smørgrav 	}
753bc5531deSDag-Erling Smørgrav 	return 0;
754bc5531deSDag-Erling Smørgrav }
755bc5531deSDag-Erling Smørgrav 
756acc1a9efSDag-Erling Smørgrav /*
757acc1a9efSDag-Erling Smørgrav  * Request key re-exchange, returns 0 on success or a ssherr.h error
758acc1a9efSDag-Erling Smørgrav  * code otherwise. Must not be called if KEX is incomplete or in-progress.
759acc1a9efSDag-Erling Smørgrav  */
760acc1a9efSDag-Erling Smørgrav int
761acc1a9efSDag-Erling Smørgrav kex_start_rekex(struct ssh *ssh)
762acc1a9efSDag-Erling Smørgrav {
763acc1a9efSDag-Erling Smørgrav 	if (ssh->kex == NULL) {
76419261079SEd Maste 		error_f("no kex");
765acc1a9efSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
766acc1a9efSDag-Erling Smørgrav 	}
767acc1a9efSDag-Erling Smørgrav 	if (ssh->kex->done == 0) {
76819261079SEd Maste 		error_f("requested twice");
769acc1a9efSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
770acc1a9efSDag-Erling Smørgrav 	}
771acc1a9efSDag-Erling Smørgrav 	ssh->kex->done = 0;
772acc1a9efSDag-Erling Smørgrav 	return kex_send_kexinit(ssh);
773acc1a9efSDag-Erling Smørgrav }
774acc1a9efSDag-Erling Smørgrav 
775bc5531deSDag-Erling Smørgrav static int
776bc5531deSDag-Erling Smørgrav choose_enc(struct sshenc *enc, char *client, char *server)
777a04a10f8SKris Kennaway {
7781e8db6e2SBrian Feldman 	char *name = match_list(client, server, NULL);
779bc5531deSDag-Erling Smørgrav 
780a04a10f8SKris Kennaway 	if (name == NULL)
781bc5531deSDag-Erling Smørgrav 		return SSH_ERR_NO_CIPHER_ALG_MATCH;
782d93a896eSDag-Erling Smørgrav 	if ((enc->cipher = cipher_by_name(name)) == NULL) {
78319261079SEd Maste 		error_f("unsupported cipher %s", name);
784d93a896eSDag-Erling Smørgrav 		free(name);
785bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
786d93a896eSDag-Erling Smørgrav 	}
787a04a10f8SKris Kennaway 	enc->name = name;
788a04a10f8SKris Kennaway 	enc->enabled = 0;
789a04a10f8SKris Kennaway 	enc->iv = NULL;
7906888a9beSDag-Erling Smørgrav 	enc->iv_len = cipher_ivlen(enc->cipher);
791a04a10f8SKris Kennaway 	enc->key = NULL;
792ae1f160dSDag-Erling Smørgrav 	enc->key_len = cipher_keylen(enc->cipher);
793ae1f160dSDag-Erling Smørgrav 	enc->block_size = cipher_blocksize(enc->cipher);
794bc5531deSDag-Erling Smørgrav 	return 0;
795a04a10f8SKris Kennaway }
796761efaa7SDag-Erling Smørgrav 
797bc5531deSDag-Erling Smørgrav static int
798bc5531deSDag-Erling Smørgrav choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
799a04a10f8SKris Kennaway {
8001e8db6e2SBrian Feldman 	char *name = match_list(client, server, NULL);
801bc5531deSDag-Erling Smørgrav 
802a04a10f8SKris Kennaway 	if (name == NULL)
803bc5531deSDag-Erling Smørgrav 		return SSH_ERR_NO_MAC_ALG_MATCH;
804d93a896eSDag-Erling Smørgrav 	if (mac_setup(mac, name) < 0) {
80519261079SEd Maste 		error_f("unsupported MAC %s", name);
806d93a896eSDag-Erling Smørgrav 		free(name);
807bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
808d93a896eSDag-Erling Smørgrav 	}
809a04a10f8SKris Kennaway 	mac->name = name;
810a04a10f8SKris Kennaway 	mac->key = NULL;
811a04a10f8SKris Kennaway 	mac->enabled = 0;
812bc5531deSDag-Erling Smørgrav 	return 0;
813a04a10f8SKris Kennaway }
814761efaa7SDag-Erling Smørgrav 
815bc5531deSDag-Erling Smørgrav static int
816bc5531deSDag-Erling Smørgrav choose_comp(struct sshcomp *comp, char *client, char *server)
817a04a10f8SKris Kennaway {
8181e8db6e2SBrian Feldman 	char *name = match_list(client, server, NULL);
819bc5531deSDag-Erling Smørgrav 
820a04a10f8SKris Kennaway 	if (name == NULL)
821bc5531deSDag-Erling Smørgrav 		return SSH_ERR_NO_COMPRESS_ALG_MATCH;
82219261079SEd Maste #ifdef WITH_ZLIB
823043840dfSDag-Erling Smørgrav 	if (strcmp(name, "zlib@openssh.com") == 0) {
824043840dfSDag-Erling Smørgrav 		comp->type = COMP_DELAYED;
825043840dfSDag-Erling Smørgrav 	} else if (strcmp(name, "zlib") == 0) {
826043840dfSDag-Erling Smørgrav 		comp->type = COMP_ZLIB;
82719261079SEd Maste 	} else
82819261079SEd Maste #endif	/* WITH_ZLIB */
82919261079SEd Maste 	if (strcmp(name, "none") == 0) {
830043840dfSDag-Erling Smørgrav 		comp->type = COMP_NONE;
831a04a10f8SKris Kennaway 	} else {
83219261079SEd Maste 		error_f("unsupported compression scheme %s", name);
833d93a896eSDag-Erling Smørgrav 		free(name);
834bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
835a04a10f8SKris Kennaway 	}
836a04a10f8SKris Kennaway 	comp->name = name;
837bc5531deSDag-Erling Smørgrav 	return 0;
838a04a10f8SKris Kennaway }
839761efaa7SDag-Erling Smørgrav 
840bc5531deSDag-Erling Smørgrav static int
841bc5531deSDag-Erling Smørgrav choose_kex(struct kex *k, char *client, char *server)
842a04a10f8SKris Kennaway {
843e4a9863fSDag-Erling Smørgrav 	const struct kexalg *kexalg;
844e4a9863fSDag-Erling Smørgrav 
8451e8db6e2SBrian Feldman 	k->name = match_list(client, server, NULL);
846bc5531deSDag-Erling Smørgrav 
847acc1a9efSDag-Erling Smørgrav 	debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
848a04a10f8SKris Kennaway 	if (k->name == NULL)
849bc5531deSDag-Erling Smørgrav 		return SSH_ERR_NO_KEX_ALG_MATCH;
85019261079SEd Maste 	if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
85119261079SEd Maste 		error_f("unsupported KEX method %s", k->name);
852bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
85319261079SEd Maste 	}
854e4a9863fSDag-Erling Smørgrav 	k->kex_type = kexalg->type;
855f7167e0eSDag-Erling Smørgrav 	k->hash_alg = kexalg->hash_alg;
856e4a9863fSDag-Erling Smørgrav 	k->ec_nid = kexalg->ec_nid;
857bc5531deSDag-Erling Smørgrav 	return 0;
858a04a10f8SKris Kennaway }
859021d409fSDag-Erling Smørgrav 
860bc5531deSDag-Erling Smørgrav static int
861bc5531deSDag-Erling Smørgrav choose_hostkeyalg(struct kex *k, char *client, char *server)
862a04a10f8SKris Kennaway {
86319261079SEd Maste 	free(k->hostkey_alg);
864acc1a9efSDag-Erling Smørgrav 	k->hostkey_alg = match_list(client, server, NULL);
865bc5531deSDag-Erling Smørgrav 
866acc1a9efSDag-Erling Smørgrav 	debug("kex: host key algorithm: %s",
867acc1a9efSDag-Erling Smørgrav 	    k->hostkey_alg ? k->hostkey_alg : "(no match)");
868acc1a9efSDag-Erling Smørgrav 	if (k->hostkey_alg == NULL)
869bc5531deSDag-Erling Smørgrav 		return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
870acc1a9efSDag-Erling Smørgrav 	k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
87119261079SEd Maste 	if (k->hostkey_type == KEY_UNSPEC) {
87219261079SEd Maste 		error_f("unsupported hostkey algorithm %s", k->hostkey_alg);
873bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
87419261079SEd Maste 	}
875acc1a9efSDag-Erling Smørgrav 	k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
876bc5531deSDag-Erling Smørgrav 	return 0;
877a04a10f8SKris Kennaway }
878a04a10f8SKris Kennaway 
879d0c8c0bcSDag-Erling Smørgrav static int
880d0c8c0bcSDag-Erling Smørgrav proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
881d0c8c0bcSDag-Erling Smørgrav {
882d0c8c0bcSDag-Erling Smørgrav 	static int check[] = {
883d0c8c0bcSDag-Erling Smørgrav 		PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
884d0c8c0bcSDag-Erling Smørgrav 	};
885d0c8c0bcSDag-Erling Smørgrav 	int *idx;
886d0c8c0bcSDag-Erling Smørgrav 	char *p;
887d0c8c0bcSDag-Erling Smørgrav 
888d0c8c0bcSDag-Erling Smørgrav 	for (idx = &check[0]; *idx != -1; idx++) {
889d0c8c0bcSDag-Erling Smørgrav 		if ((p = strchr(my[*idx], ',')) != NULL)
890d0c8c0bcSDag-Erling Smørgrav 			*p = '\0';
891d0c8c0bcSDag-Erling Smørgrav 		if ((p = strchr(peer[*idx], ',')) != NULL)
892d0c8c0bcSDag-Erling Smørgrav 			*p = '\0';
893d0c8c0bcSDag-Erling Smørgrav 		if (strcmp(my[*idx], peer[*idx]) != 0) {
894d0c8c0bcSDag-Erling Smørgrav 			debug2("proposal mismatch: my %s peer %s",
895d0c8c0bcSDag-Erling Smørgrav 			    my[*idx], peer[*idx]);
896d0c8c0bcSDag-Erling Smørgrav 			return (0);
897d0c8c0bcSDag-Erling Smørgrav 		}
898d0c8c0bcSDag-Erling Smørgrav 	}
899d0c8c0bcSDag-Erling Smørgrav 	debug2("proposals match");
900d0c8c0bcSDag-Erling Smørgrav 	return (1);
901d0c8c0bcSDag-Erling Smørgrav }
902d0c8c0bcSDag-Erling Smørgrav 
9031323ec57SEd Maste /* returns non-zero if proposal contains any algorithm from algs */
9041323ec57SEd Maste static int
9051323ec57SEd Maste has_any_alg(const char *proposal, const char *algs)
9061323ec57SEd Maste {
9071323ec57SEd Maste 	char *cp;
9081323ec57SEd Maste 
9091323ec57SEd Maste 	if ((cp = match_list(proposal, algs, NULL)) == NULL)
9101323ec57SEd Maste 		return 0;
9111323ec57SEd Maste 	free(cp);
9121323ec57SEd Maste 	return 1;
9131323ec57SEd Maste }
9141323ec57SEd Maste 
915bc5531deSDag-Erling Smørgrav static int
916bc5531deSDag-Erling Smørgrav kex_choose_conf(struct ssh *ssh)
917a04a10f8SKris Kennaway {
918bc5531deSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
919bc5531deSDag-Erling Smørgrav 	struct newkeys *newkeys;
920bc5531deSDag-Erling Smørgrav 	char **my = NULL, **peer = NULL;
9211e8db6e2SBrian Feldman 	char **cprop, **sprop;
9221e8db6e2SBrian Feldman 	int nenc, nmac, ncomp;
923f7167e0eSDag-Erling Smørgrav 	u_int mode, ctos, need, dh_need, authlen;
924bc5531deSDag-Erling Smørgrav 	int r, first_kex_follows;
925a04a10f8SKris Kennaway 
926acc1a9efSDag-Erling Smørgrav 	debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
927acc1a9efSDag-Erling Smørgrav 	if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
928acc1a9efSDag-Erling Smørgrav 		goto out;
929acc1a9efSDag-Erling Smørgrav 	debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
930acc1a9efSDag-Erling Smørgrav 	if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
931bc5531deSDag-Erling Smørgrav 		goto out;
932a04a10f8SKris Kennaway 
9331e8db6e2SBrian Feldman 	if (kex->server) {
9341e8db6e2SBrian Feldman 		cprop=peer;
9351e8db6e2SBrian Feldman 		sprop=my;
9361e8db6e2SBrian Feldman 	} else {
9371e8db6e2SBrian Feldman 		cprop=my;
9381e8db6e2SBrian Feldman 		sprop=peer;
9391e8db6e2SBrian Feldman 	}
9401e8db6e2SBrian Feldman 
941acc1a9efSDag-Erling Smørgrav 	/* Check whether client supports ext_info_c */
94219261079SEd Maste 	if (kex->server && (kex->flags & KEX_INITIAL)) {
943acc1a9efSDag-Erling Smørgrav 		char *ext;
944bc5531deSDag-Erling Smørgrav 
945acc1a9efSDag-Erling Smørgrav 		ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
946ca86bcf2SDag-Erling Smørgrav 		kex->ext_info_c = (ext != NULL);
947acc1a9efSDag-Erling Smørgrav 		free(ext);
948b15c8340SDag-Erling Smørgrav 	}
949b15c8340SDag-Erling Smørgrav 
9501323ec57SEd Maste 	/* Check whether client supports rsa-sha2 algorithms */
9511323ec57SEd Maste 	if (kex->server && (kex->flags & KEX_INITIAL)) {
9521323ec57SEd Maste 		if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
9531323ec57SEd Maste 		    "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com"))
9541323ec57SEd Maste 			kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
9551323ec57SEd Maste 		if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
9561323ec57SEd Maste 		    "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com"))
9571323ec57SEd Maste 			kex->flags |= KEX_RSA_SHA2_512_SUPPORTED;
9581323ec57SEd Maste 	}
9591323ec57SEd Maste 
9601e8db6e2SBrian Feldman 	/* Algorithm Negotiation */
961acc1a9efSDag-Erling Smørgrav 	if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
962acc1a9efSDag-Erling Smørgrav 	    sprop[PROPOSAL_KEX_ALGS])) != 0) {
963acc1a9efSDag-Erling Smørgrav 		kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
964acc1a9efSDag-Erling Smørgrav 		peer[PROPOSAL_KEX_ALGS] = NULL;
965acc1a9efSDag-Erling Smørgrav 		goto out;
966acc1a9efSDag-Erling Smørgrav 	}
967acc1a9efSDag-Erling Smørgrav 	if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
968acc1a9efSDag-Erling Smørgrav 	    sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
969acc1a9efSDag-Erling Smørgrav 		kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
970acc1a9efSDag-Erling Smørgrav 		peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
971acc1a9efSDag-Erling Smørgrav 		goto out;
972acc1a9efSDag-Erling Smørgrav 	}
973a04a10f8SKris Kennaway 	for (mode = 0; mode < MODE_MAX; mode++) {
974bc5531deSDag-Erling Smørgrav 		if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
975bc5531deSDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
976bc5531deSDag-Erling Smørgrav 			goto out;
977bc5531deSDag-Erling Smørgrav 		}
9781e8db6e2SBrian Feldman 		kex->newkeys[mode] = newkeys;
979d4af9e69SDag-Erling Smørgrav 		ctos = (!kex->server && mode == MODE_OUT) ||
980d4af9e69SDag-Erling Smørgrav 		    (kex->server && mode == MODE_IN);
981a04a10f8SKris Kennaway 		nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
982a04a10f8SKris Kennaway 		nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
983a04a10f8SKris Kennaway 		ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
984bc5531deSDag-Erling Smørgrav 		if ((r = choose_enc(&newkeys->enc, cprop[nenc],
985eccfee6eSDag-Erling Smørgrav 		    sprop[nenc])) != 0) {
986eccfee6eSDag-Erling Smørgrav 			kex->failed_choice = peer[nenc];
987eccfee6eSDag-Erling Smørgrav 			peer[nenc] = NULL;
988bc5531deSDag-Erling Smørgrav 			goto out;
989eccfee6eSDag-Erling Smørgrav 		}
9906888a9beSDag-Erling Smørgrav 		authlen = cipher_authlen(newkeys->enc.cipher);
991bc5531deSDag-Erling Smørgrav 		/* ignore mac for authenticated encryption */
992bc5531deSDag-Erling Smørgrav 		if (authlen == 0 &&
993bc5531deSDag-Erling Smørgrav 		    (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
994eccfee6eSDag-Erling Smørgrav 		    sprop[nmac])) != 0) {
995eccfee6eSDag-Erling Smørgrav 			kex->failed_choice = peer[nmac];
996eccfee6eSDag-Erling Smørgrav 			peer[nmac] = NULL;
997bc5531deSDag-Erling Smørgrav 			goto out;
998eccfee6eSDag-Erling Smørgrav 		}
999bc5531deSDag-Erling Smørgrav 		if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
1000eccfee6eSDag-Erling Smørgrav 		    sprop[ncomp])) != 0) {
1001eccfee6eSDag-Erling Smørgrav 			kex->failed_choice = peer[ncomp];
1002eccfee6eSDag-Erling Smørgrav 			peer[ncomp] = NULL;
1003bc5531deSDag-Erling Smørgrav 			goto out;
1004eccfee6eSDag-Erling Smørgrav 		}
1005acc1a9efSDag-Erling Smørgrav 		debug("kex: %s cipher: %s MAC: %s compression: %s",
1006a04a10f8SKris Kennaway 		    ctos ? "client->server" : "server->client",
10071e8db6e2SBrian Feldman 		    newkeys->enc.name,
10086888a9beSDag-Erling Smørgrav 		    authlen == 0 ? newkeys->mac.name : "<implicit>",
10091e8db6e2SBrian Feldman 		    newkeys->comp.name);
1010a04a10f8SKris Kennaway 	}
1011f7167e0eSDag-Erling Smørgrav 	need = dh_need = 0;
1012a04a10f8SKris Kennaway 	for (mode = 0; mode < MODE_MAX; mode++) {
10131e8db6e2SBrian Feldman 		newkeys = kex->newkeys[mode];
1014ca86bcf2SDag-Erling Smørgrav 		need = MAXIMUM(need, newkeys->enc.key_len);
1015ca86bcf2SDag-Erling Smørgrav 		need = MAXIMUM(need, newkeys->enc.block_size);
1016ca86bcf2SDag-Erling Smørgrav 		need = MAXIMUM(need, newkeys->enc.iv_len);
1017ca86bcf2SDag-Erling Smørgrav 		need = MAXIMUM(need, newkeys->mac.key_len);
1018ca86bcf2SDag-Erling Smørgrav 		dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
1019ca86bcf2SDag-Erling Smørgrav 		dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
1020ca86bcf2SDag-Erling Smørgrav 		dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
1021ca86bcf2SDag-Erling Smørgrav 		dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
1022a04a10f8SKris Kennaway 	}
10232632b0c8SKris Kennaway 	/* XXX need runden? */
10241e8db6e2SBrian Feldman 	kex->we_need = need;
1025f7167e0eSDag-Erling Smørgrav 	kex->dh_need = dh_need;
10261e8db6e2SBrian Feldman 
1027d0c8c0bcSDag-Erling Smørgrav 	/* ignore the next message if the proposals do not match */
102847dd1d1bSDag-Erling Smørgrav 	if (first_kex_follows && !proposals_match(my, peer))
1029bc5531deSDag-Erling Smørgrav 		ssh->dispatch_skip_packets = 1;
1030bc5531deSDag-Erling Smørgrav 	r = 0;
1031bc5531deSDag-Erling Smørgrav  out:
10321e8db6e2SBrian Feldman 	kex_prop_free(my);
10331e8db6e2SBrian Feldman 	kex_prop_free(peer);
1034bc5531deSDag-Erling Smørgrav 	return r;
1035a04a10f8SKris Kennaway }
1036a04a10f8SKris Kennaway 
1037bc5531deSDag-Erling Smørgrav static int
1038bc5531deSDag-Erling Smørgrav derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
1039bc5531deSDag-Erling Smørgrav     const struct sshbuf *shared_secret, u_char **keyp)
1040a04a10f8SKris Kennaway {
1041bc5531deSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
1042bc5531deSDag-Erling Smørgrav 	struct ssh_digest_ctx *hashctx = NULL;
10431e8db6e2SBrian Feldman 	char c = id;
1044043840dfSDag-Erling Smørgrav 	u_int have;
1045f7167e0eSDag-Erling Smørgrav 	size_t mdsz;
1046043840dfSDag-Erling Smørgrav 	u_char *digest;
1047bc5531deSDag-Erling Smørgrav 	int r;
1048043840dfSDag-Erling Smørgrav 
1049f7167e0eSDag-Erling Smørgrav 	if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
1050bc5531deSDag-Erling Smørgrav 		return SSH_ERR_INVALID_ARGUMENT;
1051ca86bcf2SDag-Erling Smørgrav 	if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
1052bc5531deSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
1053bc5531deSDag-Erling Smørgrav 		goto out;
1054bc5531deSDag-Erling Smørgrav 	}
10551e8db6e2SBrian Feldman 
10561e8db6e2SBrian Feldman 	/* K1 = HASH(K || H || "A" || session_id) */
1057bc5531deSDag-Erling Smørgrav 	if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1058bc5531deSDag-Erling Smørgrav 	    ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
1059f7167e0eSDag-Erling Smørgrav 	    ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1060f7167e0eSDag-Erling Smørgrav 	    ssh_digest_update(hashctx, &c, 1) != 0 ||
106119261079SEd Maste 	    ssh_digest_update_buffer(hashctx, kex->session_id) != 0 ||
1062bc5531deSDag-Erling Smørgrav 	    ssh_digest_final(hashctx, digest, mdsz) != 0) {
1063bc5531deSDag-Erling Smørgrav 		r = SSH_ERR_LIBCRYPTO_ERROR;
106419261079SEd Maste 		error_f("KEX hash failed");
1065bc5531deSDag-Erling Smørgrav 		goto out;
1066bc5531deSDag-Erling Smørgrav 	}
1067f7167e0eSDag-Erling Smørgrav 	ssh_digest_free(hashctx);
1068bc5531deSDag-Erling Smørgrav 	hashctx = NULL;
10691e8db6e2SBrian Feldman 
10701e8db6e2SBrian Feldman 	/*
10711e8db6e2SBrian Feldman 	 * expand key:
10721e8db6e2SBrian Feldman 	 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
10731e8db6e2SBrian Feldman 	 * Key = K1 || K2 || ... || Kn
10741e8db6e2SBrian Feldman 	 */
10751e8db6e2SBrian Feldman 	for (have = mdsz; need > have; have += mdsz) {
1076bc5531deSDag-Erling Smørgrav 		if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
1077bc5531deSDag-Erling Smørgrav 		    ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
1078f7167e0eSDag-Erling Smørgrav 		    ssh_digest_update(hashctx, hash, hashlen) != 0 ||
1079bc5531deSDag-Erling Smørgrav 		    ssh_digest_update(hashctx, digest, have) != 0 ||
1080bc5531deSDag-Erling Smørgrav 		    ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
108119261079SEd Maste 			error_f("KDF failed");
1082bc5531deSDag-Erling Smørgrav 			r = SSH_ERR_LIBCRYPTO_ERROR;
1083bc5531deSDag-Erling Smørgrav 			goto out;
10841e8db6e2SBrian Feldman 		}
1085bc5531deSDag-Erling Smørgrav 		ssh_digest_free(hashctx);
1086bc5531deSDag-Erling Smørgrav 		hashctx = NULL;
1087bc5531deSDag-Erling Smørgrav 	}
10881e8db6e2SBrian Feldman #ifdef DEBUG_KEX
10891e8db6e2SBrian Feldman 	fprintf(stderr, "key '%c'== ", c);
10901e8db6e2SBrian Feldman 	dump_digest("key", digest, need);
10911e8db6e2SBrian Feldman #endif
1092bc5531deSDag-Erling Smørgrav 	*keyp = digest;
1093bc5531deSDag-Erling Smørgrav 	digest = NULL;
1094bc5531deSDag-Erling Smørgrav 	r = 0;
1095bc5531deSDag-Erling Smørgrav  out:
1096bc5531deSDag-Erling Smørgrav 	free(digest);
1097bc5531deSDag-Erling Smørgrav 	ssh_digest_free(hashctx);
1098bc5531deSDag-Erling Smørgrav 	return r;
10991e8db6e2SBrian Feldman }
11001e8db6e2SBrian Feldman 
11011e8db6e2SBrian Feldman #define NKEYS	6
1102bc5531deSDag-Erling Smørgrav int
1103bc5531deSDag-Erling Smørgrav kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1104bc5531deSDag-Erling Smørgrav     const struct sshbuf *shared_secret)
11051e8db6e2SBrian Feldman {
1106bc5531deSDag-Erling Smørgrav 	struct kex *kex = ssh->kex;
11071e8db6e2SBrian Feldman 	u_char *keys[NKEYS];
1108bc5531deSDag-Erling Smørgrav 	u_int i, j, mode, ctos;
1109bc5531deSDag-Erling Smørgrav 	int r;
1110a04a10f8SKris Kennaway 
111119261079SEd Maste 	/* save initial hash as session id */
111219261079SEd Maste 	if ((kex->flags & KEX_INITIAL) != 0) {
111319261079SEd Maste 		if (sshbuf_len(kex->session_id) != 0) {
111419261079SEd Maste 			error_f("already have session ID at kex");
111519261079SEd Maste 			return SSH_ERR_INTERNAL_ERROR;
111619261079SEd Maste 		}
111719261079SEd Maste 		if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0)
111819261079SEd Maste 			return r;
111919261079SEd Maste 	} else if (sshbuf_len(kex->session_id) == 0) {
112019261079SEd Maste 		error_f("no session ID in rekex");
112119261079SEd Maste 		return SSH_ERR_INTERNAL_ERROR;
112219261079SEd Maste 	}
1123021d409fSDag-Erling Smørgrav 	for (i = 0; i < NKEYS; i++) {
1124bc5531deSDag-Erling Smørgrav 		if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1125bc5531deSDag-Erling Smørgrav 		    shared_secret, &keys[i])) != 0) {
1126bc5531deSDag-Erling Smørgrav 			for (j = 0; j < i; j++)
1127bc5531deSDag-Erling Smørgrav 				free(keys[j]);
1128bc5531deSDag-Erling Smørgrav 			return r;
1129021d409fSDag-Erling Smørgrav 		}
1130bc5531deSDag-Erling Smørgrav 	}
1131a04a10f8SKris Kennaway 	for (mode = 0; mode < MODE_MAX; mode++) {
1132761efaa7SDag-Erling Smørgrav 		ctos = (!kex->server && mode == MODE_OUT) ||
1133761efaa7SDag-Erling Smørgrav 		    (kex->server && mode == MODE_IN);
1134bc5531deSDag-Erling Smørgrav 		kex->newkeys[mode]->enc.iv  = keys[ctos ? 0 : 1];
1135bc5531deSDag-Erling Smørgrav 		kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1136bc5531deSDag-Erling Smørgrav 		kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
1137a04a10f8SKris Kennaway 	}
1138bc5531deSDag-Erling Smørgrav 	return 0;
1139a04a10f8SKris Kennaway }
11401e8db6e2SBrian Feldman 
1141bc5531deSDag-Erling Smørgrav int
114219261079SEd Maste kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
1143f7167e0eSDag-Erling Smørgrav {
114419261079SEd Maste 	struct kex *kex = ssh->kex;
1145f7167e0eSDag-Erling Smørgrav 
114619261079SEd Maste 	*pubp = NULL;
114719261079SEd Maste 	*prvp = NULL;
114819261079SEd Maste 	if (kex->load_host_public_key == NULL ||
114919261079SEd Maste 	    kex->load_host_private_key == NULL) {
115019261079SEd Maste 		error_f("missing hostkey loader");
115119261079SEd Maste 		return SSH_ERR_INVALID_ARGUMENT;
1152f7167e0eSDag-Erling Smørgrav 	}
115319261079SEd Maste 	*pubp = kex->load_host_public_key(kex->hostkey_type,
115419261079SEd Maste 	    kex->hostkey_nid, ssh);
115519261079SEd Maste 	*prvp = kex->load_host_private_key(kex->hostkey_type,
115619261079SEd Maste 	    kex->hostkey_nid, ssh);
115719261079SEd Maste 	if (*pubp == NULL)
115819261079SEd Maste 		return SSH_ERR_NO_HOSTKEY_LOADED;
115919261079SEd Maste 	return 0;
116019261079SEd Maste }
1161f7167e0eSDag-Erling Smørgrav 
116219261079SEd Maste int
116319261079SEd Maste kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
116419261079SEd Maste {
116519261079SEd Maste 	struct kex *kex = ssh->kex;
116619261079SEd Maste 
116719261079SEd Maste 	if (kex->verify_host_key == NULL) {
116819261079SEd Maste 		error_f("missing hostkey verifier");
116919261079SEd Maste 		return SSH_ERR_INVALID_ARGUMENT;
117019261079SEd Maste 	}
117119261079SEd Maste 	if (server_host_key->type != kex->hostkey_type ||
117219261079SEd Maste 	    (kex->hostkey_type == KEY_ECDSA &&
117319261079SEd Maste 	    server_host_key->ecdsa_nid != kex->hostkey_nid))
117419261079SEd Maste 		return SSH_ERR_KEY_TYPE_MISMATCH;
117519261079SEd Maste 	if (kex->verify_host_key(server_host_key, ssh) == -1)
117619261079SEd Maste 		return  SSH_ERR_SIGNATURE_INVALID;
117719261079SEd Maste 	return 0;
117819261079SEd Maste }
1179d74d50a8SDag-Erling Smørgrav 
11804a421b63SDag-Erling Smørgrav #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
11811e8db6e2SBrian Feldman void
118219261079SEd Maste dump_digest(const char *msg, const u_char *digest, int len)
11831e8db6e2SBrian Feldman {
11841e8db6e2SBrian Feldman 	fprintf(stderr, "%s\n", msg);
1185bc5531deSDag-Erling Smørgrav 	sshbuf_dump_data(digest, len, stderr);
11861e8db6e2SBrian Feldman }
11871e8db6e2SBrian Feldman #endif
118819261079SEd Maste 
118919261079SEd Maste /*
119019261079SEd Maste  * Send a plaintext error message to the peer, suffixed by \r\n.
119119261079SEd Maste  * Only used during banner exchange, and there only for the server.
119219261079SEd Maste  */
119319261079SEd Maste static void
119419261079SEd Maste send_error(struct ssh *ssh, char *msg)
119519261079SEd Maste {
119619261079SEd Maste 	char *crnl = "\r\n";
119719261079SEd Maste 
119819261079SEd Maste 	if (!ssh->kex->server)
119919261079SEd Maste 		return;
120019261079SEd Maste 
120119261079SEd Maste 	if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
120219261079SEd Maste 	    msg, strlen(msg)) != strlen(msg) ||
120319261079SEd Maste 	    atomicio(vwrite, ssh_packet_get_connection_out(ssh),
120419261079SEd Maste 	    crnl, strlen(crnl)) != strlen(crnl))
120519261079SEd Maste 		error_f("write: %.100s", strerror(errno));
120619261079SEd Maste }
120719261079SEd Maste 
120819261079SEd Maste /*
120919261079SEd Maste  * Sends our identification string and waits for the peer's. Will block for
121019261079SEd Maste  * up to timeout_ms (or indefinitely if timeout_ms <= 0).
121119261079SEd Maste  * Returns on 0 success or a ssherr.h code on failure.
121219261079SEd Maste  */
121319261079SEd Maste int
121419261079SEd Maste kex_exchange_identification(struct ssh *ssh, int timeout_ms,
121519261079SEd Maste     const char *version_addendum)
121619261079SEd Maste {
121719261079SEd Maste 	int remote_major, remote_minor, mismatch, oerrno = 0;
1218*f374ba41SEd Maste 	size_t len, n;
121919261079SEd Maste 	int r, expect_nl;
122019261079SEd Maste 	u_char c;
122119261079SEd Maste 	struct sshbuf *our_version = ssh->kex->server ?
122219261079SEd Maste 	    ssh->kex->server_version : ssh->kex->client_version;
122319261079SEd Maste 	struct sshbuf *peer_version = ssh->kex->server ?
122419261079SEd Maste 	    ssh->kex->client_version : ssh->kex->server_version;
122519261079SEd Maste 	char *our_version_string = NULL, *peer_version_string = NULL;
122619261079SEd Maste 	char *cp, *remote_version = NULL;
122719261079SEd Maste 
122819261079SEd Maste 	/* Prepare and send our banner */
122919261079SEd Maste 	sshbuf_reset(our_version);
123019261079SEd Maste 	if (version_addendum != NULL && *version_addendum == '\0')
123119261079SEd Maste 		version_addendum = NULL;
123219261079SEd Maste 	if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
123319261079SEd Maste 	    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
123419261079SEd Maste 	    version_addendum == NULL ? "" : " ",
123519261079SEd Maste 	    version_addendum == NULL ? "" : version_addendum)) != 0) {
123619261079SEd Maste 		oerrno = errno;
123719261079SEd Maste 		error_fr(r, "sshbuf_putf");
123819261079SEd Maste 		goto out;
123919261079SEd Maste 	}
124019261079SEd Maste 
124119261079SEd Maste 	if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
124219261079SEd Maste 	    sshbuf_mutable_ptr(our_version),
124319261079SEd Maste 	    sshbuf_len(our_version)) != sshbuf_len(our_version)) {
124419261079SEd Maste 		oerrno = errno;
124519261079SEd Maste 		debug_f("write: %.100s", strerror(errno));
124619261079SEd Maste 		r = SSH_ERR_SYSTEM_ERROR;
124719261079SEd Maste 		goto out;
124819261079SEd Maste 	}
124919261079SEd Maste 	if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
125019261079SEd Maste 		oerrno = errno;
125119261079SEd Maste 		error_fr(r, "sshbuf_consume_end");
125219261079SEd Maste 		goto out;
125319261079SEd Maste 	}
125419261079SEd Maste 	our_version_string = sshbuf_dup_string(our_version);
125519261079SEd Maste 	if (our_version_string == NULL) {
125619261079SEd Maste 		error_f("sshbuf_dup_string failed");
125719261079SEd Maste 		r = SSH_ERR_ALLOC_FAIL;
125819261079SEd Maste 		goto out;
125919261079SEd Maste 	}
126019261079SEd Maste 	debug("Local version string %.100s", our_version_string);
126119261079SEd Maste 
126219261079SEd Maste 	/* Read other side's version identification. */
126319261079SEd Maste 	for (n = 0; ; n++) {
126419261079SEd Maste 		if (n >= SSH_MAX_PRE_BANNER_LINES) {
126519261079SEd Maste 			send_error(ssh, "No SSH identification string "
126619261079SEd Maste 			    "received.");
126719261079SEd Maste 			error_f("No SSH version received in first %u lines "
126819261079SEd Maste 			    "from server", SSH_MAX_PRE_BANNER_LINES);
126919261079SEd Maste 			r = SSH_ERR_INVALID_FORMAT;
127019261079SEd Maste 			goto out;
127119261079SEd Maste 		}
127219261079SEd Maste 		sshbuf_reset(peer_version);
127319261079SEd Maste 		expect_nl = 0;
1274*f374ba41SEd Maste 		for (;;) {
127519261079SEd Maste 			if (timeout_ms > 0) {
127619261079SEd Maste 				r = waitrfd(ssh_packet_get_connection_in(ssh),
127719261079SEd Maste 				    &timeout_ms);
127819261079SEd Maste 				if (r == -1 && errno == ETIMEDOUT) {
127919261079SEd Maste 					send_error(ssh, "Timed out waiting "
128019261079SEd Maste 					    "for SSH identification string.");
128119261079SEd Maste 					error("Connection timed out during "
128219261079SEd Maste 					    "banner exchange");
128319261079SEd Maste 					r = SSH_ERR_CONN_TIMEOUT;
128419261079SEd Maste 					goto out;
128519261079SEd Maste 				} else if (r == -1) {
128619261079SEd Maste 					oerrno = errno;
128719261079SEd Maste 					error_f("%s", strerror(errno));
128819261079SEd Maste 					r = SSH_ERR_SYSTEM_ERROR;
128919261079SEd Maste 					goto out;
129019261079SEd Maste 				}
129119261079SEd Maste 			}
129219261079SEd Maste 
129319261079SEd Maste 			len = atomicio(read, ssh_packet_get_connection_in(ssh),
129419261079SEd Maste 			    &c, 1);
129519261079SEd Maste 			if (len != 1 && errno == EPIPE) {
129619261079SEd Maste 				error_f("Connection closed by remote host");
129719261079SEd Maste 				r = SSH_ERR_CONN_CLOSED;
129819261079SEd Maste 				goto out;
129919261079SEd Maste 			} else if (len != 1) {
130019261079SEd Maste 				oerrno = errno;
130119261079SEd Maste 				error_f("read: %.100s", strerror(errno));
130219261079SEd Maste 				r = SSH_ERR_SYSTEM_ERROR;
130319261079SEd Maste 				goto out;
130419261079SEd Maste 			}
130519261079SEd Maste 			if (c == '\r') {
130619261079SEd Maste 				expect_nl = 1;
130719261079SEd Maste 				continue;
130819261079SEd Maste 			}
130919261079SEd Maste 			if (c == '\n')
131019261079SEd Maste 				break;
131119261079SEd Maste 			if (c == '\0' || expect_nl) {
131219261079SEd Maste 				error_f("banner line contains invalid "
131319261079SEd Maste 				    "characters");
131419261079SEd Maste 				goto invalid;
131519261079SEd Maste 			}
131619261079SEd Maste 			if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
131719261079SEd Maste 				oerrno = errno;
131819261079SEd Maste 				error_fr(r, "sshbuf_put");
131919261079SEd Maste 				goto out;
132019261079SEd Maste 			}
132119261079SEd Maste 			if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
132219261079SEd Maste 				error_f("banner line too long");
132319261079SEd Maste 				goto invalid;
132419261079SEd Maste 			}
132519261079SEd Maste 		}
132619261079SEd Maste 		/* Is this an actual protocol banner? */
132719261079SEd Maste 		if (sshbuf_len(peer_version) > 4 &&
132819261079SEd Maste 		    memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
132919261079SEd Maste 			break;
133019261079SEd Maste 		/* If not, then just log the line and continue */
133119261079SEd Maste 		if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
133219261079SEd Maste 			error_f("sshbuf_dup_string failed");
133319261079SEd Maste 			r = SSH_ERR_ALLOC_FAIL;
133419261079SEd Maste 			goto out;
133519261079SEd Maste 		}
133619261079SEd Maste 		/* Do not accept lines before the SSH ident from a client */
133719261079SEd Maste 		if (ssh->kex->server) {
133819261079SEd Maste 			error_f("client sent invalid protocol identifier "
133919261079SEd Maste 			    "\"%.256s\"", cp);
134019261079SEd Maste 			free(cp);
134119261079SEd Maste 			goto invalid;
134219261079SEd Maste 		}
134319261079SEd Maste 		debug_f("banner line %zu: %s", n, cp);
134419261079SEd Maste 		free(cp);
134519261079SEd Maste 	}
134619261079SEd Maste 	peer_version_string = sshbuf_dup_string(peer_version);
134719261079SEd Maste 	if (peer_version_string == NULL)
134819261079SEd Maste 		error_f("sshbuf_dup_string failed");
134919261079SEd Maste 	/* XXX must be same size for sscanf */
135019261079SEd Maste 	if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
135119261079SEd Maste 		error_f("calloc failed");
135219261079SEd Maste 		r = SSH_ERR_ALLOC_FAIL;
135319261079SEd Maste 		goto out;
135419261079SEd Maste 	}
135519261079SEd Maste 
135619261079SEd Maste 	/*
135719261079SEd Maste 	 * Check that the versions match.  In future this might accept
135819261079SEd Maste 	 * several versions and set appropriate flags to handle them.
135919261079SEd Maste 	 */
136019261079SEd Maste 	if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
136119261079SEd Maste 	    &remote_major, &remote_minor, remote_version) != 3) {
136219261079SEd Maste 		error("Bad remote protocol version identification: '%.100s'",
136319261079SEd Maste 		    peer_version_string);
136419261079SEd Maste  invalid:
136519261079SEd Maste 		send_error(ssh, "Invalid SSH identification string.");
136619261079SEd Maste 		r = SSH_ERR_INVALID_FORMAT;
136719261079SEd Maste 		goto out;
136819261079SEd Maste 	}
136919261079SEd Maste 	debug("Remote protocol version %d.%d, remote software version %.100s",
137019261079SEd Maste 	    remote_major, remote_minor, remote_version);
137119261079SEd Maste 	compat_banner(ssh, remote_version);
137219261079SEd Maste 
137319261079SEd Maste 	mismatch = 0;
137419261079SEd Maste 	switch (remote_major) {
137519261079SEd Maste 	case 2:
137619261079SEd Maste 		break;
137719261079SEd Maste 	case 1:
137819261079SEd Maste 		if (remote_minor != 99)
137919261079SEd Maste 			mismatch = 1;
138019261079SEd Maste 		break;
138119261079SEd Maste 	default:
138219261079SEd Maste 		mismatch = 1;
138319261079SEd Maste 		break;
138419261079SEd Maste 	}
138519261079SEd Maste 	if (mismatch) {
138619261079SEd Maste 		error("Protocol major versions differ: %d vs. %d",
138719261079SEd Maste 		    PROTOCOL_MAJOR_2, remote_major);
138819261079SEd Maste 		send_error(ssh, "Protocol major versions differ.");
138919261079SEd Maste 		r = SSH_ERR_NO_PROTOCOL_VERSION;
139019261079SEd Maste 		goto out;
139119261079SEd Maste 	}
139219261079SEd Maste 
139319261079SEd Maste 	if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
139419261079SEd Maste 		logit("probed from %s port %d with %s.  Don't panic.",
139519261079SEd Maste 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
139619261079SEd Maste 		    peer_version_string);
139719261079SEd Maste 		r = SSH_ERR_CONN_CLOSED; /* XXX */
139819261079SEd Maste 		goto out;
139919261079SEd Maste 	}
140019261079SEd Maste 	if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
140119261079SEd Maste 		logit("scanned from %s port %d with %s.  Don't panic.",
140219261079SEd Maste 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
140319261079SEd Maste 		    peer_version_string);
140419261079SEd Maste 		r = SSH_ERR_CONN_CLOSED; /* XXX */
140519261079SEd Maste 		goto out;
140619261079SEd Maste 	}
140719261079SEd Maste 	if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
140819261079SEd Maste 		logit("Remote version \"%.100s\" uses unsafe RSA signature "
140919261079SEd Maste 		    "scheme; disabling use of RSA keys", remote_version);
141019261079SEd Maste 	}
141119261079SEd Maste 	/* success */
141219261079SEd Maste 	r = 0;
141319261079SEd Maste  out:
141419261079SEd Maste 	free(our_version_string);
141519261079SEd Maste 	free(peer_version_string);
141619261079SEd Maste 	free(remote_version);
141719261079SEd Maste 	if (r == SSH_ERR_SYSTEM_ERROR)
141819261079SEd Maste 		errno = oerrno;
141919261079SEd Maste 	return r;
142019261079SEd Maste }
142119261079SEd Maste 
1422