1*0fdf8faeSEd Maste /* $OpenBSD: kex.c,v 1.186 2024/05/17 00:30:23 djm 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" 604d3fc8b0SEd Maste #include "myproposal.h" 61bc5531deSDag-Erling Smørgrav 62bc5531deSDag-Erling Smørgrav #include "ssherr.h" 63bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 64f7167e0eSDag-Erling Smørgrav #include "digest.h" 654d3fc8b0SEd Maste #include "xmalloc.h" 66a04a10f8SKris Kennaway 67ae1f160dSDag-Erling Smørgrav /* prototype */ 6892f58c69SGordon Tetlow static int kex_choose_conf(struct ssh *, uint32_t seq); 694f52dfbbSDag-Erling Smørgrav static int kex_input_newkeys(int, u_int32_t, struct ssh *); 701e8db6e2SBrian Feldman 711323ec57SEd Maste static const char * const proposal_names[PROPOSAL_MAX] = { 72acc1a9efSDag-Erling Smørgrav "KEX algorithms", 73acc1a9efSDag-Erling Smørgrav "host key algorithms", 74acc1a9efSDag-Erling Smørgrav "ciphers ctos", 75acc1a9efSDag-Erling Smørgrav "ciphers stoc", 76acc1a9efSDag-Erling Smørgrav "MACs ctos", 77acc1a9efSDag-Erling Smørgrav "MACs stoc", 78acc1a9efSDag-Erling Smørgrav "compression ctos", 79acc1a9efSDag-Erling Smørgrav "compression stoc", 80acc1a9efSDag-Erling Smørgrav "languages ctos", 81acc1a9efSDag-Erling Smørgrav "languages stoc", 82acc1a9efSDag-Erling Smørgrav }; 83acc1a9efSDag-Erling Smørgrav 844d3fc8b0SEd Maste /* 854d3fc8b0SEd Maste * Fill out a proposal array with dynamically allocated values, which may 864d3fc8b0SEd Maste * be modified as required for compatibility reasons. 874d3fc8b0SEd Maste * Any of the options may be NULL, in which case the default is used. 884d3fc8b0SEd Maste * Array contents must be freed by calling kex_proposal_free_entries. 894d3fc8b0SEd Maste */ 904d3fc8b0SEd Maste void 914d3fc8b0SEd Maste kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX], 924d3fc8b0SEd Maste const char *kexalgos, const char *ciphers, const char *macs, 934d3fc8b0SEd Maste const char *comp, const char *hkalgs) 944d3fc8b0SEd Maste { 954d3fc8b0SEd Maste const char *defpropserver[PROPOSAL_MAX] = { KEX_SERVER }; 964d3fc8b0SEd Maste const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT }; 974d3fc8b0SEd Maste const char **defprop = ssh->kex->server ? defpropserver : defpropclient; 984d3fc8b0SEd Maste u_int i; 9992f58c69SGordon Tetlow char *cp; 1004d3fc8b0SEd Maste 1014d3fc8b0SEd Maste if (prop == NULL) 1024d3fc8b0SEd Maste fatal_f("proposal missing"); 1034d3fc8b0SEd Maste 10492f58c69SGordon Tetlow /* Append EXT_INFO signalling to KexAlgorithms */ 10592f58c69SGordon Tetlow if (kexalgos == NULL) 10692f58c69SGordon Tetlow kexalgos = defprop[PROPOSAL_KEX_ALGS]; 10792f58c69SGordon Tetlow if ((cp = kex_names_cat(kexalgos, ssh->kex->server ? 108069ac184SEd Maste "ext-info-s,kex-strict-s-v00@openssh.com" : 10992f58c69SGordon Tetlow "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) 11092f58c69SGordon Tetlow fatal_f("kex_names_cat"); 11192f58c69SGordon Tetlow 1124d3fc8b0SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) { 1134d3fc8b0SEd Maste switch(i) { 1144d3fc8b0SEd Maste case PROPOSAL_KEX_ALGS: 11592f58c69SGordon Tetlow prop[i] = compat_kex_proposal(ssh, cp); 1164d3fc8b0SEd Maste break; 1174d3fc8b0SEd Maste case PROPOSAL_ENC_ALGS_CTOS: 1184d3fc8b0SEd Maste case PROPOSAL_ENC_ALGS_STOC: 1194d3fc8b0SEd Maste prop[i] = xstrdup(ciphers ? ciphers : defprop[i]); 1204d3fc8b0SEd Maste break; 1214d3fc8b0SEd Maste case PROPOSAL_MAC_ALGS_CTOS: 1224d3fc8b0SEd Maste case PROPOSAL_MAC_ALGS_STOC: 1234d3fc8b0SEd Maste prop[i] = xstrdup(macs ? macs : defprop[i]); 1244d3fc8b0SEd Maste break; 1254d3fc8b0SEd Maste case PROPOSAL_COMP_ALGS_CTOS: 1264d3fc8b0SEd Maste case PROPOSAL_COMP_ALGS_STOC: 1274d3fc8b0SEd Maste prop[i] = xstrdup(comp ? comp : defprop[i]); 1284d3fc8b0SEd Maste break; 1294d3fc8b0SEd Maste case PROPOSAL_SERVER_HOST_KEY_ALGS: 1304d3fc8b0SEd Maste prop[i] = xstrdup(hkalgs ? hkalgs : defprop[i]); 1314d3fc8b0SEd Maste break; 1324d3fc8b0SEd Maste default: 1334d3fc8b0SEd Maste prop[i] = xstrdup(defprop[i]); 1344d3fc8b0SEd Maste } 1354d3fc8b0SEd Maste } 13692f58c69SGordon Tetlow free(cp); 1374d3fc8b0SEd Maste } 1384d3fc8b0SEd Maste 1394d3fc8b0SEd Maste void 1404d3fc8b0SEd Maste kex_proposal_free_entries(char *prop[PROPOSAL_MAX]) 1414d3fc8b0SEd Maste { 1424d3fc8b0SEd Maste u_int i; 1434d3fc8b0SEd Maste 1444d3fc8b0SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) 1454d3fc8b0SEd Maste free(prop[i]); 1464d3fc8b0SEd Maste } 1474d3fc8b0SEd Maste 1481765946bSDag-Erling Smørgrav /* put algorithm proposal into buffer */ 149bc5531deSDag-Erling Smørgrav int 150bc5531deSDag-Erling Smørgrav kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 151a04a10f8SKris Kennaway { 152043840dfSDag-Erling Smørgrav u_int i; 153bc5531deSDag-Erling Smørgrav int r; 1541e8db6e2SBrian Feldman 155bc5531deSDag-Erling Smørgrav sshbuf_reset(b); 156bc5531deSDag-Erling Smørgrav 157545d5ecaSDag-Erling Smørgrav /* 158545d5ecaSDag-Erling Smørgrav * add a dummy cookie, the cookie will be overwritten by 159545d5ecaSDag-Erling Smørgrav * kex_send_kexinit(), each time a kexinit is set 160545d5ecaSDag-Erling Smørgrav */ 161bc5531deSDag-Erling Smørgrav for (i = 0; i < KEX_COOKIE_LEN; i++) { 162bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0) 163bc5531deSDag-Erling Smørgrav return r; 164bc5531deSDag-Erling Smørgrav } 165bc5531deSDag-Erling Smørgrav for (i = 0; i < PROPOSAL_MAX; i++) { 166bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) 167bc5531deSDag-Erling Smørgrav return r; 168bc5531deSDag-Erling Smørgrav } 169bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ 170bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ 171bc5531deSDag-Erling Smørgrav return r; 172bc5531deSDag-Erling Smørgrav return 0; 173a04a10f8SKris Kennaway } 174a04a10f8SKris Kennaway 1751e8db6e2SBrian Feldman /* parse buffer and return algorithm proposal */ 176bc5531deSDag-Erling Smørgrav int 177bc5531deSDag-Erling Smørgrav kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) 1782632b0c8SKris Kennaway { 179bc5531deSDag-Erling Smørgrav struct sshbuf *b = NULL; 180bc5531deSDag-Erling Smørgrav u_char v; 181d4af9e69SDag-Erling Smørgrav u_int i; 182bc5531deSDag-Erling Smørgrav char **proposal = NULL; 183bc5531deSDag-Erling Smørgrav int r; 1842632b0c8SKris Kennaway 185bc5531deSDag-Erling Smørgrav *propp = NULL; 186bc5531deSDag-Erling Smørgrav if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) 187bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 188bc5531deSDag-Erling Smørgrav if ((b = sshbuf_fromb(raw)) == NULL) { 189bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 190bc5531deSDag-Erling Smørgrav goto out; 191bc5531deSDag-Erling Smørgrav } 19219261079SEd Maste if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */ 19319261079SEd Maste error_fr(r, "consume cookie"); 194bc5531deSDag-Erling Smørgrav goto out; 19519261079SEd Maste } 1962632b0c8SKris Kennaway /* extract kex init proposal strings */ 1972632b0c8SKris Kennaway for (i = 0; i < PROPOSAL_MAX; i++) { 19819261079SEd Maste if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) { 19919261079SEd Maste error_fr(r, "parse proposal %u", i); 200bc5531deSDag-Erling Smørgrav goto out; 20119261079SEd Maste } 202acc1a9efSDag-Erling Smørgrav debug2("%s: %s", proposal_names[i], proposal[i]); 2032632b0c8SKris Kennaway } 2041e8db6e2SBrian Feldman /* first kex follows / reserved */ 205fc1ba28aSDag-Erling Smørgrav if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ 20619261079SEd Maste (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */ 20719261079SEd Maste error_fr(r, "parse"); 208bc5531deSDag-Erling Smørgrav goto out; 20919261079SEd Maste } 210d0c8c0bcSDag-Erling Smørgrav if (first_kex_follows != NULL) 211fc1ba28aSDag-Erling Smørgrav *first_kex_follows = v; 212fc1ba28aSDag-Erling Smørgrav debug2("first_kex_follows %d ", v); 213fc1ba28aSDag-Erling Smørgrav debug2("reserved %u ", i); 214bc5531deSDag-Erling Smørgrav r = 0; 215bc5531deSDag-Erling Smørgrav *propp = proposal; 216bc5531deSDag-Erling Smørgrav out: 217bc5531deSDag-Erling Smørgrav if (r != 0 && proposal != NULL) 218bc5531deSDag-Erling Smørgrav kex_prop_free(proposal); 219bc5531deSDag-Erling Smørgrav sshbuf_free(b); 220bc5531deSDag-Erling Smørgrav return r; 2215b9b2fafSBrian Feldman } 2225b9b2fafSBrian Feldman 223bc5531deSDag-Erling Smørgrav void 2241e8db6e2SBrian Feldman kex_prop_free(char **proposal) 225a04a10f8SKris Kennaway { 226043840dfSDag-Erling Smørgrav u_int i; 2271e8db6e2SBrian Feldman 228557f75e5SDag-Erling Smørgrav if (proposal == NULL) 229557f75e5SDag-Erling Smørgrav return; 2301e8db6e2SBrian Feldman for (i = 0; i < PROPOSAL_MAX; i++) 231e4a9863fSDag-Erling Smørgrav free(proposal[i]); 232e4a9863fSDag-Erling Smørgrav free(proposal); 233a04a10f8SKris Kennaway } 234a04a10f8SKris Kennaway 23519261079SEd Maste int 2364f52dfbbSDag-Erling Smørgrav kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh) 237a04a10f8SKris Kennaway { 238acc1a9efSDag-Erling Smørgrav int r; 239acc1a9efSDag-Erling Smørgrav 24092f58c69SGordon Tetlow /* If in strict mode, any unexpected message is an error */ 24192f58c69SGordon Tetlow if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) { 24292f58c69SGordon Tetlow ssh_packet_disconnect(ssh, "strict KEX violation: " 24392f58c69SGordon Tetlow "unexpected packet type %u (seqnr %u)", type, seq); 24492f58c69SGordon Tetlow } 24592f58c69SGordon Tetlow error_f("type %u seq %u", type, seq); 246acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || 247acc1a9efSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, seq)) != 0 || 248acc1a9efSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 249acc1a9efSDag-Erling Smørgrav return r; 250bc5531deSDag-Erling Smørgrav return 0; 251a04a10f8SKris Kennaway } 252a04a10f8SKris Kennaway 253ae1f160dSDag-Erling Smørgrav static void 254bc5531deSDag-Erling Smørgrav kex_reset_dispatch(struct ssh *ssh) 2555b9b2fafSBrian Feldman { 256bc5531deSDag-Erling Smørgrav ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, 257ae1f160dSDag-Erling Smørgrav SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); 2585b9b2fafSBrian Feldman } 2595b9b2fafSBrian Feldman 260069ac184SEd Maste void 261069ac184SEd Maste kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs) 262069ac184SEd Maste { 263069ac184SEd Maste char *alg, *oalgs, *algs, *sigalgs; 264069ac184SEd Maste const char *sigalg; 265069ac184SEd Maste 266069ac184SEd Maste /* 267069ac184SEd Maste * NB. allowed algorithms may contain certificate algorithms that 268069ac184SEd Maste * map to a specific plain signature type, e.g. 269069ac184SEd Maste * rsa-sha2-512-cert-v01@openssh.com => rsa-sha2-512 270069ac184SEd Maste * We need to be careful here to match these, retain the mapping 271069ac184SEd Maste * and only add each signature algorithm once. 272069ac184SEd Maste */ 273069ac184SEd Maste if ((sigalgs = sshkey_alg_list(0, 1, 1, ',')) == NULL) 274069ac184SEd Maste fatal_f("sshkey_alg_list failed"); 275069ac184SEd Maste oalgs = algs = xstrdup(allowed_algs); 276069ac184SEd Maste free(ssh->kex->server_sig_algs); 277069ac184SEd Maste ssh->kex->server_sig_algs = NULL; 278069ac184SEd Maste for ((alg = strsep(&algs, ",")); alg != NULL && *alg != '\0'; 279069ac184SEd Maste (alg = strsep(&algs, ","))) { 280069ac184SEd Maste if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL) 281069ac184SEd Maste continue; 282*0fdf8faeSEd Maste if (!kex_has_any_alg(sigalg, sigalgs)) 283069ac184SEd Maste continue; 284069ac184SEd Maste /* Don't add an algorithm twice. */ 285069ac184SEd Maste if (ssh->kex->server_sig_algs != NULL && 286*0fdf8faeSEd Maste kex_has_any_alg(sigalg, ssh->kex->server_sig_algs)) 287069ac184SEd Maste continue; 288069ac184SEd Maste xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg); 289069ac184SEd Maste } 290069ac184SEd Maste free(oalgs); 291069ac184SEd Maste free(sigalgs); 292069ac184SEd Maste if (ssh->kex->server_sig_algs == NULL) 293069ac184SEd Maste ssh->kex->server_sig_algs = xstrdup(""); 294069ac184SEd Maste } 295069ac184SEd Maste 296acc1a9efSDag-Erling Smørgrav static int 297069ac184SEd Maste kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m) 298acc1a9efSDag-Erling Smørgrav { 299acc1a9efSDag-Erling Smørgrav int r; 300acc1a9efSDag-Erling Smørgrav 301069ac184SEd Maste if (ssh->kex->server_sig_algs == NULL && 302069ac184SEd Maste (ssh->kex->server_sig_algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) 303ca86bcf2SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 304069ac184SEd Maste if ((r = sshbuf_put_u32(m, 3)) != 0 || 305069ac184SEd Maste (r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 || 306069ac184SEd Maste (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 || 307069ac184SEd Maste (r = sshbuf_put_cstring(m, 3081323ec57SEd Maste "publickey-hostbound@openssh.com")) != 0 || 309069ac184SEd Maste (r = sshbuf_put_cstring(m, "0")) != 0 || 310069ac184SEd Maste (r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 || 311069ac184SEd Maste (r = sshbuf_put_cstring(m, "0")) != 0) { 312069ac184SEd Maste error_fr(r, "compose"); 313069ac184SEd Maste return r; 314069ac184SEd Maste } 315069ac184SEd Maste return 0; 316069ac184SEd Maste } 317069ac184SEd Maste 318069ac184SEd Maste static int 319069ac184SEd Maste kex_compose_ext_info_client(struct ssh *ssh, struct sshbuf *m) 320069ac184SEd Maste { 321069ac184SEd Maste int r; 322069ac184SEd Maste 323069ac184SEd Maste if ((r = sshbuf_put_u32(m, 1)) != 0 || 324069ac184SEd Maste (r = sshbuf_put_cstring(m, "ext-info-in-auth@openssh.com")) != 0 || 325069ac184SEd Maste (r = sshbuf_put_cstring(m, "0")) != 0) { 32619261079SEd Maste error_fr(r, "compose"); 327ca86bcf2SDag-Erling Smørgrav goto out; 32819261079SEd Maste } 329ca86bcf2SDag-Erling Smørgrav /* success */ 330ca86bcf2SDag-Erling Smørgrav r = 0; 331ca86bcf2SDag-Erling Smørgrav out: 332acc1a9efSDag-Erling Smørgrav return r; 333acc1a9efSDag-Erling Smørgrav } 334acc1a9efSDag-Erling Smørgrav 335069ac184SEd Maste static int 336069ac184SEd Maste kex_maybe_send_ext_info(struct ssh *ssh) 337069ac184SEd Maste { 338069ac184SEd Maste int r; 339069ac184SEd Maste struct sshbuf *m = NULL; 340069ac184SEd Maste 341069ac184SEd Maste if ((ssh->kex->flags & KEX_INITIAL) == 0) 342069ac184SEd Maste return 0; 343069ac184SEd Maste if (!ssh->kex->ext_info_c && !ssh->kex->ext_info_s) 344069ac184SEd Maste return 0; 345069ac184SEd Maste 346069ac184SEd Maste /* Compose EXT_INFO packet. */ 347069ac184SEd Maste if ((m = sshbuf_new()) == NULL) 348069ac184SEd Maste fatal_f("sshbuf_new failed"); 349069ac184SEd Maste if (ssh->kex->ext_info_c && 350069ac184SEd Maste (r = kex_compose_ext_info_server(ssh, m)) != 0) 351069ac184SEd Maste goto fail; 352069ac184SEd Maste if (ssh->kex->ext_info_s && 353069ac184SEd Maste (r = kex_compose_ext_info_client(ssh, m)) != 0) 354069ac184SEd Maste goto fail; 355069ac184SEd Maste 356069ac184SEd Maste /* Send the actual KEX_INFO packet */ 357069ac184SEd Maste debug("Sending SSH2_MSG_EXT_INFO"); 358069ac184SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || 359069ac184SEd Maste (r = sshpkt_putb(ssh, m)) != 0 || 360069ac184SEd Maste (r = sshpkt_send(ssh)) != 0) { 361069ac184SEd Maste error_f("send EXT_INFO"); 362069ac184SEd Maste goto fail; 363069ac184SEd Maste } 364069ac184SEd Maste 365069ac184SEd Maste r = 0; 366069ac184SEd Maste 367069ac184SEd Maste fail: 368069ac184SEd Maste sshbuf_free(m); 369069ac184SEd Maste return r; 370069ac184SEd Maste } 371069ac184SEd Maste 372069ac184SEd Maste int 373069ac184SEd Maste kex_server_update_ext_info(struct ssh *ssh) 374069ac184SEd Maste { 375069ac184SEd Maste int r; 376069ac184SEd Maste 377069ac184SEd Maste if ((ssh->kex->flags & KEX_HAS_EXT_INFO_IN_AUTH) == 0) 378069ac184SEd Maste return 0; 379069ac184SEd Maste 380069ac184SEd Maste debug_f("Sending SSH2_MSG_EXT_INFO"); 381069ac184SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || 382069ac184SEd Maste (r = sshpkt_put_u32(ssh, 1)) != 0 || 383069ac184SEd Maste (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || 384069ac184SEd Maste (r = sshpkt_put_cstring(ssh, ssh->kex->server_sig_algs)) != 0 || 385069ac184SEd Maste (r = sshpkt_send(ssh)) != 0) { 386069ac184SEd Maste error_f("send EXT_INFO"); 387069ac184SEd Maste return r; 388069ac184SEd Maste } 389069ac184SEd Maste return 0; 390069ac184SEd Maste } 391069ac184SEd Maste 392bc5531deSDag-Erling Smørgrav int 393bc5531deSDag-Erling Smørgrav kex_send_newkeys(struct ssh *ssh) 394a04a10f8SKris Kennaway { 395bc5531deSDag-Erling Smørgrav int r; 396a04a10f8SKris Kennaway 397bc5531deSDag-Erling Smørgrav kex_reset_dispatch(ssh); 398bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || 399bc5531deSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 400bc5531deSDag-Erling Smørgrav return r; 4011e8db6e2SBrian Feldman debug("SSH2_MSG_NEWKEYS sent"); 402bc5531deSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); 403069ac184SEd Maste if ((r = kex_maybe_send_ext_info(ssh)) != 0) 404acc1a9efSDag-Erling Smørgrav return r; 40519261079SEd Maste debug("expecting SSH2_MSG_NEWKEYS"); 406bc5531deSDag-Erling Smørgrav return 0; 407bc5531deSDag-Erling Smørgrav } 4081e8db6e2SBrian Feldman 409edf85781SEd Maste /* Check whether an ext_info value contains the expected version string */ 410edf85781SEd Maste static int 411edf85781SEd Maste kex_ext_info_check_ver(struct kex *kex, const char *name, 412edf85781SEd Maste const u_char *val, size_t len, const char *want_ver, u_int flag) 413edf85781SEd Maste { 414edf85781SEd Maste if (memchr(val, '\0', len) != NULL) { 415edf85781SEd Maste error("SSH2_MSG_EXT_INFO: %s value contains nul byte", name); 416edf85781SEd Maste return SSH_ERR_INVALID_FORMAT; 417edf85781SEd Maste } 418edf85781SEd Maste debug_f("%s=<%s>", name, val); 419edf85781SEd Maste if (strcmp(val, want_ver) == 0) 420edf85781SEd Maste kex->flags |= flag; 421edf85781SEd Maste else 422edf85781SEd Maste debug_f("unsupported version of %s extension", name); 423edf85781SEd Maste return 0; 424edf85781SEd Maste } 425edf85781SEd Maste 426069ac184SEd Maste static int 427069ac184SEd Maste kex_ext_info_client_parse(struct ssh *ssh, const char *name, 428069ac184SEd Maste const u_char *value, size_t vlen) 429069ac184SEd Maste { 430069ac184SEd Maste int r; 431069ac184SEd Maste 432069ac184SEd Maste /* NB. some messages are only accepted in the initial EXT_INFO */ 433069ac184SEd Maste if (strcmp(name, "server-sig-algs") == 0) { 434069ac184SEd Maste /* Ensure no \0 lurking in value */ 435069ac184SEd Maste if (memchr(value, '\0', vlen) != NULL) { 436069ac184SEd Maste error_f("nul byte in %s", name); 437069ac184SEd Maste return SSH_ERR_INVALID_FORMAT; 438069ac184SEd Maste } 439069ac184SEd Maste debug_f("%s=<%s>", name, value); 440069ac184SEd Maste free(ssh->kex->server_sig_algs); 441069ac184SEd Maste ssh->kex->server_sig_algs = xstrdup((const char *)value); 442069ac184SEd Maste } else if (ssh->kex->ext_info_received == 1 && 443069ac184SEd Maste strcmp(name, "publickey-hostbound@openssh.com") == 0) { 444069ac184SEd Maste if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, 445069ac184SEd Maste "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) { 446069ac184SEd Maste return r; 447069ac184SEd Maste } 448069ac184SEd Maste } else if (ssh->kex->ext_info_received == 1 && 449069ac184SEd Maste strcmp(name, "ping@openssh.com") == 0) { 450069ac184SEd Maste if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, 451069ac184SEd Maste "0", KEX_HAS_PING)) != 0) { 452069ac184SEd Maste return r; 453069ac184SEd Maste } 454069ac184SEd Maste } else 455069ac184SEd Maste debug_f("%s (unrecognised)", name); 456069ac184SEd Maste 457069ac184SEd Maste return 0; 458069ac184SEd Maste } 459069ac184SEd Maste 460069ac184SEd Maste static int 461069ac184SEd Maste kex_ext_info_server_parse(struct ssh *ssh, const char *name, 462069ac184SEd Maste const u_char *value, size_t vlen) 463069ac184SEd Maste { 464069ac184SEd Maste int r; 465069ac184SEd Maste 466069ac184SEd Maste if (strcmp(name, "ext-info-in-auth@openssh.com") == 0) { 467069ac184SEd Maste if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, 468069ac184SEd Maste "0", KEX_HAS_EXT_INFO_IN_AUTH)) != 0) { 469069ac184SEd Maste return r; 470069ac184SEd Maste } 471069ac184SEd Maste } else 472069ac184SEd Maste debug_f("%s (unrecognised)", name); 473069ac184SEd Maste return 0; 474069ac184SEd Maste } 475069ac184SEd Maste 476acc1a9efSDag-Erling Smørgrav int 4774f52dfbbSDag-Erling Smørgrav kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) 478acc1a9efSDag-Erling Smørgrav { 479acc1a9efSDag-Erling Smørgrav struct kex *kex = ssh->kex; 480069ac184SEd Maste const int max_ext_info = kex->server ? 1 : 2; 481acc1a9efSDag-Erling Smørgrav u_int32_t i, ninfo; 482190cef3dSDag-Erling Smørgrav char *name; 4834f52dfbbSDag-Erling Smørgrav u_char *val; 4844f52dfbbSDag-Erling Smørgrav size_t vlen; 485acc1a9efSDag-Erling Smørgrav int r; 486acc1a9efSDag-Erling Smørgrav 487acc1a9efSDag-Erling Smørgrav debug("SSH2_MSG_EXT_INFO received"); 488069ac184SEd Maste if (++kex->ext_info_received > max_ext_info) { 489069ac184SEd Maste error("too many SSH2_MSG_EXT_INFO messages sent by peer"); 490069ac184SEd Maste return dispatch_protocol_error(type, seq, ssh); 491069ac184SEd Maste } 492acc1a9efSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); 493acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) 494acc1a9efSDag-Erling Smørgrav return r; 4954d3fc8b0SEd Maste if (ninfo >= 1024) { 4964d3fc8b0SEd Maste error("SSH2_MSG_EXT_INFO with too many entries, expected " 4974d3fc8b0SEd Maste "<=1024, received %u", ninfo); 49892f58c69SGordon Tetlow return dispatch_protocol_error(type, seq, ssh); 4994d3fc8b0SEd Maste } 500acc1a9efSDag-Erling Smørgrav for (i = 0; i < ninfo; i++) { 501acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) 502acc1a9efSDag-Erling Smørgrav return r; 5034f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) { 504acc1a9efSDag-Erling Smørgrav free(name); 505acc1a9efSDag-Erling Smørgrav return r; 506acc1a9efSDag-Erling Smørgrav } 507069ac184SEd Maste debug3_f("extension %s", name); 508069ac184SEd Maste if (kex->server) { 509069ac184SEd Maste if ((r = kex_ext_info_server_parse(ssh, name, 510069ac184SEd Maste val, vlen)) != 0) 511069ac184SEd Maste return r; 512069ac184SEd Maste } else { 513069ac184SEd Maste if ((r = kex_ext_info_client_parse(ssh, name, 514069ac184SEd Maste val, vlen)) != 0) 515edf85781SEd Maste return r; 5161323ec57SEd Maste } 517acc1a9efSDag-Erling Smørgrav free(name); 518acc1a9efSDag-Erling Smørgrav free(val); 519acc1a9efSDag-Erling Smørgrav } 520acc1a9efSDag-Erling Smørgrav return sshpkt_get_end(ssh); 521acc1a9efSDag-Erling Smørgrav } 522acc1a9efSDag-Erling Smørgrav 523bc5531deSDag-Erling Smørgrav static int 5244f52dfbbSDag-Erling Smørgrav kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) 525bc5531deSDag-Erling Smørgrav { 526bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 527a91a2465SEd Maste int r, initial = (kex->flags & KEX_INITIAL) != 0; 528a91a2465SEd Maste char *cp, **prop; 529bc5531deSDag-Erling Smørgrav 530bc5531deSDag-Erling Smørgrav debug("SSH2_MSG_NEWKEYS received"); 531a91a2465SEd Maste if (kex->ext_info_c && initial) 532069ac184SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info); 533bc5531deSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); 534d93a896eSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 535bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_end(ssh)) != 0) 536bc5531deSDag-Erling Smørgrav return r; 537ca86bcf2SDag-Erling Smørgrav if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) 538ca86bcf2SDag-Erling Smørgrav return r; 539a91a2465SEd Maste if (initial) { 540a91a2465SEd Maste /* Remove initial KEX signalling from proposal for rekeying */ 541a91a2465SEd Maste if ((r = kex_buf2prop(kex->my, NULL, &prop)) != 0) 542a91a2465SEd Maste return r; 543a91a2465SEd Maste if ((cp = match_filter_denylist(prop[PROPOSAL_KEX_ALGS], 544a91a2465SEd Maste kex->server ? 545a91a2465SEd Maste "ext-info-s,kex-strict-s-v00@openssh.com" : 546a91a2465SEd Maste "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) { 547a91a2465SEd Maste error_f("match_filter_denylist failed"); 548a91a2465SEd Maste goto fail; 549a91a2465SEd Maste } 550a91a2465SEd Maste free(prop[PROPOSAL_KEX_ALGS]); 551a91a2465SEd Maste prop[PROPOSAL_KEX_ALGS] = cp; 552a91a2465SEd Maste if ((r = kex_prop2buf(ssh->kex->my, prop)) != 0) { 553a91a2465SEd Maste error_f("kex_prop2buf failed"); 554a91a2465SEd Maste fail: 555a91a2465SEd Maste kex_proposal_free_entries(prop); 556a91a2465SEd Maste free(prop); 557a91a2465SEd Maste return SSH_ERR_INTERNAL_ERROR; 558a91a2465SEd Maste } 559a91a2465SEd Maste kex_proposal_free_entries(prop); 560a91a2465SEd Maste free(prop); 561a91a2465SEd Maste } 5621e8db6e2SBrian Feldman kex->done = 1; 56319261079SEd Maste kex->flags &= ~KEX_INITIAL; 564bc5531deSDag-Erling Smørgrav sshbuf_reset(kex->peer); 5651e8db6e2SBrian Feldman kex->flags &= ~KEX_INIT_SENT; 566e4a9863fSDag-Erling Smørgrav free(kex->name); 5671e8db6e2SBrian Feldman kex->name = NULL; 568bc5531deSDag-Erling Smørgrav return 0; 569a04a10f8SKris Kennaway } 570a04a10f8SKris Kennaway 571bc5531deSDag-Erling Smørgrav int 572bc5531deSDag-Erling Smørgrav kex_send_kexinit(struct ssh *ssh) 573a04a10f8SKris Kennaway { 574545d5ecaSDag-Erling Smørgrav u_char *cookie; 575bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 576bc5531deSDag-Erling Smørgrav int r; 577545d5ecaSDag-Erling Smørgrav 57819261079SEd Maste if (kex == NULL) { 57919261079SEd Maste error_f("no kex"); 580bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 58119261079SEd Maste } 582bc5531deSDag-Erling Smørgrav if (kex->flags & KEX_INIT_SENT) 583bc5531deSDag-Erling Smørgrav return 0; 5841e8db6e2SBrian Feldman kex->done = 0; 585545d5ecaSDag-Erling Smørgrav 586545d5ecaSDag-Erling Smørgrav /* generate a random cookie */ 58719261079SEd Maste if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) { 58819261079SEd Maste error_f("bad kex length: %zu < %d", 58919261079SEd Maste sshbuf_len(kex->my), KEX_COOKIE_LEN); 590bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 59119261079SEd Maste } 59219261079SEd Maste if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) { 59319261079SEd Maste error_f("buffer error"); 594bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 59519261079SEd Maste } 596bc5531deSDag-Erling Smørgrav arc4random_buf(cookie, KEX_COOKIE_LEN); 597bc5531deSDag-Erling Smørgrav 598bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || 599bc5531deSDag-Erling Smørgrav (r = sshpkt_putb(ssh, kex->my)) != 0 || 60019261079SEd Maste (r = sshpkt_send(ssh)) != 0) { 60119261079SEd Maste error_fr(r, "compose reply"); 602bc5531deSDag-Erling Smørgrav return r; 60319261079SEd Maste } 6041e8db6e2SBrian Feldman debug("SSH2_MSG_KEXINIT sent"); 6051e8db6e2SBrian Feldman kex->flags |= KEX_INIT_SENT; 606bc5531deSDag-Erling Smørgrav return 0; 6071e8db6e2SBrian Feldman } 608a04a10f8SKris Kennaway 609bc5531deSDag-Erling Smørgrav int 6104f52dfbbSDag-Erling Smørgrav kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) 6111e8db6e2SBrian Feldman { 612bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 613bc5531deSDag-Erling Smørgrav const u_char *ptr; 614bc5531deSDag-Erling Smørgrav u_int i; 615bc5531deSDag-Erling Smørgrav size_t dlen; 616bc5531deSDag-Erling Smørgrav int r; 6172632b0c8SKris Kennaway 6181e8db6e2SBrian Feldman debug("SSH2_MSG_KEXINIT received"); 61919261079SEd Maste if (kex == NULL) { 62019261079SEd Maste error_f("no kex"); 62119261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 62219261079SEd Maste } 62392f58c69SGordon Tetlow ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error); 624bc5531deSDag-Erling Smørgrav ptr = sshpkt_ptr(ssh, &dlen); 625bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) 626bc5531deSDag-Erling Smørgrav return r; 6271e8db6e2SBrian Feldman 6281e8db6e2SBrian Feldman /* discard packet */ 62919261079SEd Maste for (i = 0; i < KEX_COOKIE_LEN; i++) { 63019261079SEd Maste if ((r = sshpkt_get_u8(ssh, NULL)) != 0) { 63119261079SEd Maste error_fr(r, "discard cookie"); 632bc5531deSDag-Erling Smørgrav return r; 63319261079SEd Maste } 63419261079SEd Maste } 63519261079SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) { 63619261079SEd Maste if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { 63719261079SEd Maste error_fr(r, "discard proposal"); 638bc5531deSDag-Erling Smørgrav return r; 63919261079SEd Maste } 64019261079SEd Maste } 6416888a9beSDag-Erling Smørgrav /* 6426888a9beSDag-Erling Smørgrav * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported 6436888a9beSDag-Erling Smørgrav * KEX method has the server move first, but a server might be using 6446888a9beSDag-Erling Smørgrav * a custom method or one that we otherwise don't support. We should 6456888a9beSDag-Erling Smørgrav * be prepared to remember first_kex_follows here so we can eat a 6466888a9beSDag-Erling Smørgrav * packet later. 6476888a9beSDag-Erling Smørgrav * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means 6486888a9beSDag-Erling Smørgrav * for cases where the server *doesn't* go first. I guess we should 6496888a9beSDag-Erling Smørgrav * ignore it when it is set for these cases, which is what we do now. 6506888a9beSDag-Erling Smørgrav */ 651bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ 652bc5531deSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ 653bc5531deSDag-Erling Smørgrav (r = sshpkt_get_end(ssh)) != 0) 654bc5531deSDag-Erling Smørgrav return r; 6551e8db6e2SBrian Feldman 6561e8db6e2SBrian Feldman if (!(kex->flags & KEX_INIT_SENT)) 657bc5531deSDag-Erling Smørgrav if ((r = kex_send_kexinit(ssh)) != 0) 658bc5531deSDag-Erling Smørgrav return r; 65992f58c69SGordon Tetlow if ((r = kex_choose_conf(ssh, seq)) != 0) 660bc5531deSDag-Erling Smørgrav return r; 6611e8db6e2SBrian Feldman 662bc5531deSDag-Erling Smørgrav if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) 663bc5531deSDag-Erling Smørgrav return (kex->kex[kex->kex_type])(ssh); 6641e8db6e2SBrian Feldman 66519261079SEd Maste error_f("unknown kex type %u", kex->kex_type); 666bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 6671e8db6e2SBrian Feldman } 668a04a10f8SKris Kennaway 66919261079SEd Maste struct kex * 67019261079SEd Maste kex_new(void) 671bc5531deSDag-Erling Smørgrav { 672bc5531deSDag-Erling Smørgrav struct kex *kex; 673bc5531deSDag-Erling Smørgrav 67419261079SEd Maste if ((kex = calloc(1, sizeof(*kex))) == NULL || 67519261079SEd Maste (kex->peer = sshbuf_new()) == NULL || 67619261079SEd Maste (kex->my = sshbuf_new()) == NULL || 67719261079SEd Maste (kex->client_version = sshbuf_new()) == NULL || 67819261079SEd Maste (kex->server_version = sshbuf_new()) == NULL || 67919261079SEd Maste (kex->session_id = sshbuf_new()) == NULL) { 680bc5531deSDag-Erling Smørgrav kex_free(kex); 68119261079SEd Maste return NULL; 68219261079SEd Maste } 68319261079SEd Maste return kex; 684bc5531deSDag-Erling Smørgrav } 685bc5531deSDag-Erling Smørgrav 686bc5531deSDag-Erling Smørgrav void 687bc5531deSDag-Erling Smørgrav kex_free_newkeys(struct newkeys *newkeys) 688bc5531deSDag-Erling Smørgrav { 689bc5531deSDag-Erling Smørgrav if (newkeys == NULL) 690bc5531deSDag-Erling Smørgrav return; 691bc5531deSDag-Erling Smørgrav if (newkeys->enc.key) { 692bc5531deSDag-Erling Smørgrav explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); 693bc5531deSDag-Erling Smørgrav free(newkeys->enc.key); 694bc5531deSDag-Erling Smørgrav newkeys->enc.key = NULL; 695bc5531deSDag-Erling Smørgrav } 696bc5531deSDag-Erling Smørgrav if (newkeys->enc.iv) { 697acc1a9efSDag-Erling Smørgrav explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); 698bc5531deSDag-Erling Smørgrav free(newkeys->enc.iv); 699bc5531deSDag-Erling Smørgrav newkeys->enc.iv = NULL; 700bc5531deSDag-Erling Smørgrav } 701bc5531deSDag-Erling Smørgrav free(newkeys->enc.name); 702bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); 703bc5531deSDag-Erling Smørgrav free(newkeys->comp.name); 704bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); 705bc5531deSDag-Erling Smørgrav mac_clear(&newkeys->mac); 706bc5531deSDag-Erling Smørgrav if (newkeys->mac.key) { 707bc5531deSDag-Erling Smørgrav explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); 708bc5531deSDag-Erling Smørgrav free(newkeys->mac.key); 709bc5531deSDag-Erling Smørgrav newkeys->mac.key = NULL; 710bc5531deSDag-Erling Smørgrav } 711bc5531deSDag-Erling Smørgrav free(newkeys->mac.name); 712bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); 71319261079SEd Maste freezero(newkeys, sizeof(*newkeys)); 714bc5531deSDag-Erling Smørgrav } 715bc5531deSDag-Erling Smørgrav 716bc5531deSDag-Erling Smørgrav void 717bc5531deSDag-Erling Smørgrav kex_free(struct kex *kex) 718bc5531deSDag-Erling Smørgrav { 719bc5531deSDag-Erling Smørgrav u_int mode; 720bc5531deSDag-Erling Smørgrav 72119261079SEd Maste if (kex == NULL) 72219261079SEd Maste return; 72319261079SEd Maste 724bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL 725bc5531deSDag-Erling Smørgrav DH_free(kex->dh); 726bc5531deSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC 727bc5531deSDag-Erling Smørgrav EC_KEY_free(kex->ec_client_key); 728bc5531deSDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */ 729bc5531deSDag-Erling Smørgrav #endif /* WITH_OPENSSL */ 730bc5531deSDag-Erling Smørgrav for (mode = 0; mode < MODE_MAX; mode++) { 731bc5531deSDag-Erling Smørgrav kex_free_newkeys(kex->newkeys[mode]); 732bc5531deSDag-Erling Smørgrav kex->newkeys[mode] = NULL; 733bc5531deSDag-Erling Smørgrav } 734bc5531deSDag-Erling Smørgrav sshbuf_free(kex->peer); 735bc5531deSDag-Erling Smørgrav sshbuf_free(kex->my); 73619261079SEd Maste sshbuf_free(kex->client_version); 73719261079SEd Maste sshbuf_free(kex->server_version); 73819261079SEd Maste sshbuf_free(kex->client_pub); 73919261079SEd Maste sshbuf_free(kex->session_id); 7401323ec57SEd Maste sshbuf_free(kex->initial_sig); 7411323ec57SEd Maste sshkey_free(kex->initial_hostkey); 742eccfee6eSDag-Erling Smørgrav free(kex->failed_choice); 743acc1a9efSDag-Erling Smørgrav free(kex->hostkey_alg); 744acc1a9efSDag-Erling Smørgrav free(kex->name); 745bc5531deSDag-Erling Smørgrav free(kex); 746bc5531deSDag-Erling Smørgrav } 747bc5531deSDag-Erling Smørgrav 748bc5531deSDag-Erling Smørgrav int 74919261079SEd Maste kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 75019261079SEd Maste { 75119261079SEd Maste int r; 75219261079SEd Maste 75319261079SEd Maste if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0) 75419261079SEd Maste return r; 75519261079SEd Maste ssh->kex->flags = KEX_INITIAL; 75619261079SEd Maste kex_reset_dispatch(ssh); 75719261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 75819261079SEd Maste return 0; 75919261079SEd Maste } 76019261079SEd Maste 76119261079SEd Maste int 762bc5531deSDag-Erling Smørgrav kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 763bc5531deSDag-Erling Smørgrav { 764bc5531deSDag-Erling Smørgrav int r; 765bc5531deSDag-Erling Smørgrav 76619261079SEd Maste if ((r = kex_ready(ssh, proposal)) != 0) 767bc5531deSDag-Erling Smørgrav return r; 768bc5531deSDag-Erling Smørgrav if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ 769bc5531deSDag-Erling Smørgrav kex_free(ssh->kex); 770bc5531deSDag-Erling Smørgrav ssh->kex = NULL; 771bc5531deSDag-Erling Smørgrav return r; 772bc5531deSDag-Erling Smørgrav } 773bc5531deSDag-Erling Smørgrav return 0; 774bc5531deSDag-Erling Smørgrav } 775bc5531deSDag-Erling Smørgrav 776acc1a9efSDag-Erling Smørgrav /* 777acc1a9efSDag-Erling Smørgrav * Request key re-exchange, returns 0 on success or a ssherr.h error 778acc1a9efSDag-Erling Smørgrav * code otherwise. Must not be called if KEX is incomplete or in-progress. 779acc1a9efSDag-Erling Smørgrav */ 780acc1a9efSDag-Erling Smørgrav int 781acc1a9efSDag-Erling Smørgrav kex_start_rekex(struct ssh *ssh) 782acc1a9efSDag-Erling Smørgrav { 783acc1a9efSDag-Erling Smørgrav if (ssh->kex == NULL) { 78419261079SEd Maste error_f("no kex"); 785acc1a9efSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 786acc1a9efSDag-Erling Smørgrav } 787acc1a9efSDag-Erling Smørgrav if (ssh->kex->done == 0) { 78819261079SEd Maste error_f("requested twice"); 789acc1a9efSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 790acc1a9efSDag-Erling Smørgrav } 791acc1a9efSDag-Erling Smørgrav ssh->kex->done = 0; 792acc1a9efSDag-Erling Smørgrav return kex_send_kexinit(ssh); 793acc1a9efSDag-Erling Smørgrav } 794acc1a9efSDag-Erling Smørgrav 795bc5531deSDag-Erling Smørgrav static int 796bc5531deSDag-Erling Smørgrav choose_enc(struct sshenc *enc, char *client, char *server) 797a04a10f8SKris Kennaway { 7981e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 799bc5531deSDag-Erling Smørgrav 800a04a10f8SKris Kennaway if (name == NULL) 801bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_CIPHER_ALG_MATCH; 802d93a896eSDag-Erling Smørgrav if ((enc->cipher = cipher_by_name(name)) == NULL) { 80319261079SEd Maste error_f("unsupported cipher %s", name); 804d93a896eSDag-Erling Smørgrav free(name); 805bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 806d93a896eSDag-Erling Smørgrav } 807a04a10f8SKris Kennaway enc->name = name; 808a04a10f8SKris Kennaway enc->enabled = 0; 809a04a10f8SKris Kennaway enc->iv = NULL; 8106888a9beSDag-Erling Smørgrav enc->iv_len = cipher_ivlen(enc->cipher); 811a04a10f8SKris Kennaway enc->key = NULL; 812ae1f160dSDag-Erling Smørgrav enc->key_len = cipher_keylen(enc->cipher); 813ae1f160dSDag-Erling Smørgrav enc->block_size = cipher_blocksize(enc->cipher); 814bc5531deSDag-Erling Smørgrav return 0; 815a04a10f8SKris Kennaway } 816761efaa7SDag-Erling Smørgrav 817bc5531deSDag-Erling Smørgrav static int 818bc5531deSDag-Erling Smørgrav choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) 819a04a10f8SKris Kennaway { 8201e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 821bc5531deSDag-Erling Smørgrav 822a04a10f8SKris Kennaway if (name == NULL) 823bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_MAC_ALG_MATCH; 824d93a896eSDag-Erling Smørgrav if (mac_setup(mac, name) < 0) { 82519261079SEd Maste error_f("unsupported MAC %s", name); 826d93a896eSDag-Erling Smørgrav free(name); 827bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 828d93a896eSDag-Erling Smørgrav } 829a04a10f8SKris Kennaway mac->name = name; 830a04a10f8SKris Kennaway mac->key = NULL; 831a04a10f8SKris Kennaway mac->enabled = 0; 832bc5531deSDag-Erling Smørgrav return 0; 833a04a10f8SKris Kennaway } 834761efaa7SDag-Erling Smørgrav 835bc5531deSDag-Erling Smørgrav static int 836bc5531deSDag-Erling Smørgrav choose_comp(struct sshcomp *comp, char *client, char *server) 837a04a10f8SKris Kennaway { 8381e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 839bc5531deSDag-Erling Smørgrav 840a04a10f8SKris Kennaway if (name == NULL) 841bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_COMPRESS_ALG_MATCH; 84219261079SEd Maste #ifdef WITH_ZLIB 843043840dfSDag-Erling Smørgrav if (strcmp(name, "zlib@openssh.com") == 0) { 844043840dfSDag-Erling Smørgrav comp->type = COMP_DELAYED; 845043840dfSDag-Erling Smørgrav } else if (strcmp(name, "zlib") == 0) { 846043840dfSDag-Erling Smørgrav comp->type = COMP_ZLIB; 84719261079SEd Maste } else 84819261079SEd Maste #endif /* WITH_ZLIB */ 84919261079SEd Maste if (strcmp(name, "none") == 0) { 850043840dfSDag-Erling Smørgrav comp->type = COMP_NONE; 851a04a10f8SKris Kennaway } else { 85219261079SEd Maste error_f("unsupported compression scheme %s", name); 853d93a896eSDag-Erling Smørgrav free(name); 854bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 855a04a10f8SKris Kennaway } 856a04a10f8SKris Kennaway comp->name = name; 857bc5531deSDag-Erling Smørgrav return 0; 858a04a10f8SKris Kennaway } 859761efaa7SDag-Erling Smørgrav 860bc5531deSDag-Erling Smørgrav static int 861bc5531deSDag-Erling Smørgrav choose_kex(struct kex *k, char *client, char *server) 862a04a10f8SKris Kennaway { 8631e8db6e2SBrian Feldman k->name = match_list(client, server, NULL); 864bc5531deSDag-Erling Smørgrav 865acc1a9efSDag-Erling Smørgrav debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); 866a04a10f8SKris Kennaway if (k->name == NULL) 867bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_KEX_ALG_MATCH; 868*0fdf8faeSEd Maste if (!kex_name_valid(k->name)) { 86919261079SEd Maste error_f("unsupported KEX method %s", k->name); 870bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 87119261079SEd Maste } 872*0fdf8faeSEd Maste k->kex_type = kex_type_from_name(k->name); 873*0fdf8faeSEd Maste k->hash_alg = kex_hash_from_name(k->name); 874*0fdf8faeSEd Maste k->ec_nid = kex_nid_from_name(k->name); 875bc5531deSDag-Erling Smørgrav return 0; 876a04a10f8SKris Kennaway } 877021d409fSDag-Erling Smørgrav 878bc5531deSDag-Erling Smørgrav static int 879bc5531deSDag-Erling Smørgrav choose_hostkeyalg(struct kex *k, char *client, char *server) 880a04a10f8SKris Kennaway { 88119261079SEd Maste free(k->hostkey_alg); 882acc1a9efSDag-Erling Smørgrav k->hostkey_alg = match_list(client, server, NULL); 883bc5531deSDag-Erling Smørgrav 884acc1a9efSDag-Erling Smørgrav debug("kex: host key algorithm: %s", 885acc1a9efSDag-Erling Smørgrav k->hostkey_alg ? k->hostkey_alg : "(no match)"); 886acc1a9efSDag-Erling Smørgrav if (k->hostkey_alg == NULL) 887bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_HOSTKEY_ALG_MATCH; 888acc1a9efSDag-Erling Smørgrav k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); 88919261079SEd Maste if (k->hostkey_type == KEY_UNSPEC) { 89019261079SEd Maste error_f("unsupported hostkey algorithm %s", k->hostkey_alg); 891bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 89219261079SEd Maste } 893acc1a9efSDag-Erling Smørgrav k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); 894bc5531deSDag-Erling Smørgrav return 0; 895a04a10f8SKris Kennaway } 896a04a10f8SKris Kennaway 897d0c8c0bcSDag-Erling Smørgrav static int 898d0c8c0bcSDag-Erling Smørgrav proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) 899d0c8c0bcSDag-Erling Smørgrav { 900d0c8c0bcSDag-Erling Smørgrav static int check[] = { 901d0c8c0bcSDag-Erling Smørgrav PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 902d0c8c0bcSDag-Erling Smørgrav }; 903d0c8c0bcSDag-Erling Smørgrav int *idx; 904d0c8c0bcSDag-Erling Smørgrav char *p; 905d0c8c0bcSDag-Erling Smørgrav 906d0c8c0bcSDag-Erling Smørgrav for (idx = &check[0]; *idx != -1; idx++) { 907d0c8c0bcSDag-Erling Smørgrav if ((p = strchr(my[*idx], ',')) != NULL) 908d0c8c0bcSDag-Erling Smørgrav *p = '\0'; 909d0c8c0bcSDag-Erling Smørgrav if ((p = strchr(peer[*idx], ',')) != NULL) 910d0c8c0bcSDag-Erling Smørgrav *p = '\0'; 911d0c8c0bcSDag-Erling Smørgrav if (strcmp(my[*idx], peer[*idx]) != 0) { 912d0c8c0bcSDag-Erling Smørgrav debug2("proposal mismatch: my %s peer %s", 913d0c8c0bcSDag-Erling Smørgrav my[*idx], peer[*idx]); 914d0c8c0bcSDag-Erling Smørgrav return (0); 915d0c8c0bcSDag-Erling Smørgrav } 916d0c8c0bcSDag-Erling Smørgrav } 917d0c8c0bcSDag-Erling Smørgrav debug2("proposals match"); 918d0c8c0bcSDag-Erling Smørgrav return (1); 919d0c8c0bcSDag-Erling Smørgrav } 920d0c8c0bcSDag-Erling Smørgrav 9211323ec57SEd Maste static int 92292f58c69SGordon Tetlow kexalgs_contains(char **peer, const char *ext) 9231323ec57SEd Maste { 924*0fdf8faeSEd Maste return kex_has_any_alg(peer[PROPOSAL_KEX_ALGS], ext); 9251323ec57SEd Maste } 9261323ec57SEd Maste 927bc5531deSDag-Erling Smørgrav static int 92892f58c69SGordon Tetlow kex_choose_conf(struct ssh *ssh, uint32_t seq) 929a04a10f8SKris Kennaway { 930bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 931bc5531deSDag-Erling Smørgrav struct newkeys *newkeys; 932bc5531deSDag-Erling Smørgrav char **my = NULL, **peer = NULL; 9331e8db6e2SBrian Feldman char **cprop, **sprop; 9341e8db6e2SBrian Feldman int nenc, nmac, ncomp; 935f7167e0eSDag-Erling Smørgrav u_int mode, ctos, need, dh_need, authlen; 936bc5531deSDag-Erling Smørgrav int r, first_kex_follows; 937a04a10f8SKris Kennaway 938acc1a9efSDag-Erling Smørgrav debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); 939acc1a9efSDag-Erling Smørgrav if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) 940acc1a9efSDag-Erling Smørgrav goto out; 941acc1a9efSDag-Erling Smørgrav debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); 942acc1a9efSDag-Erling Smørgrav if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) 943bc5531deSDag-Erling Smørgrav goto out; 944a04a10f8SKris Kennaway 9451e8db6e2SBrian Feldman if (kex->server) { 9461e8db6e2SBrian Feldman cprop=peer; 9471e8db6e2SBrian Feldman sprop=my; 9481e8db6e2SBrian Feldman } else { 9491e8db6e2SBrian Feldman cprop=my; 9501e8db6e2SBrian Feldman sprop=peer; 9511e8db6e2SBrian Feldman } 9521e8db6e2SBrian Feldman 95392f58c69SGordon Tetlow /* Check whether peer supports ext_info/kex_strict */ 95492f58c69SGordon Tetlow if ((kex->flags & KEX_INITIAL) != 0) { 95592f58c69SGordon Tetlow if (kex->server) { 95692f58c69SGordon Tetlow kex->ext_info_c = kexalgs_contains(peer, "ext-info-c"); 95792f58c69SGordon Tetlow kex->kex_strict = kexalgs_contains(peer, 95892f58c69SGordon Tetlow "kex-strict-c-v00@openssh.com"); 95992f58c69SGordon Tetlow } else { 960069ac184SEd Maste kex->ext_info_s = kexalgs_contains(peer, "ext-info-s"); 96192f58c69SGordon Tetlow kex->kex_strict = kexalgs_contains(peer, 96292f58c69SGordon Tetlow "kex-strict-s-v00@openssh.com"); 96392f58c69SGordon Tetlow } 96492f58c69SGordon Tetlow if (kex->kex_strict) { 96592f58c69SGordon Tetlow debug3_f("will use strict KEX ordering"); 96692f58c69SGordon Tetlow if (seq != 0) 96792f58c69SGordon Tetlow ssh_packet_disconnect(ssh, 96892f58c69SGordon Tetlow "strict KEX violation: " 96992f58c69SGordon Tetlow "KEXINIT was not the first packet"); 97092f58c69SGordon Tetlow } 971b15c8340SDag-Erling Smørgrav } 972b15c8340SDag-Erling Smørgrav 9731323ec57SEd Maste /* Check whether client supports rsa-sha2 algorithms */ 9741323ec57SEd Maste if (kex->server && (kex->flags & KEX_INITIAL)) { 975*0fdf8faeSEd Maste if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], 9761323ec57SEd Maste "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com")) 9771323ec57SEd Maste kex->flags |= KEX_RSA_SHA2_256_SUPPORTED; 978*0fdf8faeSEd Maste if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], 9791323ec57SEd Maste "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com")) 9801323ec57SEd Maste kex->flags |= KEX_RSA_SHA2_512_SUPPORTED; 9811323ec57SEd Maste } 9821323ec57SEd Maste 9831e8db6e2SBrian Feldman /* Algorithm Negotiation */ 984acc1a9efSDag-Erling Smørgrav if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], 985acc1a9efSDag-Erling Smørgrav sprop[PROPOSAL_KEX_ALGS])) != 0) { 986acc1a9efSDag-Erling Smørgrav kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; 987acc1a9efSDag-Erling Smørgrav peer[PROPOSAL_KEX_ALGS] = NULL; 988acc1a9efSDag-Erling Smørgrav goto out; 989acc1a9efSDag-Erling Smørgrav } 990acc1a9efSDag-Erling Smørgrav if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], 991acc1a9efSDag-Erling Smørgrav sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { 992acc1a9efSDag-Erling Smørgrav kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; 993acc1a9efSDag-Erling Smørgrav peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; 994acc1a9efSDag-Erling Smørgrav goto out; 995acc1a9efSDag-Erling Smørgrav } 996a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 997bc5531deSDag-Erling Smørgrav if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { 998bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 999bc5531deSDag-Erling Smørgrav goto out; 1000bc5531deSDag-Erling Smørgrav } 10011e8db6e2SBrian Feldman kex->newkeys[mode] = newkeys; 1002d4af9e69SDag-Erling Smørgrav ctos = (!kex->server && mode == MODE_OUT) || 1003d4af9e69SDag-Erling Smørgrav (kex->server && mode == MODE_IN); 1004a04a10f8SKris Kennaway nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; 1005a04a10f8SKris Kennaway nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; 1006a04a10f8SKris Kennaway ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; 1007bc5531deSDag-Erling Smørgrav if ((r = choose_enc(&newkeys->enc, cprop[nenc], 1008eccfee6eSDag-Erling Smørgrav sprop[nenc])) != 0) { 1009eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[nenc]; 1010eccfee6eSDag-Erling Smørgrav peer[nenc] = NULL; 1011bc5531deSDag-Erling Smørgrav goto out; 1012eccfee6eSDag-Erling Smørgrav } 10136888a9beSDag-Erling Smørgrav authlen = cipher_authlen(newkeys->enc.cipher); 1014bc5531deSDag-Erling Smørgrav /* ignore mac for authenticated encryption */ 1015bc5531deSDag-Erling Smørgrav if (authlen == 0 && 1016bc5531deSDag-Erling Smørgrav (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], 1017eccfee6eSDag-Erling Smørgrav sprop[nmac])) != 0) { 1018eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[nmac]; 1019eccfee6eSDag-Erling Smørgrav peer[nmac] = NULL; 1020bc5531deSDag-Erling Smørgrav goto out; 1021eccfee6eSDag-Erling Smørgrav } 1022bc5531deSDag-Erling Smørgrav if ((r = choose_comp(&newkeys->comp, cprop[ncomp], 1023eccfee6eSDag-Erling Smørgrav sprop[ncomp])) != 0) { 1024eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[ncomp]; 1025eccfee6eSDag-Erling Smørgrav peer[ncomp] = NULL; 1026bc5531deSDag-Erling Smørgrav goto out; 1027eccfee6eSDag-Erling Smørgrav } 1028acc1a9efSDag-Erling Smørgrav debug("kex: %s cipher: %s MAC: %s compression: %s", 1029a04a10f8SKris Kennaway ctos ? "client->server" : "server->client", 10301e8db6e2SBrian Feldman newkeys->enc.name, 10316888a9beSDag-Erling Smørgrav authlen == 0 ? newkeys->mac.name : "<implicit>", 10321e8db6e2SBrian Feldman newkeys->comp.name); 1033a04a10f8SKris Kennaway } 1034f7167e0eSDag-Erling Smørgrav need = dh_need = 0; 1035a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 10361e8db6e2SBrian Feldman newkeys = kex->newkeys[mode]; 1037ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.key_len); 1038ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.block_size); 1039ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.iv_len); 1040ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->mac.key_len); 1041ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher)); 1042ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); 1043ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); 1044ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); 1045a04a10f8SKris Kennaway } 10462632b0c8SKris Kennaway /* XXX need runden? */ 10471e8db6e2SBrian Feldman kex->we_need = need; 1048f7167e0eSDag-Erling Smørgrav kex->dh_need = dh_need; 10491e8db6e2SBrian Feldman 1050d0c8c0bcSDag-Erling Smørgrav /* ignore the next message if the proposals do not match */ 105147dd1d1bSDag-Erling Smørgrav if (first_kex_follows && !proposals_match(my, peer)) 1052bc5531deSDag-Erling Smørgrav ssh->dispatch_skip_packets = 1; 1053bc5531deSDag-Erling Smørgrav r = 0; 1054bc5531deSDag-Erling Smørgrav out: 10551e8db6e2SBrian Feldman kex_prop_free(my); 10561e8db6e2SBrian Feldman kex_prop_free(peer); 1057bc5531deSDag-Erling Smørgrav return r; 1058a04a10f8SKris Kennaway } 1059a04a10f8SKris Kennaway 1060bc5531deSDag-Erling Smørgrav static int 1061bc5531deSDag-Erling Smørgrav derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, 1062bc5531deSDag-Erling Smørgrav const struct sshbuf *shared_secret, u_char **keyp) 1063a04a10f8SKris Kennaway { 1064bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 1065bc5531deSDag-Erling Smørgrav struct ssh_digest_ctx *hashctx = NULL; 10661e8db6e2SBrian Feldman char c = id; 1067043840dfSDag-Erling Smørgrav u_int have; 1068f7167e0eSDag-Erling Smørgrav size_t mdsz; 1069043840dfSDag-Erling Smørgrav u_char *digest; 1070bc5531deSDag-Erling Smørgrav int r; 1071043840dfSDag-Erling Smørgrav 1072f7167e0eSDag-Erling Smørgrav if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) 1073bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_ARGUMENT; 1074ca86bcf2SDag-Erling Smørgrav if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) { 1075bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1076bc5531deSDag-Erling Smørgrav goto out; 1077bc5531deSDag-Erling Smørgrav } 10781e8db6e2SBrian Feldman 10791e8db6e2SBrian Feldman /* K1 = HASH(K || H || "A" || session_id) */ 1080bc5531deSDag-Erling Smørgrav if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || 1081bc5531deSDag-Erling Smørgrav ssh_digest_update_buffer(hashctx, shared_secret) != 0 || 1082f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, hash, hashlen) != 0 || 1083f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, &c, 1) != 0 || 108419261079SEd Maste ssh_digest_update_buffer(hashctx, kex->session_id) != 0 || 1085bc5531deSDag-Erling Smørgrav ssh_digest_final(hashctx, digest, mdsz) != 0) { 1086bc5531deSDag-Erling Smørgrav r = SSH_ERR_LIBCRYPTO_ERROR; 108719261079SEd Maste error_f("KEX hash failed"); 1088bc5531deSDag-Erling Smørgrav goto out; 1089bc5531deSDag-Erling Smørgrav } 1090f7167e0eSDag-Erling Smørgrav ssh_digest_free(hashctx); 1091bc5531deSDag-Erling Smørgrav hashctx = NULL; 10921e8db6e2SBrian Feldman 10931e8db6e2SBrian Feldman /* 10941e8db6e2SBrian Feldman * expand key: 10951e8db6e2SBrian Feldman * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) 10961e8db6e2SBrian Feldman * Key = K1 || K2 || ... || Kn 10971e8db6e2SBrian Feldman */ 10981e8db6e2SBrian Feldman for (have = mdsz; need > have; have += mdsz) { 1099bc5531deSDag-Erling Smørgrav if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || 1100bc5531deSDag-Erling Smørgrav ssh_digest_update_buffer(hashctx, shared_secret) != 0 || 1101f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, hash, hashlen) != 0 || 1102bc5531deSDag-Erling Smørgrav ssh_digest_update(hashctx, digest, have) != 0 || 1103bc5531deSDag-Erling Smørgrav ssh_digest_final(hashctx, digest + have, mdsz) != 0) { 110419261079SEd Maste error_f("KDF failed"); 1105bc5531deSDag-Erling Smørgrav r = SSH_ERR_LIBCRYPTO_ERROR; 1106bc5531deSDag-Erling Smørgrav goto out; 11071e8db6e2SBrian Feldman } 1108bc5531deSDag-Erling Smørgrav ssh_digest_free(hashctx); 1109bc5531deSDag-Erling Smørgrav hashctx = NULL; 1110bc5531deSDag-Erling Smørgrav } 11111e8db6e2SBrian Feldman #ifdef DEBUG_KEX 11121e8db6e2SBrian Feldman fprintf(stderr, "key '%c'== ", c); 11131e8db6e2SBrian Feldman dump_digest("key", digest, need); 11141e8db6e2SBrian Feldman #endif 1115bc5531deSDag-Erling Smørgrav *keyp = digest; 1116bc5531deSDag-Erling Smørgrav digest = NULL; 1117bc5531deSDag-Erling Smørgrav r = 0; 1118bc5531deSDag-Erling Smørgrav out: 1119bc5531deSDag-Erling Smørgrav free(digest); 1120bc5531deSDag-Erling Smørgrav ssh_digest_free(hashctx); 1121bc5531deSDag-Erling Smørgrav return r; 11221e8db6e2SBrian Feldman } 11231e8db6e2SBrian Feldman 11241e8db6e2SBrian Feldman #define NKEYS 6 1125bc5531deSDag-Erling Smørgrav int 1126bc5531deSDag-Erling Smørgrav kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, 1127bc5531deSDag-Erling Smørgrav const struct sshbuf *shared_secret) 11281e8db6e2SBrian Feldman { 1129bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 11301e8db6e2SBrian Feldman u_char *keys[NKEYS]; 1131bc5531deSDag-Erling Smørgrav u_int i, j, mode, ctos; 1132bc5531deSDag-Erling Smørgrav int r; 1133a04a10f8SKris Kennaway 113419261079SEd Maste /* save initial hash as session id */ 113519261079SEd Maste if ((kex->flags & KEX_INITIAL) != 0) { 113619261079SEd Maste if (sshbuf_len(kex->session_id) != 0) { 113719261079SEd Maste error_f("already have session ID at kex"); 113819261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 113919261079SEd Maste } 114019261079SEd Maste if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0) 114119261079SEd Maste return r; 114219261079SEd Maste } else if (sshbuf_len(kex->session_id) == 0) { 114319261079SEd Maste error_f("no session ID in rekex"); 114419261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 114519261079SEd Maste } 1146021d409fSDag-Erling Smørgrav for (i = 0; i < NKEYS; i++) { 1147bc5531deSDag-Erling Smørgrav if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, 1148bc5531deSDag-Erling Smørgrav shared_secret, &keys[i])) != 0) { 1149bc5531deSDag-Erling Smørgrav for (j = 0; j < i; j++) 1150bc5531deSDag-Erling Smørgrav free(keys[j]); 1151bc5531deSDag-Erling Smørgrav return r; 1152021d409fSDag-Erling Smørgrav } 1153bc5531deSDag-Erling Smørgrav } 1154a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 1155761efaa7SDag-Erling Smørgrav ctos = (!kex->server && mode == MODE_OUT) || 1156761efaa7SDag-Erling Smørgrav (kex->server && mode == MODE_IN); 1157bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; 1158bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; 1159bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; 1160a04a10f8SKris Kennaway } 1161bc5531deSDag-Erling Smørgrav return 0; 1162a04a10f8SKris Kennaway } 11631e8db6e2SBrian Feldman 1164bc5531deSDag-Erling Smørgrav int 116519261079SEd Maste kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) 1166f7167e0eSDag-Erling Smørgrav { 116719261079SEd Maste struct kex *kex = ssh->kex; 1168f7167e0eSDag-Erling Smørgrav 116919261079SEd Maste *pubp = NULL; 117019261079SEd Maste *prvp = NULL; 117119261079SEd Maste if (kex->load_host_public_key == NULL || 117219261079SEd Maste kex->load_host_private_key == NULL) { 117319261079SEd Maste error_f("missing hostkey loader"); 117419261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 1175f7167e0eSDag-Erling Smørgrav } 117619261079SEd Maste *pubp = kex->load_host_public_key(kex->hostkey_type, 117719261079SEd Maste kex->hostkey_nid, ssh); 117819261079SEd Maste *prvp = kex->load_host_private_key(kex->hostkey_type, 117919261079SEd Maste kex->hostkey_nid, ssh); 118019261079SEd Maste if (*pubp == NULL) 118119261079SEd Maste return SSH_ERR_NO_HOSTKEY_LOADED; 118219261079SEd Maste return 0; 118319261079SEd Maste } 1184f7167e0eSDag-Erling Smørgrav 118519261079SEd Maste int 118619261079SEd Maste kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) 118719261079SEd Maste { 118819261079SEd Maste struct kex *kex = ssh->kex; 118919261079SEd Maste 119019261079SEd Maste if (kex->verify_host_key == NULL) { 119119261079SEd Maste error_f("missing hostkey verifier"); 119219261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 119319261079SEd Maste } 119419261079SEd Maste if (server_host_key->type != kex->hostkey_type || 119519261079SEd Maste (kex->hostkey_type == KEY_ECDSA && 119619261079SEd Maste server_host_key->ecdsa_nid != kex->hostkey_nid)) 119719261079SEd Maste return SSH_ERR_KEY_TYPE_MISMATCH; 119819261079SEd Maste if (kex->verify_host_key(server_host_key, ssh) == -1) 119919261079SEd Maste return SSH_ERR_SIGNATURE_INVALID; 120019261079SEd Maste return 0; 120119261079SEd Maste } 1202d74d50a8SDag-Erling Smørgrav 12034a421b63SDag-Erling Smørgrav #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) 12041e8db6e2SBrian Feldman void 120519261079SEd Maste dump_digest(const char *msg, const u_char *digest, int len) 12061e8db6e2SBrian Feldman { 12071e8db6e2SBrian Feldman fprintf(stderr, "%s\n", msg); 1208bc5531deSDag-Erling Smørgrav sshbuf_dump_data(digest, len, stderr); 12091e8db6e2SBrian Feldman } 12101e8db6e2SBrian Feldman #endif 121119261079SEd Maste 121219261079SEd Maste /* 121319261079SEd Maste * Send a plaintext error message to the peer, suffixed by \r\n. 121419261079SEd Maste * Only used during banner exchange, and there only for the server. 121519261079SEd Maste */ 121619261079SEd Maste static void 121719261079SEd Maste send_error(struct ssh *ssh, char *msg) 121819261079SEd Maste { 121919261079SEd Maste char *crnl = "\r\n"; 122019261079SEd Maste 122119261079SEd Maste if (!ssh->kex->server) 122219261079SEd Maste return; 122319261079SEd Maste 122419261079SEd Maste if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), 122519261079SEd Maste msg, strlen(msg)) != strlen(msg) || 122619261079SEd Maste atomicio(vwrite, ssh_packet_get_connection_out(ssh), 122719261079SEd Maste crnl, strlen(crnl)) != strlen(crnl)) 122819261079SEd Maste error_f("write: %.100s", strerror(errno)); 122919261079SEd Maste } 123019261079SEd Maste 123119261079SEd Maste /* 123219261079SEd Maste * Sends our identification string and waits for the peer's. Will block for 123319261079SEd Maste * up to timeout_ms (or indefinitely if timeout_ms <= 0). 123419261079SEd Maste * Returns on 0 success or a ssherr.h code on failure. 123519261079SEd Maste */ 123619261079SEd Maste int 123719261079SEd Maste kex_exchange_identification(struct ssh *ssh, int timeout_ms, 123819261079SEd Maste const char *version_addendum) 123919261079SEd Maste { 124019261079SEd Maste int remote_major, remote_minor, mismatch, oerrno = 0; 1241f374ba41SEd Maste size_t len, n; 124219261079SEd Maste int r, expect_nl; 124319261079SEd Maste u_char c; 124419261079SEd Maste struct sshbuf *our_version = ssh->kex->server ? 124519261079SEd Maste ssh->kex->server_version : ssh->kex->client_version; 124619261079SEd Maste struct sshbuf *peer_version = ssh->kex->server ? 124719261079SEd Maste ssh->kex->client_version : ssh->kex->server_version; 124819261079SEd Maste char *our_version_string = NULL, *peer_version_string = NULL; 124919261079SEd Maste char *cp, *remote_version = NULL; 125019261079SEd Maste 125119261079SEd Maste /* Prepare and send our banner */ 125219261079SEd Maste sshbuf_reset(our_version); 125319261079SEd Maste if (version_addendum != NULL && *version_addendum == '\0') 125419261079SEd Maste version_addendum = NULL; 1255069ac184SEd Maste if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%s%s%s\r\n", 125619261079SEd Maste PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, 125719261079SEd Maste version_addendum == NULL ? "" : " ", 125819261079SEd Maste version_addendum == NULL ? "" : version_addendum)) != 0) { 125919261079SEd Maste oerrno = errno; 126019261079SEd Maste error_fr(r, "sshbuf_putf"); 126119261079SEd Maste goto out; 126219261079SEd Maste } 126319261079SEd Maste 126419261079SEd Maste if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), 126519261079SEd Maste sshbuf_mutable_ptr(our_version), 126619261079SEd Maste sshbuf_len(our_version)) != sshbuf_len(our_version)) { 126719261079SEd Maste oerrno = errno; 126819261079SEd Maste debug_f("write: %.100s", strerror(errno)); 126919261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 127019261079SEd Maste goto out; 127119261079SEd Maste } 127219261079SEd Maste if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */ 127319261079SEd Maste oerrno = errno; 127419261079SEd Maste error_fr(r, "sshbuf_consume_end"); 127519261079SEd Maste goto out; 127619261079SEd Maste } 127719261079SEd Maste our_version_string = sshbuf_dup_string(our_version); 127819261079SEd Maste if (our_version_string == NULL) { 127919261079SEd Maste error_f("sshbuf_dup_string failed"); 128019261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 128119261079SEd Maste goto out; 128219261079SEd Maste } 128319261079SEd Maste debug("Local version string %.100s", our_version_string); 128419261079SEd Maste 128519261079SEd Maste /* Read other side's version identification. */ 128619261079SEd Maste for (n = 0; ; n++) { 128719261079SEd Maste if (n >= SSH_MAX_PRE_BANNER_LINES) { 128819261079SEd Maste send_error(ssh, "No SSH identification string " 128919261079SEd Maste "received."); 129019261079SEd Maste error_f("No SSH version received in first %u lines " 129119261079SEd Maste "from server", SSH_MAX_PRE_BANNER_LINES); 129219261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 129319261079SEd Maste goto out; 129419261079SEd Maste } 129519261079SEd Maste sshbuf_reset(peer_version); 129619261079SEd Maste expect_nl = 0; 1297f374ba41SEd Maste for (;;) { 129819261079SEd Maste if (timeout_ms > 0) { 129919261079SEd Maste r = waitrfd(ssh_packet_get_connection_in(ssh), 13001b91d634SEd Maste &timeout_ms, NULL); 130119261079SEd Maste if (r == -1 && errno == ETIMEDOUT) { 130219261079SEd Maste send_error(ssh, "Timed out waiting " 130319261079SEd Maste "for SSH identification string."); 130419261079SEd Maste error("Connection timed out during " 130519261079SEd Maste "banner exchange"); 130619261079SEd Maste r = SSH_ERR_CONN_TIMEOUT; 130719261079SEd Maste goto out; 130819261079SEd Maste } else if (r == -1) { 130919261079SEd Maste oerrno = errno; 131019261079SEd Maste error_f("%s", strerror(errno)); 131119261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 131219261079SEd Maste goto out; 131319261079SEd Maste } 131419261079SEd Maste } 131519261079SEd Maste 131619261079SEd Maste len = atomicio(read, ssh_packet_get_connection_in(ssh), 131719261079SEd Maste &c, 1); 131819261079SEd Maste if (len != 1 && errno == EPIPE) { 1319edf85781SEd Maste verbose_f("Connection closed by remote host"); 132019261079SEd Maste r = SSH_ERR_CONN_CLOSED; 132119261079SEd Maste goto out; 132219261079SEd Maste } else if (len != 1) { 132319261079SEd Maste oerrno = errno; 132419261079SEd Maste error_f("read: %.100s", strerror(errno)); 132519261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 132619261079SEd Maste goto out; 132719261079SEd Maste } 132819261079SEd Maste if (c == '\r') { 132919261079SEd Maste expect_nl = 1; 133019261079SEd Maste continue; 133119261079SEd Maste } 133219261079SEd Maste if (c == '\n') 133319261079SEd Maste break; 133419261079SEd Maste if (c == '\0' || expect_nl) { 1335edf85781SEd Maste verbose_f("banner line contains invalid " 133619261079SEd Maste "characters"); 133719261079SEd Maste goto invalid; 133819261079SEd Maste } 133919261079SEd Maste if ((r = sshbuf_put_u8(peer_version, c)) != 0) { 134019261079SEd Maste oerrno = errno; 134119261079SEd Maste error_fr(r, "sshbuf_put"); 134219261079SEd Maste goto out; 134319261079SEd Maste } 134419261079SEd Maste if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { 1345edf85781SEd Maste verbose_f("banner line too long"); 134619261079SEd Maste goto invalid; 134719261079SEd Maste } 134819261079SEd Maste } 134919261079SEd Maste /* Is this an actual protocol banner? */ 135019261079SEd Maste if (sshbuf_len(peer_version) > 4 && 135119261079SEd Maste memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0) 135219261079SEd Maste break; 135319261079SEd Maste /* If not, then just log the line and continue */ 135419261079SEd Maste if ((cp = sshbuf_dup_string(peer_version)) == NULL) { 135519261079SEd Maste error_f("sshbuf_dup_string failed"); 135619261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 135719261079SEd Maste goto out; 135819261079SEd Maste } 135919261079SEd Maste /* Do not accept lines before the SSH ident from a client */ 136019261079SEd Maste if (ssh->kex->server) { 1361edf85781SEd Maste verbose_f("client sent invalid protocol identifier " 136219261079SEd Maste "\"%.256s\"", cp); 136319261079SEd Maste free(cp); 136419261079SEd Maste goto invalid; 136519261079SEd Maste } 136619261079SEd Maste debug_f("banner line %zu: %s", n, cp); 136719261079SEd Maste free(cp); 136819261079SEd Maste } 136919261079SEd Maste peer_version_string = sshbuf_dup_string(peer_version); 137019261079SEd Maste if (peer_version_string == NULL) 13714d3fc8b0SEd Maste fatal_f("sshbuf_dup_string failed"); 137219261079SEd Maste /* XXX must be same size for sscanf */ 137319261079SEd Maste if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) { 137419261079SEd Maste error_f("calloc failed"); 137519261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 137619261079SEd Maste goto out; 137719261079SEd Maste } 137819261079SEd Maste 137919261079SEd Maste /* 138019261079SEd Maste * Check that the versions match. In future this might accept 138119261079SEd Maste * several versions and set appropriate flags to handle them. 138219261079SEd Maste */ 138319261079SEd Maste if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n", 138419261079SEd Maste &remote_major, &remote_minor, remote_version) != 3) { 138519261079SEd Maste error("Bad remote protocol version identification: '%.100s'", 138619261079SEd Maste peer_version_string); 138719261079SEd Maste invalid: 138819261079SEd Maste send_error(ssh, "Invalid SSH identification string."); 138919261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 139019261079SEd Maste goto out; 139119261079SEd Maste } 139219261079SEd Maste debug("Remote protocol version %d.%d, remote software version %.100s", 139319261079SEd Maste remote_major, remote_minor, remote_version); 139419261079SEd Maste compat_banner(ssh, remote_version); 139519261079SEd Maste 139619261079SEd Maste mismatch = 0; 139719261079SEd Maste switch (remote_major) { 139819261079SEd Maste case 2: 139919261079SEd Maste break; 140019261079SEd Maste case 1: 140119261079SEd Maste if (remote_minor != 99) 140219261079SEd Maste mismatch = 1; 140319261079SEd Maste break; 140419261079SEd Maste default: 140519261079SEd Maste mismatch = 1; 140619261079SEd Maste break; 140719261079SEd Maste } 140819261079SEd Maste if (mismatch) { 140919261079SEd Maste error("Protocol major versions differ: %d vs. %d", 141019261079SEd Maste PROTOCOL_MAJOR_2, remote_major); 141119261079SEd Maste send_error(ssh, "Protocol major versions differ."); 141219261079SEd Maste r = SSH_ERR_NO_PROTOCOL_VERSION; 141319261079SEd Maste goto out; 141419261079SEd Maste } 141519261079SEd Maste 141619261079SEd Maste if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) { 141719261079SEd Maste logit("probed from %s port %d with %s. Don't panic.", 141819261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 141919261079SEd Maste peer_version_string); 142019261079SEd Maste r = SSH_ERR_CONN_CLOSED; /* XXX */ 142119261079SEd Maste goto out; 142219261079SEd Maste } 142319261079SEd Maste if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) { 142419261079SEd Maste logit("scanned from %s port %d with %s. Don't panic.", 142519261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 142619261079SEd Maste peer_version_string); 142719261079SEd Maste r = SSH_ERR_CONN_CLOSED; /* XXX */ 142819261079SEd Maste goto out; 142919261079SEd Maste } 143019261079SEd Maste /* success */ 143119261079SEd Maste r = 0; 143219261079SEd Maste out: 143319261079SEd Maste free(our_version_string); 143419261079SEd Maste free(peer_version_string); 143519261079SEd Maste free(remote_version); 143619261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR) 143719261079SEd Maste errno = oerrno; 143819261079SEd Maste return r; 143919261079SEd Maste } 144019261079SEd Maste 1441