1*a91a2465SEd Maste /* $OpenBSD: kex.c,v 1.185 2024/01/08 00:34:33 djm Exp $ */ 2a04a10f8SKris Kennaway /* 3ae1f160dSDag-Erling Smørgrav * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4a04a10f8SKris Kennaway * 5a04a10f8SKris Kennaway * Redistribution and use in source and binary forms, with or without 6a04a10f8SKris Kennaway * modification, are permitted provided that the following conditions 7a04a10f8SKris Kennaway * are met: 8a04a10f8SKris Kennaway * 1. Redistributions of source code must retain the above copyright 9a04a10f8SKris Kennaway * notice, this list of conditions and the following disclaimer. 10a04a10f8SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 11a04a10f8SKris Kennaway * notice, this list of conditions and the following disclaimer in the 12a04a10f8SKris Kennaway * documentation and/or other materials provided with the distribution. 13a04a10f8SKris Kennaway * 14a04a10f8SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15a04a10f8SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16a04a10f8SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17a04a10f8SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18a04a10f8SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19a04a10f8SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20a04a10f8SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21a04a10f8SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22a04a10f8SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23a04a10f8SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24a04a10f8SKris Kennaway */ 25a04a10f8SKris Kennaway 26a04a10f8SKris Kennaway #include "includes.h" 27761efaa7SDag-Erling Smørgrav 2819261079SEd Maste #include <sys/types.h> 2919261079SEd Maste #include <errno.h> 30761efaa7SDag-Erling Smørgrav #include <signal.h> 31761efaa7SDag-Erling Smørgrav #include <stdarg.h> 32761efaa7SDag-Erling Smørgrav #include <stdio.h> 33761efaa7SDag-Erling Smørgrav #include <stdlib.h> 34761efaa7SDag-Erling Smørgrav #include <string.h> 3519261079SEd Maste #include <unistd.h> 3619261079SEd Maste #ifdef HAVE_POLL_H 3719261079SEd Maste #include <poll.h> 3819261079SEd Maste #endif 39a04a10f8SKris Kennaway 40a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL 411e8db6e2SBrian Feldman #include <openssl/crypto.h> 42076ad2f8SDag-Erling Smørgrav #include <openssl/dh.h> 43a0ee8cc6SDag-Erling Smørgrav #endif 441e8db6e2SBrian Feldman 4519261079SEd Maste #include "ssh.h" 46761efaa7SDag-Erling Smørgrav #include "ssh2.h" 4719261079SEd Maste #include "atomicio.h" 4819261079SEd Maste #include "version.h" 492632b0c8SKris Kennaway #include "packet.h" 50a04a10f8SKris Kennaway #include "compat.h" 511e8db6e2SBrian Feldman #include "cipher.h" 52bc5531deSDag-Erling Smørgrav #include "sshkey.h" 53761efaa7SDag-Erling Smørgrav #include "kex.h" 541e8db6e2SBrian Feldman #include "log.h" 551e8db6e2SBrian Feldman #include "mac.h" 561e8db6e2SBrian Feldman #include "match.h" 57bc5531deSDag-Erling Smørgrav #include "misc.h" 581e8db6e2SBrian Feldman #include "dispatch.h" 59545d5ecaSDag-Erling Smørgrav #include "monitor.h" 604d3fc8b0SEd Maste #include "myproposal.h" 61bc5531deSDag-Erling Smørgrav 62bc5531deSDag-Erling Smørgrav #include "ssherr.h" 63bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 64f7167e0eSDag-Erling Smørgrav #include "digest.h" 654d3fc8b0SEd Maste #include "xmalloc.h" 66a04a10f8SKris Kennaway 67ae1f160dSDag-Erling Smørgrav /* prototype */ 6892f58c69SGordon Tetlow static int kex_choose_conf(struct ssh *, uint32_t seq); 694f52dfbbSDag-Erling Smørgrav static int kex_input_newkeys(int, u_int32_t, struct ssh *); 701e8db6e2SBrian Feldman 711323ec57SEd Maste static const char * const proposal_names[PROPOSAL_MAX] = { 72acc1a9efSDag-Erling Smørgrav "KEX algorithms", 73acc1a9efSDag-Erling Smørgrav "host key algorithms", 74acc1a9efSDag-Erling Smørgrav "ciphers ctos", 75acc1a9efSDag-Erling Smørgrav "ciphers stoc", 76acc1a9efSDag-Erling Smørgrav "MACs ctos", 77acc1a9efSDag-Erling Smørgrav "MACs stoc", 78acc1a9efSDag-Erling Smørgrav "compression ctos", 79acc1a9efSDag-Erling Smørgrav "compression stoc", 80acc1a9efSDag-Erling Smørgrav "languages ctos", 81acc1a9efSDag-Erling Smørgrav "languages stoc", 82acc1a9efSDag-Erling Smørgrav }; 83acc1a9efSDag-Erling Smørgrav 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 18092f58c69SGordon Tetlow /* returns non-zero if proposal contains any algorithm from algs */ 18192f58c69SGordon Tetlow static int 18292f58c69SGordon Tetlow has_any_alg(const char *proposal, const char *algs) 18392f58c69SGordon Tetlow { 18492f58c69SGordon Tetlow char *cp; 18592f58c69SGordon Tetlow 18692f58c69SGordon Tetlow if ((cp = match_list(proposal, algs, NULL)) == NULL) 18792f58c69SGordon Tetlow return 0; 18892f58c69SGordon Tetlow free(cp); 18992f58c69SGordon Tetlow return 1; 19092f58c69SGordon Tetlow } 19192f58c69SGordon Tetlow 192eccfee6eSDag-Erling Smørgrav /* 193eccfee6eSDag-Erling Smørgrav * Concatenate algorithm names, avoiding duplicates in the process. 194eccfee6eSDag-Erling Smørgrav * Caller must free returned string. 195eccfee6eSDag-Erling Smørgrav */ 196eccfee6eSDag-Erling Smørgrav char * 197eccfee6eSDag-Erling Smørgrav kex_names_cat(const char *a, const char *b) 198eccfee6eSDag-Erling Smørgrav { 19992f58c69SGordon Tetlow char *ret = NULL, *tmp = NULL, *cp, *p; 200eccfee6eSDag-Erling Smørgrav size_t len; 201eccfee6eSDag-Erling Smørgrav 202eccfee6eSDag-Erling Smørgrav if (a == NULL || *a == '\0') 203190cef3dSDag-Erling Smørgrav return strdup(b); 204eccfee6eSDag-Erling Smørgrav if (b == NULL || *b == '\0') 205eccfee6eSDag-Erling Smørgrav return strdup(a); 206eccfee6eSDag-Erling Smørgrav if (strlen(b) > 1024*1024) 207eccfee6eSDag-Erling Smørgrav return NULL; 208eccfee6eSDag-Erling Smørgrav len = strlen(a) + strlen(b) + 2; 209eccfee6eSDag-Erling Smørgrav if ((tmp = cp = strdup(b)) == NULL || 210eccfee6eSDag-Erling Smørgrav (ret = calloc(1, len)) == NULL) { 211eccfee6eSDag-Erling Smørgrav free(tmp); 212eccfee6eSDag-Erling Smørgrav return NULL; 213eccfee6eSDag-Erling Smørgrav } 214eccfee6eSDag-Erling Smørgrav strlcpy(ret, a, len); 215eccfee6eSDag-Erling Smørgrav for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { 21692f58c69SGordon Tetlow if (has_any_alg(ret, p)) 217eccfee6eSDag-Erling Smørgrav continue; /* Algorithm already present */ 218eccfee6eSDag-Erling Smørgrav if (strlcat(ret, ",", len) >= len || 219eccfee6eSDag-Erling Smørgrav strlcat(ret, p, len) >= len) { 220eccfee6eSDag-Erling Smørgrav free(tmp); 221eccfee6eSDag-Erling Smørgrav free(ret); 222eccfee6eSDag-Erling Smørgrav return NULL; /* Shouldn't happen */ 223eccfee6eSDag-Erling Smørgrav } 224eccfee6eSDag-Erling Smørgrav } 225eccfee6eSDag-Erling Smørgrav free(tmp); 226eccfee6eSDag-Erling Smørgrav return ret; 227eccfee6eSDag-Erling Smørgrav } 228eccfee6eSDag-Erling Smørgrav 229eccfee6eSDag-Erling Smørgrav /* 230eccfee6eSDag-Erling Smørgrav * Assemble a list of algorithms from a default list and a string from a 231eccfee6eSDag-Erling Smørgrav * configuration file. The user-provided string may begin with '+' to 23219261079SEd Maste * indicate that it should be appended to the default, '-' that the 23319261079SEd Maste * specified names should be removed, or '^' that they should be placed 23419261079SEd Maste * at the head. 235eccfee6eSDag-Erling Smørgrav */ 236eccfee6eSDag-Erling Smørgrav int 237190cef3dSDag-Erling Smørgrav kex_assemble_names(char **listp, const char *def, const char *all) 238eccfee6eSDag-Erling Smørgrav { 239190cef3dSDag-Erling Smørgrav char *cp, *tmp, *patterns; 240190cef3dSDag-Erling Smørgrav char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL; 241190cef3dSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 242eccfee6eSDag-Erling Smørgrav 24319261079SEd Maste if (listp == NULL || def == NULL || all == NULL) 24419261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 24519261079SEd Maste 24619261079SEd Maste if (*listp == NULL || **listp == '\0') { 247190cef3dSDag-Erling Smørgrav if ((*listp = strdup(def)) == NULL) 248190cef3dSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 249eccfee6eSDag-Erling Smørgrav return 0; 250eccfee6eSDag-Erling Smørgrav } 251190cef3dSDag-Erling Smørgrav 252190cef3dSDag-Erling Smørgrav list = *listp; 253190cef3dSDag-Erling Smørgrav *listp = NULL; 254190cef3dSDag-Erling Smørgrav if (*list == '+') { 255190cef3dSDag-Erling Smørgrav /* Append names to default list */ 256190cef3dSDag-Erling Smørgrav if ((tmp = kex_names_cat(def, list + 1)) == NULL) { 257190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 258190cef3dSDag-Erling Smørgrav goto fail; 259190cef3dSDag-Erling Smørgrav } 260190cef3dSDag-Erling Smørgrav free(list); 261190cef3dSDag-Erling Smørgrav list = tmp; 262190cef3dSDag-Erling Smørgrav } else if (*list == '-') { 263190cef3dSDag-Erling Smørgrav /* Remove names from default list */ 26419261079SEd Maste if ((*listp = match_filter_denylist(def, list + 1)) == NULL) { 265190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 266190cef3dSDag-Erling Smørgrav goto fail; 267190cef3dSDag-Erling Smørgrav } 268190cef3dSDag-Erling Smørgrav free(list); 269190cef3dSDag-Erling Smørgrav /* filtering has already been done */ 270190cef3dSDag-Erling Smørgrav return 0; 27119261079SEd Maste } else if (*list == '^') { 27219261079SEd Maste /* Place names at head of default list */ 27319261079SEd Maste if ((tmp = kex_names_cat(list + 1, def)) == NULL) { 27419261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 27519261079SEd Maste goto fail; 27619261079SEd Maste } 27719261079SEd Maste free(list); 27819261079SEd Maste list = tmp; 279190cef3dSDag-Erling Smørgrav } else { 280190cef3dSDag-Erling Smørgrav /* Explicit list, overrides default - just use "list" as is */ 281d93a896eSDag-Erling Smørgrav } 282d93a896eSDag-Erling Smørgrav 283190cef3dSDag-Erling Smørgrav /* 284190cef3dSDag-Erling Smørgrav * The supplied names may be a pattern-list. For the -list case, 285190cef3dSDag-Erling Smørgrav * the patterns are applied above. For the +list and explicit list 286190cef3dSDag-Erling Smørgrav * cases we need to do it now. 287190cef3dSDag-Erling Smørgrav */ 288190cef3dSDag-Erling Smørgrav ret = NULL; 289190cef3dSDag-Erling Smørgrav if ((patterns = opatterns = strdup(list)) == NULL) { 290190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 291190cef3dSDag-Erling Smørgrav goto fail; 292190cef3dSDag-Erling Smørgrav } 293190cef3dSDag-Erling Smørgrav /* Apply positive (i.e. non-negated) patterns from the list */ 294190cef3dSDag-Erling Smørgrav while ((cp = strsep(&patterns, ",")) != NULL) { 295190cef3dSDag-Erling Smørgrav if (*cp == '!') { 296190cef3dSDag-Erling Smørgrav /* negated matches are not supported here */ 297190cef3dSDag-Erling Smørgrav r = SSH_ERR_INVALID_ARGUMENT; 298190cef3dSDag-Erling Smørgrav goto fail; 299190cef3dSDag-Erling Smørgrav } 300190cef3dSDag-Erling Smørgrav free(matching); 30119261079SEd Maste if ((matching = match_filter_allowlist(all, cp)) == NULL) { 302190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 303190cef3dSDag-Erling Smørgrav goto fail; 304190cef3dSDag-Erling Smørgrav } 305190cef3dSDag-Erling Smørgrav if ((tmp = kex_names_cat(ret, matching)) == NULL) { 306190cef3dSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 307190cef3dSDag-Erling Smørgrav goto fail; 308190cef3dSDag-Erling Smørgrav } 309190cef3dSDag-Erling Smørgrav free(ret); 310190cef3dSDag-Erling Smørgrav ret = tmp; 311190cef3dSDag-Erling Smørgrav } 312190cef3dSDag-Erling Smørgrav if (ret == NULL || *ret == '\0') { 313190cef3dSDag-Erling Smørgrav /* An empty name-list is an error */ 314190cef3dSDag-Erling Smørgrav /* XXX better error code? */ 315190cef3dSDag-Erling Smørgrav r = SSH_ERR_INVALID_ARGUMENT; 316190cef3dSDag-Erling Smørgrav goto fail; 317190cef3dSDag-Erling Smørgrav } 318190cef3dSDag-Erling Smørgrav 319190cef3dSDag-Erling Smørgrav /* success */ 320190cef3dSDag-Erling Smørgrav *listp = ret; 321190cef3dSDag-Erling Smørgrav ret = NULL; 322190cef3dSDag-Erling Smørgrav r = 0; 323190cef3dSDag-Erling Smørgrav 324190cef3dSDag-Erling Smørgrav fail: 325190cef3dSDag-Erling Smørgrav free(matching); 326190cef3dSDag-Erling Smørgrav free(opatterns); 327190cef3dSDag-Erling Smørgrav free(list); 328190cef3dSDag-Erling Smørgrav free(ret); 329190cef3dSDag-Erling Smørgrav return r; 330eccfee6eSDag-Erling Smørgrav } 331eccfee6eSDag-Erling Smørgrav 3324d3fc8b0SEd Maste /* 3334d3fc8b0SEd Maste * Fill out a proposal array with dynamically allocated values, which may 3344d3fc8b0SEd Maste * be modified as required for compatibility reasons. 3354d3fc8b0SEd Maste * Any of the options may be NULL, in which case the default is used. 3364d3fc8b0SEd Maste * Array contents must be freed by calling kex_proposal_free_entries. 3374d3fc8b0SEd Maste */ 3384d3fc8b0SEd Maste void 3394d3fc8b0SEd Maste kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX], 3404d3fc8b0SEd Maste const char *kexalgos, const char *ciphers, const char *macs, 3414d3fc8b0SEd Maste const char *comp, const char *hkalgs) 3424d3fc8b0SEd Maste { 3434d3fc8b0SEd Maste const char *defpropserver[PROPOSAL_MAX] = { KEX_SERVER }; 3444d3fc8b0SEd Maste const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT }; 3454d3fc8b0SEd Maste const char **defprop = ssh->kex->server ? defpropserver : defpropclient; 3464d3fc8b0SEd Maste u_int i; 34792f58c69SGordon Tetlow char *cp; 3484d3fc8b0SEd Maste 3494d3fc8b0SEd Maste if (prop == NULL) 3504d3fc8b0SEd Maste fatal_f("proposal missing"); 3514d3fc8b0SEd Maste 35292f58c69SGordon Tetlow /* Append EXT_INFO signalling to KexAlgorithms */ 35392f58c69SGordon Tetlow if (kexalgos == NULL) 35492f58c69SGordon Tetlow kexalgos = defprop[PROPOSAL_KEX_ALGS]; 35592f58c69SGordon Tetlow if ((cp = kex_names_cat(kexalgos, ssh->kex->server ? 356069ac184SEd Maste "ext-info-s,kex-strict-s-v00@openssh.com" : 35792f58c69SGordon Tetlow "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) 35892f58c69SGordon Tetlow fatal_f("kex_names_cat"); 35992f58c69SGordon Tetlow 3604d3fc8b0SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) { 3614d3fc8b0SEd Maste switch(i) { 3624d3fc8b0SEd Maste case PROPOSAL_KEX_ALGS: 36392f58c69SGordon Tetlow prop[i] = compat_kex_proposal(ssh, cp); 3644d3fc8b0SEd Maste break; 3654d3fc8b0SEd Maste case PROPOSAL_ENC_ALGS_CTOS: 3664d3fc8b0SEd Maste case PROPOSAL_ENC_ALGS_STOC: 3674d3fc8b0SEd Maste prop[i] = xstrdup(ciphers ? ciphers : defprop[i]); 3684d3fc8b0SEd Maste break; 3694d3fc8b0SEd Maste case PROPOSAL_MAC_ALGS_CTOS: 3704d3fc8b0SEd Maste case PROPOSAL_MAC_ALGS_STOC: 3714d3fc8b0SEd Maste prop[i] = xstrdup(macs ? macs : defprop[i]); 3724d3fc8b0SEd Maste break; 3734d3fc8b0SEd Maste case PROPOSAL_COMP_ALGS_CTOS: 3744d3fc8b0SEd Maste case PROPOSAL_COMP_ALGS_STOC: 3754d3fc8b0SEd Maste prop[i] = xstrdup(comp ? comp : defprop[i]); 3764d3fc8b0SEd Maste break; 3774d3fc8b0SEd Maste case PROPOSAL_SERVER_HOST_KEY_ALGS: 3784d3fc8b0SEd Maste prop[i] = xstrdup(hkalgs ? hkalgs : defprop[i]); 3794d3fc8b0SEd Maste break; 3804d3fc8b0SEd Maste default: 3814d3fc8b0SEd Maste prop[i] = xstrdup(defprop[i]); 3824d3fc8b0SEd Maste } 3834d3fc8b0SEd Maste } 38492f58c69SGordon Tetlow free(cp); 3854d3fc8b0SEd Maste } 3864d3fc8b0SEd Maste 3874d3fc8b0SEd Maste void 3884d3fc8b0SEd Maste kex_proposal_free_entries(char *prop[PROPOSAL_MAX]) 3894d3fc8b0SEd Maste { 3904d3fc8b0SEd Maste u_int i; 3914d3fc8b0SEd Maste 3924d3fc8b0SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) 3934d3fc8b0SEd Maste free(prop[i]); 3944d3fc8b0SEd Maste } 3954d3fc8b0SEd Maste 3961765946bSDag-Erling Smørgrav /* put algorithm proposal into buffer */ 397bc5531deSDag-Erling Smørgrav int 398bc5531deSDag-Erling Smørgrav kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 399a04a10f8SKris Kennaway { 400043840dfSDag-Erling Smørgrav u_int i; 401bc5531deSDag-Erling Smørgrav int r; 4021e8db6e2SBrian Feldman 403bc5531deSDag-Erling Smørgrav sshbuf_reset(b); 404bc5531deSDag-Erling Smørgrav 405545d5ecaSDag-Erling Smørgrav /* 406545d5ecaSDag-Erling Smørgrav * add a dummy cookie, the cookie will be overwritten by 407545d5ecaSDag-Erling Smørgrav * kex_send_kexinit(), each time a kexinit is set 408545d5ecaSDag-Erling Smørgrav */ 409bc5531deSDag-Erling Smørgrav for (i = 0; i < KEX_COOKIE_LEN; i++) { 410bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0) 411bc5531deSDag-Erling Smørgrav return r; 412bc5531deSDag-Erling Smørgrav } 413bc5531deSDag-Erling Smørgrav for (i = 0; i < PROPOSAL_MAX; i++) { 414bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) 415bc5531deSDag-Erling Smørgrav return r; 416bc5531deSDag-Erling Smørgrav } 417bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ 418bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ 419bc5531deSDag-Erling Smørgrav return r; 420bc5531deSDag-Erling Smørgrav return 0; 421a04a10f8SKris Kennaway } 422a04a10f8SKris Kennaway 4231e8db6e2SBrian Feldman /* parse buffer and return algorithm proposal */ 424bc5531deSDag-Erling Smørgrav int 425bc5531deSDag-Erling Smørgrav kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) 4262632b0c8SKris Kennaway { 427bc5531deSDag-Erling Smørgrav struct sshbuf *b = NULL; 428bc5531deSDag-Erling Smørgrav u_char v; 429d4af9e69SDag-Erling Smørgrav u_int i; 430bc5531deSDag-Erling Smørgrav char **proposal = NULL; 431bc5531deSDag-Erling Smørgrav int r; 4322632b0c8SKris Kennaway 433bc5531deSDag-Erling Smørgrav *propp = NULL; 434bc5531deSDag-Erling Smørgrav if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) 435bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 436bc5531deSDag-Erling Smørgrav if ((b = sshbuf_fromb(raw)) == NULL) { 437bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 438bc5531deSDag-Erling Smørgrav goto out; 439bc5531deSDag-Erling Smørgrav } 44019261079SEd Maste if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */ 44119261079SEd Maste error_fr(r, "consume cookie"); 442bc5531deSDag-Erling Smørgrav goto out; 44319261079SEd Maste } 4442632b0c8SKris Kennaway /* extract kex init proposal strings */ 4452632b0c8SKris Kennaway for (i = 0; i < PROPOSAL_MAX; i++) { 44619261079SEd Maste if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) { 44719261079SEd Maste error_fr(r, "parse proposal %u", i); 448bc5531deSDag-Erling Smørgrav goto out; 44919261079SEd Maste } 450acc1a9efSDag-Erling Smørgrav debug2("%s: %s", proposal_names[i], proposal[i]); 4512632b0c8SKris Kennaway } 4521e8db6e2SBrian Feldman /* first kex follows / reserved */ 453fc1ba28aSDag-Erling Smørgrav if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ 45419261079SEd Maste (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */ 45519261079SEd Maste error_fr(r, "parse"); 456bc5531deSDag-Erling Smørgrav goto out; 45719261079SEd Maste } 458d0c8c0bcSDag-Erling Smørgrav if (first_kex_follows != NULL) 459fc1ba28aSDag-Erling Smørgrav *first_kex_follows = v; 460fc1ba28aSDag-Erling Smørgrav debug2("first_kex_follows %d ", v); 461fc1ba28aSDag-Erling Smørgrav debug2("reserved %u ", i); 462bc5531deSDag-Erling Smørgrav r = 0; 463bc5531deSDag-Erling Smørgrav *propp = proposal; 464bc5531deSDag-Erling Smørgrav out: 465bc5531deSDag-Erling Smørgrav if (r != 0 && proposal != NULL) 466bc5531deSDag-Erling Smørgrav kex_prop_free(proposal); 467bc5531deSDag-Erling Smørgrav sshbuf_free(b); 468bc5531deSDag-Erling Smørgrav return r; 4695b9b2fafSBrian Feldman } 4705b9b2fafSBrian Feldman 471bc5531deSDag-Erling Smørgrav void 4721e8db6e2SBrian Feldman kex_prop_free(char **proposal) 473a04a10f8SKris Kennaway { 474043840dfSDag-Erling Smørgrav u_int i; 4751e8db6e2SBrian Feldman 476557f75e5SDag-Erling Smørgrav if (proposal == NULL) 477557f75e5SDag-Erling Smørgrav return; 4781e8db6e2SBrian Feldman for (i = 0; i < PROPOSAL_MAX; i++) 479e4a9863fSDag-Erling Smørgrav free(proposal[i]); 480e4a9863fSDag-Erling Smørgrav free(proposal); 481a04a10f8SKris Kennaway } 482a04a10f8SKris Kennaway 48319261079SEd Maste int 4844f52dfbbSDag-Erling Smørgrav kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh) 485a04a10f8SKris Kennaway { 486acc1a9efSDag-Erling Smørgrav int r; 487acc1a9efSDag-Erling Smørgrav 48892f58c69SGordon Tetlow /* If in strict mode, any unexpected message is an error */ 48992f58c69SGordon Tetlow if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) { 49092f58c69SGordon Tetlow ssh_packet_disconnect(ssh, "strict KEX violation: " 49192f58c69SGordon Tetlow "unexpected packet type %u (seqnr %u)", type, seq); 49292f58c69SGordon Tetlow } 49392f58c69SGordon Tetlow error_f("type %u seq %u", type, seq); 494acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || 495acc1a9efSDag-Erling Smørgrav (r = sshpkt_put_u32(ssh, seq)) != 0 || 496acc1a9efSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 497acc1a9efSDag-Erling Smørgrav return r; 498bc5531deSDag-Erling Smørgrav return 0; 499a04a10f8SKris Kennaway } 500a04a10f8SKris Kennaway 501ae1f160dSDag-Erling Smørgrav static void 502bc5531deSDag-Erling Smørgrav kex_reset_dispatch(struct ssh *ssh) 5035b9b2fafSBrian Feldman { 504bc5531deSDag-Erling Smørgrav ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, 505ae1f160dSDag-Erling Smørgrav SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); 5065b9b2fafSBrian Feldman } 5075b9b2fafSBrian Feldman 508069ac184SEd Maste void 509069ac184SEd Maste kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs) 510069ac184SEd Maste { 511069ac184SEd Maste char *alg, *oalgs, *algs, *sigalgs; 512069ac184SEd Maste const char *sigalg; 513069ac184SEd Maste 514069ac184SEd Maste /* 515069ac184SEd Maste * NB. allowed algorithms may contain certificate algorithms that 516069ac184SEd Maste * map to a specific plain signature type, e.g. 517069ac184SEd Maste * rsa-sha2-512-cert-v01@openssh.com => rsa-sha2-512 518069ac184SEd Maste * We need to be careful here to match these, retain the mapping 519069ac184SEd Maste * and only add each signature algorithm once. 520069ac184SEd Maste */ 521069ac184SEd Maste if ((sigalgs = sshkey_alg_list(0, 1, 1, ',')) == NULL) 522069ac184SEd Maste fatal_f("sshkey_alg_list failed"); 523069ac184SEd Maste oalgs = algs = xstrdup(allowed_algs); 524069ac184SEd Maste free(ssh->kex->server_sig_algs); 525069ac184SEd Maste ssh->kex->server_sig_algs = NULL; 526069ac184SEd Maste for ((alg = strsep(&algs, ",")); alg != NULL && *alg != '\0'; 527069ac184SEd Maste (alg = strsep(&algs, ","))) { 528069ac184SEd Maste if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL) 529069ac184SEd Maste continue; 530069ac184SEd Maste if (!has_any_alg(sigalg, sigalgs)) 531069ac184SEd Maste continue; 532069ac184SEd Maste /* Don't add an algorithm twice. */ 533069ac184SEd Maste if (ssh->kex->server_sig_algs != NULL && 534069ac184SEd Maste has_any_alg(sigalg, ssh->kex->server_sig_algs)) 535069ac184SEd Maste continue; 536069ac184SEd Maste xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg); 537069ac184SEd Maste } 538069ac184SEd Maste free(oalgs); 539069ac184SEd Maste free(sigalgs); 540069ac184SEd Maste if (ssh->kex->server_sig_algs == NULL) 541069ac184SEd Maste ssh->kex->server_sig_algs = xstrdup(""); 542069ac184SEd Maste } 543069ac184SEd Maste 544acc1a9efSDag-Erling Smørgrav static int 545069ac184SEd Maste kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m) 546acc1a9efSDag-Erling Smørgrav { 547acc1a9efSDag-Erling Smørgrav int r; 548acc1a9efSDag-Erling Smørgrav 549069ac184SEd Maste if (ssh->kex->server_sig_algs == NULL && 550069ac184SEd Maste (ssh->kex->server_sig_algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) 551ca86bcf2SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 552069ac184SEd Maste if ((r = sshbuf_put_u32(m, 3)) != 0 || 553069ac184SEd Maste (r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 || 554069ac184SEd Maste (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 || 555069ac184SEd Maste (r = sshbuf_put_cstring(m, 5561323ec57SEd Maste "publickey-hostbound@openssh.com")) != 0 || 557069ac184SEd Maste (r = sshbuf_put_cstring(m, "0")) != 0 || 558069ac184SEd Maste (r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 || 559069ac184SEd Maste (r = sshbuf_put_cstring(m, "0")) != 0) { 560069ac184SEd Maste error_fr(r, "compose"); 561069ac184SEd Maste return r; 562069ac184SEd Maste } 563069ac184SEd Maste return 0; 564069ac184SEd Maste } 565069ac184SEd Maste 566069ac184SEd Maste static int 567069ac184SEd Maste kex_compose_ext_info_client(struct ssh *ssh, struct sshbuf *m) 568069ac184SEd Maste { 569069ac184SEd Maste int r; 570069ac184SEd Maste 571069ac184SEd Maste if ((r = sshbuf_put_u32(m, 1)) != 0 || 572069ac184SEd Maste (r = sshbuf_put_cstring(m, "ext-info-in-auth@openssh.com")) != 0 || 573069ac184SEd Maste (r = sshbuf_put_cstring(m, "0")) != 0) { 57419261079SEd Maste error_fr(r, "compose"); 575ca86bcf2SDag-Erling Smørgrav goto out; 57619261079SEd Maste } 577ca86bcf2SDag-Erling Smørgrav /* success */ 578ca86bcf2SDag-Erling Smørgrav r = 0; 579ca86bcf2SDag-Erling Smørgrav out: 580acc1a9efSDag-Erling Smørgrav return r; 581acc1a9efSDag-Erling Smørgrav } 582acc1a9efSDag-Erling Smørgrav 583069ac184SEd Maste static int 584069ac184SEd Maste kex_maybe_send_ext_info(struct ssh *ssh) 585069ac184SEd Maste { 586069ac184SEd Maste int r; 587069ac184SEd Maste struct sshbuf *m = NULL; 588069ac184SEd Maste 589069ac184SEd Maste if ((ssh->kex->flags & KEX_INITIAL) == 0) 590069ac184SEd Maste return 0; 591069ac184SEd Maste if (!ssh->kex->ext_info_c && !ssh->kex->ext_info_s) 592069ac184SEd Maste return 0; 593069ac184SEd Maste 594069ac184SEd Maste /* Compose EXT_INFO packet. */ 595069ac184SEd Maste if ((m = sshbuf_new()) == NULL) 596069ac184SEd Maste fatal_f("sshbuf_new failed"); 597069ac184SEd Maste if (ssh->kex->ext_info_c && 598069ac184SEd Maste (r = kex_compose_ext_info_server(ssh, m)) != 0) 599069ac184SEd Maste goto fail; 600069ac184SEd Maste if (ssh->kex->ext_info_s && 601069ac184SEd Maste (r = kex_compose_ext_info_client(ssh, m)) != 0) 602069ac184SEd Maste goto fail; 603069ac184SEd Maste 604069ac184SEd Maste /* Send the actual KEX_INFO packet */ 605069ac184SEd Maste debug("Sending SSH2_MSG_EXT_INFO"); 606069ac184SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || 607069ac184SEd Maste (r = sshpkt_putb(ssh, m)) != 0 || 608069ac184SEd Maste (r = sshpkt_send(ssh)) != 0) { 609069ac184SEd Maste error_f("send EXT_INFO"); 610069ac184SEd Maste goto fail; 611069ac184SEd Maste } 612069ac184SEd Maste 613069ac184SEd Maste r = 0; 614069ac184SEd Maste 615069ac184SEd Maste fail: 616069ac184SEd Maste sshbuf_free(m); 617069ac184SEd Maste return r; 618069ac184SEd Maste } 619069ac184SEd Maste 620069ac184SEd Maste int 621069ac184SEd Maste kex_server_update_ext_info(struct ssh *ssh) 622069ac184SEd Maste { 623069ac184SEd Maste int r; 624069ac184SEd Maste 625069ac184SEd Maste if ((ssh->kex->flags & KEX_HAS_EXT_INFO_IN_AUTH) == 0) 626069ac184SEd Maste return 0; 627069ac184SEd Maste 628069ac184SEd Maste debug_f("Sending SSH2_MSG_EXT_INFO"); 629069ac184SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || 630069ac184SEd Maste (r = sshpkt_put_u32(ssh, 1)) != 0 || 631069ac184SEd Maste (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || 632069ac184SEd Maste (r = sshpkt_put_cstring(ssh, ssh->kex->server_sig_algs)) != 0 || 633069ac184SEd Maste (r = sshpkt_send(ssh)) != 0) { 634069ac184SEd Maste error_f("send EXT_INFO"); 635069ac184SEd Maste return r; 636069ac184SEd Maste } 637069ac184SEd Maste return 0; 638069ac184SEd Maste } 639069ac184SEd Maste 640bc5531deSDag-Erling Smørgrav int 641bc5531deSDag-Erling Smørgrav kex_send_newkeys(struct ssh *ssh) 642a04a10f8SKris Kennaway { 643bc5531deSDag-Erling Smørgrav int r; 644a04a10f8SKris Kennaway 645bc5531deSDag-Erling Smørgrav kex_reset_dispatch(ssh); 646bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || 647bc5531deSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 648bc5531deSDag-Erling Smørgrav return r; 6491e8db6e2SBrian Feldman debug("SSH2_MSG_NEWKEYS sent"); 650bc5531deSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); 651069ac184SEd Maste if ((r = kex_maybe_send_ext_info(ssh)) != 0) 652acc1a9efSDag-Erling Smørgrav return r; 65319261079SEd Maste debug("expecting SSH2_MSG_NEWKEYS"); 654bc5531deSDag-Erling Smørgrav return 0; 655bc5531deSDag-Erling Smørgrav } 6561e8db6e2SBrian Feldman 657edf85781SEd Maste /* Check whether an ext_info value contains the expected version string */ 658edf85781SEd Maste static int 659edf85781SEd Maste kex_ext_info_check_ver(struct kex *kex, const char *name, 660edf85781SEd Maste const u_char *val, size_t len, const char *want_ver, u_int flag) 661edf85781SEd Maste { 662edf85781SEd Maste if (memchr(val, '\0', len) != NULL) { 663edf85781SEd Maste error("SSH2_MSG_EXT_INFO: %s value contains nul byte", name); 664edf85781SEd Maste return SSH_ERR_INVALID_FORMAT; 665edf85781SEd Maste } 666edf85781SEd Maste debug_f("%s=<%s>", name, val); 667edf85781SEd Maste if (strcmp(val, want_ver) == 0) 668edf85781SEd Maste kex->flags |= flag; 669edf85781SEd Maste else 670edf85781SEd Maste debug_f("unsupported version of %s extension", name); 671edf85781SEd Maste return 0; 672edf85781SEd Maste } 673edf85781SEd Maste 674069ac184SEd Maste static int 675069ac184SEd Maste kex_ext_info_client_parse(struct ssh *ssh, const char *name, 676069ac184SEd Maste const u_char *value, size_t vlen) 677069ac184SEd Maste { 678069ac184SEd Maste int r; 679069ac184SEd Maste 680069ac184SEd Maste /* NB. some messages are only accepted in the initial EXT_INFO */ 681069ac184SEd Maste if (strcmp(name, "server-sig-algs") == 0) { 682069ac184SEd Maste /* Ensure no \0 lurking in value */ 683069ac184SEd Maste if (memchr(value, '\0', vlen) != NULL) { 684069ac184SEd Maste error_f("nul byte in %s", name); 685069ac184SEd Maste return SSH_ERR_INVALID_FORMAT; 686069ac184SEd Maste } 687069ac184SEd Maste debug_f("%s=<%s>", name, value); 688069ac184SEd Maste free(ssh->kex->server_sig_algs); 689069ac184SEd Maste ssh->kex->server_sig_algs = xstrdup((const char *)value); 690069ac184SEd Maste } else if (ssh->kex->ext_info_received == 1 && 691069ac184SEd Maste strcmp(name, "publickey-hostbound@openssh.com") == 0) { 692069ac184SEd Maste if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, 693069ac184SEd Maste "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) { 694069ac184SEd Maste return r; 695069ac184SEd Maste } 696069ac184SEd Maste } else if (ssh->kex->ext_info_received == 1 && 697069ac184SEd Maste strcmp(name, "ping@openssh.com") == 0) { 698069ac184SEd Maste if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, 699069ac184SEd Maste "0", KEX_HAS_PING)) != 0) { 700069ac184SEd Maste return r; 701069ac184SEd Maste } 702069ac184SEd Maste } else 703069ac184SEd Maste debug_f("%s (unrecognised)", name); 704069ac184SEd Maste 705069ac184SEd Maste return 0; 706069ac184SEd Maste } 707069ac184SEd Maste 708069ac184SEd Maste static int 709069ac184SEd Maste kex_ext_info_server_parse(struct ssh *ssh, const char *name, 710069ac184SEd Maste const u_char *value, size_t vlen) 711069ac184SEd Maste { 712069ac184SEd Maste int r; 713069ac184SEd Maste 714069ac184SEd Maste if (strcmp(name, "ext-info-in-auth@openssh.com") == 0) { 715069ac184SEd Maste if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, 716069ac184SEd Maste "0", KEX_HAS_EXT_INFO_IN_AUTH)) != 0) { 717069ac184SEd Maste return r; 718069ac184SEd Maste } 719069ac184SEd Maste } else 720069ac184SEd Maste debug_f("%s (unrecognised)", name); 721069ac184SEd Maste return 0; 722069ac184SEd Maste } 723069ac184SEd Maste 724acc1a9efSDag-Erling Smørgrav int 7254f52dfbbSDag-Erling Smørgrav kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) 726acc1a9efSDag-Erling Smørgrav { 727acc1a9efSDag-Erling Smørgrav struct kex *kex = ssh->kex; 728069ac184SEd Maste const int max_ext_info = kex->server ? 1 : 2; 729acc1a9efSDag-Erling Smørgrav u_int32_t i, ninfo; 730190cef3dSDag-Erling Smørgrav char *name; 7314f52dfbbSDag-Erling Smørgrav u_char *val; 7324f52dfbbSDag-Erling Smørgrav size_t vlen; 733acc1a9efSDag-Erling Smørgrav int r; 734acc1a9efSDag-Erling Smørgrav 735acc1a9efSDag-Erling Smørgrav debug("SSH2_MSG_EXT_INFO received"); 736069ac184SEd Maste if (++kex->ext_info_received > max_ext_info) { 737069ac184SEd Maste error("too many SSH2_MSG_EXT_INFO messages sent by peer"); 738069ac184SEd Maste return dispatch_protocol_error(type, seq, ssh); 739069ac184SEd Maste } 740acc1a9efSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); 741acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) 742acc1a9efSDag-Erling Smørgrav return r; 7434d3fc8b0SEd Maste if (ninfo >= 1024) { 7444d3fc8b0SEd Maste error("SSH2_MSG_EXT_INFO with too many entries, expected " 7454d3fc8b0SEd Maste "<=1024, received %u", ninfo); 74692f58c69SGordon Tetlow return dispatch_protocol_error(type, seq, ssh); 7474d3fc8b0SEd Maste } 748acc1a9efSDag-Erling Smørgrav for (i = 0; i < ninfo; i++) { 749acc1a9efSDag-Erling Smørgrav if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) 750acc1a9efSDag-Erling Smørgrav return r; 7514f52dfbbSDag-Erling Smørgrav if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) { 752acc1a9efSDag-Erling Smørgrav free(name); 753acc1a9efSDag-Erling Smørgrav return r; 754acc1a9efSDag-Erling Smørgrav } 755069ac184SEd Maste debug3_f("extension %s", name); 756069ac184SEd Maste if (kex->server) { 757069ac184SEd Maste if ((r = kex_ext_info_server_parse(ssh, name, 758069ac184SEd Maste val, vlen)) != 0) 759069ac184SEd Maste return r; 760069ac184SEd Maste } else { 761069ac184SEd Maste if ((r = kex_ext_info_client_parse(ssh, name, 762069ac184SEd Maste val, vlen)) != 0) 763edf85781SEd Maste return r; 7641323ec57SEd Maste } 765acc1a9efSDag-Erling Smørgrav free(name); 766acc1a9efSDag-Erling Smørgrav free(val); 767acc1a9efSDag-Erling Smørgrav } 768acc1a9efSDag-Erling Smørgrav return sshpkt_get_end(ssh); 769acc1a9efSDag-Erling Smørgrav } 770acc1a9efSDag-Erling Smørgrav 771bc5531deSDag-Erling Smørgrav static int 7724f52dfbbSDag-Erling Smørgrav kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) 773bc5531deSDag-Erling Smørgrav { 774bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 775*a91a2465SEd Maste int r, initial = (kex->flags & KEX_INITIAL) != 0; 776*a91a2465SEd Maste char *cp, **prop; 777bc5531deSDag-Erling Smørgrav 778bc5531deSDag-Erling Smørgrav debug("SSH2_MSG_NEWKEYS received"); 779*a91a2465SEd Maste if (kex->ext_info_c && initial) 780069ac184SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info); 781bc5531deSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); 782d93a896eSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 783bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_end(ssh)) != 0) 784bc5531deSDag-Erling Smørgrav return r; 785ca86bcf2SDag-Erling Smørgrav if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) 786ca86bcf2SDag-Erling Smørgrav return r; 787*a91a2465SEd Maste if (initial) { 788*a91a2465SEd Maste /* Remove initial KEX signalling from proposal for rekeying */ 789*a91a2465SEd Maste if ((r = kex_buf2prop(kex->my, NULL, &prop)) != 0) 790*a91a2465SEd Maste return r; 791*a91a2465SEd Maste if ((cp = match_filter_denylist(prop[PROPOSAL_KEX_ALGS], 792*a91a2465SEd Maste kex->server ? 793*a91a2465SEd Maste "ext-info-s,kex-strict-s-v00@openssh.com" : 794*a91a2465SEd Maste "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) { 795*a91a2465SEd Maste error_f("match_filter_denylist failed"); 796*a91a2465SEd Maste goto fail; 797*a91a2465SEd Maste } 798*a91a2465SEd Maste free(prop[PROPOSAL_KEX_ALGS]); 799*a91a2465SEd Maste prop[PROPOSAL_KEX_ALGS] = cp; 800*a91a2465SEd Maste if ((r = kex_prop2buf(ssh->kex->my, prop)) != 0) { 801*a91a2465SEd Maste error_f("kex_prop2buf failed"); 802*a91a2465SEd Maste fail: 803*a91a2465SEd Maste kex_proposal_free_entries(prop); 804*a91a2465SEd Maste free(prop); 805*a91a2465SEd Maste return SSH_ERR_INTERNAL_ERROR; 806*a91a2465SEd Maste } 807*a91a2465SEd Maste kex_proposal_free_entries(prop); 808*a91a2465SEd Maste free(prop); 809*a91a2465SEd Maste } 8101e8db6e2SBrian Feldman kex->done = 1; 81119261079SEd Maste kex->flags &= ~KEX_INITIAL; 812bc5531deSDag-Erling Smørgrav sshbuf_reset(kex->peer); 8131e8db6e2SBrian Feldman kex->flags &= ~KEX_INIT_SENT; 814e4a9863fSDag-Erling Smørgrav free(kex->name); 8151e8db6e2SBrian Feldman kex->name = NULL; 816bc5531deSDag-Erling Smørgrav return 0; 817a04a10f8SKris Kennaway } 818a04a10f8SKris Kennaway 819bc5531deSDag-Erling Smørgrav int 820bc5531deSDag-Erling Smørgrav kex_send_kexinit(struct ssh *ssh) 821a04a10f8SKris Kennaway { 822545d5ecaSDag-Erling Smørgrav u_char *cookie; 823bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 824bc5531deSDag-Erling Smørgrav int r; 825545d5ecaSDag-Erling Smørgrav 82619261079SEd Maste if (kex == NULL) { 82719261079SEd Maste error_f("no kex"); 828bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 82919261079SEd Maste } 830bc5531deSDag-Erling Smørgrav if (kex->flags & KEX_INIT_SENT) 831bc5531deSDag-Erling Smørgrav return 0; 8321e8db6e2SBrian Feldman kex->done = 0; 833545d5ecaSDag-Erling Smørgrav 834545d5ecaSDag-Erling Smørgrav /* generate a random cookie */ 83519261079SEd Maste if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) { 83619261079SEd Maste error_f("bad kex length: %zu < %d", 83719261079SEd Maste sshbuf_len(kex->my), KEX_COOKIE_LEN); 838bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 83919261079SEd Maste } 84019261079SEd Maste if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) { 84119261079SEd Maste error_f("buffer error"); 842bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 84319261079SEd Maste } 844bc5531deSDag-Erling Smørgrav arc4random_buf(cookie, KEX_COOKIE_LEN); 845bc5531deSDag-Erling Smørgrav 846bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || 847bc5531deSDag-Erling Smørgrav (r = sshpkt_putb(ssh, kex->my)) != 0 || 84819261079SEd Maste (r = sshpkt_send(ssh)) != 0) { 84919261079SEd Maste error_fr(r, "compose reply"); 850bc5531deSDag-Erling Smørgrav return r; 85119261079SEd Maste } 8521e8db6e2SBrian Feldman debug("SSH2_MSG_KEXINIT sent"); 8531e8db6e2SBrian Feldman kex->flags |= KEX_INIT_SENT; 854bc5531deSDag-Erling Smørgrav return 0; 8551e8db6e2SBrian Feldman } 856a04a10f8SKris Kennaway 857bc5531deSDag-Erling Smørgrav int 8584f52dfbbSDag-Erling Smørgrav kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) 8591e8db6e2SBrian Feldman { 860bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 861bc5531deSDag-Erling Smørgrav const u_char *ptr; 862bc5531deSDag-Erling Smørgrav u_int i; 863bc5531deSDag-Erling Smørgrav size_t dlen; 864bc5531deSDag-Erling Smørgrav int r; 8652632b0c8SKris Kennaway 8661e8db6e2SBrian Feldman debug("SSH2_MSG_KEXINIT received"); 86719261079SEd Maste if (kex == NULL) { 86819261079SEd Maste error_f("no kex"); 86919261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 87019261079SEd Maste } 87192f58c69SGordon Tetlow ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error); 872bc5531deSDag-Erling Smørgrav ptr = sshpkt_ptr(ssh, &dlen); 873bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) 874bc5531deSDag-Erling Smørgrav return r; 8751e8db6e2SBrian Feldman 8761e8db6e2SBrian Feldman /* discard packet */ 87719261079SEd Maste for (i = 0; i < KEX_COOKIE_LEN; i++) { 87819261079SEd Maste if ((r = sshpkt_get_u8(ssh, NULL)) != 0) { 87919261079SEd Maste error_fr(r, "discard cookie"); 880bc5531deSDag-Erling Smørgrav return r; 88119261079SEd Maste } 88219261079SEd Maste } 88319261079SEd Maste for (i = 0; i < PROPOSAL_MAX; i++) { 88419261079SEd Maste if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { 88519261079SEd Maste error_fr(r, "discard proposal"); 886bc5531deSDag-Erling Smørgrav return r; 88719261079SEd Maste } 88819261079SEd Maste } 8896888a9beSDag-Erling Smørgrav /* 8906888a9beSDag-Erling Smørgrav * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported 8916888a9beSDag-Erling Smørgrav * KEX method has the server move first, but a server might be using 8926888a9beSDag-Erling Smørgrav * a custom method or one that we otherwise don't support. We should 8936888a9beSDag-Erling Smørgrav * be prepared to remember first_kex_follows here so we can eat a 8946888a9beSDag-Erling Smørgrav * packet later. 8956888a9beSDag-Erling Smørgrav * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means 8966888a9beSDag-Erling Smørgrav * for cases where the server *doesn't* go first. I guess we should 8976888a9beSDag-Erling Smørgrav * ignore it when it is set for these cases, which is what we do now. 8986888a9beSDag-Erling Smørgrav */ 899bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ 900bc5531deSDag-Erling Smørgrav (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ 901bc5531deSDag-Erling Smørgrav (r = sshpkt_get_end(ssh)) != 0) 902bc5531deSDag-Erling Smørgrav return r; 9031e8db6e2SBrian Feldman 9041e8db6e2SBrian Feldman if (!(kex->flags & KEX_INIT_SENT)) 905bc5531deSDag-Erling Smørgrav if ((r = kex_send_kexinit(ssh)) != 0) 906bc5531deSDag-Erling Smørgrav return r; 90792f58c69SGordon Tetlow if ((r = kex_choose_conf(ssh, seq)) != 0) 908bc5531deSDag-Erling Smørgrav return r; 9091e8db6e2SBrian Feldman 910bc5531deSDag-Erling Smørgrav if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) 911bc5531deSDag-Erling Smørgrav return (kex->kex[kex->kex_type])(ssh); 9121e8db6e2SBrian Feldman 91319261079SEd Maste error_f("unknown kex type %u", kex->kex_type); 914bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 9151e8db6e2SBrian Feldman } 916a04a10f8SKris Kennaway 91719261079SEd Maste struct kex * 91819261079SEd Maste kex_new(void) 919bc5531deSDag-Erling Smørgrav { 920bc5531deSDag-Erling Smørgrav struct kex *kex; 921bc5531deSDag-Erling Smørgrav 92219261079SEd Maste if ((kex = calloc(1, sizeof(*kex))) == NULL || 92319261079SEd Maste (kex->peer = sshbuf_new()) == NULL || 92419261079SEd Maste (kex->my = sshbuf_new()) == NULL || 92519261079SEd Maste (kex->client_version = sshbuf_new()) == NULL || 92619261079SEd Maste (kex->server_version = sshbuf_new()) == NULL || 92719261079SEd Maste (kex->session_id = sshbuf_new()) == NULL) { 928bc5531deSDag-Erling Smørgrav kex_free(kex); 92919261079SEd Maste return NULL; 93019261079SEd Maste } 93119261079SEd Maste return kex; 932bc5531deSDag-Erling Smørgrav } 933bc5531deSDag-Erling Smørgrav 934bc5531deSDag-Erling Smørgrav void 935bc5531deSDag-Erling Smørgrav kex_free_newkeys(struct newkeys *newkeys) 936bc5531deSDag-Erling Smørgrav { 937bc5531deSDag-Erling Smørgrav if (newkeys == NULL) 938bc5531deSDag-Erling Smørgrav return; 939bc5531deSDag-Erling Smørgrav if (newkeys->enc.key) { 940bc5531deSDag-Erling Smørgrav explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); 941bc5531deSDag-Erling Smørgrav free(newkeys->enc.key); 942bc5531deSDag-Erling Smørgrav newkeys->enc.key = NULL; 943bc5531deSDag-Erling Smørgrav } 944bc5531deSDag-Erling Smørgrav if (newkeys->enc.iv) { 945acc1a9efSDag-Erling Smørgrav explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); 946bc5531deSDag-Erling Smørgrav free(newkeys->enc.iv); 947bc5531deSDag-Erling Smørgrav newkeys->enc.iv = NULL; 948bc5531deSDag-Erling Smørgrav } 949bc5531deSDag-Erling Smørgrav free(newkeys->enc.name); 950bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); 951bc5531deSDag-Erling Smørgrav free(newkeys->comp.name); 952bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); 953bc5531deSDag-Erling Smørgrav mac_clear(&newkeys->mac); 954bc5531deSDag-Erling Smørgrav if (newkeys->mac.key) { 955bc5531deSDag-Erling Smørgrav explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); 956bc5531deSDag-Erling Smørgrav free(newkeys->mac.key); 957bc5531deSDag-Erling Smørgrav newkeys->mac.key = NULL; 958bc5531deSDag-Erling Smørgrav } 959bc5531deSDag-Erling Smørgrav free(newkeys->mac.name); 960bc5531deSDag-Erling Smørgrav explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); 96119261079SEd Maste freezero(newkeys, sizeof(*newkeys)); 962bc5531deSDag-Erling Smørgrav } 963bc5531deSDag-Erling Smørgrav 964bc5531deSDag-Erling Smørgrav void 965bc5531deSDag-Erling Smørgrav kex_free(struct kex *kex) 966bc5531deSDag-Erling Smørgrav { 967bc5531deSDag-Erling Smørgrav u_int mode; 968bc5531deSDag-Erling Smørgrav 96919261079SEd Maste if (kex == NULL) 97019261079SEd Maste return; 97119261079SEd Maste 972bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL 973bc5531deSDag-Erling Smørgrav DH_free(kex->dh); 974bc5531deSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC 975bc5531deSDag-Erling Smørgrav EC_KEY_free(kex->ec_client_key); 976bc5531deSDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */ 977bc5531deSDag-Erling Smørgrav #endif /* WITH_OPENSSL */ 978bc5531deSDag-Erling Smørgrav for (mode = 0; mode < MODE_MAX; mode++) { 979bc5531deSDag-Erling Smørgrav kex_free_newkeys(kex->newkeys[mode]); 980bc5531deSDag-Erling Smørgrav kex->newkeys[mode] = NULL; 981bc5531deSDag-Erling Smørgrav } 982bc5531deSDag-Erling Smørgrav sshbuf_free(kex->peer); 983bc5531deSDag-Erling Smørgrav sshbuf_free(kex->my); 98419261079SEd Maste sshbuf_free(kex->client_version); 98519261079SEd Maste sshbuf_free(kex->server_version); 98619261079SEd Maste sshbuf_free(kex->client_pub); 98719261079SEd Maste sshbuf_free(kex->session_id); 9881323ec57SEd Maste sshbuf_free(kex->initial_sig); 9891323ec57SEd Maste sshkey_free(kex->initial_hostkey); 990eccfee6eSDag-Erling Smørgrav free(kex->failed_choice); 991acc1a9efSDag-Erling Smørgrav free(kex->hostkey_alg); 992acc1a9efSDag-Erling Smørgrav free(kex->name); 993bc5531deSDag-Erling Smørgrav free(kex); 994bc5531deSDag-Erling Smørgrav } 995bc5531deSDag-Erling Smørgrav 996bc5531deSDag-Erling Smørgrav int 99719261079SEd Maste kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 99819261079SEd Maste { 99919261079SEd Maste int r; 100019261079SEd Maste 100119261079SEd Maste if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0) 100219261079SEd Maste return r; 100319261079SEd Maste ssh->kex->flags = KEX_INITIAL; 100419261079SEd Maste kex_reset_dispatch(ssh); 100519261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 100619261079SEd Maste return 0; 100719261079SEd Maste } 100819261079SEd Maste 100919261079SEd Maste int 1010bc5531deSDag-Erling Smørgrav kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) 1011bc5531deSDag-Erling Smørgrav { 1012bc5531deSDag-Erling Smørgrav int r; 1013bc5531deSDag-Erling Smørgrav 101419261079SEd Maste if ((r = kex_ready(ssh, proposal)) != 0) 1015bc5531deSDag-Erling Smørgrav return r; 1016bc5531deSDag-Erling Smørgrav if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ 1017bc5531deSDag-Erling Smørgrav kex_free(ssh->kex); 1018bc5531deSDag-Erling Smørgrav ssh->kex = NULL; 1019bc5531deSDag-Erling Smørgrav return r; 1020bc5531deSDag-Erling Smørgrav } 1021bc5531deSDag-Erling Smørgrav return 0; 1022bc5531deSDag-Erling Smørgrav } 1023bc5531deSDag-Erling Smørgrav 1024acc1a9efSDag-Erling Smørgrav /* 1025acc1a9efSDag-Erling Smørgrav * Request key re-exchange, returns 0 on success or a ssherr.h error 1026acc1a9efSDag-Erling Smørgrav * code otherwise. Must not be called if KEX is incomplete or in-progress. 1027acc1a9efSDag-Erling Smørgrav */ 1028acc1a9efSDag-Erling Smørgrav int 1029acc1a9efSDag-Erling Smørgrav kex_start_rekex(struct ssh *ssh) 1030acc1a9efSDag-Erling Smørgrav { 1031acc1a9efSDag-Erling Smørgrav if (ssh->kex == NULL) { 103219261079SEd Maste error_f("no kex"); 1033acc1a9efSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 1034acc1a9efSDag-Erling Smørgrav } 1035acc1a9efSDag-Erling Smørgrav if (ssh->kex->done == 0) { 103619261079SEd Maste error_f("requested twice"); 1037acc1a9efSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 1038acc1a9efSDag-Erling Smørgrav } 1039acc1a9efSDag-Erling Smørgrav ssh->kex->done = 0; 1040acc1a9efSDag-Erling Smørgrav return kex_send_kexinit(ssh); 1041acc1a9efSDag-Erling Smørgrav } 1042acc1a9efSDag-Erling Smørgrav 1043bc5531deSDag-Erling Smørgrav static int 1044bc5531deSDag-Erling Smørgrav choose_enc(struct sshenc *enc, char *client, char *server) 1045a04a10f8SKris Kennaway { 10461e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 1047bc5531deSDag-Erling Smørgrav 1048a04a10f8SKris Kennaway if (name == NULL) 1049bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_CIPHER_ALG_MATCH; 1050d93a896eSDag-Erling Smørgrav if ((enc->cipher = cipher_by_name(name)) == NULL) { 105119261079SEd Maste error_f("unsupported cipher %s", name); 1052d93a896eSDag-Erling Smørgrav free(name); 1053bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 1054d93a896eSDag-Erling Smørgrav } 1055a04a10f8SKris Kennaway enc->name = name; 1056a04a10f8SKris Kennaway enc->enabled = 0; 1057a04a10f8SKris Kennaway enc->iv = NULL; 10586888a9beSDag-Erling Smørgrav enc->iv_len = cipher_ivlen(enc->cipher); 1059a04a10f8SKris Kennaway enc->key = NULL; 1060ae1f160dSDag-Erling Smørgrav enc->key_len = cipher_keylen(enc->cipher); 1061ae1f160dSDag-Erling Smørgrav enc->block_size = cipher_blocksize(enc->cipher); 1062bc5531deSDag-Erling Smørgrav return 0; 1063a04a10f8SKris Kennaway } 1064761efaa7SDag-Erling Smørgrav 1065bc5531deSDag-Erling Smørgrav static int 1066bc5531deSDag-Erling Smørgrav choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) 1067a04a10f8SKris Kennaway { 10681e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 1069bc5531deSDag-Erling Smørgrav 1070a04a10f8SKris Kennaway if (name == NULL) 1071bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_MAC_ALG_MATCH; 1072d93a896eSDag-Erling Smørgrav if (mac_setup(mac, name) < 0) { 107319261079SEd Maste error_f("unsupported MAC %s", name); 1074d93a896eSDag-Erling Smørgrav free(name); 1075bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 1076d93a896eSDag-Erling Smørgrav } 1077a04a10f8SKris Kennaway mac->name = name; 1078a04a10f8SKris Kennaway mac->key = NULL; 1079a04a10f8SKris Kennaway mac->enabled = 0; 1080bc5531deSDag-Erling Smørgrav return 0; 1081a04a10f8SKris Kennaway } 1082761efaa7SDag-Erling Smørgrav 1083bc5531deSDag-Erling Smørgrav static int 1084bc5531deSDag-Erling Smørgrav choose_comp(struct sshcomp *comp, char *client, char *server) 1085a04a10f8SKris Kennaway { 10861e8db6e2SBrian Feldman char *name = match_list(client, server, NULL); 1087bc5531deSDag-Erling Smørgrav 1088a04a10f8SKris Kennaway if (name == NULL) 1089bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_COMPRESS_ALG_MATCH; 109019261079SEd Maste #ifdef WITH_ZLIB 1091043840dfSDag-Erling Smørgrav if (strcmp(name, "zlib@openssh.com") == 0) { 1092043840dfSDag-Erling Smørgrav comp->type = COMP_DELAYED; 1093043840dfSDag-Erling Smørgrav } else if (strcmp(name, "zlib") == 0) { 1094043840dfSDag-Erling Smørgrav comp->type = COMP_ZLIB; 109519261079SEd Maste } else 109619261079SEd Maste #endif /* WITH_ZLIB */ 109719261079SEd Maste if (strcmp(name, "none") == 0) { 1098043840dfSDag-Erling Smørgrav comp->type = COMP_NONE; 1099a04a10f8SKris Kennaway } else { 110019261079SEd Maste error_f("unsupported compression scheme %s", name); 1101d93a896eSDag-Erling Smørgrav free(name); 1102bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 1103a04a10f8SKris Kennaway } 1104a04a10f8SKris Kennaway comp->name = name; 1105bc5531deSDag-Erling Smørgrav return 0; 1106a04a10f8SKris Kennaway } 1107761efaa7SDag-Erling Smørgrav 1108bc5531deSDag-Erling Smørgrav static int 1109bc5531deSDag-Erling Smørgrav choose_kex(struct kex *k, char *client, char *server) 1110a04a10f8SKris Kennaway { 1111e4a9863fSDag-Erling Smørgrav const struct kexalg *kexalg; 1112e4a9863fSDag-Erling Smørgrav 11131e8db6e2SBrian Feldman k->name = match_list(client, server, NULL); 1114bc5531deSDag-Erling Smørgrav 1115acc1a9efSDag-Erling Smørgrav debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); 1116a04a10f8SKris Kennaway if (k->name == NULL) 1117bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_KEX_ALG_MATCH; 111819261079SEd Maste if ((kexalg = kex_alg_by_name(k->name)) == NULL) { 111919261079SEd Maste error_f("unsupported KEX method %s", k->name); 1120bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 112119261079SEd Maste } 1122e4a9863fSDag-Erling Smørgrav k->kex_type = kexalg->type; 1123f7167e0eSDag-Erling Smørgrav k->hash_alg = kexalg->hash_alg; 1124e4a9863fSDag-Erling Smørgrav k->ec_nid = kexalg->ec_nid; 1125bc5531deSDag-Erling Smørgrav return 0; 1126a04a10f8SKris Kennaway } 1127021d409fSDag-Erling Smørgrav 1128bc5531deSDag-Erling Smørgrav static int 1129bc5531deSDag-Erling Smørgrav choose_hostkeyalg(struct kex *k, char *client, char *server) 1130a04a10f8SKris Kennaway { 113119261079SEd Maste free(k->hostkey_alg); 1132acc1a9efSDag-Erling Smørgrav k->hostkey_alg = match_list(client, server, NULL); 1133bc5531deSDag-Erling Smørgrav 1134acc1a9efSDag-Erling Smørgrav debug("kex: host key algorithm: %s", 1135acc1a9efSDag-Erling Smørgrav k->hostkey_alg ? k->hostkey_alg : "(no match)"); 1136acc1a9efSDag-Erling Smørgrav if (k->hostkey_alg == NULL) 1137bc5531deSDag-Erling Smørgrav return SSH_ERR_NO_HOSTKEY_ALG_MATCH; 1138acc1a9efSDag-Erling Smørgrav k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); 113919261079SEd Maste if (k->hostkey_type == KEY_UNSPEC) { 114019261079SEd Maste error_f("unsupported hostkey algorithm %s", k->hostkey_alg); 1141bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 114219261079SEd Maste } 1143acc1a9efSDag-Erling Smørgrav k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); 1144bc5531deSDag-Erling Smørgrav return 0; 1145a04a10f8SKris Kennaway } 1146a04a10f8SKris Kennaway 1147d0c8c0bcSDag-Erling Smørgrav static int 1148d0c8c0bcSDag-Erling Smørgrav proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) 1149d0c8c0bcSDag-Erling Smørgrav { 1150d0c8c0bcSDag-Erling Smørgrav static int check[] = { 1151d0c8c0bcSDag-Erling Smørgrav PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 1152d0c8c0bcSDag-Erling Smørgrav }; 1153d0c8c0bcSDag-Erling Smørgrav int *idx; 1154d0c8c0bcSDag-Erling Smørgrav char *p; 1155d0c8c0bcSDag-Erling Smørgrav 1156d0c8c0bcSDag-Erling Smørgrav for (idx = &check[0]; *idx != -1; idx++) { 1157d0c8c0bcSDag-Erling Smørgrav if ((p = strchr(my[*idx], ',')) != NULL) 1158d0c8c0bcSDag-Erling Smørgrav *p = '\0'; 1159d0c8c0bcSDag-Erling Smørgrav if ((p = strchr(peer[*idx], ',')) != NULL) 1160d0c8c0bcSDag-Erling Smørgrav *p = '\0'; 1161d0c8c0bcSDag-Erling Smørgrav if (strcmp(my[*idx], peer[*idx]) != 0) { 1162d0c8c0bcSDag-Erling Smørgrav debug2("proposal mismatch: my %s peer %s", 1163d0c8c0bcSDag-Erling Smørgrav my[*idx], peer[*idx]); 1164d0c8c0bcSDag-Erling Smørgrav return (0); 1165d0c8c0bcSDag-Erling Smørgrav } 1166d0c8c0bcSDag-Erling Smørgrav } 1167d0c8c0bcSDag-Erling Smørgrav debug2("proposals match"); 1168d0c8c0bcSDag-Erling Smørgrav return (1); 1169d0c8c0bcSDag-Erling Smørgrav } 1170d0c8c0bcSDag-Erling Smørgrav 11711323ec57SEd Maste static int 117292f58c69SGordon Tetlow kexalgs_contains(char **peer, const char *ext) 11731323ec57SEd Maste { 117492f58c69SGordon Tetlow return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext); 11751323ec57SEd Maste } 11761323ec57SEd Maste 1177bc5531deSDag-Erling Smørgrav static int 117892f58c69SGordon Tetlow kex_choose_conf(struct ssh *ssh, uint32_t seq) 1179a04a10f8SKris Kennaway { 1180bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 1181bc5531deSDag-Erling Smørgrav struct newkeys *newkeys; 1182bc5531deSDag-Erling Smørgrav char **my = NULL, **peer = NULL; 11831e8db6e2SBrian Feldman char **cprop, **sprop; 11841e8db6e2SBrian Feldman int nenc, nmac, ncomp; 1185f7167e0eSDag-Erling Smørgrav u_int mode, ctos, need, dh_need, authlen; 1186bc5531deSDag-Erling Smørgrav int r, first_kex_follows; 1187a04a10f8SKris Kennaway 1188acc1a9efSDag-Erling Smørgrav debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); 1189acc1a9efSDag-Erling Smørgrav if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) 1190acc1a9efSDag-Erling Smørgrav goto out; 1191acc1a9efSDag-Erling Smørgrav debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); 1192acc1a9efSDag-Erling Smørgrav if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) 1193bc5531deSDag-Erling Smørgrav goto out; 1194a04a10f8SKris Kennaway 11951e8db6e2SBrian Feldman if (kex->server) { 11961e8db6e2SBrian Feldman cprop=peer; 11971e8db6e2SBrian Feldman sprop=my; 11981e8db6e2SBrian Feldman } else { 11991e8db6e2SBrian Feldman cprop=my; 12001e8db6e2SBrian Feldman sprop=peer; 12011e8db6e2SBrian Feldman } 12021e8db6e2SBrian Feldman 120392f58c69SGordon Tetlow /* Check whether peer supports ext_info/kex_strict */ 120492f58c69SGordon Tetlow if ((kex->flags & KEX_INITIAL) != 0) { 120592f58c69SGordon Tetlow if (kex->server) { 120692f58c69SGordon Tetlow kex->ext_info_c = kexalgs_contains(peer, "ext-info-c"); 120792f58c69SGordon Tetlow kex->kex_strict = kexalgs_contains(peer, 120892f58c69SGordon Tetlow "kex-strict-c-v00@openssh.com"); 120992f58c69SGordon Tetlow } else { 1210069ac184SEd Maste kex->ext_info_s = kexalgs_contains(peer, "ext-info-s"); 121192f58c69SGordon Tetlow kex->kex_strict = kexalgs_contains(peer, 121292f58c69SGordon Tetlow "kex-strict-s-v00@openssh.com"); 121392f58c69SGordon Tetlow } 121492f58c69SGordon Tetlow if (kex->kex_strict) { 121592f58c69SGordon Tetlow debug3_f("will use strict KEX ordering"); 121692f58c69SGordon Tetlow if (seq != 0) 121792f58c69SGordon Tetlow ssh_packet_disconnect(ssh, 121892f58c69SGordon Tetlow "strict KEX violation: " 121992f58c69SGordon Tetlow "KEXINIT was not the first packet"); 122092f58c69SGordon Tetlow } 1221b15c8340SDag-Erling Smørgrav } 1222b15c8340SDag-Erling Smørgrav 12231323ec57SEd Maste /* Check whether client supports rsa-sha2 algorithms */ 12241323ec57SEd Maste if (kex->server && (kex->flags & KEX_INITIAL)) { 12251323ec57SEd Maste if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], 12261323ec57SEd Maste "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com")) 12271323ec57SEd Maste kex->flags |= KEX_RSA_SHA2_256_SUPPORTED; 12281323ec57SEd Maste if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], 12291323ec57SEd Maste "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com")) 12301323ec57SEd Maste kex->flags |= KEX_RSA_SHA2_512_SUPPORTED; 12311323ec57SEd Maste } 12321323ec57SEd Maste 12331e8db6e2SBrian Feldman /* Algorithm Negotiation */ 1234acc1a9efSDag-Erling Smørgrav if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], 1235acc1a9efSDag-Erling Smørgrav sprop[PROPOSAL_KEX_ALGS])) != 0) { 1236acc1a9efSDag-Erling Smørgrav kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; 1237acc1a9efSDag-Erling Smørgrav peer[PROPOSAL_KEX_ALGS] = NULL; 1238acc1a9efSDag-Erling Smørgrav goto out; 1239acc1a9efSDag-Erling Smørgrav } 1240acc1a9efSDag-Erling Smørgrav if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], 1241acc1a9efSDag-Erling Smørgrav sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { 1242acc1a9efSDag-Erling Smørgrav kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; 1243acc1a9efSDag-Erling Smørgrav peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; 1244acc1a9efSDag-Erling Smørgrav goto out; 1245acc1a9efSDag-Erling Smørgrav } 1246a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 1247bc5531deSDag-Erling Smørgrav if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { 1248bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1249bc5531deSDag-Erling Smørgrav goto out; 1250bc5531deSDag-Erling Smørgrav } 12511e8db6e2SBrian Feldman kex->newkeys[mode] = newkeys; 1252d4af9e69SDag-Erling Smørgrav ctos = (!kex->server && mode == MODE_OUT) || 1253d4af9e69SDag-Erling Smørgrav (kex->server && mode == MODE_IN); 1254a04a10f8SKris Kennaway nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; 1255a04a10f8SKris Kennaway nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; 1256a04a10f8SKris Kennaway ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; 1257bc5531deSDag-Erling Smørgrav if ((r = choose_enc(&newkeys->enc, cprop[nenc], 1258eccfee6eSDag-Erling Smørgrav sprop[nenc])) != 0) { 1259eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[nenc]; 1260eccfee6eSDag-Erling Smørgrav peer[nenc] = NULL; 1261bc5531deSDag-Erling Smørgrav goto out; 1262eccfee6eSDag-Erling Smørgrav } 12636888a9beSDag-Erling Smørgrav authlen = cipher_authlen(newkeys->enc.cipher); 1264bc5531deSDag-Erling Smørgrav /* ignore mac for authenticated encryption */ 1265bc5531deSDag-Erling Smørgrav if (authlen == 0 && 1266bc5531deSDag-Erling Smørgrav (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], 1267eccfee6eSDag-Erling Smørgrav sprop[nmac])) != 0) { 1268eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[nmac]; 1269eccfee6eSDag-Erling Smørgrav peer[nmac] = NULL; 1270bc5531deSDag-Erling Smørgrav goto out; 1271eccfee6eSDag-Erling Smørgrav } 1272bc5531deSDag-Erling Smørgrav if ((r = choose_comp(&newkeys->comp, cprop[ncomp], 1273eccfee6eSDag-Erling Smørgrav sprop[ncomp])) != 0) { 1274eccfee6eSDag-Erling Smørgrav kex->failed_choice = peer[ncomp]; 1275eccfee6eSDag-Erling Smørgrav peer[ncomp] = NULL; 1276bc5531deSDag-Erling Smørgrav goto out; 1277eccfee6eSDag-Erling Smørgrav } 1278acc1a9efSDag-Erling Smørgrav debug("kex: %s cipher: %s MAC: %s compression: %s", 1279a04a10f8SKris Kennaway ctos ? "client->server" : "server->client", 12801e8db6e2SBrian Feldman newkeys->enc.name, 12816888a9beSDag-Erling Smørgrav authlen == 0 ? newkeys->mac.name : "<implicit>", 12821e8db6e2SBrian Feldman newkeys->comp.name); 1283a04a10f8SKris Kennaway } 1284f7167e0eSDag-Erling Smørgrav need = dh_need = 0; 1285a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 12861e8db6e2SBrian Feldman newkeys = kex->newkeys[mode]; 1287ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.key_len); 1288ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.block_size); 1289ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->enc.iv_len); 1290ca86bcf2SDag-Erling Smørgrav need = MAXIMUM(need, newkeys->mac.key_len); 1291ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher)); 1292ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); 1293ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); 1294ca86bcf2SDag-Erling Smørgrav dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); 1295a04a10f8SKris Kennaway } 12962632b0c8SKris Kennaway /* XXX need runden? */ 12971e8db6e2SBrian Feldman kex->we_need = need; 1298f7167e0eSDag-Erling Smørgrav kex->dh_need = dh_need; 12991e8db6e2SBrian Feldman 1300d0c8c0bcSDag-Erling Smørgrav /* ignore the next message if the proposals do not match */ 130147dd1d1bSDag-Erling Smørgrav if (first_kex_follows && !proposals_match(my, peer)) 1302bc5531deSDag-Erling Smørgrav ssh->dispatch_skip_packets = 1; 1303bc5531deSDag-Erling Smørgrav r = 0; 1304bc5531deSDag-Erling Smørgrav out: 13051e8db6e2SBrian Feldman kex_prop_free(my); 13061e8db6e2SBrian Feldman kex_prop_free(peer); 1307bc5531deSDag-Erling Smørgrav return r; 1308a04a10f8SKris Kennaway } 1309a04a10f8SKris Kennaway 1310bc5531deSDag-Erling Smørgrav static int 1311bc5531deSDag-Erling Smørgrav derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, 1312bc5531deSDag-Erling Smørgrav const struct sshbuf *shared_secret, u_char **keyp) 1313a04a10f8SKris Kennaway { 1314bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 1315bc5531deSDag-Erling Smørgrav struct ssh_digest_ctx *hashctx = NULL; 13161e8db6e2SBrian Feldman char c = id; 1317043840dfSDag-Erling Smørgrav u_int have; 1318f7167e0eSDag-Erling Smørgrav size_t mdsz; 1319043840dfSDag-Erling Smørgrav u_char *digest; 1320bc5531deSDag-Erling Smørgrav int r; 1321043840dfSDag-Erling Smørgrav 1322f7167e0eSDag-Erling Smørgrav if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) 1323bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_ARGUMENT; 1324ca86bcf2SDag-Erling Smørgrav if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) { 1325bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1326bc5531deSDag-Erling Smørgrav goto out; 1327bc5531deSDag-Erling Smørgrav } 13281e8db6e2SBrian Feldman 13291e8db6e2SBrian Feldman /* K1 = HASH(K || H || "A" || session_id) */ 1330bc5531deSDag-Erling Smørgrav if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || 1331bc5531deSDag-Erling Smørgrav ssh_digest_update_buffer(hashctx, shared_secret) != 0 || 1332f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, hash, hashlen) != 0 || 1333f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, &c, 1) != 0 || 133419261079SEd Maste ssh_digest_update_buffer(hashctx, kex->session_id) != 0 || 1335bc5531deSDag-Erling Smørgrav ssh_digest_final(hashctx, digest, mdsz) != 0) { 1336bc5531deSDag-Erling Smørgrav r = SSH_ERR_LIBCRYPTO_ERROR; 133719261079SEd Maste error_f("KEX hash failed"); 1338bc5531deSDag-Erling Smørgrav goto out; 1339bc5531deSDag-Erling Smørgrav } 1340f7167e0eSDag-Erling Smørgrav ssh_digest_free(hashctx); 1341bc5531deSDag-Erling Smørgrav hashctx = NULL; 13421e8db6e2SBrian Feldman 13431e8db6e2SBrian Feldman /* 13441e8db6e2SBrian Feldman * expand key: 13451e8db6e2SBrian Feldman * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) 13461e8db6e2SBrian Feldman * Key = K1 || K2 || ... || Kn 13471e8db6e2SBrian Feldman */ 13481e8db6e2SBrian Feldman for (have = mdsz; need > have; have += mdsz) { 1349bc5531deSDag-Erling Smørgrav if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || 1350bc5531deSDag-Erling Smørgrav ssh_digest_update_buffer(hashctx, shared_secret) != 0 || 1351f7167e0eSDag-Erling Smørgrav ssh_digest_update(hashctx, hash, hashlen) != 0 || 1352bc5531deSDag-Erling Smørgrav ssh_digest_update(hashctx, digest, have) != 0 || 1353bc5531deSDag-Erling Smørgrav ssh_digest_final(hashctx, digest + have, mdsz) != 0) { 135419261079SEd Maste error_f("KDF failed"); 1355bc5531deSDag-Erling Smørgrav r = SSH_ERR_LIBCRYPTO_ERROR; 1356bc5531deSDag-Erling Smørgrav goto out; 13571e8db6e2SBrian Feldman } 1358bc5531deSDag-Erling Smørgrav ssh_digest_free(hashctx); 1359bc5531deSDag-Erling Smørgrav hashctx = NULL; 1360bc5531deSDag-Erling Smørgrav } 13611e8db6e2SBrian Feldman #ifdef DEBUG_KEX 13621e8db6e2SBrian Feldman fprintf(stderr, "key '%c'== ", c); 13631e8db6e2SBrian Feldman dump_digest("key", digest, need); 13641e8db6e2SBrian Feldman #endif 1365bc5531deSDag-Erling Smørgrav *keyp = digest; 1366bc5531deSDag-Erling Smørgrav digest = NULL; 1367bc5531deSDag-Erling Smørgrav r = 0; 1368bc5531deSDag-Erling Smørgrav out: 1369bc5531deSDag-Erling Smørgrav free(digest); 1370bc5531deSDag-Erling Smørgrav ssh_digest_free(hashctx); 1371bc5531deSDag-Erling Smørgrav return r; 13721e8db6e2SBrian Feldman } 13731e8db6e2SBrian Feldman 13741e8db6e2SBrian Feldman #define NKEYS 6 1375bc5531deSDag-Erling Smørgrav int 1376bc5531deSDag-Erling Smørgrav kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, 1377bc5531deSDag-Erling Smørgrav const struct sshbuf *shared_secret) 13781e8db6e2SBrian Feldman { 1379bc5531deSDag-Erling Smørgrav struct kex *kex = ssh->kex; 13801e8db6e2SBrian Feldman u_char *keys[NKEYS]; 1381bc5531deSDag-Erling Smørgrav u_int i, j, mode, ctos; 1382bc5531deSDag-Erling Smørgrav int r; 1383a04a10f8SKris Kennaway 138419261079SEd Maste /* save initial hash as session id */ 138519261079SEd Maste if ((kex->flags & KEX_INITIAL) != 0) { 138619261079SEd Maste if (sshbuf_len(kex->session_id) != 0) { 138719261079SEd Maste error_f("already have session ID at kex"); 138819261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 138919261079SEd Maste } 139019261079SEd Maste if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0) 139119261079SEd Maste return r; 139219261079SEd Maste } else if (sshbuf_len(kex->session_id) == 0) { 139319261079SEd Maste error_f("no session ID in rekex"); 139419261079SEd Maste return SSH_ERR_INTERNAL_ERROR; 139519261079SEd Maste } 1396021d409fSDag-Erling Smørgrav for (i = 0; i < NKEYS; i++) { 1397bc5531deSDag-Erling Smørgrav if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, 1398bc5531deSDag-Erling Smørgrav shared_secret, &keys[i])) != 0) { 1399bc5531deSDag-Erling Smørgrav for (j = 0; j < i; j++) 1400bc5531deSDag-Erling Smørgrav free(keys[j]); 1401bc5531deSDag-Erling Smørgrav return r; 1402021d409fSDag-Erling Smørgrav } 1403bc5531deSDag-Erling Smørgrav } 1404a04a10f8SKris Kennaway for (mode = 0; mode < MODE_MAX; mode++) { 1405761efaa7SDag-Erling Smørgrav ctos = (!kex->server && mode == MODE_OUT) || 1406761efaa7SDag-Erling Smørgrav (kex->server && mode == MODE_IN); 1407bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; 1408bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; 1409bc5531deSDag-Erling Smørgrav kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; 1410a04a10f8SKris Kennaway } 1411bc5531deSDag-Erling Smørgrav return 0; 1412a04a10f8SKris Kennaway } 14131e8db6e2SBrian Feldman 1414bc5531deSDag-Erling Smørgrav int 141519261079SEd Maste kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) 1416f7167e0eSDag-Erling Smørgrav { 141719261079SEd Maste struct kex *kex = ssh->kex; 1418f7167e0eSDag-Erling Smørgrav 141919261079SEd Maste *pubp = NULL; 142019261079SEd Maste *prvp = NULL; 142119261079SEd Maste if (kex->load_host_public_key == NULL || 142219261079SEd Maste kex->load_host_private_key == NULL) { 142319261079SEd Maste error_f("missing hostkey loader"); 142419261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 1425f7167e0eSDag-Erling Smørgrav } 142619261079SEd Maste *pubp = kex->load_host_public_key(kex->hostkey_type, 142719261079SEd Maste kex->hostkey_nid, ssh); 142819261079SEd Maste *prvp = kex->load_host_private_key(kex->hostkey_type, 142919261079SEd Maste kex->hostkey_nid, ssh); 143019261079SEd Maste if (*pubp == NULL) 143119261079SEd Maste return SSH_ERR_NO_HOSTKEY_LOADED; 143219261079SEd Maste return 0; 143319261079SEd Maste } 1434f7167e0eSDag-Erling Smørgrav 143519261079SEd Maste int 143619261079SEd Maste kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) 143719261079SEd Maste { 143819261079SEd Maste struct kex *kex = ssh->kex; 143919261079SEd Maste 144019261079SEd Maste if (kex->verify_host_key == NULL) { 144119261079SEd Maste error_f("missing hostkey verifier"); 144219261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 144319261079SEd Maste } 144419261079SEd Maste if (server_host_key->type != kex->hostkey_type || 144519261079SEd Maste (kex->hostkey_type == KEY_ECDSA && 144619261079SEd Maste server_host_key->ecdsa_nid != kex->hostkey_nid)) 144719261079SEd Maste return SSH_ERR_KEY_TYPE_MISMATCH; 144819261079SEd Maste if (kex->verify_host_key(server_host_key, ssh) == -1) 144919261079SEd Maste return SSH_ERR_SIGNATURE_INVALID; 145019261079SEd Maste return 0; 145119261079SEd Maste } 1452d74d50a8SDag-Erling Smørgrav 14534a421b63SDag-Erling Smørgrav #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) 14541e8db6e2SBrian Feldman void 145519261079SEd Maste dump_digest(const char *msg, const u_char *digest, int len) 14561e8db6e2SBrian Feldman { 14571e8db6e2SBrian Feldman fprintf(stderr, "%s\n", msg); 1458bc5531deSDag-Erling Smørgrav sshbuf_dump_data(digest, len, stderr); 14591e8db6e2SBrian Feldman } 14601e8db6e2SBrian Feldman #endif 146119261079SEd Maste 146219261079SEd Maste /* 146319261079SEd Maste * Send a plaintext error message to the peer, suffixed by \r\n. 146419261079SEd Maste * Only used during banner exchange, and there only for the server. 146519261079SEd Maste */ 146619261079SEd Maste static void 146719261079SEd Maste send_error(struct ssh *ssh, char *msg) 146819261079SEd Maste { 146919261079SEd Maste char *crnl = "\r\n"; 147019261079SEd Maste 147119261079SEd Maste if (!ssh->kex->server) 147219261079SEd Maste return; 147319261079SEd Maste 147419261079SEd Maste if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), 147519261079SEd Maste msg, strlen(msg)) != strlen(msg) || 147619261079SEd Maste atomicio(vwrite, ssh_packet_get_connection_out(ssh), 147719261079SEd Maste crnl, strlen(crnl)) != strlen(crnl)) 147819261079SEd Maste error_f("write: %.100s", strerror(errno)); 147919261079SEd Maste } 148019261079SEd Maste 148119261079SEd Maste /* 148219261079SEd Maste * Sends our identification string and waits for the peer's. Will block for 148319261079SEd Maste * up to timeout_ms (or indefinitely if timeout_ms <= 0). 148419261079SEd Maste * Returns on 0 success or a ssherr.h code on failure. 148519261079SEd Maste */ 148619261079SEd Maste int 148719261079SEd Maste kex_exchange_identification(struct ssh *ssh, int timeout_ms, 148819261079SEd Maste const char *version_addendum) 148919261079SEd Maste { 149019261079SEd Maste int remote_major, remote_minor, mismatch, oerrno = 0; 1491f374ba41SEd Maste size_t len, n; 149219261079SEd Maste int r, expect_nl; 149319261079SEd Maste u_char c; 149419261079SEd Maste struct sshbuf *our_version = ssh->kex->server ? 149519261079SEd Maste ssh->kex->server_version : ssh->kex->client_version; 149619261079SEd Maste struct sshbuf *peer_version = ssh->kex->server ? 149719261079SEd Maste ssh->kex->client_version : ssh->kex->server_version; 149819261079SEd Maste char *our_version_string = NULL, *peer_version_string = NULL; 149919261079SEd Maste char *cp, *remote_version = NULL; 150019261079SEd Maste 150119261079SEd Maste /* Prepare and send our banner */ 150219261079SEd Maste sshbuf_reset(our_version); 150319261079SEd Maste if (version_addendum != NULL && *version_addendum == '\0') 150419261079SEd Maste version_addendum = NULL; 1505069ac184SEd Maste if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%s%s%s\r\n", 150619261079SEd Maste PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, 150719261079SEd Maste version_addendum == NULL ? "" : " ", 150819261079SEd Maste version_addendum == NULL ? "" : version_addendum)) != 0) { 150919261079SEd Maste oerrno = errno; 151019261079SEd Maste error_fr(r, "sshbuf_putf"); 151119261079SEd Maste goto out; 151219261079SEd Maste } 151319261079SEd Maste 151419261079SEd Maste if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), 151519261079SEd Maste sshbuf_mutable_ptr(our_version), 151619261079SEd Maste sshbuf_len(our_version)) != sshbuf_len(our_version)) { 151719261079SEd Maste oerrno = errno; 151819261079SEd Maste debug_f("write: %.100s", strerror(errno)); 151919261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 152019261079SEd Maste goto out; 152119261079SEd Maste } 152219261079SEd Maste if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */ 152319261079SEd Maste oerrno = errno; 152419261079SEd Maste error_fr(r, "sshbuf_consume_end"); 152519261079SEd Maste goto out; 152619261079SEd Maste } 152719261079SEd Maste our_version_string = sshbuf_dup_string(our_version); 152819261079SEd Maste if (our_version_string == NULL) { 152919261079SEd Maste error_f("sshbuf_dup_string failed"); 153019261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 153119261079SEd Maste goto out; 153219261079SEd Maste } 153319261079SEd Maste debug("Local version string %.100s", our_version_string); 153419261079SEd Maste 153519261079SEd Maste /* Read other side's version identification. */ 153619261079SEd Maste for (n = 0; ; n++) { 153719261079SEd Maste if (n >= SSH_MAX_PRE_BANNER_LINES) { 153819261079SEd Maste send_error(ssh, "No SSH identification string " 153919261079SEd Maste "received."); 154019261079SEd Maste error_f("No SSH version received in first %u lines " 154119261079SEd Maste "from server", SSH_MAX_PRE_BANNER_LINES); 154219261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 154319261079SEd Maste goto out; 154419261079SEd Maste } 154519261079SEd Maste sshbuf_reset(peer_version); 154619261079SEd Maste expect_nl = 0; 1547f374ba41SEd Maste for (;;) { 154819261079SEd Maste if (timeout_ms > 0) { 154919261079SEd Maste r = waitrfd(ssh_packet_get_connection_in(ssh), 15501b91d634SEd Maste &timeout_ms, NULL); 155119261079SEd Maste if (r == -1 && errno == ETIMEDOUT) { 155219261079SEd Maste send_error(ssh, "Timed out waiting " 155319261079SEd Maste "for SSH identification string."); 155419261079SEd Maste error("Connection timed out during " 155519261079SEd Maste "banner exchange"); 155619261079SEd Maste r = SSH_ERR_CONN_TIMEOUT; 155719261079SEd Maste goto out; 155819261079SEd Maste } else if (r == -1) { 155919261079SEd Maste oerrno = errno; 156019261079SEd Maste error_f("%s", strerror(errno)); 156119261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 156219261079SEd Maste goto out; 156319261079SEd Maste } 156419261079SEd Maste } 156519261079SEd Maste 156619261079SEd Maste len = atomicio(read, ssh_packet_get_connection_in(ssh), 156719261079SEd Maste &c, 1); 156819261079SEd Maste if (len != 1 && errno == EPIPE) { 1569edf85781SEd Maste verbose_f("Connection closed by remote host"); 157019261079SEd Maste r = SSH_ERR_CONN_CLOSED; 157119261079SEd Maste goto out; 157219261079SEd Maste } else if (len != 1) { 157319261079SEd Maste oerrno = errno; 157419261079SEd Maste error_f("read: %.100s", strerror(errno)); 157519261079SEd Maste r = SSH_ERR_SYSTEM_ERROR; 157619261079SEd Maste goto out; 157719261079SEd Maste } 157819261079SEd Maste if (c == '\r') { 157919261079SEd Maste expect_nl = 1; 158019261079SEd Maste continue; 158119261079SEd Maste } 158219261079SEd Maste if (c == '\n') 158319261079SEd Maste break; 158419261079SEd Maste if (c == '\0' || expect_nl) { 1585edf85781SEd Maste verbose_f("banner line contains invalid " 158619261079SEd Maste "characters"); 158719261079SEd Maste goto invalid; 158819261079SEd Maste } 158919261079SEd Maste if ((r = sshbuf_put_u8(peer_version, c)) != 0) { 159019261079SEd Maste oerrno = errno; 159119261079SEd Maste error_fr(r, "sshbuf_put"); 159219261079SEd Maste goto out; 159319261079SEd Maste } 159419261079SEd Maste if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { 1595edf85781SEd Maste verbose_f("banner line too long"); 159619261079SEd Maste goto invalid; 159719261079SEd Maste } 159819261079SEd Maste } 159919261079SEd Maste /* Is this an actual protocol banner? */ 160019261079SEd Maste if (sshbuf_len(peer_version) > 4 && 160119261079SEd Maste memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0) 160219261079SEd Maste break; 160319261079SEd Maste /* If not, then just log the line and continue */ 160419261079SEd Maste if ((cp = sshbuf_dup_string(peer_version)) == NULL) { 160519261079SEd Maste error_f("sshbuf_dup_string failed"); 160619261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 160719261079SEd Maste goto out; 160819261079SEd Maste } 160919261079SEd Maste /* Do not accept lines before the SSH ident from a client */ 161019261079SEd Maste if (ssh->kex->server) { 1611edf85781SEd Maste verbose_f("client sent invalid protocol identifier " 161219261079SEd Maste "\"%.256s\"", cp); 161319261079SEd Maste free(cp); 161419261079SEd Maste goto invalid; 161519261079SEd Maste } 161619261079SEd Maste debug_f("banner line %zu: %s", n, cp); 161719261079SEd Maste free(cp); 161819261079SEd Maste } 161919261079SEd Maste peer_version_string = sshbuf_dup_string(peer_version); 162019261079SEd Maste if (peer_version_string == NULL) 16214d3fc8b0SEd Maste fatal_f("sshbuf_dup_string failed"); 162219261079SEd Maste /* XXX must be same size for sscanf */ 162319261079SEd Maste if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) { 162419261079SEd Maste error_f("calloc failed"); 162519261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 162619261079SEd Maste goto out; 162719261079SEd Maste } 162819261079SEd Maste 162919261079SEd Maste /* 163019261079SEd Maste * Check that the versions match. In future this might accept 163119261079SEd Maste * several versions and set appropriate flags to handle them. 163219261079SEd Maste */ 163319261079SEd Maste if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n", 163419261079SEd Maste &remote_major, &remote_minor, remote_version) != 3) { 163519261079SEd Maste error("Bad remote protocol version identification: '%.100s'", 163619261079SEd Maste peer_version_string); 163719261079SEd Maste invalid: 163819261079SEd Maste send_error(ssh, "Invalid SSH identification string."); 163919261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 164019261079SEd Maste goto out; 164119261079SEd Maste } 164219261079SEd Maste debug("Remote protocol version %d.%d, remote software version %.100s", 164319261079SEd Maste remote_major, remote_minor, remote_version); 164419261079SEd Maste compat_banner(ssh, remote_version); 164519261079SEd Maste 164619261079SEd Maste mismatch = 0; 164719261079SEd Maste switch (remote_major) { 164819261079SEd Maste case 2: 164919261079SEd Maste break; 165019261079SEd Maste case 1: 165119261079SEd Maste if (remote_minor != 99) 165219261079SEd Maste mismatch = 1; 165319261079SEd Maste break; 165419261079SEd Maste default: 165519261079SEd Maste mismatch = 1; 165619261079SEd Maste break; 165719261079SEd Maste } 165819261079SEd Maste if (mismatch) { 165919261079SEd Maste error("Protocol major versions differ: %d vs. %d", 166019261079SEd Maste PROTOCOL_MAJOR_2, remote_major); 166119261079SEd Maste send_error(ssh, "Protocol major versions differ."); 166219261079SEd Maste r = SSH_ERR_NO_PROTOCOL_VERSION; 166319261079SEd Maste goto out; 166419261079SEd Maste } 166519261079SEd Maste 166619261079SEd Maste if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) { 166719261079SEd Maste logit("probed from %s port %d with %s. Don't panic.", 166819261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 166919261079SEd Maste peer_version_string); 167019261079SEd Maste r = SSH_ERR_CONN_CLOSED; /* XXX */ 167119261079SEd Maste goto out; 167219261079SEd Maste } 167319261079SEd Maste if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) { 167419261079SEd Maste logit("scanned from %s port %d with %s. Don't panic.", 167519261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 167619261079SEd Maste peer_version_string); 167719261079SEd Maste r = SSH_ERR_CONN_CLOSED; /* XXX */ 167819261079SEd Maste goto out; 167919261079SEd Maste } 168019261079SEd Maste /* success */ 168119261079SEd Maste r = 0; 168219261079SEd Maste out: 168319261079SEd Maste free(our_version_string); 168419261079SEd Maste free(peer_version_string); 168519261079SEd Maste free(remote_version); 168619261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR) 168719261079SEd Maste errno = oerrno; 168819261079SEd Maste return r; 168919261079SEd Maste } 169019261079SEd Maste 1691