parent
ab0d991457
commit
044f5935ef
@ -0,0 +1,110 @@
|
||||
diff -up openssh-8.7p1/sshkey.c.evpgenrsa openssh-8.7p1/sshkey.c
|
||||
--- openssh-8.7p1/sshkey.c.evpgenrsa 2022-06-30 15:14:58.200518353 +0200
|
||||
+++ openssh-8.7p1/sshkey.c 2022-06-30 15:24:31.499641196 +0200
|
||||
@@ -1657,7 +1657,8 @@ sshkey_cert_type(const struct sshkey *k)
|
||||
static int
|
||||
rsa_generate_private_key(u_int bits, RSA **rsap)
|
||||
{
|
||||
- RSA *private = NULL;
|
||||
+ EVP_PKEY_CTX *ctx = NULL;
|
||||
+ EVP_PKEY *res = NULL;
|
||||
BIGNUM *f4 = NULL;
|
||||
int ret = SSH_ERR_INTERNAL_ERROR;
|
||||
|
||||
@@ -1667,20 +1668,42 @@ rsa_generate_private_key(u_int bits, RSA
|
||||
bits > SSHBUF_MAX_BIGNUM * 8)
|
||||
return SSH_ERR_KEY_LENGTH;
|
||||
*rsap = NULL;
|
||||
- if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
|
||||
+
|
||||
+ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) == NULL
|
||||
+ || (f4 = BN_new()) == NULL || !BN_set_word(f4, RSA_F4)) {
|
||||
ret = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
- if (!BN_set_word(f4, RSA_F4) ||
|
||||
- !RSA_generate_key_ex(private, bits, f4, NULL)) {
|
||||
+
|
||||
+ if (EVP_PKEY_keygen_init(ctx) <= 0) {
|
||||
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) {
|
||||
+ ret = SSH_ERR_KEY_LENGTH;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, f4) <= 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (EVP_PKEY_keygen(ctx, &res) <= 0) {
|
||||
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* This function is deprecated in OpenSSL 3.0 but OpenSSH doesn't worry about it*/
|
||||
+ *rsap = EVP_PKEY_get1_RSA(res);
|
||||
+ if (*rsap) {
|
||||
+ ret = 0;
|
||||
+ } else {
|
||||
ret = SSH_ERR_LIBCRYPTO_ERROR;
|
||||
goto out;
|
||||
}
|
||||
- *rsap = private;
|
||||
- private = NULL;
|
||||
- ret = 0;
|
||||
out:
|
||||
- RSA_free(private);
|
||||
+ EVP_PKEY_CTX_free(ctx);
|
||||
+ EVP_PKEY_free(res);
|
||||
BN_free(f4);
|
||||
return ret;
|
||||
}
|
||||
@@ -1820,7 +1820,8 @@ sshkey_ecdsa_key_to_nid(EC_KEY *k)
|
||||
static int
|
||||
ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
|
||||
{
|
||||
- EC_KEY *private;
|
||||
+ EVP_PKEY_CTX *ctx = NULL;
|
||||
+ EVP_PKEY *res = NULL;
|
||||
int ret = SSH_ERR_INTERNAL_ERROR;
|
||||
|
||||
if (nid == NULL || ecdsap == NULL)
|
||||
@@ -1828,20 +1829,29 @@ ecdsa_generate_private_key(u_int bits, i
|
||||
if ((*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
|
||||
return SSH_ERR_KEY_LENGTH;
|
||||
*ecdsap = NULL;
|
||||
- if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) {
|
||||
+
|
||||
+ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL) {
|
||||
ret = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
- if (EC_KEY_generate_key(private) != 1) {
|
||||
+
|
||||
+ if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_group_name(ctx, OBJ_nid2sn(*nid)) <= 0
|
||||
+ || EVP_PKEY_keygen(ctx, &res) <= 0) {
|
||||
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ /* This function is deprecated in OpenSSL 3.0 but OpenSSH doesn't worry about it*/
|
||||
+ *ecdsap = EVP_PKEY_get1_EC_KEY(res);
|
||||
+ if (*ecdsap) {
|
||||
+ EC_KEY_set_asn1_flag(*ecdsap, OPENSSL_EC_NAMED_CURVE);
|
||||
+ ret = 0;
|
||||
+ } else {
|
||||
ret = SSH_ERR_LIBCRYPTO_ERROR;
|
||||
goto out;
|
||||
}
|
||||
- EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
|
||||
- *ecdsap = private;
|
||||
- private = NULL;
|
||||
- ret = 0;
|
||||
out:
|
||||
- EC_KEY_free(private);
|
||||
+ EVP_PKEY_CTX_free(ctx);
|
||||
+ EVP_PKEY_free(res);
|
||||
return ret;
|
||||
}
|
||||
# endif /* OPENSSL_HAS_ECC */
|
@ -0,0 +1,20 @@
|
||||
diff --color -rup a/monitor.c b/monitor.c
|
||||
--- a/monitor.c 2022-07-11 15:11:28.146863144 +0200
|
||||
+++ b/monitor.c 2022-07-11 15:15:35.726655877 +0200
|
||||
@@ -376,8 +376,15 @@ monitor_child_preauth(struct ssh *ssh, s
|
||||
if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
|
||||
auth_log(ssh, authenticated, partial,
|
||||
auth_method, auth_submethod);
|
||||
- if (!partial && !authenticated)
|
||||
+ if (!partial && !authenticated) {
|
||||
+#ifdef GSSAPI
|
||||
+ /* If gssapi-with-mic failed, MONITOR_REQ_GSSCHECKMIC is disabled.
|
||||
+ * We have to reenable it to try again for gssapi-keyex */
|
||||
+ if (strcmp(auth_method, "gssapi-with-mic") == 0 && options.gss_keyex)
|
||||
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
|
||||
+#endif
|
||||
authctxt->failures++;
|
||||
+ }
|
||||
if (authenticated || partial) {
|
||||
auth2_update_session_info(authctxt,
|
||||
auth_method, auth_submethod);
|
@ -0,0 +1,151 @@
|
||||
diff --color -rup a/sshconnect2.c b/sshconnect2.c
|
||||
--- a/sshconnect2.c 2022-07-11 17:00:02.618575727 +0200
|
||||
+++ b/sshconnect2.c 2022-07-11 17:03:05.096085690 +0200
|
||||
@@ -2288,9 +2288,9 @@ userauth_hostbased(struct ssh *ssh)
|
||||
if (authctxt->sensitive->keys[i] == NULL ||
|
||||
authctxt->sensitive->keys[i]->type == KEY_UNSPEC)
|
||||
continue;
|
||||
- if (match_pattern_list(
|
||||
+ if (!sshkey_match_keyname_to_sigalgs(
|
||||
sshkey_ssh_name(authctxt->sensitive->keys[i]),
|
||||
- authctxt->active_ktype, 0) != 1)
|
||||
+ authctxt->active_ktype))
|
||||
continue;
|
||||
/* we take and free the key */
|
||||
private = authctxt->sensitive->keys[i];
|
||||
@@ -2316,7 +2316,8 @@ userauth_hostbased(struct ssh *ssh)
|
||||
error_f("sshkey_fingerprint failed");
|
||||
goto out;
|
||||
}
|
||||
- debug_f("trying hostkey %s %s", sshkey_ssh_name(private), fp);
|
||||
+ debug_f("trying hostkey %s %s using sigalg %s",
|
||||
+ sshkey_ssh_name(private), fp, authctxt->active_ktype);
|
||||
|
||||
/* figure out a name for the client host */
|
||||
lname = get_local_name(ssh_packet_get_connection_in(ssh));
|
||||
diff --color -rup a/sshkey.c b/sshkey.c
|
||||
--- a/sshkey.c 2022-07-11 17:00:02.609575554 +0200
|
||||
+++ b/sshkey.c 2022-07-11 17:12:30.905976443 +0200
|
||||
@@ -252,6 +252,29 @@ sshkey_ecdsa_nid_from_name(const char *n
|
||||
return -1;
|
||||
}
|
||||
|
||||
+int
|
||||
+sshkey_match_keyname_to_sigalgs(const char *keyname, const char *sigalgs)
|
||||
+{
|
||||
+ int ktype;
|
||||
+
|
||||
+ if (sigalgs == NULL || *sigalgs == '\0' ||
|
||||
+ (ktype = sshkey_type_from_name(keyname)) == KEY_UNSPEC)
|
||||
+ return 0;
|
||||
+ else if (ktype == KEY_RSA) {
|
||||
+ return match_pattern_list("ssh-rsa", sigalgs, 0) == 1 ||
|
||||
+ match_pattern_list("rsa-sha2-256", sigalgs, 0) == 1 ||
|
||||
+ match_pattern_list("rsa-sha2-512", sigalgs, 0) == 1;
|
||||
+ } else if (ktype == KEY_RSA_CERT) {
|
||||
+ return match_pattern_list("ssh-rsa-cert-v01@openssh.com",
|
||||
+ sigalgs, 0) == 1 ||
|
||||
+ match_pattern_list("rsa-sha2-256-cert-v01@openssh.com",
|
||||
+ sigalgs, 0) == 1 ||
|
||||
+ match_pattern_list("rsa-sha2-512-cert-v01@openssh.com",
|
||||
+ sigalgs, 0) == 1;
|
||||
+ } else
|
||||
+ return match_pattern_list(keyname, sigalgs, 0) == 1;
|
||||
+}
|
||||
+
|
||||
char *
|
||||
sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
|
||||
{
|
||||
diff --color -rup a/sshkey.h b/sshkey.h
|
||||
--- a/sshkey.h 2022-07-11 17:00:02.603575438 +0200
|
||||
+++ b/sshkey.h 2022-07-11 17:13:01.052556879 +0200
|
||||
@@ -194,6 +194,10 @@ int sshkey_is_cert(const struct sshkey
|
||||
int sshkey_is_sk(const struct sshkey *);
|
||||
int sshkey_type_is_cert(int);
|
||||
int sshkey_type_plain(int);
|
||||
+
|
||||
+/* Returns non-zero if key name match sigalgs pattern list. (handles RSA) */
|
||||
+int sshkey_match_keyname_to_sigalgs(const char *, const char *);
|
||||
+
|
||||
int sshkey_to_certified(struct sshkey *);
|
||||
int sshkey_drop_cert(struct sshkey *);
|
||||
int sshkey_cert_copy(const struct sshkey *, struct sshkey *);
|
||||
diff --color -rup a/ssh-keysign.c b/ssh-keysign.c
|
||||
--- a/ssh-keysign.c 2021-08-20 06:03:49.000000000 +0200
|
||||
+++ b/ssh-keysign.c 2022-07-11 17:00:23.306973667 +0200
|
||||
@@ -62,7 +62,7 @@
|
||||
extern char *__progname;
|
||||
|
||||
static int
|
||||
-valid_request(struct passwd *pw, char *host, struct sshkey **ret,
|
||||
+valid_request(struct passwd *pw, char *host, struct sshkey **ret, char **pkalgp,
|
||||
u_char *data, size_t datalen)
|
||||
{
|
||||
struct sshbuf *b;
|
||||
@@ -75,6 +75,8 @@ valid_request(struct passwd *pw, char *h
|
||||
|
||||
if (ret != NULL)
|
||||
*ret = NULL;
|
||||
+ if (pkalgp != NULL)
|
||||
+ *pkalgp = NULL;
|
||||
fail = 0;
|
||||
|
||||
if ((b = sshbuf_from(data, datalen)) == NULL)
|
||||
@@ -122,8 +124,6 @@ valid_request(struct passwd *pw, char *h
|
||||
fail++;
|
||||
} else if (key->type != pktype)
|
||||
fail++;
|
||||
- free(pkalg);
|
||||
- free(pkblob);
|
||||
|
||||
/* client host name, handle trailing dot */
|
||||
if ((r = sshbuf_get_cstring(b, &p, &len)) != 0)
|
||||
@@ -154,8 +154,19 @@ valid_request(struct passwd *pw, char *h
|
||||
|
||||
if (fail)
|
||||
sshkey_free(key);
|
||||
- else if (ret != NULL)
|
||||
- *ret = key;
|
||||
+ else {
|
||||
+ if (ret != NULL) {
|
||||
+ *ret = key;
|
||||
+ key = NULL;
|
||||
+ }
|
||||
+ if (pkalgp != NULL) {
|
||||
+ *pkalgp = pkalg;
|
||||
+ pkalg = NULL;
|
||||
+ }
|
||||
+ }
|
||||
+ sshkey_free(key);
|
||||
+ free(pkalg);
|
||||
+ free(pkblob);
|
||||
|
||||
return (fail ? -1 : 0);
|
||||
}
|
||||
@@ -170,7 +181,7 @@ main(int argc, char **argv)
|
||||
struct passwd *pw;
|
||||
int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd;
|
||||
u_char *signature, *data, rver;
|
||||
- char *host, *fp;
|
||||
+ char *host, *fp, *pkalg;
|
||||
size_t slen, dlen;
|
||||
|
||||
if (pledge("stdio rpath getpw dns id", NULL) != 0)
|
||||
@@ -258,7 +269,7 @@ main(int argc, char **argv)
|
||||
|
||||
if ((r = sshbuf_get_string(b, &data, &dlen)) != 0)
|
||||
fatal_r(r, "%s: buffer error", __progname);
|
||||
- if (valid_request(pw, host, &key, data, dlen) < 0)
|
||||
+ if (valid_request(pw, host, &key, &pkalg, data, dlen) < 0)
|
||||
fatal("%s: not a valid request", __progname);
|
||||
free(host);
|
||||
|
||||
@@ -279,7 +290,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen,
|
||||
- NULL, NULL, NULL, 0)) != 0)
|
||||
+ pkalg, NULL, NULL, 0)) != 0)
|
||||
fatal_r(r, "%s: sshkey_sign failed", __progname);
|
||||
free(data);
|
||||
|
@ -0,0 +1,12 @@
|
||||
--- openssh-8.7p1/openbsd-compat/bsd-closefrom.c.orig 2022-04-12 15:47:03.815044607 +0200
|
||||
+++ openssh-8.7p1/openbsd-compat/bsd-closefrom.c 2022-04-12 15:48:12.464963511 +0200
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
-#ifndef HAVE_CLOSEFROM
|
||||
+#if (!defined HAVE_CLOSEFROM) || (defined __s390__)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
@ -0,0 +1,156 @@
|
||||
diff --color -rup a/compat.c b/compat.c
|
||||
--- a/compat.c 2021-08-20 06:03:49.000000000 +0200
|
||||
+++ b/compat.c 2022-07-14 17:39:23.770268440 +0200
|
||||
@@ -157,11 +157,12 @@ compat_banner(struct ssh *ssh, const cha
|
||||
debug_f("no match: %s", version);
|
||||
}
|
||||
|
||||
+/* Always returns pointer to allocated memory, caller must free. */
|
||||
char *
|
||||
compat_cipher_proposal(struct ssh *ssh, char *cipher_prop)
|
||||
{
|
||||
if (!(ssh->compat & SSH_BUG_BIGENDIANAES))
|
||||
- return cipher_prop;
|
||||
+ return xstrdup(cipher_prop);
|
||||
debug2_f("original cipher proposal: %s", cipher_prop);
|
||||
if ((cipher_prop = match_filter_denylist(cipher_prop, "aes*")) == NULL)
|
||||
fatal("match_filter_denylist failed");
|
||||
@@ -171,11 +172,12 @@ compat_cipher_proposal(struct ssh *ssh,
|
||||
return cipher_prop;
|
||||
}
|
||||
|
||||
+/* Always returns pointer to allocated memory, caller must free. */
|
||||
char *
|
||||
compat_pkalg_proposal(struct ssh *ssh, char *pkalg_prop)
|
||||
{
|
||||
if (!(ssh->compat & SSH_BUG_RSASIGMD5))
|
||||
- return pkalg_prop;
|
||||
+ return xstrdup(pkalg_prop);
|
||||
debug2_f("original public key proposal: %s", pkalg_prop);
|
||||
if ((pkalg_prop = match_filter_denylist(pkalg_prop, "ssh-rsa")) == NULL)
|
||||
fatal("match_filter_denylist failed");
|
||||
@@ -185,21 +187,26 @@ compat_pkalg_proposal(struct ssh *ssh, c
|
||||
return pkalg_prop;
|
||||
}
|
||||
|
||||
+/* Always returns pointer to allocated memory, caller must free. */
|
||||
char *
|
||||
compat_kex_proposal(struct ssh *ssh, char *p)
|
||||
{
|
||||
+ char *cp = NULL;
|
||||
+
|
||||
if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0)
|
||||
- return p;
|
||||
+ return xstrdup(p);
|
||||
debug2_f("original KEX proposal: %s", p);
|
||||
if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0)
|
||||
if ((p = match_filter_denylist(p,
|
||||
"curve25519-sha256@libssh.org")) == NULL)
|
||||
fatal("match_filter_denylist failed");
|
||||
if ((ssh->compat & SSH_OLD_DHGEX) != 0) {
|
||||
+ cp = p;
|
||||
if ((p = match_filter_denylist(p,
|
||||
"diffie-hellman-group-exchange-sha256,"
|
||||
"diffie-hellman-group-exchange-sha1")) == NULL)
|
||||
fatal("match_filter_denylist failed");
|
||||
+ free(cp);
|
||||
}
|
||||
debug2_f("compat KEX proposal: %s", p);
|
||||
if (*p == '\0')
|
||||
diff --color -rup a/sshconnect2.c b/sshconnect2.c
|
||||
--- a/sshconnect2.c 2022-07-14 17:38:43.241496549 +0200
|
||||
+++ b/sshconnect2.c 2022-07-14 17:39:23.772268479 +0200
|
||||
@@ -222,6 +222,7 @@ ssh_kex2(struct ssh *ssh, char *host, st
|
||||
{
|
||||
char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
|
||||
char *s, *all_key;
|
||||
+ char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL;
|
||||
int r, use_known_hosts_order = 0;
|
||||
|
||||
#if defined(GSSAPI) && defined(WITH_OPENSSL)
|
||||
@@ -252,10 +253,9 @@ ssh_kex2(struct ssh *ssh, char *host, st
|
||||
|
||||
if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
|
||||
fatal_f("kex_names_cat");
|
||||
- myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(ssh, s);
|
||||
+ myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, s);
|
||||
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
||||
- compat_cipher_proposal(ssh, options.ciphers);
|
||||
- myproposal[PROPOSAL_ENC_ALGS_STOC] =
|
||||
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc =
|
||||
compat_cipher_proposal(ssh, options.ciphers);
|
||||
myproposal[PROPOSAL_COMP_ALGS_CTOS] =
|
||||
myproposal[PROPOSAL_COMP_ALGS_STOC] =
|
||||
@@ -264,12 +264,12 @@ ssh_kex2(struct ssh *ssh, char *host, st
|
||||
myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
|
||||
if (use_known_hosts_order) {
|
||||
/* Query known_hosts and prefer algorithms that appear there */
|
||||
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
|
||||
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
|
||||
compat_pkalg_proposal(ssh,
|
||||
order_hostkeyalgs(host, hostaddr, port, cinfo));
|
||||
} else {
|
||||
/* Use specified HostkeyAlgorithms exactly */
|
||||
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
|
||||
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
|
||||
compat_pkalg_proposal(ssh, options.hostkeyalgorithms);
|
||||
}
|
||||
|
||||
@@ -383,6 +383,10 @@ ssh_kex2(struct ssh *ssh, char *host, st
|
||||
(r = ssh_packet_write_wait(ssh)) != 0)
|
||||
fatal_fr(r, "send packet");
|
||||
#endif
|
||||
+ /* Free only parts of proposal that were dynamically allocated here. */
|
||||
+ free(prop_kex);
|
||||
+ free(prop_enc);
|
||||
+ free(prop_hostkey);
|
||||
}
|
||||
|
||||
/*
|
||||
diff --color -rup a/sshd.c b/sshd.c
|
||||
--- a/sshd.c 2022-07-14 17:38:43.242496568 +0200
|
||||
+++ b/sshd.c 2022-07-14 17:42:07.616388978 +0200
|
||||
@@ -2493,14 +2493,15 @@ do_ssh2_kex(struct ssh *ssh)
|
||||
{
|
||||
char *myproposal[PROPOSAL_MAX] = { KEX_SERVER };
|
||||
struct kex *kex;
|
||||
+ char *hostkey_types = NULL;
|
||||
+ char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL;
|
||||
int r;
|
||||
|
||||
- myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(ssh,
|
||||
+ myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh,
|
||||
options.kex_algorithms);
|
||||
- myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(ssh,
|
||||
- options.ciphers);
|
||||
- myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(ssh,
|
||||
- options.ciphers);
|
||||
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
||||
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc =
|
||||
+ compat_cipher_proposal(ssh, options.ciphers);
|
||||
myproposal[PROPOSAL_MAC_ALGS_CTOS] =
|
||||
myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
|
||||
|
||||
@@ -2513,8 +2514,10 @@ do_ssh2_kex(struct ssh *ssh)
|
||||
ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
|
||||
options.rekey_interval);
|
||||
|
||||
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
|
||||
- ssh, list_hostkey_types());
|
||||
+ hostkey_types = list_hostkey_types();
|
||||
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
|
||||
+ compat_pkalg_proposal(ssh, hostkey_types);
|
||||
+ free(hostkey_types);
|
||||
|
||||
#if defined(GSSAPI) && defined(WITH_OPENSSL)
|
||||
{
|
||||
@@ -2606,6 +2609,9 @@ do_ssh2_kex(struct ssh *ssh)
|
||||
(r = ssh_packet_write_wait(ssh)) != 0)
|
||||
fatal_fr(r, "send test");
|
||||
#endif
|
||||
+ free(prop_kex);
|
||||
+ free(prop_enc);
|
||||
+ free(prop_hostkey);
|
||||
debug("KEX done");
|
||||
}
|
||||
|
@ -1,102 +1,207 @@
|
||||
diff -up openssh-8.7p1/kex.c.minsha1 openssh-8.7p1/kex.c
|
||||
--- openssh-8.7p1/kex.c.minsha1 2021-12-20 17:38:51.438294309 +0100
|
||||
+++ openssh-8.7p1/kex.c 2021-12-21 11:02:48.379991319 +0100
|
||||
@@ -994,6 +994,35 @@ kex_choose_conf(struct ssh *ssh)
|
||||
free(ext);
|
||||
}
|
||||
diff --color -ru a/clientloop.c b/clientloop.c
|
||||
--- a/clientloop.c 2022-06-29 16:35:06.677597259 +0200
|
||||
+++ b/clientloop.c 2022-06-29 16:40:29.737926205 +0200
|
||||
@@ -116,6 +116,9 @@
|
||||
#include "ssh-gss.h"
|
||||
#endif
|
||||
|
||||
+ /* Check whether client supports rsa-sha2 algorithms */
|
||||
+ if (kex->server && (kex->flags & KEX_INITIAL)) {
|
||||
+ char *ext;
|
||||
+/* Permitted RSA signature algorithms for UpdateHostkeys proofs */
|
||||
+#define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256"
|
||||
+
|
||||
+ ext = match_list("rsa-sha2-256", peer[PROPOSAL_SERVER_HOST_KEY_ALGS], NULL);
|
||||
+ if (ext) {
|
||||
+ kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
|
||||
+ free(ext);
|
||||
/* import options */
|
||||
extern Options options;
|
||||
|
||||
@@ -2110,8 +2113,10 @@
|
||||
struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx;
|
||||
size_t i, ndone;
|
||||
struct sshbuf *signdata;
|
||||
- int r, kexsigtype, use_kexsigtype;
|
||||
+ int r, plaintype;
|
||||
const u_char *sig;
|
||||
+ const char *rsa_kexalg = NULL;
|
||||
+ char *alg = NULL;
|
||||
size_t siglen;
|
||||
|
||||
if (ctx->nnew == 0)
|
||||
@@ -2122,9 +2127,9 @@
|
||||
hostkeys_update_ctx_free(ctx);
|
||||
return;
|
||||
}
|
||||
- kexsigtype = sshkey_type_plain(
|
||||
- sshkey_type_from_name(ssh->kex->hostkey_alg));
|
||||
-
|
||||
+ if (sshkey_type_plain(sshkey_type_from_name(
|
||||
+ ssh->kex->hostkey_alg)) == KEY_RSA)
|
||||
+ rsa_kexalg = ssh->kex->hostkey_alg;
|
||||
if ((signdata = sshbuf_new()) == NULL)
|
||||
fatal_f("sshbuf_new failed");
|
||||
/*
|
||||
@@ -2135,6 +2140,7 @@
|
||||
for (ndone = i = 0; i < ctx->nkeys; i++) {
|
||||
if (ctx->keys_match[i])
|
||||
continue;
|
||||
+ plaintype = sshkey_type_plain(ctx->keys[i]->type);
|
||||
/* Prepare data to be signed: session ID, unique string, key */
|
||||
sshbuf_reset(signdata);
|
||||
if ( (r = sshbuf_put_cstring(signdata,
|
||||
@@ -2148,19 +2154,33 @@
|
||||
error_fr(r, "parse sig");
|
||||
goto out;
|
||||
}
|
||||
+ if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) {
|
||||
+ error_fr(r, "server gave unintelligible signature "
|
||||
+ "for %s key %zu", sshkey_type(ctx->keys[i]), i);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ext = match_list("rsa-sha2-512", peer[PROPOSAL_SERVER_HOST_KEY_ALGS], NULL);
|
||||
+ if (ext) {
|
||||
+ kex->flags |= KEX_RSA_SHA2_512_SUPPORTED;
|
||||
+ free(ext);
|
||||
/*
|
||||
- * For RSA keys, prefer to use the signature type negotiated
|
||||
- * during KEX to the default (SHA1).
|
||||
+ * Special case for RSA keys: if a RSA hostkey was negotiated,
|
||||
+ * then use its signature type for verification of RSA hostkey
|
||||
+ * proofs. Otherwise, accept only RSA-SHA256/512 signatures.
|
||||
*/
|
||||
- use_kexsigtype = kexsigtype == KEY_RSA &&
|
||||
- sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA;
|
||||
- debug3_f("verify %s key %zu using %s sigalg",
|
||||
- sshkey_type(ctx->keys[i]), i,
|
||||
- use_kexsigtype ? ssh->kex->hostkey_alg : "default");
|
||||
+ if (plaintype == KEY_RSA && rsa_kexalg == NULL &&
|
||||
+ match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) {
|
||||
+ debug_f("server used untrusted RSA signature algorithm "
|
||||
+ "%s for key %zu, disregarding", alg, i);
|
||||
+ free(alg);
|
||||
+ /* zap the key from the list */
|
||||
+ sshkey_free(ctx->keys[i]);
|
||||
+ ctx->keys[i] = NULL;
|
||||
+ ndone++;
|
||||
+ continue;
|
||||
+ }
|
||||
+ debug3_f("verify %s key %zu using sigalg %s",
|
||||
+ sshkey_type(ctx->keys[i]), i, alg);
|
||||
+ free(alg);
|
||||
if ((r = sshkey_verify(ctx->keys[i], sig, siglen,
|
||||
sshbuf_ptr(signdata), sshbuf_len(signdata),
|
||||
- use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0,
|
||||
- NULL)) != 0) {
|
||||
+ plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) {
|
||||
error_fr(r, "server gave bad signature for %s key %zu",
|
||||
sshkey_type(ctx->keys[i]), i);
|
||||
goto out;
|
||||
diff --git a/hostfile.c b/hostfile.c
|
||||
index a035b381..bd49e3ac 100644
|
||||
--- a/hostfile.c
|
||||
+++ b/hostfile.c
|
||||
@@ -642,7 +642,7 @@ hostfile_replace_entries(const char *filename, const char *host, const char *ip,
|
||||
/* Re-add the requested keys */
|
||||
want = HKF_MATCH_HOST | (ip == NULL ? 0 : HKF_MATCH_IP);
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
- if ((want & ctx.match_keys[i]) == want)
|
||||
+ if (keys[i] == NULL || (want & ctx.match_keys[i]) == want)
|
||||
continue;
|
||||
if ((fp = sshkey_fingerprint(keys[i], hash_alg,
|
||||
SSH_FP_DEFAULT)) == NULL) {
|
||||
diff --color -ru a/kex.c b/kex.c
|
||||
--- a/kex.c 2022-06-29 16:35:06.775599179 +0200
|
||||
+++ b/kex.c 2022-06-29 16:42:00.839710940 +0200
|
||||
@@ -959,6 +959,18 @@
|
||||
return (1);
|
||||
}
|
||||
|
||||
+/* returns non-zero if proposal contains any algorithm from algs */
|
||||
+static int
|
||||
+has_any_alg(const char *proposal, const char *algs)
|
||||
+{
|
||||
+ char *cp;
|
||||
+
|
||||
+ ext = match_list("rsa-sha2-256-cert-v01@openssh.com", peer[PROPOSAL_SERVER_HOST_KEY_ALGS], NULL);
|
||||
+ if (ext) {
|
||||
+ kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
|
||||
+ free(ext);
|
||||
+ }
|
||||
+ if ((cp = match_list(proposal, algs, NULL)) == NULL)
|
||||
+ return 0;
|
||||
+ free(cp);
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+ ext = match_list("rsa-sha2-512-cert-v01@openssh.com", peer[PROPOSAL_SERVER_HOST_KEY_ALGS], NULL);
|
||||
+ if (ext) {
|
||||
static int
|
||||
kex_choose_conf(struct ssh *ssh)
|
||||
{
|
||||
@@ -994,6 +1006,16 @@
|
||||
free(ext);
|
||||
}
|
||||
|
||||
+ /* Check whether client supports rsa-sha2 algorithms */
|
||||
+ if (kex->server && (kex->flags & KEX_INITIAL)) {
|
||||
+ if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
|
||||
+ "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com"))
|
||||
+ kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
|
||||
+ if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
|
||||
+ "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com"))
|
||||
+ kex->flags |= KEX_RSA_SHA2_512_SUPPORTED;
|
||||
+ free(ext);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* Algorithm Negotiation */
|
||||
if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
|
||||
sprop[PROPOSAL_KEX_ALGS])) != 0) {
|
||||
diff -up openssh-8.7p1/kex.h.minsha1 openssh-8.7p1/kex.h
|
||||
--- openssh-8.7p1/kex.h.minsha1 2021-12-20 17:38:51.430294240 +0100
|
||||
+++ openssh-8.7p1/kex.h 2021-12-21 10:56:29.066735608 +0100
|
||||
@@ -116,6 +116,8 @@ enum kex_exchange {
|
||||
diff --color -ru a/kex.h b/kex.h
|
||||
--- a/kex.h 2022-06-29 16:35:06.766599003 +0200
|
||||
+++ b/kex.h 2022-06-29 16:42:24.199168567 +0200
|
||||
@@ -116,6 +116,8 @@
|
||||
|
||||
#define KEX_INIT_SENT 0x0001
|
||||
#define KEX_INITIAL 0x0002
|
||||
+#define KEX_RSA_SHA2_256_SUPPORTED 0x0004
|
||||
+#define KEX_RSA_SHA2_512_SUPPORTED 0x0008
|
||||
+#define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */
|
||||
+#define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */
|
||||
|
||||
struct sshenc {
|
||||
char *name;
|
||||
diff -up openssh-8.7p1/serverloop.c.minsha1 openssh-8.7p1/serverloop.c
|
||||
--- openssh-8.7p1/serverloop.c.minsha1 2021-08-20 06:03:49.000000000 +0200
|
||||
+++ openssh-8.7p1/serverloop.c 2021-12-21 11:01:00.594047538 +0100
|
||||
@@ -684,7 +685,7 @@ server_input_hostkeys_prove(struct ssh *
|
||||
diff --color -ru a/serverloop.c b/serverloop.c
|
||||
--- a/serverloop.c 2021-08-20 06:03:49.000000000 +0200
|
||||
+++ b/serverloop.c 2022-06-29 16:45:05.902336428 +0200
|
||||
@@ -684,16 +684,18 @@
|
||||
struct sshbuf *resp = NULL;
|
||||
struct sshbuf *sigbuf = NULL;
|
||||
struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL;
|
||||
- int r, ndx, kexsigtype, use_kexsigtype, success = 0;
|
||||
+ int r, ndx, success = 0;
|
||||
const u_char *blob;
|
||||
+ const char *sigalg, *kex_rsa_sigalg = NULL;
|
||||
u_char *sig = 0;
|
||||
size_t blen, slen;
|
||||
@@ -692,9 +693,11 @@ server_input_hostkeys_prove(struct ssh *
|
||||
|
||||
if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL)
|
||||
fatal_f("sshbuf_new");
|
||||
|
||||
- kexsigtype = sshkey_type_plain(
|
||||
- sshkey_type_from_name(ssh->kex->hostkey_alg));
|
||||
+ if (sshkey_type_plain(sshkey_type_from_name(
|
||||
+ ssh->kex->hostkey_alg)) == KEY_RSA)
|
||||
+ kex_rsa_sigalg = ssh->kex->hostkey_alg;
|
||||
while (ssh_packet_remaining(ssh) > 0) {
|
||||
+ const char *pkexstr = NULL;
|
||||
+ const char *rsa_sha2_256 = "rsa-sha2-256";
|
||||
+ const char *rsa_sha2_512 = "rsa-sha2-512";
|
||||
+
|
||||
sshkey_free(key);
|
||||
key = NULL;
|
||||
if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 ||
|
||||
@@ -726,8 +729,13 @@ server_input_hostkeys_prove(struct ssh *
|
||||
@@ -726,16 +728,24 @@
|
||||
* For RSA keys, prefer to use the signature type negotiated
|
||||
* during KEX to the default (SHA1).
|
||||
*/
|
||||
- use_kexsigtype = kexsigtype == KEY_RSA &&
|
||||
- sshkey_type_plain(key->type) == KEY_RSA;
|
||||
+ sigalg = NULL;
|
||||
+ if (sshkey_type_plain(key->type) == KEY_RSA) {
|
||||
+ if (ssh->kex->flags & KEX_RSA_SHA2_512_SUPPORTED)
|
||||
+ pkexstr = rsa_sha2_512;
|
||||
+ else if (ssh->kex->flags & KEX_RSA_SHA2_256_SUPPORTED)
|
||||
+ pkexstr = rsa_sha2_256;
|
||||
+ if (kex_rsa_sigalg != NULL)
|
||||
+ sigalg = kex_rsa_sigalg;
|
||||
+ else if (ssh->kex->flags & KEX_RSA_SHA2_512_SUPPORTED)
|
||||
+ sigalg = "rsa-sha2-512";
|
||||
+ else if (ssh->kex->flags & KEX_RSA_SHA2_256_SUPPORTED)
|
||||
+ sigalg = "rsa-sha2-256";
|
||||
+ }
|
||||
+
|
||||
+ debug3_f("sign %s key (index %d) using sigalg %s",
|
||||
+ sshkey_type(key), ndx, sigalg == NULL ? "default" : sigalg);
|
||||
if ((r = sshbuf_put_cstring(sigbuf,
|
||||
"hostkeys-prove-00@openssh.com")) != 0 ||
|
||||
(r = sshbuf_put_stringb(sigbuf,
|
||||
@@ -735,7 +743,7 @@ server_input_hostkeys_prove(struct ssh *
|
||||
ssh->kex->session_id)) != 0 ||
|
||||
(r = sshkey_puts(key, sigbuf)) != 0 ||
|
||||
(r = ssh->kex->sign(ssh, key_prv, key_pub, &sig, &slen,
|
||||
sshbuf_ptr(sigbuf), sshbuf_len(sigbuf),
|
||||
- sshbuf_ptr(sigbuf), sshbuf_len(sigbuf),
|
||||
- use_kexsigtype ? ssh->kex->hostkey_alg : NULL)) != 0 ||
|
||||
+ pkexstr)) != 0 ||
|
||||
+ sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), sigalg)) != 0 ||
|
||||
(r = sshbuf_put_string(resp, sig, slen)) != 0) {
|
||||
error_fr(r, "assemble signature");
|
||||
goto out;
|
||||
|
@ -0,0 +1,437 @@
|
||||
diff --git a/auth2-hostbased.c b/auth2-hostbased.c
|
||||
index 2ab222ed6..4e9437912 100644
|
||||
--- a/auth2-hostbased.c
|
||||
+++ b/auth2-hostbased.c
|
||||
@@ -118,6 +118,10 @@ userauth_hostbased(struct ssh *ssh, const char *method)
|
||||
"(null)" : key->cert->signature_type);
|
||||
goto done;
|
||||
}
|
||||
+ if ((r = sshkey_check_rsa_length(key, options.rsa_min_size)) != 0) {
|
||||
+ logit("refusing %s key", sshkey_type(key));
|
||||
+ goto done;
|
||||
+ }
|
||||
|
||||
if (!authctxt->valid || authctxt->user == NULL) {
|
||||
debug2_f("disabled because of invalid user");
|
||||
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
|
||||
index daa756a01..68e7dea1f 100644
|
||||
--- a/auth2-pubkey.c
|
||||
+++ b/auth2-pubkey.c
|
||||
@@ -172,6 +172,10 @@ userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
"(null)" : key->cert->signature_type);
|
||||
goto done;
|
||||
}
|
||||
+ if ((r = sshkey_check_rsa_length(key, options.rsa_min_size)) != 0) {
|
||||
+ logit("refusing %s key", sshkey_type(key));
|
||||
+ goto done;
|
||||
+ }
|
||||
key_s = format_key(key);
|
||||
if (sshkey_is_cert(key))
|
||||
ca_s = format_key(key->cert->signature_key);
|
||||
diff --git a/readconf.c b/readconf.c
|
||||
index 5b5afa8e3..5e17abd41 100644
|
||||
--- a/readconf.c
|
||||
+++ b/readconf.c
|
||||
@@ -160,7 +160,7 @@ typedef enum {
|
||||
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
|
||||
oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
|
||||
oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
|
||||
- oSecurityKeyProvider, oKnownHostsCommand,
|
||||
+ oSecurityKeyProvider, oKnownHostsCommand, oRSAMinSize,
|
||||
oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
|
||||
} OpCodes;
|
||||
|
||||
@@ -306,6 +306,7 @@ static struct {
|
||||
{ "proxyjump", oProxyJump },
|
||||
{ "securitykeyprovider", oSecurityKeyProvider },
|
||||
{ "knownhostscommand", oKnownHostsCommand },
|
||||
+ { "rsaminsize", oRSAMinSize },
|
||||
|
||||
{ NULL, oBadOption }
|
||||
};
|
||||
@@ -2162,6 +2163,10 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
|
||||
*charptr = xstrdup(arg);
|
||||
break;
|
||||
|
||||
+ case oRSAMinSize:
|
||||
+ intptr = &options->rsa_min_size;
|
||||
+ goto parse_int;
|
||||
+
|
||||
case oDeprecated:
|
||||
debug("%s line %d: Deprecated option \"%s\"",
|
||||
filename, linenum, keyword);
|
||||
@@ -2409,6 +2414,7 @@ initialize_options(Options * options)
|
||||
options->hostbased_accepted_algos = NULL;
|
||||
options->pubkey_accepted_algos = NULL;
|
||||
options->known_hosts_command = NULL;
|
||||
+ options->rsa_min_size = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2598,6 +2604,8 @@ fill_default_options(Options * options)
|
||||
if (options->sk_provider == NULL)
|
||||
options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
|
||||
#endif
|
||||
+ if (options->rsa_min_size == -1)
|
||||
+ options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
|
||||
|
||||
/* Expand KEX name lists */
|
||||
all_cipher = cipher_alg_list(',', 0);
|
||||
@@ -3287,6 +3295,7 @@ dump_client_config(Options *o, const char *host)
|
||||
dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
|
||||
dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
|
||||
dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
|
||||
+ dump_cfg_int(oRSAMinSize, o->rsa_min_size);
|
||||
|
||||
/* String options */
|
||||
dump_cfg_string(oBindAddress, o->bind_address);
|
||||
diff --git a/readconf.h b/readconf.h
|
||||
index f647bd42a..29db353ab 100644
|
||||
--- a/readconf.h
|
||||
+++ b/readconf.h
|
||||
@@ -176,6 +176,8 @@ typedef struct {
|
||||
|
||||
char *known_hosts_command;
|
||||
|
||||
+ int rsa_min_size; /* minimum size of RSA keys */
|
||||
+
|
||||
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
|
||||
} Options;
|
||||
|
||||
diff --git a/servconf.c b/servconf.c
|
||||
index f7317a5cb..362ff5b67 100644
|
||||
--- a/servconf.c
|
||||
+++ b/servconf.c
|
||||
@@ -177,6 +177,7 @@ initialize_server_options(ServerOptions *options)
|
||||
options->fingerprint_hash = -1;
|
||||
options->disable_forwarding = -1;
|
||||
options->expose_userauth_info = -1;
|
||||
+ options->rsa_min_size = -1;
|
||||
}
|
||||
|
||||
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
|
||||
@@ -416,6 +417,8 @@ fill_default_server_options(ServerOptions *options)
|
||||
options->expose_userauth_info = 0;
|
||||
if (options->sk_provider == NULL)
|
||||
options->sk_provider = xstrdup("internal");
|
||||
+ if (options->rsa_min_size == -1)
|
||||
+ options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
|
||||
|
||||
assemble_algorithms(options);
|
||||
|
||||
@@ -489,6 +492,7 @@ typedef enum {
|
||||
sStreamLocalBindMask, sStreamLocalBindUnlink,
|
||||
sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
|
||||
sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
|
||||
+ sRSAMinSize,
|
||||
sDeprecated, sIgnore, sUnsupported
|
||||
} ServerOpCodes;
|
||||
|
||||
@@ -632,6 +636,7 @@ static struct {
|
||||
{ "rdomain", sRDomain, SSHCFG_ALL },
|
||||
{ "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
|
||||
{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
|
||||
+ { "rsaminsize", sRSAMinSize, SSHCFG_ALL },
|
||||
{ NULL, sBadOption, 0 }
|
||||
};
|
||||
|
||||
@@ -2377,6 +2382,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
|
||||
*charptr = xstrdup(arg);
|
||||
break;
|
||||
|
||||
+ case sRSAMinSize:
|
||||
+ intptr = &options->rsa_min_size;
|
||||
+ goto parse_int;
|
||||
+
|
||||
case sDeprecated:
|
||||
case sIgnore:
|
||||
case sUnsupported:
|
||||
@@ -2549,6 +2558,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
|
||||
M_CP_INTOPT(rekey_limit);
|
||||
M_CP_INTOPT(rekey_interval);
|
||||
M_CP_INTOPT(log_level);
|
||||
+ M_CP_INTOPT(rsa_min_size);
|
||||
|
||||
/*
|
||||
* The bind_mask is a mode_t that may be unsigned, so we can't use
|
||||
@@ -2810,6 +2820,7 @@ dump_config(ServerOptions *o)
|
||||
dump_cfg_int(sMaxSessions, o->max_sessions);
|
||||
dump_cfg_int(sClientAliveInterval, o->client_alive_interval);
|
||||
dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max);
|
||||
+ dump_cfg_int(sRSAMinSize, o->rsa_min_size);
|
||||
dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask);
|
||||
|
||||
/* formatted integer arguments */
|
||||
diff --git a/servconf.h b/servconf.h
|
||||
index 115db1e79..2e3486906 100644
|
||||
--- a/servconf.h
|
||||
+++ b/servconf.h
|
||||
@@ -227,6 +227,7 @@ typedef struct {
|
||||
int expose_userauth_info;
|
||||
u_int64_t timing_secret;
|
||||
char *sk_provider;
|
||||
+ int rsa_min_size; /* minimum size of RSA keys */
|
||||
} ServerOptions;
|
||||
|
||||
/* Information about the incoming connection as used by Match */
|
||||
diff --git a/ssh.c b/ssh.c
|
||||
index a926cc007..cd13fb879 100644
|
||||
--- a/ssh.c
|
||||
+++ b/ssh.c
|
||||
@@ -500,14 +500,22 @@ resolve_canonicalize(char **hostp, int port)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Check the result of hostkey loading, ignoring some errors and
|
||||
- * fatal()ing for others.
|
||||
+ * Check the result of hostkey loading, ignoring some errors and either
|
||||
+ * discarding the key or fatal()ing for others.
|
||||
*/
|
||||
static void
|
||||
-check_load(int r, const char *path, const char *message)
|
||||
+check_load(int r, struct sshkey **k, const char *path, const char *message)
|
||||
{
|
||||
switch (r) {
|
||||
case 0:
|
||||
+ /* Check RSA keys size and discard if undersized */
|
||||
+ if (k != NULL && *k != NULL &&
|
||||
+ (r = sshkey_check_rsa_length(*k,
|
||||
+ options.rsa_min_size)) != 0) {
|
||||
+ error_r(r, "load %s \"%s\"", message, path);
|
||||
+ free(*k);
|
||||
+ *k = NULL;
|
||||
+ }
|
||||
break;
|
||||
case SSH_ERR_INTERNAL_ERROR:
|
||||
case SSH_ERR_ALLOC_FAIL:
|
||||
@@ -1557,12 +1565,13 @@ main(int ac, char **av)
|
||||
if ((o) >= sensitive_data.nkeys) \
|
||||
fatal_f("pubkey out of array bounds"); \
|
||||
check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \
|
||||
- p, "pubkey"); \
|
||||
+ &(sensitive_data.keys[o]), p, "pubkey"); \
|
||||
} while (0)
|
||||
#define L_CERT(p,o) do { \
|
||||
if ((o) >= sensitive_data.nkeys) \
|
||||
fatal_f("cert out of array bounds"); \
|
||||
- check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), p, "cert"); \
|
||||
+ check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), \
|
||||
+ &(sensitive_data.keys[o]), p, "cert"); \
|
||||
} while (0)
|
||||
|
||||
if (options.hostbased_authentication == 1) {
|
||||
@@ -2244,7 +2253,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
|
||||
filename = default_client_percent_dollar_expand(cp, cinfo);
|
||||
free(cp);
|
||||
check_load(sshkey_load_public(filename, &public, NULL),
|
||||
- filename, "pubkey");
|
||||
+ &public, filename, "pubkey");
|
||||
debug("identity file %s type %d", filename,
|
||||
public ? public->type : -1);
|
||||
free(options.identity_files[i]);
|
||||
@@ -2263,7 +2272,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
|
||||
continue;
|
||||
xasprintf(&cp, "%s-cert", filename);
|
||||
check_load(sshkey_load_public(cp, &public, NULL),
|
||||
- filename, "pubkey");
|
||||
+ &public, filename, "pubkey");
|
||||
debug("identity file %s type %d", cp,
|
||||
public ? public->type : -1);
|
||||
if (public == NULL) {
|
||||
@@ -2294,7 +2303,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
|
||||
free(cp);
|
||||
|
||||
check_load(sshkey_load_public(filename, &public, NULL),
|
||||
- filename, "certificate");
|
||||
+ &public, filename, "certificate");
|
||||
debug("certificate file %s type %d", filename,
|
||||
public ? public->type : -1);
|
||||
free(options.certificate_files[i]);
|
||||
diff --git a/sshconnect2.c b/sshconnect2.c
|
||||
index 67f8e0309..d050c1656 100644
|
||||
--- a/sshconnect2.c
|
||||
+++ b/sshconnect2.c
|
||||
@@ -91,6 +91,10 @@ static const struct ssh_conn_info *xxx_conn_info;
|
||||
static int
|
||||
verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
|
||||
{
|
||||
+ int r;
|
||||
+
|
||||
+ if ((r = sshkey_check_rsa_length(hostkey, options.rsa_min_size)) != 0)
|
||||
+ fatal_r(r, "Bad server host key");
|
||||
if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
|
||||
xxx_conn_info) == -1)
|
||||
fatal("Host key verification failed.");
|
||||
@@ -1762,6 +1762,12 @@ load_identity_file(Identity *id)
|
||||
private = NULL;
|
||||
quit = 1;
|
||||
}
|
||||
+ if (r = sshkey_check_rsa_length(private, options.rsa_min_size) != 0) {
|
||||
+ debug_fr(r, "Skipping key %s", id->filename);
|
||||
+ sshkey_free(private);
|
||||
+ private = NULL;
|
||||
+ quit = 1;
|
||||
+ }
|
||||
if (!quit && private != NULL && id->agent_fd == -1 &&
|
||||
!(id->key && id->isprivate))
|
||||
maybe_add_key_to_agent(id->filename, private, comment,
|
||||
@@ -1747,6 +1751,12 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
|
||||
close(agent_fd);
|
||||
} else {
|
||||
for (j = 0; j < idlist->nkeys; j++) {
|
||||
+ if ((r = sshkey_check_rsa_length(idlist->keys[j],
|
||||
+ options.rsa_min_size)) != 0) {
|
||||
+ debug_fr(r, "ignoring %s agent key",
|
||||
+ sshkey_ssh_name(idlist->keys[j]));
|
||||
+ continue;
|
||||
+ }
|
||||
found = 0;
|
||||
TAILQ_FOREACH(id, &files, next) {
|
||||
/*
|
||||
diff --git a/sshd.c b/sshd.c
|
||||
index d26eb86ae..5f36905a1 100644
|
||||
--- a/sshd.c
|
||||
+++ b/sshd.c
|
||||
@@ -1746,6 +1746,13 @@ main(int ac, char **av)
|
||||
fatal_r(r, "Could not demote key: \"%s\"",
|
||||
options.host_key_files[i]);
|
||||
}
|
||||
+ if (pubkey != NULL && (r = sshkey_check_rsa_length(pubkey,
|
||||
+ options.rsa_min_size)) != 0) {
|
||||
+ error_fr(r, "Host key %s", options.host_key_files[i]);
|
||||
+ sshkey_free(pubkey);
|
||||
+ sshkey_free(key);
|
||||
+ continue;
|
||||
+ }
|
||||
sensitive_data.host_keys[i] = key;
|
||||
sensitive_data.host_pubkeys[i] = pubkey;
|
||||
|
||||
diff --git a/sshkey.c b/sshkey.c
|
||||
index 47864e6d8..8bad6bd99 100644
|
||||
--- a/sshkey.c
|
||||
+++ b/sshkey.c
|
||||
@@ -2319,18 +2319,24 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
|
||||
return ret;
|
||||
}
|
||||
|
||||
-#ifdef WITH_OPENSSL
|
||||
-static int
|
||||
-check_rsa_length(const RSA *rsa)
|
||||
+int
|
||||
+sshkey_check_rsa_length(const struct sshkey *k, int min_size)
|
||||
{
|
||||
+#ifdef WITH_OPENSSL
|
||||
const BIGNUM *rsa_n;
|
||||
+ int nbits;
|
||||
|
||||
- RSA_get0_key(rsa, &rsa_n, NULL, NULL);
|
||||
- if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
|
||||
+ if (k == NULL || k->rsa == NULL ||
|
||||
+ (k->type != KEY_RSA && k->type != KEY_RSA_CERT))
|
||||
+ return 0;
|
||||
+ RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
|
||||
+ nbits = BN_num_bits(rsa_n);
|
||||
+ if (nbits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
|
||||
+ (min_size > 0 && nbits < min_size))
|
||||
return SSH_ERR_KEY_LENGTH;
|
||||
+#endif /* WITH_OPENS |