1*1b91d634SEd Maste /* $OpenBSD: kex.c,v 1.179 2023/08/18 01:37:41 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 */ 68bc5531deSDag-Erling Smørgrav static int kex_choose_conf(struct ssh *); 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 84e4a9863fSDag-Erling Smørgrav struct kexalg { 85e4a9863fSDag-Erling Smørgrav char *name; 86bc5531deSDag-Erling Smørgrav u_int type; 87e4a9863fSDag-Erling Smørgrav int ec_nid; 88f7167e0eSDag-Erling Smørgrav int hash_alg; 89e4a9863fSDag-Erling Smørgrav }; 90e4a9863fSDag-Erling Smørgrav static const struct kexalg kexalgs[] = { 91a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL 92f7167e0eSDag-Erling Smørgrav { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, 93076ad2f8SDag-Erling Smørgrav { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, 94076ad2f8SDag-Erling Smørgrav { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, 95076ad2f8SDag-Erling Smørgrav { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, 96076ad2f8SDag-Erling Smørgrav { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 }, 97f7167e0eSDag-Erling Smørgrav { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, 98e4a9863fSDag-Erling Smørgrav #ifdef HAVE_EVP_SHA256 99f7167e0eSDag-Erling Smørgrav { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, 100a0ee8cc6SDag-Erling Smørgrav #endif /* HAVE_EVP_SHA256 */ 101e4a9863fSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC 102f7167e0eSDag-Erling Smørgrav { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, 103f7167e0eSDag-Erling Smørgrav NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, 104f7167e0eSDag-Erling Smørgrav { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, 105f7167e0eSDag-Erling Smørgrav SSH_DIGEST_SHA384 }, 106f7167e0eSDag-Erling Smørgrav # ifdef OPENSSL_HAS_NISTP521 107f7167e0eSDag-Erling Smørgrav { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, 108f7167e0eSDag-Erling Smørgrav SSH_DIGEST_SHA512 }, 109a0ee8cc6SDag-Erling Smørgrav # endif /* OPENSSL_HAS_NISTP521 */ 110a0ee8cc6SDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */ 111a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_OPENSSL */ 112bc5531deSDag-Erling Smørgrav #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) 113f7167e0eSDag-Erling Smørgrav { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, 114ca86bcf2SDag-Erling Smørgrav { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, 11519261079SEd Maste #ifdef USE_SNTRUP761X25519 11619261079SEd Maste { KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0, 11719261079SEd Maste SSH_DIGEST_SHA512 }, 11819261079SEd Maste #endif 119bc5531deSDag-Erling Smørgrav #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 12019261079SEd Maste { NULL, 0, -1, -1}, 121e4a9863fSDag-Erling Smørgrav }; 122e4a9863fSDag-Erling Smørgrav 123e4a9863fSDag-Erling Smørgrav char * 124f7167e0eSDag-Erling Smørgrav kex_alg_list(char sep) 125e4a9863fSDag-Erling Smørgrav { 126bc5531deSDag-Erling Smørgrav char *ret = NULL, *tmp; 127e4a9863fSDag-Erling Smørgrav size_t nlen, rlen = 0; 128e4a9863fSDag-Erling Smørgrav const struct kexalg *k; 129e4a9863fSDag-Erling Smørgrav 130e4a9863fSDag-Erling Smørgrav for (k = kexalgs; k->name != NULL; k++) { 131e4a9863fSDag-Erling Smørgrav if (ret != NULL) 132f7167e0eSDag-Erling Smørgrav ret[rlen++] = sep; 133e4a9863fSDag-Erling Smørgrav nlen = strlen(k->name); 134bc5531deSDag-Erling Smørgrav if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { 135bc5531deSDag-Erling Smørgrav free(ret); 136bc5531deSDag-Erling Smørgrav return NULL; 137bc5531deSDag-Erling Smørgrav } 138bc5531deSDag-Erling Smørgrav ret = tmp; 139e4a9863fSDag-Erling Smørgrav memcpy(ret + rlen, k->name, nlen + 1); 140e4a9863fSDag-Erling Smørgrav rlen += nlen; 141e4a9863fSDag-Erling Smørgrav } 142e4a9863fSDag-Erling Smørgrav return ret; 143e4a9863fSDag-Erling Smørgrav } 144e4a9863fSDag-Erling Smørgrav 145e4a9863fSDag-Erling Smørgrav static const struct kexalg * 146e4a9863fSDag-Erling Smørgrav kex_alg_by_name(const char *name) 147e4a9863fSDag-Erling Smørgrav { 148e4a9863fSDag-Erling Smørgrav const struct kexalg *k; 149e4a9863fSDag-Erling Smørgrav 150e4a9863fSDag-Erling Smørgrav for (k = kexalgs; k->name != NULL; k++) { 151e4a9863fSDag-Erling Smørgrav if (strcmp(k->name, name) == 0) 152e4a9863fSDag-Erling Smørgrav return k; 153e4a9863fSDag-Erling Smørgrav } 154e4a9863fSDag-Erling Smørgrav return NULL; 155e4a9863fSDag-Erling Smørgrav } 156e4a9863fSDag-Erling Smørgrav 1574a421b63SDag-Erling Smørgrav /* Validate KEX method name list */ 1584a421b63SDag-Erling Smørgrav int 1594a421b63SDag-Erling Smørgrav kex_names_valid(const char *names) 1604a421b63SDag-Erling Smørgrav { 1614a421b63SDag-Erling Smørgrav char *s, *cp, *p; 1624a421b63SDag-Erling Smørgrav 1634a421b63SDag-Erling Smørgrav if (names == NULL || strcmp(names, "") == 0) 1644a421b63SDag-Erling Smørgrav return 0; 165bc5531deSDag-Erling Smørgrav if ((s = cp = strdup(names)) == NULL) 166bc5531deSDag-Erling Smørgrav return 0; 1674a421b63SDag-Erling Smørgrav for ((p = strsep(&cp, ",")); p && *p != '\0'; 1684a421b63SDag-Erling Smørgrav (p = strsep(&cp, ","))) { 169e4a9863fSDag-Erling Smørgrav if (kex_alg_by_name(p) == NULL) { 1704a421b63SDag-Erling Smørgrav error("Unsupported KEX algorithm \"%.100s\"", p); 171e4a9863fSDag-Erling Smørgrav free(s); 1724a421b63SDag-Erling Smørgrav return 0; 1734a421b63SDag-Erling Smørgrav } 1744a421b63SDag-Erling Smørgrav } 1754a421b63SDag-Erling Smørgrav debug3("kex names ok: [%s]", names); 176e4a9863fSDag-Erling Smørgrav free(s); 1774a421b63SDag-Erling Smørgrav return 1; 1784a421b63SDag-Erling Smørgrav } 1794a421b63SDag-Erling Smørgrav 180eccfee6eSDag-Erling Smørgrav /* 181eccfee6eSDag-Erling Smørgrav * Concatenate algorithm names, avoiding duplicates in the process. 182eccfee6eSDag-Erling Smørgrav * Caller must free returned string. 183eccfee6eSDag-Erling Smørgrav */ 184eccfee6eSDag-Erling Smørgrav char * 185eccfee6eSDag-Erling Smørgrav kex_names_cat(const char *a, const char *b) 186eccfee6eSDag-Erling Smørgrav { 187d93a896eSDag-Erling Smørgrav char *ret = NULL, *tmp = NULL, *cp, *p, *m; 188eccfee6eSDag-Erling Smørgrav size_t len; 189eccfee6eSDag-Erling Smørgrav 190eccfee6eSDag-Erling Smørgrav if (a == NULL || *a == '\0') 191190cef3dSDag-Erling Smørgrav return strdup(b); 192eccfee6eSDag-Erling Smørgrav if (b == NULL || *b == '\0') 193eccfee6eSDag-Erling Smørgrav return strdup(a); 194eccfee6eSDag-Erling Smørgrav if (strlen(b) > 1024*1024) 195eccfee6eSDag-Erling Smørgrav return NULL; 196eccfee6eSDag-Erling Smørgrav len = strlen(a) + strlen(b) + 2; 197eccfee6eSDag-Erling Smørgrav if ((tmp = cp = strdup(b)) == NULL || 198eccfee6eSDag-Erling Smørgrav (ret = calloc(1, len)) == NULL) { 199eccfee6eSDag-Erling Smørgrav free(tmp); 200eccfee6eSDag-Erling Smørgrav return NULL; 201eccfee6eSDag-Erling Smørgrav } 202eccfee6eSDag-Erling Smørgrav strlcpy(ret, a, len); 203eccfee6eSDag-Erling Smørgrav for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { 204d93a896eSDag-Erling Smørgrav if ((m = match_list(ret, p, NULL)) != NULL) { 205d93a896eSDag-Erling Smørgrav free(m); 206eccfee6eSDag-Erling Smørgrav continue; /* Algorithm already present */ 207d93a896eSDag-Erling Smørgrav } 208eccfee6eSDag-Erling Smørgrav if (strlcat(ret, ",", len) >= len || 209eccfee6eSDag-Erling Smørgrav strlcat(ret, p, len) >= len) { 210eccfee6eSDag-Erling Smørgrav free(tmp); 211eccfee6eSDag-Erling Smørgrav free(ret); 212eccfee6eSDag-Erling Smørgrav return NULL; /* Shouldn't happen */ 213eccfee6eSDag-Erling Smørgrav } 214eccfee6eSDag-Erling Smørgrav } 215eccfee6eSDag-Erling Smørgrav free(tmp); 216eccfee6eSDag-Erling Smørgrav return ret; 217eccfee6eSDag-Erling Smørgrav } 218eccfee6eSDag-Erling Smørgrav 219eccfee6eSDag-Erling Smørgrav /* 220eccfee6eSDag-Erling Smørgrav * Assemble a list of algorithms from a default list and a string from a 221eccfee6eSDag-Erling Smørgrav * configuration file. The user-provided string may begin with '+' to 22219261079SEd Maste * indicate that it should be appended to the default, '-' that the 22319261079SEd Maste * specified names should be removed, or '^' that they should be placed 22419261079SEd Maste * at the head. 225eccfee6eSDag-Erling Smørgrav */ 226eccfee6eSDag-Erling Smørgrav int 227190cef3dSDag-Erling Smørgrav kex_assemble_names(char **listp, const char *def, const char *all) 228eccfee6eSDag-Erling Smørgrav { 229190cef3dSDag-Erling Smørgrav char *cp, *tmp, *patterns; 230190cef3dSDag-Erling Smørgrav char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL; 231190cef3dSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 232eccfee6eSDag-Erling Smørgrav 23319261079SEd Maste if (listp == NULL || def == NULL || all == NULL) 23419261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 23519261079SEd Maste 23619261079SEd Maste if (*listp == NULL || **listp == '\0') { 237190cef3dSDag-Erling Smørgrav if ((*listp = strdup(def)) == NULL) 238190cef3dSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 239eccfee6eSDag-Erling Smørgrav return 0; 240eccfee6eSDag-Erling Smørgrav } 241190cef3dSDag-Erling Smørgrav 242190cef3dSDag-Erling Smørgrav list = *listp; 243190cef3dSDag-Erling Smørgrav *listp = NULL; 244190cef3dSDag-Erling Smørgrav if (*list == '+') { 245190cef3dSDag-Erling Smørgrav /* Append names to default list */ 246190cef3dSDag-Erling Smørgrav if ((tmp = kex_names_cat(def, list + 1)) == NULL) { 247190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 248190cef3dSDag-Erling Smørgrav goto fail; 249190cef3dSDag-Erling Smørgrav } 250190cef3dSDag-Erling Smørgrav free(list); 251190cef3dSDag-Erling Smørgrav list = tmp; 252190cef3dSDag-Erling Smørgrav } else if (*list == '-') { 253190cef3dSDag-Erling Smørgrav /* Remove names from default list */ 25419261079SEd Maste if ((*listp = match_filter_denylist(def, list + 1)) == NULL) { 255190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 256190cef3dSDag-Erling Smørgrav goto fail; 257190cef3dSDag-Erling Smørgrav } 258190cef3dSDag-Erling Smørgrav free(list); 259190cef3dSDag-Erling Smørgrav /* filtering has already been done */ 260190cef3dSDag-Erling Smørgrav return 0; 26119261079SEd Maste } else if (*list == '^') { 26219261079SEd Maste /* Place names at head of default list */ 26319261079SEd Maste if ((tmp = kex_names_cat(list + 1, def)) == NULL) { 26419261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 26519261079SEd Maste goto fail; 26619261079SEd Maste } 26719261079SEd Maste free(list); 26819261079SEd Maste list = tmp; 269190cef3dSDag-Erling Smørgrav } else { 270190cef3dSDag-Erling Smørgrav /* Explicit list, overrides default - just use "list" as is */ 271d93a896eSDag-Erling Smørgrav } 272d93a896eSDag-Erling Smørgrav 273190cef3dSDag-Erling Smørgrav /* 274190cef3dSDag-Erling Smørgrav * The supplied names may be a pattern-list. For the -list case, 275190cef3dSDag-Erling Smørgrav * the patterns are applied above. For the +list and explicit list 276190cef3dSDag-Erling Smørgrav * cases we need to do it now. 277190cef3dSDag-Erling Smørgrav */ 278190cef3dSDag-Erling Smørgrav ret = NULL; 279190cef3dSDag-Erling Smørgrav if ((patterns = opatterns = strdup(list)) == NULL) { 280190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 281190cef3dSDag-Erling Smørgrav goto fail; 282190cef3dSDag-Erling Smørgrav } 283190cef3dSDag-Erling Smørgrav /* Apply positive (i.e. non-negated) patterns from the list */ 284190cef3dSDag-Erling Smørgrav while ((cp = strsep(&patterns, ",")) != NULL) { 285190cef3dSDag-Erling Smørgrav if (*cp == '!') { 286190cef3dSDag-Erling Smørgrav /* negated matches are not supported here */ 287190cef3dSDag-Erling Smørgrav r = SSH_ERR_INVALID_ARGUMENT; 288190cef3dSDag-Erling Smørgrav goto fail; 289190cef3dSDag-Erling Smørgrav } 290190cef3dSDag-Erling Smørgrav free(matching); 29119261079SEd Maste if ((matching = match_filter_allowlist(all, cp)) == NULL) { 292190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 293190cef3dSDag-Erling Smørgrav goto fail; 294190cef3dSDag-Erling Smørgrav } 295190cef3dSDag-Erling Smørgrav if ((tmp = kex_names_cat(ret, matching)) == NULL) { 296190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 297190cef3dSDag-Erling Smørgrav goto fail; 298190cef3dSDag-Erling Smørgrav } 299190cef3dSDag-Erling Smørgrav free(ret); 300190cef3dSDag-Erling Smørgrav ret = tmp; 301190cef3dSDag-Erling Smørgrav } 302190cef3dSDag-Erling Smørgrav if (ret == NULL || *ret == '\0') { 303190cef3dSDag-Erling Smørgrav /* An empty name-list is an error */ 304190cef3dSDag-Erling Smørgrav /* XXX better error code? */ 305190cef3dSDag-Erling Smørgrav r = SSH_ERR_INVALID_ARGUMENT; 306190cef3dSDag-Erling Smørgrav goto fail; 307190cef3dSDag-Erling Smørgrav } 308190cef3dSDag-Erling Smørgrav 309190cef3dSDag-Erling Smørgrav /* success */ 310190cef3dSDag-Erling Smørgrav *listp = ret; 311190cef3dSDag-Erling Smørgrav ret = NULL; 312190cef3dSDag-Erling Smørgrav r = 0; 313190cef3dSDag-Erling Smørgrav 314190cef3dSDag-Erling Smørgrav fail: 315190cef3dSDag-Erling Smørgrav free(matching); 316190cef3dSDag-Erling Smørgrav free(opatterns); 317190cef3dSDag-Erling Smørgrav free(list); 318190cef3dSDag-Erling Smørgrav free(ret); 319190cef3dSDag-Erling Smørgrav return r; 320eccfee6eSDag-Erling Smørgrav } 321eccfee6eSDag-Erling Smørgrav 3224d3fc8b0SEd Maste /* 3234d3fc8b0SEd Maste * Fill out a proposal array with dynamically allocated values, which may 3244d3fc8b0SEd Maste * be modified as required for compatibility reasons. 3254d3fc8b0SEd Maste * Any of the options may be NULL, in which case the default is used. 3264d3fc8b0SEd Maste * Array contents must be freed by calling kex_proposal_free_entries. 3274d3fc8b0SEd Maste */ 3284d3fc8b0SEd Maste void 3294d3fc8b0SEd Maste kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX], 3304d3fc8b0SEd Maste const char *kexalgos, const char *ciphers, const char *macs, 3314d3fc8b0SEd Maste const char *comp, const char *hkalgs) 3324d3fc8b0SEd Maste { 3334d3fc8b0SEd Maste const char *defpropserver[PROPOSAL_MAX] = { KEX_SERVER }; 3344d3fc8b0SEd Maste const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT }; 3354d3fc8b0SEd Maste const char **defprop = ssh->kex->server ? defpropserver : defpropclient; 3364d3fc8b0SEd Maste u_int i; 3374d3fc8b0SEd Maste 3384d3fc8b0SEd Maste if (prop == NULL) 3394d3fc8b0SEd Maste fatal_f("proposal missing"); 3404d3fc8b0SEd Maste 3414d3fc8b0SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) { 3424d3fc8b0SEd Maste switch(i) { 3434d3fc8b0SEd Maste case PROPOSAL_KEX_ALGS: 3444d3fc8b0SEd Maste prop[i] = compat_kex_proposal(ssh, 3454d3fc8b0SEd Maste kexalgos ? kexalgos : defprop[i]); 3464d3fc8b0SEd Maste break; 3474d3fc8b0SEd Maste case PROPOSAL_ENC_ALGS_CTOS: 3484d3fc8b0SEd Maste case PROPOSAL_ENC_ALGS_STOC: 3494d3fc8b0SEd Maste prop[i] = xstrdup(ciphers ? ciphers : defprop[i]); 3504d3fc8b0SEd Maste break; 3514d3fc8b0SEd Maste case PROPOSAL_MAC_ALGS_CTOS: 3524d3fc8b0SEd Maste case PROPOSAL_MAC_ALGS_STOC: 3534d3fc8b0SEd Maste prop[i] = xstrdup(macs ? macs : defprop[i]); 3544d3fc8b0SEd Maste break; 3554d3fc8b0SEd Maste case PROPOSAL_COMP_ALGS_CTOS: 3564d3fc8b0SEd Maste case PROPOSAL_COMP_ALGS_STOC: 3574d3fc8b0SEd Maste prop[i] = xstrdup(comp ? comp : defprop[i]); 3584d3fc8b0SEd Maste break; 3594d3fc8b0SEd Maste case PROPOSAL_SERVER_HOST_KEY_ALGS: 3604d3fc8b0SEd Maste prop[i] = xstrdup(hkalgs ? hkalgs : defprop[i]); 3614d3fc8b0SEd Maste break; 3624d3fc8b0SEd Maste default: 3634d3fc8b0SEd Maste prop[i] = xstrdup(defprop[i]); 3644d3fc8b0SEd Maste } 3654d3fc8b0SEd Maste } 3664d3fc8b0SEd Maste } 3674d3fc8b0SEd Maste 3684d3fc8b0SEd Maste void 3694d3fc8b0SEd Maste kex_proposal_free_entries(char *prop[PROPOSAL_MAX]) 3704d3fc8b0SEd Maste { 3714d3fc8b0SEd Maste u_int i; 3724d3fc8b0SEd Maste 3734d3fc8b0SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) 3744d3fc8b0SEd Maste free(prop[i]); 3754d3fc8b0SEd Maste } 3764d3fc8b0SEd Maste 3771765946bSDag-Erling Smørgrav /* put algorithm proposal into buffer */ 378bc5531deSDag-Erling Smørgrav int 379bc5531deSDag-Erling Smørgrav kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 380a04a10f8SKris Kennaway { 381043840dfSDag-Erling Smørgrav u_int i; 382bc5531deSDag-Erling Smørgrav int r; 3831e8db6e2SBrian Feldman 384bc5531deSDag-Erling Smørgrav sshbuf_reset(b); 385bc5531deSDag-Erling Smørgrav 386545d5ecaSDag-Erling Smørgrav /* 387545d5ecaSDag-Erling Smørgrav * add a dummy cookie, the cookie will be overwritten by 388545d5ecaSDag-Erling Smørgrav * kex_send_kexinit(), each time a kexinit is set 389545d5ecaSDag-Erling Smørgrav */ 390bc5531deSDag-Erling Smørgrav for (i = 0; i < KEX_COOKIE_LEN; i++) { 391bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0) 392bc5531deSDag-Erling Smørgrav return r; 393bc5531deSDag-Erling Smørgrav } 394bc5531deSDag-Erling Smørgrav for (i = 0; i < PROPOSAL_MAX; i++) { 395bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) 396bc5531deSDag-Erling Smørgrav return r; 397bc5531deSDag-Erling Smørgrav } 398bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ 399bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ 400bc5531deSDag-Erling Smørgrav return r; 401bc5531deSDag-Erling Smørgrav return 0; 402a04a10f8SKris Kennaway } 403a04a10f8SKris Kennaway 4041e8db6e2SBrian Feldman /* parse buffer and return algorithm proposal */ 405bc5531deSDag-Erling Smørgrav int 406bc5531deSDag-Erling Smørgrav kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) 4072632b0c8SKris Kennaway { 408bc5531deSDag-Erling Smørgrav struct sshbuf *b = NULL; 409bc5531deSDag-Erling Smørgrav u_char v; 410d4af9e69SDag-Erling Smørgrav u_int i; 411bc5531deSDag-Erling Smørgrav char **proposal = NULL; 412bc5531deSDag-Erling Smørgrav int r; 4132632b0c8SKris Kennaway 414bc5531deSDag-Erling Smørgrav *propp = NULL; 415bc5531deSDag-Erling Smørgrav if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) 416bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 417bc5531deSDag-Erling Smørgrav if ((b = sshbuf_fromb(raw)) == NULL) { 418bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 419bc5531deSDag-Erling Smørgrav goto out; 420bc5531deSDag-Erling Smørgrav } 42119261079SEd Maste if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */ 42219261079SEd Maste error_fr(r, "consume cookie"); 423bc5531deSDag-Erling Smørgrav goto out; 42419261079SEd Maste } 4252632b0c8SKris Kennaway /* extract kex init proposal strings */ 4262632b0c8SKris Kennaway for (i = 0; i < PROPOSAL_MAX; i++) { 42719261079SEd Maste if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) { 42819261079SEd Maste error_fr(r, "parse proposal %u", i); 429bc5531deSDag-Erling Smørgrav goto out; 43019261079SEd Maste } 431acc1a9efSDag-Erling Smørgrav debug2("%s: %s", proposal_names[i], proposal[i]); 4322632b0c8SKris Kennaway } 4331e8db6e2SBrian Feldman /* first kex follows / reserved */ 434fc1ba28aSDag-Erling Smørgrav if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ 43519261079SEd Maste (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */ 43619261079SEd Maste error_fr(r, "parse"); 437bc5531deSDag-Erling Smørgrav goto out; 43819261079SEd Maste } 439d0c8c0bcSDag-Erling Smørgrav if (first_kex_follows != NULL) 440fc1ba28aSDag-Erling Smørgrav *first_kex_follows = v; 441fc1ba28aSDag-Erling Smørgrav debug2("first_kex_follows %d ", v); 442fc1ba28aSDag-Erling Smørgrav debug2("reserved %u ", i); 443bc5531deSDag-Erling Smørgrav r = 0; 444bc5531deSDag-Erling Smørgrav *propp = proposal; 445bc5531deSDag-Erling Smørgrav out: 446bc5531deSDag-Erling Smørgrav if (r != 0 && proposal != NULL) 447bc5531deSDag-Erling Smørgrav kex_prop_free(proposal); 448bc5531deSDag-Erling Smørgrav sshbuf_free(b); 449bc5531deSDag-Erling Smørgrav return r; 4505b9b2fafSBrian Feldman } 4515b9b2fafSBrian Feldman 452bc5531deSDag-Erling Smørgrav void 4531e8db6e2SBrian Feldman kex_prop_free(char **proposal) 454a04a10f8SKris Kennaway { 455043840dfSDag-Erling Smørgrav u_int i; 4561e8db6e2SBrian Feldman 457557f75e5SDag-Erling Smørgrav if (proposal == NULL) 458557f75e5SDag-Erling Smørgrav return; 4591e8db6e2SBrian Feldman for (i = 0; i < PROPOSAL_MAX; i++) 460e4a9863fSDag-Erling Smørgrav free(proposal[i]); 461e4a9863fSDag-Erling Smørgrav free(proposal); 462a04a10f8SKris Kennaway } 463a04a10f8SKris Kennaway 46419261079SEd Maste int 4654f52dfbbSDag-Erling Smørgrav kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh) 466a04a10f8SKris Kennaway { 467acc1a9efSDag-Erling Smørgrav int r; 468acc1a9efSDag-Erling Smørgrav 469acc1a9efSDag-Erling Smørgrav error("kex protocol error: type %d seq %u", type, seq); 470acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || 471acc1a9efSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, seq)) != 0 || 472acc1a9efSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 473acc1a9efSDag-Erling Smørgrav return r; 474bc5531deSDag-Erling Smørgrav return 0; 475a04a10f8SKris Kennaway } 476a04a10f8SKris Kennaway 477ae1f160dSDag-Erling Smørgrav static void 478bc5531deSDag-Erling Smørgrav kex_reset_dispatch(struct ssh *ssh) 4795b9b2fafSBrian Feldman { 480bc5531deSDag-Erling Smørgrav ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, 481ae1f160dSDag-Erling Smørgrav SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); 4825b9b2fafSBrian Feldman } 4835b9b2fafSBrian Feldman 484acc1a9efSDag-Erling Smørgrav static int 485acc1a9efSDag-Erling Smørgrav kex_send_ext_info(struct ssh *ssh) 486acc1a9efSDag-Erling Smørgrav { 487acc1a9efSDag-Erling Smørgrav int r; 488ca86bcf2SDag-Erling Smørgrav char *algs; 489acc1a9efSDag-Erling Smørgrav 49019261079SEd Maste debug("Sending SSH2_MSG_EXT_INFO"); 491d93a896eSDag-Erling Smørgrav if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) 492ca86bcf2SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 493190cef3dSDag-Erling Smørgrav /* XXX filter algs list by allowed pubkey/hostbased types */ 494acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || 4951323ec57SEd Maste (r = sshpkt_put_u32(ssh, 2)) != 0 || 496acc1a9efSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || 497ca86bcf2SDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, algs)) != 0 || 4981323ec57SEd Maste (r = sshpkt_put_cstring(ssh, 4991323ec57SEd Maste "publickey-hostbound@openssh.com")) != 0 || 5001323ec57SEd Maste (r = sshpkt_put_cstring(ssh, "0")) != 0 || 50119261079SEd Maste (r = sshpkt_send(ssh)) != 0) { 50219261079SEd Maste error_fr(r, "compose"); 503ca86bcf2SDag-Erling Smørgrav goto out; 50419261079SEd Maste } 505ca86bcf2SDag-Erling Smørgrav /* success */ 506ca86bcf2SDag-Erling Smørgrav r = 0; 507ca86bcf2SDag-Erling Smørgrav out: 508ca86bcf2SDag-Erling Smørgrav free(algs); 509acc1a9efSDag-Erling Smørgrav return r; 510acc1a9efSDag-Erling Smørgrav } 511acc1a9efSDag-Erling Smørgrav 512bc5531deSDag-Erling Smørgrav int 513bc5531deSDag-Erling Smørgrav kex_send_newkeys(struct ssh *ssh) 514a04a10f8SKris Kennaway { 515bc5531deSDag-Erling Smørgrav int r; 516a04a10f8SKris Kennaway 517bc5531deSDag-Erling Smørgrav kex_reset_dispatch(ssh); 518bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || 519bc5531deSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 520bc5531deSDag-Erling Smørgrav return r; 5211e8db6e2SBrian Feldman debug("SSH2_MSG_NEWKEYS sent"); 522bc5531deSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); 52319261079SEd Maste if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0) 524acc1a9efSDag-Erling Smørgrav if ((r = kex_send_ext_info(ssh)) != 0) 525acc1a9efSDag-Erling Smørgrav return r; 52619261079SEd Maste debug("expecting SSH2_MSG_NEWKEYS"); 527bc5531deSDag-Erling Smørgrav return 0; 528bc5531deSDag-Erling Smørgrav } 5291e8db6e2SBrian Feldman 530acc1a9efSDag-Erling Smørgrav int 5314f52dfbbSDag-Erling Smørgrav kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) 532acc1a9efSDag-Erling Smørgrav { 533acc1a9efSDag-Erling Smørgrav struct kex *kex = ssh->kex; 534acc1a9efSDag-Erling Smørgrav u_int32_t i, ninfo; 535190cef3dSDag-Erling Smørgrav char *name; 5364f52dfbbSDag-Erling Smørgrav u_char *val; 5374f52dfbbSDag-Erling Smørgrav size_t vlen; 538acc1a9efSDag-Erling Smørgrav int r; 539acc1a9efSDag-Erling Smørgrav 540acc1a9efSDag-Erling Smørgrav debug("SSH2_MSG_EXT_INFO received"); 541acc1a9efSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); 542acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) 543acc1a9efSDag-Erling Smørgrav return r; 5444d3fc8b0SEd Maste if (ninfo >= 1024) { 5454d3fc8b0SEd Maste error("SSH2_MSG_EXT_INFO with too many entries, expected " 5464d3fc8b0SEd Maste "<=1024, received %u", ninfo); 5474d3fc8b0SEd Maste return SSH_ERR_INVALID_FORMAT; 5484d3fc8b0SEd Maste } 549acc1a9efSDag-Erling Smørgrav for (i = 0; i < ninfo; i++) { 550acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) 551acc1a9efSDag-Erling Smørgrav return r; 5524f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) { 553acc1a9efSDag-Erling Smørgrav free(name); 554acc1a9efSDag-Erling Smørgrav return r; 555acc1a9efSDag-Erling Smørgrav } 556acc1a9efSDag-Erling Smørgrav if (strcmp(name, "server-sig-algs") == 0) { 5574f52dfbbSDag-Erling Smørgrav /* Ensure no \0 lurking in value */ 5584f52dfbbSDag-Erling Smørgrav if (memchr(val, '\0', vlen) != NULL) { 55919261079SEd Maste error_f("nul byte in %s", name); 5604f52dfbbSDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 5614f52dfbbSDag-Erling Smørgrav } 56219261079SEd Maste debug_f("%s=<%s>", name, val); 563190cef3dSDag-Erling Smørgrav kex->server_sig_algs = val; 564190cef3dSDag-Erling Smørgrav val = NULL; 5651323ec57SEd Maste } else if (strcmp(name, 5661323ec57SEd Maste "publickey-hostbound@openssh.com") == 0) { 5671323ec57SEd Maste /* XXX refactor */ 5681323ec57SEd Maste /* Ensure no \0 lurking in value */ 5691323ec57SEd Maste if (memchr(val, '\0', vlen) != NULL) { 5701323ec57SEd Maste error_f("nul byte in %s", name); 5711323ec57SEd Maste return SSH_ERR_INVALID_FORMAT; 5721323ec57SEd Maste } 5731323ec57SEd Maste debug_f("%s=<%s>", name, val); 5741323ec57SEd Maste if (strcmp(val, "0") == 0) 5751323ec57SEd Maste kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND; 5761323ec57SEd Maste else { 5771323ec57SEd Maste debug_f("unsupported version of %s extension", 5781323ec57SEd Maste name); 5791323ec57SEd Maste } 5804f52dfbbSDag-Erling Smørgrav } else 58119261079SEd Maste debug_f("%s (unrecognised)", name); 582acc1a9efSDag-Erling Smørgrav free(name); 583acc1a9efSDag-Erling Smørgrav free(val); 584acc1a9efSDag-Erling Smørgrav } 585acc1a9efSDag-Erling Smørgrav return sshpkt_get_end(ssh); 586acc1a9efSDag-Erling Smørgrav } 587acc1a9efSDag-Erling Smørgrav 588bc5531deSDag-Erling Smørgrav static int 5894f52dfbbSDag-Erling Smørgrav kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) 590bc5531deSDag-Erling Smørgrav { 591bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 592bc5531deSDag-Erling Smørgrav int r; 593bc5531deSDag-Erling Smørgrav 594bc5531deSDag-Erling Smørgrav debug("SSH2_MSG_NEWKEYS received"); 595bc5531deSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); 596d93a896eSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 597bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_end(ssh)) != 0) 598bc5531deSDag-Erling Smørgrav return r; 599ca86bcf2SDag-Erling Smørgrav if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) 600ca86bcf2SDag-Erling Smørgrav return r; 6011e8db6e2SBrian Feldman kex->done = 1; 60219261079SEd Maste kex->flags &= ~KEX_INITIAL; 603bc5531deSDag-Erling Smørgrav sshbuf_reset(kex->peer); 604bc5531deSDag-Erling Smørgrav /* sshbuf_reset(kex->my); */ 6051e8db6e2SBrian Feldman kex->flags &= ~KEX_INIT_SENT; 606e4a9863fSDag-Erling Smørgrav free(kex->name); 6071e8db6e2SBrian Feldman kex->name = NULL; 608bc5531deSDag-Erling Smørgrav return 0; 609a04a10f8SKris Kennaway } 610a04a10f8SKris Kennaway 611bc5531deSDag-Erling Smørgrav int 612bc5531deSDag-Erling Smørgrav kex_send_kexinit(struct ssh *ssh) 613a04a10f8SKris Kennaway { 614545d5ecaSDag-Erling Smørgrav u_char *cookie; 615bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 616bc5531deSDag-Erling Smørgrav int r; 617545d5ecaSDag-Erling Smørgrav 61819261079SEd Maste if (kex == NULL) { 61919261079SEd Maste error_f("no kex"); 620bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 62119261079SEd Maste } 622bc5531deSDag-Erling Smørgrav if (kex->flags & KEX_INIT_SENT) 623bc5531deSDag-Erling Smørgrav return 0; 6241e8db6e2SBrian Feldman kex->done = 0; 625545d5ecaSDag-Erling Smørgrav 626545d5ecaSDag-Erling Smørgrav /* generate a random cookie */ 62719261079SEd Maste if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) { 62819261079SEd Maste error_f("bad kex length: %zu < %d", 62919261079SEd Maste sshbuf_len(kex->my), KEX_COOKIE_LEN); 630bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 63119261079SEd Maste } 63219261079SEd Maste if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) { 63319261079SEd Maste error_f("buffer error"); 634bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 63519261079SEd Maste } 636bc5531deSDag-Erling Smørgrav arc4random_buf(cookie, KEX_COOKIE_LEN); 637bc5531deSDag-Erling Smørgrav 638bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || 639bc5531deSDag-Erling Smørgrav (r = sshpkt_putb(ssh, kex->my)) != 0 || 64019261079SEd Maste (r = sshpkt_send(ssh)) != 0) { 64119261079SEd Maste error_fr(r, "compose reply"); 642bc5531deSDag-Erling Smørgrav return r; 64319261079SEd Maste } 6441e8db6e2SBrian Feldman debug("SSH2_MSG_KEXINIT sent"); 6451e8db6e2SBrian Feldman kex->flags |= KEX_INIT_SENT; 646bc5531deSDag-Erling Smørgrav return 0; 6471e8db6e2SBrian Feldman } 648a04a10f8SKris Kennaway 649bc5531deSDag-Erling Smørgrav int 6504f52dfbbSDag-Erling Smørgrav kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) 6511e8db6e2SBrian Feldman { 652bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 653bc5531deSDag-Erling Smørgrav const u_char *ptr; 654bc5531deSDag-Erling Smørgrav u_int i; 655bc5531deSDag-Erling Smørgrav size_t dlen; 656bc5531deSDag-Erling Smørgrav int r; 6572632b0c8SKris Kennaway 6581e8db6e2SBrian Feldman debug("SSH2_MSG_KEXINIT received"); 65919261079SEd Maste if (kex == NULL) { 66019261079SEd Maste error_f("no kex"); 66119261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 66219261079SEd Maste } 663ca86bcf2SDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); 664bc5531deSDag-Erling Smørgrav ptr = sshpkt_ptr(ssh, &dlen); 665bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) 666bc5531deSDag-Erling Smørgrav return r; 6671e8db6e2SBrian Feldman 6681e8db6e2SBrian Feldman /* discard packet */ 66919261079SEd Maste for (i = 0; i < KEX_COOKIE_LEN; i++) { 67019261079SEd Maste if ((r = sshpkt_get_u8(ssh, NULL)) != 0) { 67119261079SEd Maste error_fr(r, "discard cookie"); 672bc5531deSDag-Erling Smørgrav return r; 67319261079SEd Maste } 67419261079SEd Maste } 67519261079SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) { 67619261079SEd Maste if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { 67719261079SEd Maste error_fr(r, "discard proposal"); 678bc5531deSDag-Erling Smørgrav return r; 67919261079SEd Maste } 68019261079SEd Maste } 6816888a9beSDag-Erling Smørgrav /* 6826888a9beSDag-Erling Smørgrav * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported 6836888a9beSDag-Erling Smørgrav * KEX method has the server move first, but a server might be using 6846888a9beSDag-Erling Smørgrav * a custom method or one that we otherwise don't support. We should 6856888a9beSDag-Erling Smørgrav * be prepared to remember first_kex_follows here so we can eat a 6866888a9beSDag-Erling Smørgrav * packet later. 6876888a9beSDag-Erling Smørgrav * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means 6886888a9beSDag-Erling Smørgrav * for cases where the server *doesn't* go first. I guess we should 6896888a9beSDag-Erling Smørgrav * ignore it when it is set for these cases, which is what we do now. 6906888a9beSDag-Erling Smørgrav */ 691bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ 692bc5531deSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ 693bc5531deSDag-Erling Smørgrav (r = sshpkt_get_end(ssh)) != 0) 694bc5531deSDag-Erling Smørgrav return r; 6951e8db6e2SBrian Feldman 6961e8db6e2SBrian Feldman if (!(kex->flags & KEX_INIT_SENT)) 697bc5531deSDag-Erling Smørgrav if ((r = kex_send_kexinit(ssh)) != 0) 698bc5531deSDag-Erling Smørgrav return r; 699bc5531deSDag-Erling Smørgrav if ((r = kex_choose_conf(ssh)) != 0) 700bc5531deSDag-Erling Smørgrav return r; 7011e8db6e2SBrian Feldman 702bc5531deSDag-Erling Smørgrav if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) 703bc5531deSDag-Erling Smørgrav return (kex->kex[kex->kex_type])(ssh); 7041e8db6e2SBrian Feldman 70519261079SEd Maste error_f("unknown kex type %u", kex->kex_type); 706bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 7071e8db6e2SBrian Feldman } 708a04a10f8SKris Kennaway 70919261079SEd Maste struct kex * 71019261079SEd Maste kex_new(void) 711bc5531deSDag-Erling Smørgrav { 712bc5531deSDag-Erling Smørgrav struct kex *kex; 713bc5531deSDag-Erling Smørgrav 71419261079SEd Maste if ((kex = calloc(1, sizeof(*kex))) == NULL || 71519261079SEd Maste (kex->peer = sshbuf_new()) == NULL || 71619261079SEd Maste (kex->my = sshbuf_new()) == NULL || 71719261079SEd Maste (kex->client_version = sshbuf_new()) == NULL || 71819261079SEd Maste (kex->server_version = sshbuf_new()) == NULL || 71919261079SEd Maste (kex->session_id = sshbuf_new()) == NULL) { 720bc5531deSDag-Erling Smørgrav kex_free(kex); 72119261079SEd Maste return NULL; 72219261079SEd Maste } 72319261079SEd Maste return kex; 724bc5531deSDag-Erling Smørgrav } 725bc5531deSDag-Erling Smørgrav 726bc5531deSDag-Erling Smørgrav void 727bc5531deSDag-Erling Smørgrav kex_free_newkeys(struct newkeys *newkeys) 728bc5531deSDag-Erling Smørgrav { 729bc5531deSDag-Erling Smørgrav if (newkeys == NULL) 730bc5531deSDag-Erling Smørgrav return; 731bc5531deSDag-Erling Smørgrav if (newkeys->enc.key) { 732bc5531deSDag-Erling Smørgrav explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); 733bc5531deSDag-Erling Smørgrav free(newkeys->enc.key); 734bc5531deSDag-Erling Smørgrav newkeys->enc.key = NULL; 735bc5531deSDag-Erling Smørgrav } 736bc5531deSDag-Erling Smørgrav if (newkeys->enc.iv) { 737acc1a9efSDag-Erling Smørgrav explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); 738bc5531deSDag-Erling Smørgrav free(newkeys->enc.iv); 739bc5531deSDag-Erling Smørgrav newkeys->enc.iv = NULL; 740bc5531deSDag-Erling Smørgrav } 741bc5531deSDag-Erling Smørgrav free(newkeys->enc.name); 742bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); 743bc5531deSDag-Erling Smørgrav free(newkeys->comp.name); 744bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); 745bc5531deSDag-Erling Smørgrav mac_clear(&newkeys->mac); 746bc5531deSDag-Erling Smørgrav if (newkeys->mac.key) { 747bc5531deSDag-Erling Smørgrav explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); 748bc5531deSDag-Erling Smørgrav free(newkeys->mac.key); 749bc5531deSDag-Erling Smørgrav newkeys->mac.key = NULL; 750bc5531deSDag-Erling Smørgrav } 751bc5531deSDag-Erling Smørgrav free(newkeys->mac.name); 752bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); 75319261079SEd Maste freezero(newkeys, sizeof(*newkeys)); 754bc5531deSDag-Erling Smørgrav } 755bc5531deSDag-Erling Smørgrav 756bc5531deSDag-Erling Smørgrav void 757bc5531deSDag-Erling Smørgrav kex_free(struct kex *kex) 758bc5531deSDag-Erling Smørgrav { 759bc5531deSDag-Erling Smørgrav u_int mode; 760bc5531deSDag-Erling Smørgrav 76119261079SEd Maste if (kex == NULL) 76219261079SEd Maste return; 76319261079SEd Maste 764bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL 765bc5531deSDag-Erling Smørgrav DH_free(kex->dh); 766bc5531deSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC 767bc5531deSDag-Erling Smørgrav EC_KEY_free(kex->ec_client_key); 768bc5531deSDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */ 769bc5531deSDag-Erling Smørgrav #endif /* WITH_OPENSSL */ 770bc5531deSDag-Erling Smørgrav for (mode = 0; mode < MODE_MAX; mode++) { 771bc5531deSDag-Erling Smørgrav kex_free_newkeys(kex->newkeys[mode]); 772bc5531deSDag-Erling Smørgrav kex->newkeys[mode] = NULL; 773bc5531deSDag-Erling Smørgrav } 774bc5531deSDag-Erling Smørgrav sshbuf_free(kex->peer); 775bc5531deSDag-Erling Smørgrav sshbuf_free(kex->my); 77619261079SEd Maste sshbuf_free(kex->client_version); 77719261079SEd Maste sshbuf_free(kex->server_version); 77819261079SEd Maste sshbuf_free(kex->client_pub); 77919261079SEd Maste sshbuf_free(kex->session_id); 7801323ec57SEd Maste sshbuf_free(kex->initial_sig); 7811323ec57SEd Maste sshkey_free(kex->initial_hostkey); 782eccfee6eSDag-Erling Smørgrav free(kex->failed_choice); 783acc1a9efSDag-Erling Smørgrav free(kex->hostkey_alg); 784acc1a9efSDag-Erling Smørgrav free(kex->name); 785bc5531deSDag-Erling Smørgrav free(kex); 786bc5531deSDag-Erling Smørgrav } 787bc5531deSDag-Erling Smørgrav 788bc5531deSDag-Erling Smørgrav int 78919261079SEd Maste kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 79019261079SEd Maste { 79119261079SEd Maste int r; 79219261079SEd Maste 79319261079SEd Maste if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0) 79419261079SEd Maste return r; 79519261079SEd Maste ssh->kex->flags = KEX_INITIAL; 79619261079SEd Maste kex_reset_dispatch(ssh); 79719261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 79819261079SEd Maste return 0; 79919261079SEd Maste } 80019261079SEd Maste 80119261079SEd Maste int 802bc5531deSDag-Erling Smørgrav kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 803bc5531deSDag-Erling Smørgrav { 804bc5531deSDag-Erling Smørgrav int r; 805bc5531deSDag-Erling Smørgrav 80619261079SEd Maste if ((r = kex_ready(ssh, proposal)) != 0) 807bc5531deSDag-Erling Smørgrav return r; 808bc5531deSDag-Erling Smørgrav if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ 809bc5531deSDag-Erling Smørgrav kex_free(ssh->kex); 810bc5531deSDag-Erling Smørgrav ssh->kex = NULL; 811bc5531deSDag-Erling Smørgrav return r; 812bc5531deSDag-Erling Smørgrav } 813bc5531deSDag-Erling Smørgrav return 0; 814bc5531deSDag-Erling Smørgrav } 815bc5531deSDag-Erling Smørgrav 816acc1a9efSDag-Erling Smørgrav /* 817acc1a9efSDag-Erling Smørgrav * Request key re-exchange, returns 0 on success or a ssherr.h error 818acc1a9efSDag-Erling Smørgrav * code otherwise. Must not be called if KEX is incomplete or in-progress. 819acc1a9efSDag-Erling Smørgrav */ 820acc1a9efSDag-Erling Smørgrav int 821acc1a9efSDag-Erling Smørgrav kex_start_rekex(struct ssh *ssh) 822acc1a9efSDag-Erling Smørgrav { 823acc1a9efSDag-Erling Smørgrav if (ssh->kex == NULL) { 82419261079SEd Maste error_f("no kex"); 825acc1a9efSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 826acc1a9efSDag-Erling Smørgrav } 827acc1a9efSDag-Erling Smørgrav if (ssh->kex->done == 0) { 82819261079SEd Maste error_f("requested twice"); 829acc1a9efSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 830acc1a9efSDag-Erling Smørgrav } 831acc1a9efSDag-Erling Smørgrav ssh->kex->done = 0; 832acc1a9efSDag-Erling Smørgrav return kex_send_kexinit(ssh); 833acc1a9efSDag-Erling Smørgrav } 834acc1a9efSDag-Erling Smørgrav 835bc5531deSDag-Erling Smørgrav static int 836bc5531deSDag-Erling Smørgrav choose_enc(struct sshenc *enc, 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_CIPHER_ALG_MATCH; 842d93a896eSDag-Erling Smørgrav if ((enc->cipher = cipher_by_name(name)) == NULL) { 84319261079SEd Maste error_f("unsupported cipher %s", name); 844d93a896eSDag-Erling Smørgrav free(name); 845bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 846d93a896eSDag-Erling Smørgrav } 847a04a10f8SKris Kennaway enc->name = name; 848a04a10f8SKris Kennaway enc->enabled = 0; 849a04a10f8SKris Kennaway enc->iv = NULL; 8506888a9beSDag-Erling Smørgrav enc->iv_len = cipher_ivlen(enc->cipher); 851a04a10f8SKris Kennaway enc->key = NULL; 852ae1f160dSDag-Erling Smørgrav enc->key_len = cipher_keylen(enc->cipher); 853ae1f160dSDag-Erling Smørgrav enc->block_size = cipher_blocksize(enc->cipher); 854bc5531deSDag-Erling Smørgrav return 0; 855a04a10f8SKris Kennaway } 856761efaa7SDag-Erling Smørgrav 857bc5531deSDag-Erling Smørgrav static int 858bc5531deSDag-Erling Smørgrav choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) 859a04a10f8SKris Kennaway { 8601e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 861bc5531deSDag-Erling Smørgrav 862a04a10f8SKris Kennaway if (name == NULL) 863bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_MAC_ALG_MATCH; 864d93a896eSDag-Erling Smørgrav if (mac_setup(mac, name) < 0) { 86519261079SEd Maste error_f("unsupported MAC %s", name); 866d93a896eSDag-Erling Smørgrav free(name); 867bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 868d93a896eSDag-Erling Smørgrav } 869a04a10f8SKris Kennaway mac->name = name; 870a04a10f8SKris Kennaway mac->key = NULL; 871a04a10f8SKris Kennaway mac->enabled = 0; 872bc5531deSDag-Erling Smørgrav return 0; 873a04a10f8SKris Kennaway } 874761efaa7SDag-Erling Smørgrav 875bc5531deSDag-Erling Smørgrav static int 876bc5531deSDag-Erling Smørgrav choose_comp(struct sshcomp *comp, char *client, char *server) 877a04a10f8SKris Kennaway { 8781e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 879bc5531deSDag-Erling Smørgrav 880a04a10f8SKris Kennaway if (name == NULL) 881bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_COMPRESS_ALG_MATCH; 88219261079SEd Maste #ifdef WITH_ZLIB 883043840dfSDag-Erling Smørgrav if (strcmp(name, "zlib@openssh.com") == 0) { 884043840dfSDag-Erling Smørgrav comp->type = COMP_DELAYED; 885043840dfSDag-Erling Smørgrav } else if (strcmp(name, "zlib") == 0) { 886043840dfSDag-Erling Smørgrav comp->type = COMP_ZLIB; 88719261079SEd Maste } else 88819261079SEd Maste #endif /* WITH_ZLIB */ 88919261079SEd Maste if (strcmp(name, "none") == 0) { 890043840dfSDag-Erling Smørgrav comp->type = COMP_NONE; 891a04a10f8SKris Kennaway } else { 89219261079SEd Maste error_f("unsupported compression scheme %s", name); 893d93a896eSDag-Erling Smørgrav free(name); 894bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 895a04a10f8SKris Kennaway } 896a04a10f8SKris Kennaway comp->name = name; 897bc5531deSDag-Erling Smørgrav return 0; 898a04a10f8SKris Kennaway } 899761efaa7SDag-Erling Smørgrav 900bc5531deSDag-Erling Smørgrav static int 901bc5531deSDag-Erling Smørgrav choose_kex(struct kex *k, char *client, char *server) 902a04a10f8SKris Kennaway { 903e4a9863fSDag-Erling Smørgrav const struct kexalg *kexalg; 904e4a9863fSDag-Erling Smørgrav 9051e8db6e2SBrian Feldman k->name = match_list(client, server, NULL); 906bc5531deSDag-Erling Smørgrav 907acc1a9efSDag-Erling Smørgrav debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); 908a04a10f8SKris Kennaway if (k->name == NULL) 909bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_KEX_ALG_MATCH; 91019261079SEd Maste if ((kexalg = kex_alg_by_name(k->name)) == NULL) { 91119261079SEd Maste error_f("unsupported KEX method %s", k->name); 912bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 91319261079SEd Maste } 914e4a9863fSDag-Erling Smørgrav k->kex_type = kexalg->type; 915f7167e0eSDag-Erling Smørgrav k->hash_alg = kexalg->hash_alg; 916e4a9863fSDag-Erling Smørgrav k->ec_nid = kexalg->ec_nid; 917bc5531deSDag-Erling Smørgrav return 0; 918a04a10f8SKris Kennaway } 919021d409fSDag-Erling Smørgrav 920bc5531deSDag-Erling Smørgrav static int 921bc5531deSDag-Erling Smørgrav choose_hostkeyalg(struct kex *k, char *client, char *server) 922a04a10f8SKris Kennaway { 92319261079SEd Maste free(k->hostkey_alg); 924acc1a9efSDag-Erling Smørgrav k->hostkey_alg = match_list(client, server, NULL); 925bc5531deSDag-Erling Smørgrav 926acc1a9efSDag-Erling Smørgrav debug("kex: host key algorithm: %s", 927acc1a9efSDag-Erling Smørgrav k->hostkey_alg ? k->hostkey_alg : "(no match)"); 928acc1a9efSDag-Erling Smørgrav if (k->hostkey_alg == NULL) 929bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_HOSTKEY_ALG_MATCH; 930acc1a9efSDag-Erling Smørgrav k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); 93119261079SEd Maste if (k->hostkey_type == KEY_UNSPEC) { 93219261079SEd Maste error_f("unsupported hostkey algorithm %s", k->hostkey_alg); 933bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 93419261079SEd Maste } 935acc1a9efSDag-Erling Smørgrav k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); 936bc5531deSDag-Erling Smørgrav return 0; 937a04a10f8SKris Kennaway } 938a04a10f8SKris Kennaway 939d0c8c0bcSDag-Erling Smørgrav static int 940d0c8c0bcSDag-Erling Smørgrav proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) 941d0c8c0bcSDag-Erling Smørgrav { 942d0c8c0bcSDag-Erling Smørgrav static int check[] = { 943d0c8c0bcSDag-Erling Smørgrav PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 944d0c8c0bcSDag-Erling Smørgrav }; 945d0c8c0bcSDag-Erling Smørgrav int *idx; 946d0c8c0bcSDag-Erling Smørgrav char *p; 947d0c8c0bcSDag-Erling Smørgrav 948d0c8c0bcSDag-Erling Smørgrav for (idx = &check[0]; *idx != -1; idx++) { 949d0c8c0bcSDag-Erling Smørgrav if ((p = strchr(my[*idx], ',')) != NULL) 950d0c8c0bcSDag-Erling Smørgrav *p = '\0'; 951d0c8c0bcSDag-Erling Smørgrav if ((p = strchr(peer[*idx], ',')) != NULL) 952d0c8c0bcSDag-Erling Smørgrav *p = '\0'; 953d0c8c0bcSDag-Erling Smørgrav if (strcmp(my[*idx], peer[*idx]) != 0) { 954d0c8c0bcSDag-Erling Smørgrav debug2("proposal mismatch: my %s peer %s", 955d0c8c0bcSDag-Erling Smørgrav my[*idx], peer[*idx]); 956d0c8c0bcSDag-Erling Smørgrav return (0); 957d0c8c0bcSDag-Erling Smørgrav } 958d0c8c0bcSDag-Erling Smørgrav } 959d0c8c0bcSDag-Erling Smørgrav debug2("proposals match"); 960d0c8c0bcSDag-Erling Smørgrav return (1); 961d0c8c0bcSDag-Erling Smørgrav } 962d0c8c0bcSDag-Erling Smørgrav 9631323ec57SEd Maste /* returns non-zero if proposal contains any algorithm from algs */ 9641323ec57SEd Maste static int 9651323ec57SEd Maste has_any_alg(const char *proposal, const char *algs) 9661323ec57SEd Maste { 9671323ec57SEd Maste char *cp; 9681323ec57SEd Maste 9691323ec57SEd Maste if ((cp = match_list(proposal, algs, NULL)) == NULL) 9701323ec57SEd Maste return 0; 9711323ec57SEd Maste free(cp); 9721323ec57SEd Maste return 1; 9731323ec57SEd Maste } 9741323ec57SEd Maste 975bc5531deSDag-Erling Smørgrav static int 976bc5531deSDag-Erling Smørgrav kex_choose_conf(struct ssh *ssh) 977a04a10f8SKris Kennaway { 978bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 979bc5531deSDag-Erling Smørgrav struct newkeys *newkeys; 980bc5531deSDag-Erling Smørgrav char **my = NULL, **peer = NULL; 9811e8db6e2SBrian Feldman char **cprop, **sprop; 9821e8db6e2SBrian Feldman int nenc, nmac, ncomp; 983f7167e0eSDag-Erling Smørgrav u_int mode, ctos, need, dh_need, authlen; 984bc5531deSDag-Erling Smørgrav int r, first_kex_follows; 985a04a10f8SKris Kennaway 986acc1a9efSDag-Erling Smørgrav debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); 987acc1a9efSDag-Erling Smørgrav if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) 988acc1a9efSDag-Erling Smørgrav goto out; 989acc1a9efSDag-Erling Smørgrav debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); 990acc1a9efSDag-Erling Smørgrav if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) 991bc5531deSDag-Erling Smørgrav goto out; 992a04a10f8SKris Kennaway 9931e8db6e2SBrian Feldman if (kex->server) { 9941e8db6e2SBrian Feldman cprop=peer; 9951e8db6e2SBrian Feldman sprop=my; 9961e8db6e2SBrian Feldman } else { 9971e8db6e2SBrian Feldman cprop=my; 9981e8db6e2SBrian Feldman sprop=peer; 9991e8db6e2SBrian Feldman } 10001e8db6e2SBrian Feldman 1001acc1a9efSDag-Erling Smørgrav /* Check whether client supports ext_info_c */ 100219261079SEd Maste if (kex->server && (kex->flags & KEX_INITIAL)) { 1003acc1a9efSDag-Erling Smørgrav char *ext; 1004bc5531deSDag-Erling Smørgrav 1005acc1a9efSDag-Erling Smørgrav ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); 1006ca86bcf2SDag-Erling Smørgrav kex->ext_info_c = (ext != NULL); 1007acc1a9efSDag-Erling Smørgrav free(ext); 1008b15c8340SDag-Erling Smørgrav } 1009b15c8340SDag-Erling Smørgrav 10101323ec57SEd Maste /* Check whether client supports rsa-sha2 algorithms */ 10111323ec57SEd Maste if (kex->server && (kex->flags & KEX_INITIAL)) { 10121323ec57SEd Maste if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], 10131323ec57SEd Maste "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com")) 10141323ec57SEd Maste kex->flags |= KEX_RSA_SHA2_256_SUPPORTED; 10151323ec57SEd Maste if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], 10161323ec57SEd Maste "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com")) 10171323ec57SEd Maste kex->flags |= KEX_RSA_SHA2_512_SUPPORTED; 10181323ec57SEd Maste } 10191323ec57SEd Maste 10201e8db6e2SBrian Feldman /* Algorithm Negotiation */ 1021acc1a9efSDag-Erling Smørgrav if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], 1022acc1a9efSDag-Erling Smørgrav sprop[PROPOSAL_KEX_ALGS])) != 0) { 1023acc1a9efSDag-Erling Smørgrav kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; 1024acc1a9efSDag-Erling Smørgrav peer[PROPOSAL_KEX_ALGS] = NULL; 1025acc1a9efSDag-Erling Smørgrav goto out; 1026acc1a9efSDag-Erling Smørgrav } 1027acc1a9efSDag-Erling Smørgrav if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], 1028acc1a9efSDag-Erling Smørgrav sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { 1029acc1a9efSDag-Erling Smørgrav kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; 1030acc1a9efSDag-Erling Smørgrav peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; 1031acc1a9efSDag-Erling Smørgrav goto out; 1032acc1a9efSDag-Erling Smørgrav } 1033a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 1034bc5531deSDag-Erling Smørgrav if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { 1035bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1036bc5531deSDag-Erling Smørgrav goto out; 1037bc5531deSDag-Erling Smørgrav } 10381e8db6e2SBrian Feldman kex->newkeys[mode] = newkeys; 1039d4af9e69SDag-Erling Smørgrav ctos = (!kex->server && mode == MODE_OUT) || 1040d4af9e69SDag-Erling Smørgrav (kex->server && mode == MODE_IN); 1041a04a10f8SKris Kennaway nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; 1042a04a10f8SKris Kennaway nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; 1043a04a10f8SKris Kennaway ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; 1044bc5531deSDag-Erling Smørgrav if ((r = choose_enc(&newkeys->enc, cprop[nenc], 1045eccfee6eSDag-Erling Smørgrav sprop[nenc])) != 0) { 1046eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[nenc]; 1047eccfee6eSDag-Erling Smørgrav peer[nenc] = NULL; 1048bc5531deSDag-Erling Smørgrav goto out; 1049eccfee6eSDag-Erling Smørgrav } 10506888a9beSDag-Erling Smørgrav authlen = cipher_authlen(newkeys->enc.cipher); 1051bc5531deSDag-Erling Smørgrav /* ignore mac for authenticated encryption */ 1052bc5531deSDag-Erling Smørgrav if (authlen == 0 && 1053bc5531deSDag-Erling Smørgrav (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], 1054eccfee6eSDag-Erling Smørgrav sprop[nmac])) != 0) { 1055eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[nmac]; 1056eccfee6eSDag-Erling Smørgrav peer[nmac] = NULL; 1057bc5531deSDag-Erling Smørgrav goto out; 1058eccfee6eSDag-Erling Smørgrav } 1059bc5531deSDag-Erling Smørgrav if ((r = choose_comp(&newkeys->comp, cprop[ncomp], 1060eccfee6eSDag-Erling Smørgrav sprop[ncomp])) != 0) { 1061eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[ncomp]; 1062eccfee6eSDag-Erling Smørgrav peer[ncomp] = NULL; 1063bc5531deSDag-Erling Smørgrav goto out; 1064eccfee6eSDag-Erling Smørgrav } 1065acc1a9efSDag-Erling Smørgrav debug("kex: %s cipher: %s MAC: %s compression: %s", 1066a04a10f8SKris Kennaway ctos ? "client->server" : "server->client", 10671e8db6e2SBrian Feldman newkeys->enc.name, 10686888a9beSDag-Erling Smørgrav authlen == 0 ? newkeys->mac.name : "<implicit>", 10691e8db6e2SBrian Feldman newkeys->comp.name); 1070a04a10f8SKris Kennaway } 1071f7167e0eSDag-Erling Smørgrav need = dh_need = 0; 1072a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 10731e8db6e2SBrian Feldman newkeys = kex->newkeys[mode]; 1074ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.key_len); 1075ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.block_size); 1076ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.iv_len); 1077ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->mac.key_len); 1078ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher)); 1079ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); 1080ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); 1081ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); 1082a04a10f8SKris Kennaway } 10832632b0c8SKris Kennaway /* XXX need runden? */ 10841e8db6e2SBrian Feldman kex->we_need = need; 1085f7167e0eSDag-Erling Smørgrav kex->dh_need = dh_need; 10861e8db6e2SBrian Feldman 1087d0c8c0bcSDag-Erling Smørgrav /* ignore the next message if the proposals do not match */ 108847dd1d1bSDag-Erling Smørgrav if (first_kex_follows && !proposals_match(my, peer)) 1089bc5531deSDag-Erling Smørgrav ssh->dispatch_skip_packets = 1; 1090bc5531deSDag-Erling Smørgrav r = 0; 1091bc5531deSDag-Erling Smørgrav out: 10921e8db6e2SBrian Feldman kex_prop_free(my); 10931e8db6e2SBrian Feldman kex_prop_free(peer); 1094bc5531deSDag-Erling Smørgrav return r; 1095a04a10f8SKris Kennaway } 1096a04a10f8SKris Kennaway 1097bc5531deSDag-Erling Smørgrav static int 1098bc5531deSDag-Erling Smørgrav derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, 1099bc5531deSDag-Erling Smørgrav const struct sshbuf *shared_secret, u_char **keyp) 1100a04a10f8SKris Kennaway { 1101bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 1102bc5531deSDag-Erling Smørgrav struct ssh_digest_ctx *hashctx = NULL; 11031e8db6e2SBrian Feldman char c = id; 1104043840dfSDag-Erling Smørgrav u_int have; 1105f7167e0eSDag-Erling Smørgrav size_t mdsz; 1106043840dfSDag-Erling Smørgrav u_char *digest; 1107bc5531deSDag-Erling Smørgrav int r; 1108043840dfSDag-Erling Smørgrav 1109f7167e0eSDag-Erling Smørgrav if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) 1110bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_ARGUMENT; 1111ca86bcf2SDag-Erling Smørgrav if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) { 1112bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1113bc5531deSDag-Erling Smørgrav goto out; 1114bc5531deSDag-Erling Smørgrav } 11151e8db6e2SBrian Feldman 11161e8db6e2SBrian Feldman /* K1 = HASH(K || H || "A" || session_id) */ 1117bc5531deSDag-Erling Smørgrav if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || 1118bc5531deSDag-Erling Smørgrav ssh_digest_update_buffer(hashctx, shared_secret) != 0 || 1119f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, hash, hashlen) != 0 || 1120f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, &c, 1) != 0 || 112119261079SEd Maste ssh_digest_update_buffer(hashctx, kex->session_id) != 0 || 1122bc5531deSDag-Erling Smørgrav ssh_digest_final(hashctx, digest, mdsz) != 0) { 1123bc5531deSDag-Erling Smørgrav r = SSH_ERR_LIBCRYPTO_ERROR; 112419261079SEd Maste error_f("KEX hash failed"); 1125bc5531deSDag-Erling Smørgrav goto out; 1126bc5531deSDag-Erling Smørgrav } 1127f7167e0eSDag-Erling Smørgrav ssh_digest_free(hashctx); 1128bc5531deSDag-Erling Smørgrav hashctx = NULL; 11291e8db6e2SBrian Feldman 11301e8db6e2SBrian Feldman /* 11311e8db6e2SBrian Feldman * expand key: 11321e8db6e2SBrian Feldman * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) 11331e8db6e2SBrian Feldman * Key = K1 || K2 || ... || Kn 11341e8db6e2SBrian Feldman */ 11351e8db6e2SBrian Feldman for (have = mdsz; need > have; have += mdsz) { 1136bc5531deSDag-Erling Smørgrav if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || 1137bc5531deSDag-Erling Smørgrav ssh_digest_update_buffer(hashctx, shared_secret) != 0 || 1138f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, hash, hashlen) != 0 || 1139bc5531deSDag-Erling Smørgrav ssh_digest_update(hashctx, digest, have) != 0 || 1140bc5531deSDag-Erling Smørgrav ssh_digest_final(hashctx, digest + have, mdsz) != 0) { 114119261079SEd Maste error_f("KDF failed"); 1142bc5531deSDag-Erling Smørgrav r = SSH_ERR_LIBCRYPTO_ERROR; 1143bc5531deSDag-Erling Smørgrav goto out; 11441e8db6e2SBrian Feldman } 1145bc5531deSDag-Erling Smørgrav ssh_digest_free(hashctx); 1146bc5531deSDag-Erling Smørgrav hashctx = NULL; 1147bc5531deSDag-Erling Smørgrav } 11481e8db6e2SBrian Feldman #ifdef DEBUG_KEX 11491e8db6e2SBrian Feldman fprintf(stderr, "key '%c'== ", c); 11501e8db6e2SBrian Feldman dump_digest("key", digest, need); 11511e8db6e2SBrian Feldman #endif 1152bc5531deSDag-Erling Smørgrav *keyp = digest; 1153bc5531deSDag-Erling Smørgrav digest = NULL; 1154bc5531deSDag-Erling Smørgrav r = 0; 1155bc5531deSDag-Erling Smørgrav out: 1156bc5531deSDag-Erling Smørgrav free(digest); 1157bc5531deSDag-Erling Smørgrav ssh_digest_free(hashctx); 1158bc5531deSDag-Erling Smørgrav return r; 11591e8db6e2SBrian Feldman } 11601e8db6e2SBrian Feldman 11611e8db6e2SBrian Feldman #define NKEYS 6 1162bc5531deSDag-Erling Smørgrav int 1163bc5531deSDag-Erling Smørgrav kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, 1164bc5531deSDag-Erling Smørgrav const struct sshbuf *shared_secret) 11651e8db6e2SBrian Feldman { 1166bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 11671e8db6e2SBrian Feldman u_char *keys[NKEYS]; 1168bc5531deSDag-Erling Smørgrav u_int i, j, mode, ctos; 1169bc5531deSDag-Erling Smørgrav int r; 1170a04a10f8SKris Kennaway 117119261079SEd Maste /* save initial hash as session id */ 117219261079SEd Maste if ((kex->flags & KEX_INITIAL) != 0) { 117319261079SEd Maste if (sshbuf_len(kex->session_id) != 0) { 117419261079SEd Maste error_f("already have session ID at kex"); 117519261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 117619261079SEd Maste } 117719261079SEd Maste if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0) 117819261079SEd Maste return r; 117919261079SEd Maste } else if (sshbuf_len(kex->session_id) == 0) { 118019261079SEd Maste error_f("no session ID in rekex"); 118119261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 118219261079SEd Maste } 1183021d409fSDag-Erling Smørgrav for (i = 0; i < NKEYS; i++) { 1184bc5531deSDag-Erling Smørgrav if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, 1185bc5531deSDag-Erling Smørgrav shared_secret, &keys[i])) != 0) { 1186bc5531deSDag-Erling Smørgrav for (j = 0; j < i; j++) 1187bc5531deSDag-Erling Smørgrav free(keys[j]); 1188bc5531deSDag-Erling Smørgrav return r; 1189021d409fSDag-Erling Smørgrav } 1190bc5531deSDag-Erling Smørgrav } 1191a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 1192761efaa7SDag-Erling Smørgrav ctos = (!kex->server && mode == MODE_OUT) || 1193761efaa7SDag-Erling Smørgrav (kex->server && mode == MODE_IN); 1194bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; 1195bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; 1196bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; 1197a04a10f8SKris Kennaway } 1198bc5531deSDag-Erling Smørgrav return 0; 1199a04a10f8SKris Kennaway } 12001e8db6e2SBrian Feldman 1201bc5531deSDag-Erling Smørgrav int 120219261079SEd Maste kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) 1203f7167e0eSDag-Erling Smørgrav { 120419261079SEd Maste struct kex *kex = ssh->kex; 1205f7167e0eSDag-Erling Smørgrav 120619261079SEd Maste *pubp = NULL; 120719261079SEd Maste *prvp = NULL; 120819261079SEd Maste if (kex->load_host_public_key == NULL || 120919261079SEd Maste kex->load_host_private_key == NULL) { 121019261079SEd Maste error_f("missing hostkey loader"); 121119261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 1212f7167e0eSDag-Erling Smørgrav } 121319261079SEd Maste *pubp = kex->load_host_public_key(kex->hostkey_type, 121419261079SEd Maste kex->hostkey_nid, ssh); 121519261079SEd Maste *prvp = kex->load_host_private_key(kex->hostkey_type, 121619261079SEd Maste kex->hostkey_nid, ssh); 121719261079SEd Maste if (*pubp == NULL) 121819261079SEd Maste return SSH_ERR_NO_HOSTKEY_LOADED; 121919261079SEd Maste return 0; 122019261079SEd Maste } 1221f7167e0eSDag-Erling Smørgrav 122219261079SEd Maste int 122319261079SEd Maste kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) 122419261079SEd Maste { 122519261079SEd Maste struct kex *kex = ssh->kex; 122619261079SEd Maste 122719261079SEd Maste if (kex->verify_host_key == NULL) { 122819261079SEd Maste error_f("missing hostkey verifier"); 122919261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 123019261079SEd Maste } 123119261079SEd Maste if (server_host_key->type != kex->hostkey_type || 123219261079SEd Maste (kex->hostkey_type == KEY_ECDSA && 123319261079SEd Maste server_host_key->ecdsa_nid != kex->hostkey_nid)) 123419261079SEd Maste return SSH_ERR_KEY_TYPE_MISMATCH; 123519261079SEd Maste if (kex->verify_host_key(server_host_key, ssh) == -1) 123619261079SEd Maste return SSH_ERR_SIGNATURE_INVALID; 123719261079SEd Maste return 0; 123819261079SEd Maste } 1239d74d50a8SDag-Erling Smørgrav 12404a421b63SDag-Erling Smørgrav #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) 12411e8db6e2SBrian Feldman void 124219261079SEd Maste dump_digest(const char *msg, const u_char *digest, int len) 12431e8db6e2SBrian Feldman { 12441e8db6e2SBrian Feldman fprintf(stderr, "%s\n", msg); 1245bc5531deSDag-Erling Smørgrav sshbuf_dump_data(digest, len, stderr); 12461e8db6e2SBrian Feldman } 12471e8db6e2SBrian Feldman #endif 124819261079SEd Maste 124919261079SEd Maste /* 125019261079SEd Maste * Send a plaintext error message to the peer, suffixed by \r\n. 125119261079SEd Maste * Only used during banner exchange, and there only for the server. 125219261079SEd Maste */ 125319261079SEd Maste static void 125419261079SEd Maste send_error(struct ssh *ssh, char *msg) 125519261079SEd Maste { 125619261079SEd Maste char *crnl = "\r\n"; 125719261079SEd Maste 125819261079SEd Maste if (!ssh->kex->server) 125919261079SEd Maste return; 126019261079SEd Maste 126119261079SEd Maste if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), 126219261079SEd Maste msg, strlen(msg)) != strlen(msg) || 126319261079SEd Maste atomicio(vwrite, ssh_packet_get_connection_out(ssh), 126419261079SEd Maste crnl, strlen(crnl)) != strlen(crnl)) 126519261079SEd Maste error_f("write: %.100s", strerror(errno)); 126619261079SEd Maste } 126719261079SEd Maste 126819261079SEd Maste /* 126919261079SEd Maste * Sends our identification string and waits for the peer's. Will block for 127019261079SEd Maste * up to timeout_ms (or indefinitely if timeout_ms <= 0). 127119261079SEd Maste * Returns on 0 success or a ssherr.h code on failure. 127219261079SEd Maste */ 127319261079SEd Maste int 127419261079SEd Maste kex_exchange_identification(struct ssh *ssh, int timeout_ms, 127519261079SEd Maste const char *version_addendum) 127619261079SEd Maste { 127719261079SEd Maste int remote_major, remote_minor, mismatch, oerrno = 0; 1278f374ba41SEd Maste size_t len, n; 127919261079SEd Maste int r, expect_nl; 128019261079SEd Maste u_char c; 128119261079SEd Maste struct sshbuf *our_version = ssh->kex->server ? 128219261079SEd Maste ssh->kex->server_version : ssh->kex->client_version; 128319261079SEd Maste struct sshbuf *peer_version = ssh->kex->server ? 128419261079SEd Maste ssh->kex->client_version : ssh->kex->server_version; 128519261079SEd Maste char *our_version_string = NULL, *peer_version_string = NULL; 128619261079SEd Maste char *cp, *remote_version = NULL; 128719261079SEd Maste 128819261079SEd Maste /* Prepare and send our banner */ 128919261079SEd Maste sshbuf_reset(our_version); 129019261079SEd Maste if (version_addendum != NULL && *version_addendum == '\0') 129119261079SEd Maste version_addendum = NULL; 129219261079SEd Maste if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n", 129319261079SEd Maste PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, 129419261079SEd Maste version_addendum == NULL ? "" : " ", 129519261079SEd Maste version_addendum == NULL ? "" : version_addendum)) != 0) { 129619261079SEd Maste oerrno = errno; 129719261079SEd Maste error_fr(r, "sshbuf_putf"); 129819261079SEd Maste goto out; 129919261079SEd Maste } 130019261079SEd Maste 130119261079SEd Maste if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), 130219261079SEd Maste sshbuf_mutable_ptr(our_version), 130319261079SEd Maste sshbuf_len(our_version)) != sshbuf_len(our_version)) { 130419261079SEd Maste oerrno = errno; 130519261079SEd Maste debug_f("write: %.100s", strerror(errno)); 130619261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 130719261079SEd Maste goto out; 130819261079SEd Maste } 130919261079SEd Maste if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */ 131019261079SEd Maste oerrno = errno; 131119261079SEd Maste error_fr(r, "sshbuf_consume_end"); 131219261079SEd Maste goto out; 131319261079SEd Maste } 131419261079SEd Maste our_version_string = sshbuf_dup_string(our_version); 131519261079SEd Maste if (our_version_string == NULL) { 131619261079SEd Maste error_f("sshbuf_dup_string failed"); 131719261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 131819261079SEd Maste goto out; 131919261079SEd Maste } 132019261079SEd Maste debug("Local version string %.100s", our_version_string); 132119261079SEd Maste 132219261079SEd Maste /* Read other side's version identification. */ 132319261079SEd Maste for (n = 0; ; n++) { 132419261079SEd Maste if (n >= SSH_MAX_PRE_BANNER_LINES) { 132519261079SEd Maste send_error(ssh, "No SSH identification string " 132619261079SEd Maste "received."); 132719261079SEd Maste error_f("No SSH version received in first %u lines " 132819261079SEd Maste "from server", SSH_MAX_PRE_BANNER_LINES); 132919261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 133019261079SEd Maste goto out; 133119261079SEd Maste } 133219261079SEd Maste sshbuf_reset(peer_version); 133319261079SEd Maste expect_nl = 0; 1334f374ba41SEd Maste for (;;) { 133519261079SEd Maste if (timeout_ms > 0) { 133619261079SEd Maste r = waitrfd(ssh_packet_get_connection_in(ssh), 1337*1b91d634SEd Maste &timeout_ms, NULL); 133819261079SEd Maste if (r == -1 && errno == ETIMEDOUT) { 133919261079SEd Maste send_error(ssh, "Timed out waiting " 134019261079SEd Maste "for SSH identification string."); 134119261079SEd Maste error("Connection timed out during " 134219261079SEd Maste "banner exchange"); 134319261079SEd Maste r = SSH_ERR_CONN_TIMEOUT; 134419261079SEd Maste goto out; 134519261079SEd Maste } else if (r == -1) { 134619261079SEd Maste oerrno = errno; 134719261079SEd Maste error_f("%s", strerror(errno)); 134819261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 134919261079SEd Maste goto out; 135019261079SEd Maste } 135119261079SEd Maste } 135219261079SEd Maste 135319261079SEd Maste len = atomicio(read, ssh_packet_get_connection_in(ssh), 135419261079SEd Maste &c, 1); 135519261079SEd Maste if (len != 1 && errno == EPIPE) { 135619261079SEd Maste error_f("Connection closed by remote host"); 135719261079SEd Maste r = SSH_ERR_CONN_CLOSED; 135819261079SEd Maste goto out; 135919261079SEd Maste } else if (len != 1) { 136019261079SEd Maste oerrno = errno; 136119261079SEd Maste error_f("read: %.100s", strerror(errno)); 136219261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 136319261079SEd Maste goto out; 136419261079SEd Maste } 136519261079SEd Maste if (c == '\r') { 136619261079SEd Maste expect_nl = 1; 136719261079SEd Maste continue; 136819261079SEd Maste } 136919261079SEd Maste if (c == '\n') 137019261079SEd Maste break; 137119261079SEd Maste if (c == '\0' || expect_nl) { 137219261079SEd Maste error_f("banner line contains invalid " 137319261079SEd Maste "characters"); 137419261079SEd Maste goto invalid; 137519261079SEd Maste } 137619261079SEd Maste if ((r = sshbuf_put_u8(peer_version, c)) != 0) { 137719261079SEd Maste oerrno = errno; 137819261079SEd Maste error_fr(r, "sshbuf_put"); 137919261079SEd Maste goto out; 138019261079SEd Maste } 138119261079SEd Maste if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { 138219261079SEd Maste error_f("banner line too long"); 138319261079SEd Maste goto invalid; 138419261079SEd Maste } 138519261079SEd Maste } 138619261079SEd Maste /* Is this an actual protocol banner? */ 138719261079SEd Maste if (sshbuf_len(peer_version) > 4 && 138819261079SEd Maste memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0) 138919261079SEd Maste break; 139019261079SEd Maste /* If not, then just log the line and continue */ 139119261079SEd Maste if ((cp = sshbuf_dup_string(peer_version)) == NULL) { 139219261079SEd Maste error_f("sshbuf_dup_string failed"); 139319261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 139419261079SEd Maste goto out; 139519261079SEd Maste } 139619261079SEd Maste /* Do not accept lines before the SSH ident from a client */ 139719261079SEd Maste if (ssh->kex->server) { 139819261079SEd Maste error_f("client sent invalid protocol identifier " 139919261079SEd Maste "\"%.256s\"", cp); 140019261079SEd Maste free(cp); 140119261079SEd Maste goto invalid; 140219261079SEd Maste } 140319261079SEd Maste debug_f("banner line %zu: %s", n, cp); 140419261079SEd Maste free(cp); 140519261079SEd Maste } 140619261079SEd Maste peer_version_string = sshbuf_dup_string(peer_version); 140719261079SEd Maste if (peer_version_string == NULL) 14084d3fc8b0SEd Maste fatal_f("sshbuf_dup_string failed"); 140919261079SEd Maste /* XXX must be same size for sscanf */ 141019261079SEd Maste if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) { 141119261079SEd Maste error_f("calloc failed"); 141219261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 141319261079SEd Maste goto out; 141419261079SEd Maste } 141519261079SEd Maste 141619261079SEd Maste /* 141719261079SEd Maste * Check that the versions match. In future this might accept 141819261079SEd Maste * several versions and set appropriate flags to handle them. 141919261079SEd Maste */ 142019261079SEd Maste if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n", 142119261079SEd Maste &remote_major, &remote_minor, remote_version) != 3) { 142219261079SEd Maste error("Bad remote protocol version identification: '%.100s'", 142319261079SEd Maste peer_version_string); 142419261079SEd Maste invalid: 142519261079SEd Maste send_error(ssh, "Invalid SSH identification string."); 142619261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 142719261079SEd Maste goto out; 142819261079SEd Maste } 142919261079SEd Maste debug("Remote protocol version %d.%d, remote software version %.100s", 143019261079SEd Maste remote_major, remote_minor, remote_version); 143119261079SEd Maste compat_banner(ssh, remote_version); 143219261079SEd Maste 143319261079SEd Maste mismatch = 0; 143419261079SEd Maste switch (remote_major) { 143519261079SEd Maste case 2: 143619261079SEd Maste break; 143719261079SEd Maste case 1: 143819261079SEd Maste if (remote_minor != 99) 143919261079SEd Maste mismatch = 1; 144019261079SEd Maste break; 144119261079SEd Maste default: 144219261079SEd Maste mismatch = 1; 144319261079SEd Maste break; 144419261079SEd Maste } 144519261079SEd Maste if (mismatch) { 144619261079SEd Maste error("Protocol major versions differ: %d vs. %d", 144719261079SEd Maste PROTOCOL_MAJOR_2, remote_major); 144819261079SEd Maste send_error(ssh, "Protocol major versions differ."); 144919261079SEd Maste r = SSH_ERR_NO_PROTOCOL_VERSION; 145019261079SEd Maste goto out; 145119261079SEd Maste } 145219261079SEd Maste 145319261079SEd Maste if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) { 145419261079SEd Maste logit("probed from %s port %d with %s. Don't panic.", 145519261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 145619261079SEd Maste peer_version_string); 145719261079SEd Maste r = SSH_ERR_CONN_CLOSED; /* XXX */ 145819261079SEd Maste goto out; 145919261079SEd Maste } 146019261079SEd Maste if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) { 146119261079SEd Maste logit("scanned from %s port %d with %s. Don't panic.", 146219261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 146319261079SEd Maste peer_version_string); 146419261079SEd Maste r = SSH_ERR_CONN_CLOSED; /* XXX */ 146519261079SEd Maste goto out; 146619261079SEd Maste } 146719261079SEd Maste /* success */ 146819261079SEd Maste r = 0; 146919261079SEd Maste out: 147019261079SEd Maste free(our_version_string); 147119261079SEd Maste free(peer_version_string); 147219261079SEd Maste free(remote_version); 147319261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR) 147419261079SEd Maste errno = oerrno; 147519261079SEd Maste return r; 147619261079SEd Maste } 147719261079SEd Maste 1478