4b21ae5fcb
Related: rhbz#2066882
425 lines
14 KiB
Diff
425 lines
14 KiB
Diff
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.");
|
|
@@ -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_OPENSSL */
|
|
return 0;
|
|
}
|
|
-#endif
|
|
|
|
static int
|
|
sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
|
|
@@ -2391,7 +2397,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
|
|
goto out;
|
|
}
|
|
rsa_n = rsa_e = NULL; /* transferred */
|
|
- if ((ret = check_rsa_length(key->rsa)) != 0)
|
|
+ if ((ret = sshkey_check_rsa_length(key, 0)) != 0)
|
|
goto out;
|
|
#ifdef DEBUG_PK
|
|
RSA_print_fp(stderr, key->rsa, 8);
|
|
@@ -3580,7 +3586,7 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
|
|
goto out;
|
|
}
|
|
rsa_p = rsa_q = NULL; /* transferred */
|
|
- if ((r = check_rsa_length(k->rsa)) != 0)
|
|
+ if ((r = sshkey_check_rsa_length(k, 0)) != 0)
|
|
goto out;
|
|
if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
|
|
goto out;
|
|
@@ -4566,7 +4572,7 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
|
|
r = SSH_ERR_LIBCRYPTO_ERROR;
|
|
goto out;
|
|
}
|
|
- if ((r = check_rsa_length(prv->rsa)) != 0)
|
|
+ if ((r = sshkey_check_rsa_length(prv, 0)) != 0)
|
|
goto out;
|
|
} else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA &&
|
|
(type == KEY_UNSPEC || type == KEY_DSA)) {
|
|
diff --git a/sshkey.h b/sshkey.h
|
|
index 125cadb64..52e879456 100644
|
|
--- a/sshkey.h
|
|
+++ b/sshkey.h
|
|
@@ -267,6 +267,7 @@ int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
|
|
int sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob,
|
|
int type, struct sshkey **pubkeyp);
|
|
|
|
+int sshkey_check_rsa_length(const struct sshkey *, int);
|
|
/* XXX should be internal, but used by ssh-keygen */
|
|
int ssh_rsa_complete_crt_parameters(struct sshkey *, const BIGNUM *);
|
|
|
|
diff --git a/ssh.1 b/ssh.1
|
|
index b4956aec..b1a40ebd 100644
|
|
--- a/ssh.1
|
|
+++ b/ssh.1
|
|
@@ -554,6 +554,7 @@ For full details of the options listed below, and their possible values, see
|
|
.It LogLevel
|
|
.It MACs
|
|
.It Match
|
|
+.It RSAMinSize
|
|
.It NoHostAuthenticationForLocalhost
|
|
.It NumberOfPasswordPrompts
|
|
.It PasswordAuthentication
|
|
diff --git a/ssh_config.5 b/ssh_config.5
|
|
index 24a46460..68771e4b 100644
|
|
--- a/ssh_config.5
|
|
+++ b/ssh_config.5
|
|
@@ -1322,6 +1322,10 @@ The argument to this keyword must be
|
|
or
|
|
.Cm no
|
|
(the default).
|
|
+.It Cm RSAMinSize
|
|
+Provides a minimal bits requirement for RSA keys when used for signature and
|
|
+verification but not for the key generation. The default value is 1024 and
|
|
+can't be reduced.
|
|
.It Cm NumberOfPasswordPrompts
|
|
Specifies the number of password prompts before giving up.
|
|
The argument to this keyword must be an integer.
|
|
diff --git a/sshd_config.5 b/sshd_config.5
|
|
index 867a747d..e08811ca 100644
|
|
--- a/sshd_config.5
|
|
+++ b/sshd_config.5
|
|
@@ -1266,6 +1266,10 @@ will refuse connection attempts with a probability of rate/100 (30%)
|
|
if there are currently start (10) unauthenticated connections.
|
|
The probability increases linearly and all connection attempts
|
|
are refused if the number of unauthenticated connections reaches full (60).
|
|
+.It Cm RSAMinSize
|
|
+Provides a minimal bits requirement for RSA keys when used for signature and
|
|
+verification but not for the key generation. The default value is 1024 and
|
|
+can't be reduced.
|
|
.It Cm ModuliFile
|
|
Specifies the
|
|
.Xr moduli 5
|
|
diff --git a/sshkey.h b/sshkey.h
|
|
index 094815e0..2bb8cb90 100644
|
|
--- a/sshkey.h
|
|
+++ b/sshkey.h
|
|
@@ -286,6 +286,8 @@ int sshkey_private_serialize_maxsign(struct sshkey *key,
|
|
|
|
void sshkey_sig_details_free(struct sshkey_sig_details *);
|
|
|
|
+int ssh_set_rsa_min_bits(int minbits);
|
|
+
|
|
#ifdef SSHKEY_INTERNAL
|
|
int ssh_rsa_sign(const struct sshkey *key,
|
|
u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
|