aboutsummaryrefslogtreecommitdiff
path: root/kex.c
diff options
context:
space:
mode:
Diffstat (limited to 'kex.c')
-rw-r--r--kex.c133
1 files changed, 67 insertions, 66 deletions
diff --git a/kex.c b/kex.c
index aecb9394d805..30425ab8ab9b 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.159 2020/07/05 23:59:45 djm Exp $ */
+/* $OpenBSD: kex.c,v 1.167 2021/01/31 22:55:29 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@@ -110,8 +110,10 @@ static const struct kexalg kexalgs[] = {
#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
{ KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
- { KEX_SNTRUP4591761X25519_SHA512, KEX_KEM_SNTRUP4591761X25519_SHA512, 0,
+#ifdef USE_SNTRUP761X25519
+ { KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0,
SSH_DIGEST_SHA512 },
+#endif
#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
{ NULL, 0, -1, -1},
};
@@ -360,14 +362,13 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
goto out;
}
if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */
- error("%s: consume cookie: %s", __func__, ssh_err(r));
+ error_fr(r, "consume cookie");
goto out;
}
/* extract kex init proposal strings */
for (i = 0; i < PROPOSAL_MAX; i++) {
if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) {
- error("%s: parse proposal %u: %s", __func__,
- i, ssh_err(r));
+ error_fr(r, "parse proposal %u", i);
goto out;
}
debug2("%s: %s", proposal_names[i], proposal[i]);
@@ -375,7 +376,7 @@ kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
/* first kex follows / reserved */
if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */
(r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */
- error("%s: parse: %s", __func__, ssh_err(r));
+ error_fr(r, "parse");
goto out;
}
if (first_kex_follows != NULL)
@@ -404,7 +405,7 @@ kex_prop_free(char **proposal)
}
/* ARGSUSED */
-static int
+int
kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
{
int r;
@@ -439,7 +440,7 @@ kex_send_ext_info(struct ssh *ssh)
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
(r = sshpkt_put_cstring(ssh, algs)) != 0 ||
(r = sshpkt_send(ssh)) != 0) {
- error("%s: compose: %s", __func__, ssh_err(r));
+ error_fr(r, "compose");
goto out;
}
/* success */
@@ -491,14 +492,14 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
if (strcmp(name, "server-sig-algs") == 0) {
/* Ensure no \0 lurking in value */
if (memchr(val, '\0', vlen) != NULL) {
- error("%s: nul byte in %s", __func__, name);
+ error_f("nul byte in %s", name);
return SSH_ERR_INVALID_FORMAT;
}
- debug("%s: %s=<%s>", __func__, name, val);
+ debug_f("%s=<%s>", name, val);
kex->server_sig_algs = val;
val = NULL;
} else
- debug("%s: %s (unrecognised)", __func__, name);
+ debug_f("%s (unrecognised)", name);
free(name);
free(val);
}
@@ -536,7 +537,7 @@ kex_send_kexinit(struct ssh *ssh)
int r;
if (kex == NULL) {
- error("%s: no hex", __func__);
+ error_f("no kex");
return SSH_ERR_INTERNAL_ERROR;
}
if (kex->flags & KEX_INIT_SENT)
@@ -545,12 +546,12 @@ kex_send_kexinit(struct ssh *ssh)
/* generate a random cookie */
if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) {
- error("%s: bad kex length: %zu < %d", __func__,
+ error_f("bad kex length: %zu < %d",
sshbuf_len(kex->my), KEX_COOKIE_LEN);
return SSH_ERR_INVALID_FORMAT;
}
if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) {
- error("%s: buffer error", __func__);
+ error_f("buffer error");
return SSH_ERR_INTERNAL_ERROR;
}
arc4random_buf(cookie, KEX_COOKIE_LEN);
@@ -558,7 +559,7 @@ kex_send_kexinit(struct ssh *ssh)
if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
(r = sshpkt_putb(ssh, kex->my)) != 0 ||
(r = sshpkt_send(ssh)) != 0) {
- error("%s: compose reply: %s", __func__, ssh_err(r));
+ error_fr(r, "compose reply");
return r;
}
debug("SSH2_MSG_KEXINIT sent");
@@ -578,7 +579,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
debug("SSH2_MSG_KEXINIT received");
if (kex == NULL) {
- error("%s: no hex", __func__);
+ error_f("no kex");
return SSH_ERR_INTERNAL_ERROR;
}
ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
@@ -589,13 +590,13 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
/* discard packet */
for (i = 0; i < KEX_COOKIE_LEN; i++) {
if ((r = sshpkt_get_u8(ssh, NULL)) != 0) {
- error("%s: discard cookie: %s", __func__, ssh_err(r));
+ error_fr(r, "discard cookie");
return r;
}
}
for (i = 0; i < PROPOSAL_MAX; i++) {
if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
- error("%s: discard proposal: %s", __func__, ssh_err(r));
+ error_fr(r, "discard proposal");
return r;
}
}
@@ -623,7 +624,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
return (kex->kex[kex->kex_type])(ssh);
- error("%s: unknown kex type %u", __func__, kex->kex_type);
+ error_f("unknown kex type %u", kex->kex_type);
return SSH_ERR_INTERNAL_ERROR;
}
@@ -636,7 +637,8 @@ kex_new(void)
(kex->peer = sshbuf_new()) == NULL ||
(kex->my = sshbuf_new()) == NULL ||
(kex->client_version = sshbuf_new()) == NULL ||
- (kex->server_version = sshbuf_new()) == NULL) {
+ (kex->server_version = sshbuf_new()) == NULL ||
+ (kex->session_id = sshbuf_new()) == NULL) {
kex_free(kex);
return NULL;
}
@@ -696,7 +698,7 @@ kex_free(struct kex *kex)
sshbuf_free(kex->client_version);
sshbuf_free(kex->server_version);
sshbuf_free(kex->client_pub);
- free(kex->session_id);
+ sshbuf_free(kex->session_id);
free(kex->failed_choice);
free(kex->hostkey_alg);
free(kex->name);
@@ -739,11 +741,11 @@ int
kex_start_rekex(struct ssh *ssh)
{
if (ssh->kex == NULL) {
- error("%s: no kex", __func__);
+ error_f("no kex");
return SSH_ERR_INTERNAL_ERROR;
}
if (ssh->kex->done == 0) {
- error("%s: requested twice", __func__);
+ error_f("requested twice");
return SSH_ERR_INTERNAL_ERROR;
}
ssh->kex->done = 0;
@@ -758,7 +760,7 @@ choose_enc(struct sshenc *enc, char *client, char *server)
if (name == NULL)
return SSH_ERR_NO_CIPHER_ALG_MATCH;
if ((enc->cipher = cipher_by_name(name)) == NULL) {
- error("%s: unsupported cipher %s", __func__, name);
+ error_f("unsupported cipher %s", name);
free(name);
return SSH_ERR_INTERNAL_ERROR;
}
@@ -780,7 +782,7 @@ choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
if (name == NULL)
return SSH_ERR_NO_MAC_ALG_MATCH;
if (mac_setup(mac, name) < 0) {
- error("%s: unsupported MAC %s", __func__, name);
+ error_f("unsupported MAC %s", name);
free(name);
return SSH_ERR_INTERNAL_ERROR;
}
@@ -807,7 +809,7 @@ choose_comp(struct sshcomp *comp, char *client, char *server)
if (strcmp(name, "none") == 0) {
comp->type = COMP_NONE;
} else {
- error("%s: unsupported compression scheme %s", __func__, name);
+ error_f("unsupported compression scheme %s", name);
free(name);
return SSH_ERR_INTERNAL_ERROR;
}
@@ -826,7 +828,7 @@ choose_kex(struct kex *k, char *client, char *server)
if (k->name == NULL)
return SSH_ERR_NO_KEX_ALG_MATCH;
if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
- error("%s: unsupported KEX method %s", __func__, k->name);
+ error_f("unsupported KEX method %s", k->name);
return SSH_ERR_INTERNAL_ERROR;
}
k->kex_type = kexalg->type;
@@ -838,6 +840,7 @@ choose_kex(struct kex *k, char *client, char *server)
static int
choose_hostkeyalg(struct kex *k, char *client, char *server)
{
+ free(k->hostkey_alg);
k->hostkey_alg = match_list(client, server, NULL);
debug("kex: host key algorithm: %s",
@@ -846,8 +849,7 @@ choose_hostkeyalg(struct kex *k, char *client, char *server)
return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
k->hostkey_type = sshkey_type_from_name(k->hostkey_alg);
if (k->hostkey_type == KEY_UNSPEC) {
- error("%s: unsupported hostkey algorithm %s", __func__,
- k->hostkey_alg);
+ error_f("unsupported hostkey algorithm %s", k->hostkey_alg);
return SSH_ERR_INTERNAL_ERROR;
}
k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg);
@@ -1014,11 +1016,10 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
ssh_digest_update(hashctx, hash, hashlen) != 0 ||
ssh_digest_update(hashctx, &c, 1) != 0 ||
- ssh_digest_update(hashctx, kex->session_id,
- kex->session_id_len) != 0 ||
+ ssh_digest_update_buffer(hashctx, kex->session_id) != 0 ||
ssh_digest_final(hashctx, digest, mdsz) != 0) {
r = SSH_ERR_LIBCRYPTO_ERROR;
- error("%s: KEX hash failed", __func__);
+ error_f("KEX hash failed");
goto out;
}
ssh_digest_free(hashctx);
@@ -1035,7 +1036,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
ssh_digest_update(hashctx, hash, hashlen) != 0 ||
ssh_digest_update(hashctx, digest, have) != 0 ||
ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
- error("%s: KDF failed", __func__);
+ error_f("KDF failed");
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
@@ -1066,12 +1067,16 @@ kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
int r;
/* save initial hash as session id */
- if (kex->session_id == NULL) {
- kex->session_id_len = hashlen;
- kex->session_id = malloc(kex->session_id_len);
- if (kex->session_id == NULL)
- return SSH_ERR_ALLOC_FAIL;
- memcpy(kex->session_id, hash, kex->session_id_len);
+ if ((kex->flags & KEX_INITIAL) != 0) {
+ if (sshbuf_len(kex->session_id) != 0) {
+ error_f("already have session ID at kex");
+ return SSH_ERR_INTERNAL_ERROR;
+ }
+ if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0)
+ return r;
+ } else if (sshbuf_len(kex->session_id) == 0) {
+ error_f("no session ID in rekex");
+ return SSH_ERR_INTERNAL_ERROR;
}
for (i = 0; i < NKEYS; i++) {
if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
@@ -1100,7 +1105,7 @@ kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp)
*prvp = NULL;
if (kex->load_host_public_key == NULL ||
kex->load_host_private_key == NULL) {
- error("%s: missing hostkey loader", __func__);
+ error_f("missing hostkey loader");
return SSH_ERR_INVALID_ARGUMENT;
}
*pubp = kex->load_host_public_key(kex->hostkey_type,
@@ -1118,7 +1123,7 @@ kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key)
struct kex *kex = ssh->kex;
if (kex->verify_host_key == NULL) {
- error("%s: missing hostkey verifier", __func__);
+ error_f("missing hostkey verifier");
return SSH_ERR_INVALID_ARGUMENT;
}
if (server_host_key->type != kex->hostkey_type ||
@@ -1155,7 +1160,7 @@ send_error(struct ssh *ssh, char *msg)
msg, strlen(msg)) != strlen(msg) ||
atomicio(vwrite, ssh_packet_get_connection_out(ssh),
crnl, strlen(crnl)) != strlen(crnl))
- error("%s: write: %.100s", __func__, strerror(errno));
+ error_f("write: %.100s", strerror(errno));
}
/*
@@ -1187,7 +1192,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
version_addendum == NULL ? "" : " ",
version_addendum == NULL ? "" : version_addendum)) != 0) {
oerrno = errno;
- error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+ error_fr(r, "sshbuf_putf");
goto out;
}
@@ -1195,18 +1200,18 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
sshbuf_mutable_ptr(our_version),
sshbuf_len(our_version)) != sshbuf_len(our_version)) {
oerrno = errno;
- debug("%s: write: %.100s", __func__, strerror(errno));
+ debug_f("write: %.100s", strerror(errno));
r = SSH_ERR_SYSTEM_ERROR;
goto out;
}
if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */
oerrno = errno;
- error("%s: sshbuf_consume_end: %s", __func__, ssh_err(r));
+ error_fr(r, "sshbuf_consume_end");
goto out;
}
our_version_string = sshbuf_dup_string(our_version);
if (our_version_string == NULL) {
- error("%s: sshbuf_dup_string failed", __func__);
+ error_f("sshbuf_dup_string failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
@@ -1217,8 +1222,8 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
if (n >= SSH_MAX_PRE_BANNER_LINES) {
send_error(ssh, "No SSH identification string "
"received.");
- error("%s: No SSH version received in first %u lines "
- "from server", __func__, SSH_MAX_PRE_BANNER_LINES);
+ error_f("No SSH version received in first %u lines "
+ "from server", SSH_MAX_PRE_BANNER_LINES);
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
@@ -1237,8 +1242,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
goto out;
} else if (r == -1) {
oerrno = errno;
- error("%s: %s",
- __func__, strerror(errno));
+ error_f("%s", strerror(errno));
r = SSH_ERR_SYSTEM_ERROR;
goto out;
}
@@ -1247,14 +1251,12 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
len = atomicio(read, ssh_packet_get_connection_in(ssh),
&c, 1);
if (len != 1 && errno == EPIPE) {
- error("%s: Connection closed by remote host",
- __func__);
+ error_f("Connection closed by remote host");
r = SSH_ERR_CONN_CLOSED;
goto out;
} else if (len != 1) {
oerrno = errno;
- error("%s: read: %.100s",
- __func__, strerror(errno));
+ error_f("read: %.100s", strerror(errno));
r = SSH_ERR_SYSTEM_ERROR;
goto out;
}
@@ -1265,18 +1267,17 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
if (c == '\n')
break;
if (c == '\0' || expect_nl) {
- error("%s: banner line contains invalid "
- "characters", __func__);
+ error_f("banner line contains invalid "
+ "characters");
goto invalid;
}
if ((r = sshbuf_put_u8(peer_version, c)) != 0) {
oerrno = errno;
- error("%s: sshbuf_put: %s",
- __func__, ssh_err(r));
+ error_fr(r, "sshbuf_put");
goto out;
}
if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) {
- error("%s: banner line too long", __func__);
+ error_f("banner line too long");
goto invalid;
}
}
@@ -1286,26 +1287,26 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
break;
/* If not, then just log the line and continue */
if ((cp = sshbuf_dup_string(peer_version)) == NULL) {
- error("%s: sshbuf_dup_string failed", __func__);
+ error_f("sshbuf_dup_string failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
/* Do not accept lines before the SSH ident from a client */
if (ssh->kex->server) {
- error("%s: client sent invalid protocol identifier "
- "\"%.256s\"", __func__, cp);
+ error_f("client sent invalid protocol identifier "
+ "\"%.256s\"", cp);
free(cp);
goto invalid;
}
- debug("%s: banner line %zu: %s", __func__, n, cp);
+ debug_f("banner line %zu: %s", n, cp);
free(cp);
}
peer_version_string = sshbuf_dup_string(peer_version);
if (peer_version_string == NULL)
- error("%s: sshbuf_dup_string failed", __func__);
+ error_f("sshbuf_dup_string failed");
/* XXX must be same size for sscanf */
if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) {
- error("%s: calloc failed", __func__);
+ error_f("calloc failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
@@ -1325,7 +1326,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
}
debug("Remote protocol version %d.%d, remote software version %.100s",
remote_major, remote_minor, remote_version);
- ssh->compat = compat_datafellows(remote_version);
+ compat_banner(ssh, remote_version);
mismatch = 0;
switch (remote_major) {