blob: 34808b5c39dab7000f67ddd4ed3c7a9efb783d84 [file] [log] [blame]
djm@openbsd.org70edd732019-01-21 12:08:13 +00001/* $OpenBSD: kex.c,v 1.150 2019/01/21 12:08:13 djm Exp $ */
Damien Millera664e872000-04-16 11:52:47 +10002/*
Ben Lindstrom44697232001-07-04 03:32:30 +00003 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millera664e872000-04-16 11:52:47 +10004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
Damien Millera664e872000-04-16 11:52:47 +100013 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millera664e872000-04-16 11:52:47 +100027
djm@openbsd.org0a843d92018-12-27 03:25:24 +000028#include <sys/types.h>
29#include <errno.h>
Damien Millerd7834352006-08-05 12:39:39 +100030#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100031#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100032#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100033#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100034#include <string.h>
djm@openbsd.org0a843d92018-12-27 03:25:24 +000035#include <unistd.h>
36#include <poll.h>
Damien Millere3476ed2006-07-24 14:13:33 +100037
Damien Miller1f0311c2014-05-15 14:24:09 +100038#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100039#include <openssl/crypto.h>
Damien Millerbd5f2b72016-07-15 19:14:48 +100040#include <openssl/dh.h>
Damien Miller1f0311c2014-05-15 14:24:09 +100041#endif
Damien Millerd7834352006-08-05 12:39:39 +100042
djm@openbsd.org0a843d92018-12-27 03:25:24 +000043#include "ssh.h"
Damien Millerd7834352006-08-05 12:39:39 +100044#include "ssh2.h"
djm@openbsd.org0a843d92018-12-27 03:25:24 +000045#include "atomicio.h"
46#include "version.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000047#include "packet.h"
48#include "compat.h"
49#include "cipher.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000050#include "sshkey.h"
Damien Millerd7834352006-08-05 12:39:39 +100051#include "kex.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000052#include "log.h"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000053#include "mac.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000054#include "match.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000055#include "misc.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000056#include "dispatch.h"
Ben Lindstrom7a2073c2002-03-22 02:30:41 +000057#include "monitor.h"
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000058
59#include "ssherr.h"
60#include "sshbuf.h"
Damien Millerb3051d02014-01-10 10:58:53 +110061#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100062
Ben Lindstrombba81212001-06-25 05:01:22 +000063/* prototype */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000064static int kex_choose_conf(struct ssh *);
markus@openbsd.org2ae666a2017-05-30 14:23:52 +000065static int kex_input_newkeys(int, u_int32_t, struct ssh *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000066
djm@openbsd.org9690b782015-08-21 23:57:48 +000067static const char *proposal_names[PROPOSAL_MAX] = {
68 "KEX algorithms",
69 "host key algorithms",
70 "ciphers ctos",
71 "ciphers stoc",
72 "MACs ctos",
73 "MACs stoc",
74 "compression ctos",
75 "compression stoc",
76 "languages ctos",
77 "languages stoc",
78};
79
Damien Millerea111192013-04-23 19:24:32 +100080struct kexalg {
81 char *name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +000082 u_int type;
Damien Millerea111192013-04-23 19:24:32 +100083 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110084 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100085};
86static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100087#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110088 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
djm@openbsd.org0e8eeec2016-05-02 10:26:04 +000089 { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
90 { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
91 { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
92 { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
Damien Millerb3051d02014-01-10 10:58:53 +110093 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100094#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110095 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100096#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100097#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +110098 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
99 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
100 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
101 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +1100102# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +1100103 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
104 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +1000105# endif /* OPENSSL_HAS_NISTP521 */
106#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +1000107#endif /* WITH_OPENSSL */
Damien Miller72ef7c12015-01-15 02:21:31 +1100108#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
Damien Millerb3051d02014-01-10 10:58:53 +1100109 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.org04937662016-09-22 17:52:53 +0000110 { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
djm@openbsd.orgdfd59162019-01-21 10:20:12 +0000111 { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0,
112 SSH_DIGEST_SHA512 },
Damien Miller72ef7c12015-01-15 02:21:31 +1100113#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
Damien Millerb3051d02014-01-10 10:58:53 +1100114 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +1000115};
116
117char *
Damien Miller690d9892013-11-08 12:16:49 +1100118kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000119{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000120 char *ret = NULL, *tmp;
Damien Millerea111192013-04-23 19:24:32 +1000121 size_t nlen, rlen = 0;
122 const struct kexalg *k;
123
124 for (k = kexalgs; k->name != NULL; k++) {
125 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100126 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000127 nlen = strlen(k->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000128 if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
129 free(ret);
130 return NULL;
131 }
132 ret = tmp;
Damien Millerea111192013-04-23 19:24:32 +1000133 memcpy(ret + rlen, k->name, nlen + 1);
134 rlen += nlen;
135 }
136 return ret;
137}
138
139static const struct kexalg *
140kex_alg_by_name(const char *name)
141{
142 const struct kexalg *k;
143
144 for (k = kexalgs; k->name != NULL; k++) {
145 if (strcmp(k->name, name) == 0)
146 return k;
147 }
148 return NULL;
149}
150
Damien Millerd5f62bf2010-09-24 22:11:14 +1000151/* Validate KEX method name list */
152int
153kex_names_valid(const char *names)
154{
155 char *s, *cp, *p;
156
157 if (names == NULL || strcmp(names, "") == 0)
158 return 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000159 if ((s = cp = strdup(names)) == NULL)
160 return 0;
Damien Millerd5f62bf2010-09-24 22:11:14 +1000161 for ((p = strsep(&cp, ",")); p && *p != '\0';
162 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000163 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000164 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000165 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000166 return 0;
167 }
168 }
169 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000170 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000171 return 1;
172}
173
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000174/*
175 * Concatenate algorithm names, avoiding duplicates in the process.
176 * Caller must free returned string.
177 */
178char *
179kex_names_cat(const char *a, const char *b)
180{
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000181 char *ret = NULL, *tmp = NULL, *cp, *p, *m;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000182 size_t len;
183
184 if (a == NULL || *a == '\0')
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000185 return strdup(b);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000186 if (b == NULL || *b == '\0')
187 return strdup(a);
188 if (strlen(b) > 1024*1024)
189 return NULL;
190 len = strlen(a) + strlen(b) + 2;
191 if ((tmp = cp = strdup(b)) == NULL ||
192 (ret = calloc(1, len)) == NULL) {
193 free(tmp);
194 return NULL;
195 }
196 strlcpy(ret, a, len);
197 for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000198 if ((m = match_list(ret, p, NULL)) != NULL) {
199 free(m);
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000200 continue; /* Algorithm already present */
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000201 }
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000202 if (strlcat(ret, ",", len) >= len ||
203 strlcat(ret, p, len) >= len) {
204 free(tmp);
205 free(ret);
206 return NULL; /* Shouldn't happen */
207 }
208 }
209 free(tmp);
210 return ret;
211}
212
213/*
214 * Assemble a list of algorithms from a default list and a string from a
215 * configuration file. The user-provided string may begin with '+' to
djm@openbsd.org68bc8cf2017-02-03 23:01:19 +0000216 * indicate that it should be appended to the default or '-' that the
217 * specified names should be removed.
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000218 */
219int
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000220kex_assemble_names(char **listp, const char *def, const char *all)
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000221{
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000222 char *cp, *tmp, *patterns;
223 char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
224 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000225
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000226 if (listp == NULL || *listp == NULL || **listp == '\0') {
227 if ((*listp = strdup(def)) == NULL)
228 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000229 return 0;
230 }
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000231
232 list = *listp;
233 *listp = NULL;
234 if (*list == '+') {
235 /* Append names to default list */
236 if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
237 r = SSH_ERR_ALLOC_FAIL;
238 goto fail;
239 }
240 free(list);
241 list = tmp;
242 } else if (*list == '-') {
243 /* Remove names from default list */
244 if ((*listp = match_filter_blacklist(def, list + 1)) == NULL) {
245 r = SSH_ERR_ALLOC_FAIL;
246 goto fail;
247 }
248 free(list);
249 /* filtering has already been done */
250 return 0;
251 } else {
252 /* Explicit list, overrides default - just use "list" as is */
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000253 }
254
djm@openbsd.org312d2f22018-07-04 13:49:31 +0000255 /*
256 * The supplied names may be a pattern-list. For the -list case,
257 * the patterns are applied above. For the +list and explicit list
258 * cases we need to do it now.
259 */
260 ret = NULL;
261 if ((patterns = opatterns = strdup(list)) == NULL) {
262 r = SSH_ERR_ALLOC_FAIL;
263 goto fail;
264 }
265 /* Apply positive (i.e. non-negated) patterns from the list */
266 while ((cp = strsep(&patterns, ",")) != NULL) {
267 if (*cp == '!') {
268 /* negated matches are not supported here */
269 r = SSH_ERR_INVALID_ARGUMENT;
270 goto fail;
271 }
272 free(matching);
273 if ((matching = match_filter_whitelist(all, cp)) == NULL) {
274 r = SSH_ERR_ALLOC_FAIL;
275 goto fail;
276 }
277 if ((tmp = kex_names_cat(ret, matching)) == NULL) {
278 r = SSH_ERR_ALLOC_FAIL;
279 goto fail;
280 }
281 free(ret);
282 ret = tmp;
283 }
284 if (ret == NULL || *ret == '\0') {
285 /* An empty name-list is an error */
286 /* XXX better error code? */
287 r = SSH_ERR_INVALID_ARGUMENT;
288 goto fail;
289 }
290
291 /* success */
292 *listp = ret;
293 ret = NULL;
294 r = 0;
295
296 fail:
297 free(matching);
298 free(opatterns);
299 free(list);
300 free(ret);
301 return r;
djm@openbsd.orgf9eca242015-07-30 00:01:34 +0000302}
303
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000304/* put algorithm proposal into buffer */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000305int
306kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000307{
Damien Millereccb9de2005-06-17 12:59:34 +1000308 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000309 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000310
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000311 sshbuf_reset(b);
312
Ben Lindstrom59971722002-03-27 17:42:57 +0000313 /*
314 * add a dummy cookie, the cookie will be overwritten by
315 * kex_send_kexinit(), each time a kexinit is set
316 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000317 for (i = 0; i < KEX_COOKIE_LEN; i++) {
318 if ((r = sshbuf_put_u8(b, 0)) != 0)
319 return r;
320 }
321 for (i = 0; i < PROPOSAL_MAX; i++) {
322 if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
323 return r;
324 }
325 if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */
326 (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */
327 return r;
328 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000329}
330
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000331/* parse buffer and return algorithm proposal */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000332int
333kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
Damien Millerb1715dc2000-05-30 13:44:51 +1000334{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000335 struct sshbuf *b = NULL;
336 u_char v;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000337 u_int i;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000338 char **proposal = NULL;
339 int r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000340
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000341 *propp = NULL;
342 if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
343 return SSH_ERR_ALLOC_FAIL;
344 if ((b = sshbuf_fromb(raw)) == NULL) {
345 r = SSH_ERR_ALLOC_FAIL;
346 goto out;
347 }
348 if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
349 goto out;
Damien Millerb1715dc2000-05-30 13:44:51 +1000350 /* extract kex init proposal strings */
351 for (i = 0; i < PROPOSAL_MAX; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000352 if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
353 goto out;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000354 debug2("%s: %s", proposal_names[i], proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000355 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000356 /* first kex follows / reserved */
djm@openbsd.org271df812015-12-13 22:42:23 +0000357 if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
358 (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000359 goto out;
Damien Millerbabb47a2003-02-24 11:53:32 +1100360 if (first_kex_follows != NULL)
djm@openbsd.org271df812015-12-13 22:42:23 +0000361 *first_kex_follows = v;
djm@openbsd.org9690b782015-08-21 23:57:48 +0000362 debug2("first_kex_follows %d ", v);
363 debug2("reserved %u ", i);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000364 r = 0;
365 *propp = proposal;
366 out:
367 if (r != 0 && proposal != NULL)
368 kex_prop_free(proposal);
369 sshbuf_free(b);
370 return r;
Damien Millerb1715dc2000-05-30 13:44:51 +1000371}
372
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000373void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000374kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000375{
Damien Millereccb9de2005-06-17 12:59:34 +1000376 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000377
djm@openbsd.org44a8e7c2015-04-17 13:25:52 +0000378 if (proposal == NULL)
379 return;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000380 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000381 free(proposal[i]);
382 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000383}
384
Darren Tucker0d0d1952007-06-05 18:23:28 +1000385/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000386static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000387kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
Damien Miller874d77b2000-10-14 16:23:11 +1100388{
djm@openbsd.orgd87063d2015-11-13 04:39:35 +0000389 int r;
390
391 error("kex protocol error: type %d seq %u", type, seq);
392 if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
393 (r = sshpkt_put_u32(ssh, seq)) != 0 ||
394 (r = sshpkt_send(ssh)) != 0)
395 return r;
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000396 return 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100397}
398
Ben Lindstrombba81212001-06-25 05:01:22 +0000399static void
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000400kex_reset_dispatch(struct ssh *ssh)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000401{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000402 ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
Damien Miller7d053392002-01-22 23:24:13 +1100403 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000404}
405
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000406static int
407kex_send_ext_info(struct ssh *ssh)
408{
409 int r;
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000410 char *algs;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000411
djm@openbsd.org183ba552017-03-10 04:07:20 +0000412 if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000413 return SSH_ERR_ALLOC_FAIL;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000414 /* XXX filter algs list by allowed pubkey/hostbased types */
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000415 if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
416 (r = sshpkt_put_u32(ssh, 1)) != 0 ||
417 (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000418 (r = sshpkt_put_cstring(ssh, algs)) != 0 ||
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000419 (r = sshpkt_send(ssh)) != 0)
djm@openbsd.org130f5df2016-09-12 23:31:27 +0000420 goto out;
421 /* success */
422 r = 0;
423 out:
424 free(algs);
djm@openbsd.org16226492016-09-21 19:53:12 +0000425 return r;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000426}
427
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000428int
429kex_send_newkeys(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000430{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000431 int r;
Ben Lindstrom238abf62001-04-04 17:52:53 +0000432
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000433 kex_reset_dispatch(ssh);
434 if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
435 (r = sshpkt_send(ssh)) != 0)
436 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000437 debug("SSH2_MSG_NEWKEYS sent");
Ben Lindstrom064496f2002-12-23 02:04:22 +0000438 debug("expecting SSH2_MSG_NEWKEYS");
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000439 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000440 if (ssh->kex->ext_info_c)
441 if ((r = kex_send_ext_info(ssh)) != 0)
442 return r;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000443 return 0;
444}
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000445
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000446int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000447kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000448{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000449 struct kex *kex = ssh->kex;
450 u_int32_t i, ninfo;
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000451 char *name;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000452 u_char *val;
453 size_t vlen;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000454 int r;
455
456 debug("SSH2_MSG_EXT_INFO received");
457 ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
458 if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
459 return r;
460 for (i = 0; i < ninfo; i++) {
461 if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
462 return r;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000463 if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000464 free(name);
465 return r;
466 }
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000467 if (strcmp(name, "server-sig-algs") == 0) {
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000468 /* Ensure no \0 lurking in value */
469 if (memchr(val, '\0', vlen) != NULL) {
470 error("%s: nul byte in %s", __func__, name);
471 return SSH_ERR_INVALID_FORMAT;
472 }
473 debug("%s: %s=<%s>", __func__, name, val);
djm@openbsd.org4ba0d542018-07-03 11:39:54 +0000474 kex->server_sig_algs = val;
475 val = NULL;
djm@openbsd.orgc9480302017-06-13 12:13:59 +0000476 } else
477 debug("%s: %s (unrecognised)", __func__, name);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000478 free(name);
479 free(val);
480 }
481 return sshpkt_get_end(ssh);
482}
483
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000484static int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000485kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000486{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000487 struct kex *kex = ssh->kex;
488 int r;
489
490 debug("SSH2_MSG_NEWKEYS received");
491 ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
markus@openbsd.org2adbe1e2017-03-15 07:07:39 +0000492 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000493 if ((r = sshpkt_get_end(ssh)) != 0)
494 return r;
markus@openbsd.org28652bc2016-09-19 19:02:19 +0000495 if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0)
496 return r;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000497 kex->done = 1;
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000498 kex->flags &= ~KEX_INITIAL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000499 sshbuf_reset(kex->peer);
500 /* sshbuf_reset(kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000501 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000502 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000503 kex->name = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000504 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000505}
Damien Millera664e872000-04-16 11:52:47 +1000506
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000507int
508kex_send_kexinit(struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000509{
Ben Lindstrom59971722002-03-27 17:42:57 +0000510 u_char *cookie;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000511 struct kex *kex = ssh->kex;
512 int r;
Ben Lindstrom59971722002-03-27 17:42:57 +0000513
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000514 if (kex == NULL)
515 return SSH_ERR_INTERNAL_ERROR;
516 if (kex->flags & KEX_INIT_SENT)
517 return 0;
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000518 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000519
520 /* generate a random cookie */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000521 if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
522 return SSH_ERR_INVALID_FORMAT;
523 if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
524 return SSH_ERR_INTERNAL_ERROR;
525 arc4random_buf(cookie, KEX_COOKIE_LEN);
526
527 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
528 (r = sshpkt_putb(ssh, kex->my)) != 0 ||
529 (r = sshpkt_send(ssh)) != 0)
530 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000531 debug("SSH2_MSG_KEXINIT sent");
532 kex->flags |= KEX_INIT_SENT;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000533 return 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000534}
535
Darren Tucker0d0d1952007-06-05 18:23:28 +1000536/* ARGSUSED */
markus@openbsd.org3fdc88a2015-01-19 20:07:45 +0000537int
markus@openbsd.org2ae666a2017-05-30 14:23:52 +0000538kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000539{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000540 struct kex *kex = ssh->kex;
541 const u_char *ptr;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000542 u_int i;
543 size_t dlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000544 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000545
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000546 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000547 if (kex == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000548 return SSH_ERR_INVALID_ARGUMENT;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000549
markus@openbsd.orgec165c32016-10-10 19:28:48 +0000550 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000551 ptr = sshpkt_ptr(ssh, &dlen);
552 if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
553 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000554
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000555 /* discard packet */
556 for (i = 0; i < KEX_COOKIE_LEN; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000557 if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
558 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000559 for (i = 0; i < PROPOSAL_MAX; i++)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000560 if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
561 return r;
Darren Tuckerae608bd2012-09-06 21:19:51 +1000562 /*
563 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
564 * KEX method has the server move first, but a server might be using
565 * a custom method or one that we otherwise don't support. We should
566 * be prepared to remember first_kex_follows here so we can eat a
567 * packet later.
568 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
569 * for cases where the server *doesn't* go first. I guess we should
570 * ignore it when it is set for these cases, which is what we do now.
571 */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000572 if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */
573 (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */
574 (r = sshpkt_get_end(ssh)) != 0)
575 return r;
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000576
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000577 if (!(kex->flags & KEX_INIT_SENT))
578 if ((r = kex_send_kexinit(ssh)) != 0)
579 return r;
580 if ((r = kex_choose_conf(ssh)) != 0)
581 return r;
582
583 if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
584 return (kex->kex[kex->kex_type])(ssh);
585
586 return SSH_ERR_INTERNAL_ERROR;
587}
588
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000589struct kex *
590kex_new(void)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000591{
592 struct kex *kex;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000593
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000594 if ((kex = calloc(1, sizeof(*kex))) == NULL ||
595 (kex->peer = sshbuf_new()) == NULL ||
596 (kex->my = sshbuf_new()) == NULL ||
597 (kex->client_version = sshbuf_new()) == NULL ||
598 (kex->server_version = sshbuf_new()) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000599 kex_free(kex);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000600 return NULL;
601 }
602 return kex;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000603}
604
markus@openbsd.org091c3022015-01-19 19:52:16 +0000605void
606kex_free_newkeys(struct newkeys *newkeys)
607{
608 if (newkeys == NULL)
609 return;
610 if (newkeys->enc.key) {
611 explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
612 free(newkeys->enc.key);
613 newkeys->enc.key = NULL;
614 }
615 if (newkeys->enc.iv) {
djm@openbsd.org179c3532015-10-13 00:21:27 +0000616 explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len);
markus@openbsd.org091c3022015-01-19 19:52:16 +0000617 free(newkeys->enc.iv);
618 newkeys->enc.iv = NULL;
619 }
620 free(newkeys->enc.name);
621 explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
622 free(newkeys->comp.name);
623 explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
624 mac_clear(&newkeys->mac);
625 if (newkeys->mac.key) {
626 explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
627 free(newkeys->mac.key);
628 newkeys->mac.key = NULL;
629 }
630 free(newkeys->mac.name);
631 explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
632 explicit_bzero(newkeys, sizeof(*newkeys));
633 free(newkeys);
634}
635
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000636void
637kex_free(struct kex *kex)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000638{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000639 u_int mode;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000640
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000641 if (kex == NULL)
642 return;
643
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000644#ifdef WITH_OPENSSL
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000645 DH_free(kex->dh);
Damien Miller4df590c2015-03-11 10:02:39 +1100646#ifdef OPENSSL_HAS_ECC
jsing@openbsd.org7cd31632018-02-07 02:06:50 +0000647 EC_KEY_free(kex->ec_client_key);
Damien Miller4df590c2015-03-11 10:02:39 +1100648#endif /* OPENSSL_HAS_ECC */
649#endif /* WITH_OPENSSL */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000650 for (mode = 0; mode < MODE_MAX; mode++) {
651 kex_free_newkeys(kex->newkeys[mode]);
652 kex->newkeys[mode] = NULL;
markus@openbsd.org091c3022015-01-19 19:52:16 +0000653 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000654 sshbuf_free(kex->peer);
655 sshbuf_free(kex->my);
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000656 sshbuf_free(kex->client_version);
657 sshbuf_free(kex->server_version);
djm@openbsd.orgaaca72d2019-01-21 10:40:11 +0000658 sshbuf_free(kex->client_pub);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000659 free(kex->session_id);
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000660 free(kex->failed_choice);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000661 free(kex->hostkey_alg);
662 free(kex->name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000663 free(kex);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000664}
665
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000666int
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000667kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
668{
669 int r;
670
671 if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0)
672 return r;
673 ssh->kex->flags = KEX_INITIAL;
674 kex_reset_dispatch(ssh);
675 ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
676 return 0;
677}
678
679int
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000680kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000681{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000682 int r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000683
djm@openbsd.org0a843d92018-12-27 03:25:24 +0000684 if ((r = kex_ready(ssh, proposal)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000685 return r;
686 if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */
687 kex_free(ssh->kex);
688 ssh->kex = NULL;
689 return r;
Damien Millera664e872000-04-16 11:52:47 +1000690 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000691 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000692}
693
djm@openbsd.org19bcf2e2016-02-08 10:57:07 +0000694/*
695 * Request key re-exchange, returns 0 on success or a ssherr.h error
696 * code otherwise. Must not be called if KEX is incomplete or in-progress.
697 */
698int
699kex_start_rekex(struct ssh *ssh)
700{
701 if (ssh->kex == NULL) {
702 error("%s: no kex", __func__);
703 return SSH_ERR_INTERNAL_ERROR;
704 }
705 if (ssh->kex->done == 0) {
706 error("%s: requested twice", __func__);
707 return SSH_ERR_INTERNAL_ERROR;
708 }
709 ssh->kex->done = 0;
710 return kex_send_kexinit(ssh);
711}
712
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000713static int
714choose_enc(struct sshenc *enc, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000715{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000716 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000717
Damien Millera664e872000-04-16 11:52:47 +1000718 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000719 return SSH_ERR_NO_CIPHER_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000720 if ((enc->cipher = cipher_by_name(name)) == NULL) {
721 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000722 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000723 }
Damien Millera664e872000-04-16 11:52:47 +1000724 enc->name = name;
725 enc->enabled = 0;
726 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100727 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000728 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100729 enc->key_len = cipher_keylen(enc->cipher);
730 enc->block_size = cipher_blocksize(enc->cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000731 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000732}
Damien Miller4f7becb2006-03-26 14:10:14 +1100733
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000734static int
735choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000736{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000737 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000738
Damien Millera664e872000-04-16 11:52:47 +1000739 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000740 return SSH_ERR_NO_MAC_ALG_MATCH;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000741 if (mac_setup(mac, name) < 0) {
742 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000743 return SSH_ERR_INTERNAL_ERROR;
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000744 }
Damien Millera664e872000-04-16 11:52:47 +1000745 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000746 mac->key = NULL;
747 mac->enabled = 0;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000748 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000749}
Damien Miller4f7becb2006-03-26 14:10:14 +1100750
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000751static int
752choose_comp(struct sshcomp *comp, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000753{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000754 char *name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000755
Damien Millera664e872000-04-16 11:52:47 +1000756 if (name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000757 return SSH_ERR_NO_COMPRESS_ALG_MATCH;
Damien Miller9786e6e2005-07-26 21:54:56 +1000758 if (strcmp(name, "zlib@openssh.com") == 0) {
sf@openbsd.org168b46f2018-07-09 13:37:10 +0000759 comp->type = COMP_DELAYED;
760 } else if (strcmp(name, "zlib") == 0) {
sf@openbsd.orgab392672018-07-06 09:06:14 +0000761 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000762 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000763 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000764 } else {
dtucker@openbsd.org5a06b9e2017-03-10 03:45:40 +0000765 free(name);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000766 return SSH_ERR_INTERNAL_ERROR;
Damien Millera664e872000-04-16 11:52:47 +1000767 }
768 comp->name = name;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000769 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000770}
Damien Miller4f7becb2006-03-26 14:10:14 +1100771
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000772static int
773choose_kex(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000774{
Damien Millerea111192013-04-23 19:24:32 +1000775 const struct kexalg *kexalg;
776
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000777 k->name = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000778
djm@openbsd.org9690b782015-08-21 23:57:48 +0000779 debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
Damien Millera664e872000-04-16 11:52:47 +1000780 if (k->name == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000781 return SSH_ERR_NO_KEX_ALG_MATCH;
Damien Millerea111192013-04-23 19:24:32 +1000782 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000783 return SSH_ERR_INTERNAL_ERROR;
Damien Millerea111192013-04-23 19:24:32 +1000784 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100785 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000786 k->ec_nid = kexalg->ec_nid;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000787 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000788}
Damien Miller19bb3a52005-11-05 15:19:35 +1100789
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000790static int
791choose_hostkeyalg(struct kex *k, char *client, char *server)
Damien Millera664e872000-04-16 11:52:47 +1000792{
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000793 k->hostkey_alg = match_list(client, server, NULL);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000794
djm@openbsd.org9690b782015-08-21 23:57:48 +0000795 debug("kex: host key algorithm: %s",
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000796 k->hostkey_alg ? k->hostkey_alg : "(no match)");
797 if (k->hostkey_alg == NULL)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000798 return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000799 k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100800 if (k->hostkey_type == KEY_UNSPEC)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000801 return SSH_ERR_INTERNAL_ERROR;
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000802 k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000803 return 0;
Damien Millera664e872000-04-16 11:52:47 +1000804}
805
Damien Millera8e06ce2003-11-21 23:48:55 +1100806static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100807proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
808{
809 static int check[] = {
810 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
811 };
812 int *idx;
813 char *p;
814
815 for (idx = &check[0]; *idx != -1; idx++) {
816 if ((p = strchr(my[*idx], ',')) != NULL)
817 *p = '\0';
818 if ((p = strchr(peer[*idx], ',')) != NULL)
819 *p = '\0';
820 if (strcmp(my[*idx], peer[*idx]) != 0) {
821 debug2("proposal mismatch: my %s peer %s",
822 my[*idx], peer[*idx]);
823 return (0);
824 }
825 }
826 debug2("proposals match");
827 return (1);
828}
829
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000830static int
831kex_choose_conf(struct ssh *ssh)
Damien Millera664e872000-04-16 11:52:47 +1000832{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000833 struct kex *kex = ssh->kex;
834 struct newkeys *newkeys;
835 char **my = NULL, **peer = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000836 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000837 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100838 u_int mode, ctos, need, dh_need, authlen;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000839 int r, first_kex_follows;
Damien Millera664e872000-04-16 11:52:47 +1000840
djm@openbsd.org9690b782015-08-21 23:57:48 +0000841 debug2("local %s KEXINIT proposal", kex->server ? "server" : "client");
842 if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0)
843 goto out;
844 debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server");
845 if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000846 goto out;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000847
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000848 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000849 cprop=peer;
850 sprop=my;
851 } else {
852 cprop=my;
853 sprop=peer;
854 }
Damien Millera664e872000-04-16 11:52:47 +1000855
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000856 /* Check whether client supports ext_info_c */
djm@openbsd.orga6a07882018-12-07 03:39:40 +0000857 if (kex->server && (kex->flags & KEX_INITIAL)) {
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000858 char *ext;
859
860 ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
markus@openbsd.orge5e8d912016-09-06 09:14:05 +0000861 kex->ext_info_c = (ext != NULL);
862 free(ext);
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000863 }
864
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000865 /* Algorithm Negotiation */
djm@openbsd.org9690b782015-08-21 23:57:48 +0000866 if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
867 sprop[PROPOSAL_KEX_ALGS])) != 0) {
868 kex->failed_choice = peer[PROPOSAL_KEX_ALGS];
869 peer[PROPOSAL_KEX_ALGS] = NULL;
870 goto out;
871 }
872 if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
873 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) {
874 kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS];
875 peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL;
876 goto out;
877 }
Damien Millera664e872000-04-16 11:52:47 +1000878 for (mode = 0; mode < MODE_MAX; mode++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000879 if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
880 r = SSH_ERR_ALLOC_FAIL;
881 goto out;
882 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000883 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000884 ctos = (!kex->server && mode == MODE_OUT) ||
885 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000886 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
887 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
888 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000889 if ((r = choose_enc(&newkeys->enc, cprop[nenc],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000890 sprop[nenc])) != 0) {
891 kex->failed_choice = peer[nenc];
892 peer[nenc] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000893 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000894 }
Damien Miller1d75abf2013-01-09 16:12:19 +1100895 authlen = cipher_authlen(newkeys->enc.cipher);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000896 /* ignore mac for authenticated encryption */
897 if (authlen == 0 &&
898 (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000899 sprop[nmac])) != 0) {
900 kex->failed_choice = peer[nmac];
901 peer[nmac] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000902 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000903 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000904 if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000905 sprop[ncomp])) != 0) {
906 kex->failed_choice = peer[ncomp];
907 peer[ncomp] = NULL;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000908 goto out;
djm@openbsd.orgf3199122015-07-29 04:43:06 +0000909 }
djm@openbsd.org9690b782015-08-21 23:57:48 +0000910 debug("kex: %s cipher: %s MAC: %s compression: %s",
Damien Millera664e872000-04-16 11:52:47 +1000911 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000912 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100913 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000914 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000915 }
Damien Miller76eea4a2014-01-26 09:37:25 +1100916 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000917 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000918 newkeys = kex->newkeys[mode];
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000919 need = MAXIMUM(need, newkeys->enc.key_len);
920 need = MAXIMUM(need, newkeys->enc.block_size);
921 need = MAXIMUM(need, newkeys->enc.iv_len);
922 need = MAXIMUM(need, newkeys->mac.key_len);
923 dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher));
924 dh_need = MAXIMUM(dh_need, newkeys->enc.block_size);
925 dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len);
926 dh_need = MAXIMUM(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000927 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000928 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000929 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100930 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000931
Damien Millerbabb47a2003-02-24 11:53:32 +1100932 /* ignore the next message if the proposals do not match */
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000933 if (first_kex_follows && !proposals_match(my, peer))
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000934 ssh->dispatch_skip_packets = 1;
935 r = 0;
936 out:
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000937 kex_prop_free(my);
938 kex_prop_free(peer);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000939 return r;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000940}
941
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000942static int
943derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
944 const struct sshbuf *shared_secret, u_char **keyp)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000945{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000946 struct kex *kex = ssh->kex;
947 struct ssh_digest_ctx *hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000948 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000949 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100950 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000951 u_char *digest;
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000952 int r;
Damien Miller46d38de2005-07-17 17:02:09 +1000953
Damien Millerb3051d02014-01-10 10:58:53 +1100954 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000955 return SSH_ERR_INVALID_ARGUMENT;
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000956 if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000957 r = SSH_ERR_ALLOC_FAIL;
958 goto out;
959 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000960
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000961 /* K1 = HASH(K || H || "A" || session_id) */
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000962 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
963 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100964 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
965 ssh_digest_update(hashctx, &c, 1) != 0 ||
966 ssh_digest_update(hashctx, kex->session_id,
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000967 kex->session_id_len) != 0 ||
968 ssh_digest_final(hashctx, digest, mdsz) != 0) {
969 r = SSH_ERR_LIBCRYPTO_ERROR;
970 goto out;
971 }
Damien Millerb3051d02014-01-10 10:58:53 +1100972 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000973 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000974
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000975 /*
976 * expand key:
977 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
978 * Key = K1 || K2 || ... || Kn
979 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000980 for (have = mdsz; need > have; have += mdsz) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000981 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
982 ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
Damien Millerb3051d02014-01-10 10:58:53 +1100983 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000984 ssh_digest_update(hashctx, digest, have) != 0 ||
985 ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
986 r = SSH_ERR_LIBCRYPTO_ERROR;
987 goto out;
988 }
Damien Millerb3051d02014-01-10 10:58:53 +1100989 ssh_digest_free(hashctx);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000990 hashctx = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000991 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000992#ifdef DEBUG_KEX
993 fprintf(stderr, "key '%c'== ", c);
994 dump_digest("key", digest, need);
995#endif
markus@openbsd.org57d10cb2015-01-19 20:16:15 +0000996 *keyp = digest;
997 digest = NULL;
998 r = 0;
999 out:
mmcc@openbsd.orgd59ce082015-12-10 17:08:40 +00001000 free(digest);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001001 ssh_digest_free(hashctx);
1002 return r;
Damien Millera664e872000-04-16 11:52:47 +10001003}
1004
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001005#define NKEYS 6
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001006int
1007kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
1008 const struct sshbuf *shared_secret)
Damien Millera664e872000-04-16 11:52:47 +10001009{
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001010 struct kex *kex = ssh->kex;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001011 u_char *keys[NKEYS];
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001012 u_int i, j, mode, ctos;
1013 int r;
Damien Millera664e872000-04-16 11:52:47 +10001014
djm@openbsd.org5ae3f6d2019-01-21 09:55:52 +00001015 /* save initial hash as session id */
1016 if (kex->session_id == NULL) {
1017 kex->session_id_len = hashlen;
1018 kex->session_id = malloc(kex->session_id_len);
1019 if (kex->session_id == NULL)
1020 return SSH_ERR_ALLOC_FAIL;
1021 memcpy(kex->session_id, hash, kex->session_id_len);
1022 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001023 for (i = 0; i < NKEYS; i++) {
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001024 if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
1025 shared_secret, &keys[i])) != 0) {
1026 for (j = 0; j < i; j++)
1027 free(keys[j]);
1028 return r;
1029 }
Damien Miller19bb3a52005-11-05 15:19:35 +11001030 }
Damien Millera664e872000-04-16 11:52:47 +10001031 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller4f7becb2006-03-26 14:10:14 +11001032 ctos = (!kex->server && mode == MODE_OUT) ||
1033 (kex->server && mode == MODE_IN);
markus@openbsd.org091c3022015-01-19 19:52:16 +00001034 kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1];
1035 kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
1036 kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +10001037 }
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001038 return 0;
Damien Millera664e872000-04-16 11:52:47 +10001039}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001040
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001041int
djm@openbsd.org70edd732019-01-21 12:08:13 +00001042kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
djm@openbsd.orgbb39baf2019-01-21 10:05:09 +00001043{
1044 struct kex *kex = ssh->kex;
1045
1046 *pubp = NULL;
1047 *prvp = NULL;
1048 if (kex->load_host_public_key == NULL ||
1049 kex->load_host_private_key == NULL)
1050 return SSH_ERR_INVALID_ARGUMENT;
1051 *pubp = kex->load_host_public_key(kex->hostkey_type,
1052 kex->hostkey_nid, ssh);
1053 *prvp = kex->load_host_private_key(kex->hostkey_type,
1054 kex->hostkey_nid, ssh);
1055 if (*pubp == NULL)
1056 return SSH_ERR_NO_HOSTKEY_LOADED;
1057 return 0;
1058}
Darren Tuckere14e0052004-05-13 16:30:44 +10001059
djm@openbsd.orgb1b2ff42019-01-21 10:07:22 +00001060int
1061kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
1062{
1063 struct kex *kex = ssh->kex;
1064
1065 if (kex->verify_host_key == NULL)
1066 return SSH_ERR_INVALID_ARGUMENT;
1067 if (server_host_key->type != kex->hostkey_type ||
1068 (kex->hostkey_type == KEY_ECDSA &&
1069 server_host_key->ecdsa_nid != kex->hostkey_nid))
1070 return SSH_ERR_KEY_TYPE_MISMATCH;
1071 if (kex->verify_host_key(server_host_key, ssh) == -1)
1072 return SSH_ERR_SIGNATURE_INVALID;
1073 return 0;
1074}
1075
Damien Millereb8b60e2010-08-31 22:41:14 +10001076#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001077void
djm@openbsd.orgdfd59162019-01-21 10:20:12 +00001078dump_digest(const char *msg, const u_char *digest, int len)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001079{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001080 fprintf(stderr, "%s\n", msg);
markus@openbsd.org57d10cb2015-01-19 20:16:15 +00001081 sshbuf_dump_data(digest, len, stderr);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +00001082}
1083#endif
djm@openbsd.org0a843d92018-12-27 03:25:24 +00001084
1085/*
1086 * Send a plaintext error message to the peer, suffixed by \r\n.
1087 * Only used during banner exchange, and there only for the server.
1088 */
1089static void
1090send_error(struct ssh *ssh, char *msg)
1091{
1092 char *crnl = "\r\n";
1093
1094 if (!ssh->kex->server)
1095 return;
1096
1097 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1098 msg, strlen(msg)) != strlen(msg) ||
1099 atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1100 crnl, strlen(crnl)) != strlen(crnl))
1101 error("%s: write: %.100s", __func__, strerror(errno));
1102}
1103
1104/*
1105 * Sends our identification string and waits for the peer's. Will block for
1106 * up to timeout_ms (or indefinitely if timeout_ms <= 0).
1107 * Returns on 0 success or a ssherr.h code on failure.
1108 */
1109int
1110kex_exchange_identification(struct ssh *ssh, int timeout_ms,
1111 const char *version_addendum)
1112{
1113 int remote_major, remote_minor, mismatch;
1114 size_t len, i, n;
1115 int r, expect_nl;
1116 u_char c;
1117 struct sshbuf *our_version = ssh->kex->server ?
1118 ssh->kex->server_version : ssh->kex->client_version;
1119 struct sshbuf *peer_version = ssh->kex->server ?
1120 ssh->kex->client_version : ssh->kex->server_version;
1121 char *our_version_string = NULL, *peer_version_string = NULL;
1122 char *cp, *remote_version = NULL;
1123
1124 /* Prepare and send our banner */
1125 sshbuf_reset(our_version);
1126 if (version_addendum != NULL && *version_addendum == '\0')
1127 version_addendum = NULL;
1128 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
1129 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
1130 version_addendum == NULL ? "" : " ",
1131 version_addendum == NULL ? "" : version_addendum)) != 0) {
1132 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
1133 goto out;
1134 }
1135
1136 if (atomicio(vwrite, ssh_packet_get_connection_out(ssh),
1137 sshbuf_mutable_ptr(our_version),
1138 sshbuf_len(our_version)) != sshbuf_len(our_version)) {
1139 error("%s: write: %.100s", __func__, strerror(errno));
1140 r = SSH_ERR_SYSTEM_ERROR;
1141 goto out;
1142 }
1143 if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
1144 error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
1145 goto out;
1146 }
1147 our_version_string = sshbuf_dup_string(our_version);
1148 if (our_version_string == NULL) {
1149 error("%s: sshbuf_dup_string failed", __func__);
1150 r = SSH_ERR_ALLOC_FAIL;
1151 goto out;
1152 }
1153 debug("Local version string %.100s", our_version_string);
1154
1155 /* Read other side's version identification. */
1156 for (n = 0; ; n++) {
1157 if (n >= SSH_MAX_PRE_BANNER_LINES) {
1158 send_error(ssh, "No SSH identification string "
1159 "received.");
1160 error("%s: No SSH version received in first %u lines "
1161 "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
1162 r = SSH_ERR_INVALID_FORMAT;
1163 goto out;
1164 }
1165 sshbuf_reset(peer_version);
1166 expect_nl = 0;
1167 for (i = 0; ; i++) {
1168 if (timeout_ms > 0) {
1169 r = waitrfd(ssh_packet_get_connection_in(ssh),
1170 &timeout_ms);
1171 if (r == -1 && errno == ETIMEDOUT) {
1172 send_error(ssh, "Timed out waiting "
1173 "for SSH identification string.");
1174 error("Connection timed out during "
1175 "banner exchange");
1176 r = SSH_ERR_CONN_TIMEOUT;
1177 goto out;
1178 } else if (r == -1) {
1179 error("%s: %s",
1180 __func__, strerror(errno));
1181 r = SSH_ERR_SYSTEM_ERROR;
1182 goto out;
1183 }
1184 }
1185
1186 len = atomicio(read, ssh_packet_get_connection_in(ssh),
1187 &c, 1);
1188 if (len != 1 && errno == EPIPE) {
1189 error("%s: Connection closed by remote host",
1190 __func__);
1191 r = SSH_ERR_CONN_CLOSED;
1192 goto out;
1193 } else if (len != 1) {
1194 error("%s: read: %.100s",
1195 __func__, strerror(errno));
1196 r = SSH_ERR_SYSTEM_ERROR;
1197 goto out;
1198 }
1199 if (c == '\r') {
1200 expect_nl = 1;
1201 continue;
1202 }
1203 if (c == '\n')
1204 break;
1205 if (c == '\0' || expect_nl) {
1206 error("%s: banner line contains invalid "
1207 "characters", __func__);
1208 goto invalid;
1209 }
1210 if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
1211 error("%s: sshbuf_put: %s",
1212 __func__, ssh_err(r));
1213 goto out;
1214 }
1215 if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
1216 error("%s: banner line too long", __func__);
1217 goto invalid;
1218 }
1219 }
1220 /* Is this an actual protocol banner? */
1221 if (sshbuf_len(peer_version) > 4 &&
1222 memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0)
1223 break;
1224 /* If not, then just log the line and continue */
1225 if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
1226 error("%s: sshbuf_dup_string failed", __func__);
1227 r = SSH_ERR_ALLOC_FAIL;
1228 goto out;
1229 }
1230 /* Do not accept lines before the SSH ident from a client */
1231 if (ssh->kex->server) {
1232 error("%s: client sent invalid protocol identifier "
1233 "\"%.256s\"", __func__, cp);
1234 free(cp);
1235 goto invalid;
1236 }
1237 debug("%s: banner line %zu: %s", __func__, n, cp);
1238 free(cp);
1239 }
1240 peer_version_string = sshbuf_dup_string(peer_version);
1241 if (peer_version_string == NULL)
1242 error("%s: sshbuf_dup_string failed", __func__);
1243 /* XXX must be same size for sscanf */
1244 if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
1245 error("%s: calloc failed", __func__);
1246 r = SSH_ERR_ALLOC_FAIL;
1247 goto out;
1248 }
1249
1250 /*
1251 * Check that the versions match. In future this might accept
1252 * several versions and set appropriate flags to handle them.
1253 */
1254 if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n",
1255 &remote_major, &remote_minor, remote_version) != 3) {
1256 error("Bad remote protocol version identification: '%.100s'",
1257 peer_version_string);
1258 invalid:
1259 send_error(ssh, "Invalid SSH identification string.");
1260 r = SSH_ERR_INVALID_FORMAT;
1261 goto out;
1262 }
1263 debug("Remote protocol version %d.%d, remote software version %.100s",
1264 remote_major, remote_minor, remote_version);
1265 ssh->compat = compat_datafellows(remote_version);
1266
1267 mismatch = 0;
1268 switch (remote_major) {
1269 case 2:
1270 break;
1271 case 1:
1272 if (remote_minor != 99)
1273 mismatch = 1;
1274 break;
1275 default:
1276 mismatch = 1;
1277 break;
1278 }
1279 if (mismatch) {
1280 error("Protocol major versions differ: %d vs. %d",
1281 PROTOCOL_MAJOR_2, remote_major);
1282 send_error(ssh, "Protocol major versions differ.");
1283 r = SSH_ERR_NO_PROTOCOL_VERSION;
1284 goto out;
1285 }
1286
1287 if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) {
1288 logit("probed from %s port %d with %s. Don't panic.",
1289 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1290 peer_version_string);
1291 r = SSH_ERR_CONN_CLOSED; /* XXX */
1292 goto out;
1293 }
1294 if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) {
1295 logit("scanned from %s port %d with %s. Don't panic.",
1296 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1297 peer_version_string);
1298 r = SSH_ERR_CONN_CLOSED; /* XXX */
1299 goto out;
1300 }
1301 if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1302 logit("Remote version \"%.100s\" uses unsafe RSA signature "
1303 "scheme; disabling use of RSA keys", remote_version);
1304 }
1305 /* success */
1306 r = 0;
1307 out:
1308 free(our_version_string);
1309 free(peer_version_string);
1310 free(remote_version);
1311 return r;
1312}
1313