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