Dmitry Belyavskiy 4b21ae
diff --git a/auth2-hostbased.c b/auth2-hostbased.c
Dmitry Belyavskiy d4ff0b
index 36b9d2f5..6b517db4 100644
Dmitry Belyavskiy 4b21ae
--- a/auth2-hostbased.c
Dmitry Belyavskiy 4b21ae
+++ b/auth2-hostbased.c
Dmitry Belyavskiy d4ff0b
@@ -119,6 +119,11 @@ userauth_hostbased(struct ssh *ssh, const char *method)
Dmitry Belyavskiy 4b21ae
 		    "(null)" : key->cert->signature_type);
Dmitry Belyavskiy 4b21ae
 		goto done;
Dmitry Belyavskiy 4b21ae
 	}
Dmitry Belyavskiy d4ff0b
+	if ((r = sshkey_check_rsa_length(key,
Dmitry Belyavskiy d4ff0b
+	    options.required_rsa_size)) != 0) {
Dmitry Belyavskiy d4ff0b
+		logit_r(r, "refusing %s key", sshkey_type(key));
Dmitry Belyavskiy 4b21ae
+		goto done;
Dmitry Belyavskiy 4b21ae
+	}
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	if (!authctxt->valid || authctxt->user == NULL) {
Dmitry Belyavskiy 4b21ae
 		debug2_f("disabled because of invalid user");
Dmitry Belyavskiy 4b21ae
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
Dmitry Belyavskiy d4ff0b
index 962fd342..5d59febc 100644
Dmitry Belyavskiy 4b21ae
--- a/auth2-pubkey.c
Dmitry Belyavskiy 4b21ae
+++ b/auth2-pubkey.c
Dmitry Belyavskiy d4ff0b
@@ -175,6 +175,11 @@ userauth_pubkey(struct ssh *ssh, const char *method)
Dmitry Belyavskiy 4b21ae
 		    "(null)" : key->cert->signature_type);
Dmitry Belyavskiy 4b21ae
 		goto done;
Dmitry Belyavskiy 4b21ae
 	}
Dmitry Belyavskiy d4ff0b
+	if ((r = sshkey_check_rsa_length(key,
Dmitry Belyavskiy d4ff0b
+	    options.required_rsa_size)) != 0) {
Dmitry Belyavskiy d4ff0b
+		logit_r(r, "refusing %s key", sshkey_type(key));
Dmitry Belyavskiy 4b21ae
+		goto done;
Dmitry Belyavskiy 4b21ae
+	}
Dmitry Belyavskiy 4b21ae
 	key_s = format_key(key);
Dmitry Belyavskiy 4b21ae
 	if (sshkey_is_cert(key))
Dmitry Belyavskiy 4b21ae
 		ca_s = format_key(key->cert->signature_key);
Dmitry Belyavskiy 4b21ae
diff --git a/readconf.c b/readconf.c
Dmitry Belyavskiy d4ff0b
index 7f26c680..42be690b 100644
Dmitry Belyavskiy 4b21ae
--- a/readconf.c
Dmitry Belyavskiy 4b21ae
+++ b/readconf.c
Dmitry Belyavskiy d4ff0b
@@ -174,7 +174,7 @@ typedef enum {
Dmitry Belyavskiy 4b21ae
 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
Dmitry Belyavskiy 4b21ae
 	oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
Dmitry Belyavskiy 4b21ae
 	oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
Dmitry Belyavskiy 4b21ae
-	oSecurityKeyProvider, oKnownHostsCommand,
Dmitry Belyavskiy d4ff0b
+	oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
Dmitry Belyavskiy 4b21ae
 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
Dmitry Belyavskiy 4b21ae
 } OpCodes;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy ad9644
@@ -320,6 +320,8 @@ static struct {
Dmitry Belyavskiy 4b21ae
 	{ "proxyjump", oProxyJump },
Dmitry Belyavskiy 4b21ae
 	{ "securitykeyprovider", oSecurityKeyProvider },
Dmitry Belyavskiy 4b21ae
 	{ "knownhostscommand", oKnownHostsCommand },
Dmitry Belyavskiy d4ff0b
+	{ "requiredrsasize", oRequiredRSASize },
Dmitry Belyavskiy ad9644
+	{ "rsaminsize", oRequiredRSASize }, /* alias */
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	{ NULL, oBadOption }
Dmitry Belyavskiy 4b21ae
 };
Dmitry Belyavskiy d4ff0b
@@ -2176,6 +2177,10 @@ parse_pubkey_algos:
Dmitry Belyavskiy 4b21ae
 			*charptr = xstrdup(arg);
Dmitry Belyavskiy 4b21ae
 		break;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy d4ff0b
+	case oRequiredRSASize:
Dmitry Belyavskiy d4ff0b
+		intptr = &options->required_rsa_size;
Dmitry Belyavskiy 4b21ae
+		goto parse_int;
Dmitry Belyavskiy 4b21ae
+
Dmitry Belyavskiy 4b21ae
 	case oDeprecated:
Dmitry Belyavskiy 4b21ae
 		debug("%s line %d: Deprecated option \"%s\"",
Dmitry Belyavskiy 4b21ae
 		    filename, linenum, keyword);
Dmitry Belyavskiy d4ff0b
@@ -2423,6 +2428,7 @@ initialize_options(Options * options)
Dmitry Belyavskiy 4b21ae
 	options->hostbased_accepted_algos = NULL;
Dmitry Belyavskiy 4b21ae
 	options->pubkey_accepted_algos = NULL;
Dmitry Belyavskiy 4b21ae
 	options->known_hosts_command = NULL;
Dmitry Belyavskiy d4ff0b
+	options->required_rsa_size = -1;
Dmitry Belyavskiy 4b21ae
 }
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 /*
Dmitry Belyavskiy d4ff0b
@@ -2619,6 +2625,8 @@ fill_default_options(Options * options)
Dmitry Belyavskiy 4b21ae
 	if (options->sk_provider == NULL)
Dmitry Belyavskiy 4b21ae
 		options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
Dmitry Belyavskiy 4b21ae
 #endif
Dmitry Belyavskiy d4ff0b
+	if (options->required_rsa_size == -1)
Dmitry Belyavskiy d4ff0b
+		options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	/* Expand KEX name lists */
Dmitry Belyavskiy 4b21ae
 	all_cipher = cipher_alg_list(',', 0);
Dmitry Belyavskiy d4ff0b
@@ -3308,6 +3316,7 @@ dump_client_config(Options *o, const char *host)
Dmitry Belyavskiy 4b21ae
 	dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
Dmitry Belyavskiy 4b21ae
 	dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
Dmitry Belyavskiy 4b21ae
 	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
Dmitry Belyavskiy d4ff0b
+	dump_cfg_int(oRequiredRSASize, o->required_rsa_size);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	/* String options */
Dmitry Belyavskiy 4b21ae
 	dump_cfg_string(oBindAddress, o->bind_address);
Dmitry Belyavskiy 4b21ae
diff --git a/readconf.h b/readconf.h
Dmitry Belyavskiy d4ff0b
index f647bd42..ffb5ec4f 100644
Dmitry Belyavskiy 4b21ae
--- a/readconf.h
Dmitry Belyavskiy 4b21ae
+++ b/readconf.h
Dmitry Belyavskiy 4b21ae
@@ -176,6 +176,8 @@ typedef struct {
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	char   *known_hosts_command;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy d4ff0b
+	int	required_rsa_size;	/* minimum size of RSA keys */
Dmitry Belyavskiy 4b21ae
+
Dmitry Belyavskiy 4b21ae
 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
Dmitry Belyavskiy 4b21ae
 }       Options;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
diff --git a/servconf.c b/servconf.c
Dmitry Belyavskiy d4ff0b
index 29df0463..423772b1 100644
Dmitry Belyavskiy 4b21ae
--- a/servconf.c
Dmitry Belyavskiy 4b21ae
+++ b/servconf.c
Dmitry Belyavskiy d4ff0b
@@ -195,6 +195,7 @@ initialize_server_options(ServerOptions *options)
Dmitry Belyavskiy 4b21ae
 	options->fingerprint_hash = -1;
Dmitry Belyavskiy 4b21ae
 	options->disable_forwarding = -1;
Dmitry Belyavskiy 4b21ae
 	options->expose_userauth_info = -1;
Dmitry Belyavskiy d4ff0b
+	options->required_rsa_size = -1;
Dmitry Belyavskiy 4b21ae
 }
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
Dmitry Belyavskiy d4ff0b
@@ -441,6 +442,8 @@ fill_default_server_options(ServerOptions *options)
Dmitry Belyavskiy 4b21ae
 		options->expose_userauth_info = 0;
Dmitry Belyavskiy 4b21ae
 	if (options->sk_provider == NULL)
Dmitry Belyavskiy 4b21ae
 		options->sk_provider = xstrdup("internal");
Dmitry Belyavskiy d4ff0b
+	if (options->required_rsa_size == -1)
Dmitry Belyavskiy d4ff0b
+		options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	assemble_algorithms(options);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy d4ff0b
@@ -517,6 +520,7 @@ typedef enum {
Dmitry Belyavskiy 4b21ae
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
Dmitry Belyavskiy 4b21ae
 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
Dmitry Belyavskiy 4b21ae
 	sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
Dmitry Belyavskiy d4ff0b
+	sRequiredRSASize,
Dmitry Belyavskiy 4b21ae
 	sDeprecated, sIgnore, sUnsupported
Dmitry Belyavskiy 4b21ae
 } ServerOpCodes;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy ad9644
@@ -676,6 +680,8 @@ static struct {
Dmitry Belyavskiy 4b21ae
 	{ "rdomain", sRDomain, SSHCFG_ALL },
Dmitry Belyavskiy 4b21ae
 	{ "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
Dmitry Belyavskiy 4b21ae
 	{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
Dmitry Belyavskiy d4ff0b
+	{ "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
Dmitry Belyavskiy ad9644
+	{ "rsaminsize", sRequiredRSASize, SSHCFG_ALL }, /* alias */
Dmitry Belyavskiy 4b21ae
 	{ NULL, sBadOption, 0 }
Dmitry Belyavskiy 4b21ae
 };
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy d4ff0b
@@ -2438,6 +2443,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
Dmitry Belyavskiy 4b21ae
 			*charptr = xstrdup(arg);
Dmitry Belyavskiy 4b21ae
 		break;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy d4ff0b
+	case sRequiredRSASize:
Dmitry Belyavskiy d4ff0b
+		intptr = &options->required_rsa_size;
Dmitry Belyavskiy 4b21ae
+		goto parse_int;
Dmitry Belyavskiy 4b21ae
+
Dmitry Belyavskiy 4b21ae
 	case sDeprecated:
Dmitry Belyavskiy 4b21ae
 	case sIgnore:
Dmitry Belyavskiy 4b21ae
 	case sUnsupported:
Dmitry Belyavskiy d4ff0b
@@ -2610,6 +2619,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
Dmitry Belyavskiy 4b21ae
 	M_CP_INTOPT(rekey_limit);
Dmitry Belyavskiy 4b21ae
 	M_CP_INTOPT(rekey_interval);
Dmitry Belyavskiy 4b21ae
 	M_CP_INTOPT(log_level);
Dmitry Belyavskiy d4ff0b
+	M_CP_INTOPT(required_rsa_size);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	/*
Dmitry Belyavskiy 4b21ae
 	 * The bind_mask is a mode_t that may be unsigned, so we can't use
Dmitry Belyavskiy d4ff0b
@@ -2874,6 +2884,7 @@ dump_config(ServerOptions *o)
Dmitry Belyavskiy 4b21ae
 	dump_cfg_int(sMaxSessions, o->max_sessions);
Dmitry Belyavskiy 4b21ae
 	dump_cfg_int(sClientAliveInterval, o->client_alive_interval);
Dmitry Belyavskiy 4b21ae
 	dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max);
Dmitry Belyavskiy d4ff0b
+	dump_cfg_int(sRequiredRSASize, o->required_rsa_size);
Dmitry Belyavskiy 4b21ae
 	dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	/* formatted integer arguments */
Dmitry Belyavskiy 4b21ae
diff --git a/servconf.h b/servconf.h
Dmitry Belyavskiy d4ff0b
index 8a04463e..9346155c 100644
Dmitry Belyavskiy 4b21ae
--- a/servconf.h
Dmitry Belyavskiy 4b21ae
+++ b/servconf.h
Dmitry Belyavskiy d4ff0b
@@ -229,6 +229,7 @@ typedef struct {
Dmitry Belyavskiy 4b21ae
 	int	expose_userauth_info;
Dmitry Belyavskiy 4b21ae
 	u_int64_t timing_secret;
Dmitry Belyavskiy 4b21ae
 	char   *sk_provider;
Dmitry Belyavskiy d4ff0b
+	int	required_rsa_size;	/* minimum size of RSA keys */
Dmitry Belyavskiy 4b21ae
 }       ServerOptions;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 /* Information about the incoming connection as used by Match */
Dmitry Belyavskiy 4b21ae
diff --git a/ssh.c b/ssh.c
Dmitry Belyavskiy d4ff0b
index 559bf2af..25be53d5 100644
Dmitry Belyavskiy 4b21ae
--- a/ssh.c
Dmitry Belyavskiy 4b21ae
+++ b/ssh.c
Dmitry Belyavskiy d4ff0b
@@ -516,14 +516,22 @@ resolve_canonicalize(char **hostp, int port)
Dmitry Belyavskiy 4b21ae
 }
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 /*
Dmitry Belyavskiy 4b21ae
- * Check the result of hostkey loading, ignoring some errors and
Dmitry Belyavskiy 4b21ae
- * fatal()ing for others.
Dmitry Belyavskiy 4b21ae
+ * Check the result of hostkey loading, ignoring some errors and either
Dmitry Belyavskiy 4b21ae
+ * discarding the key or fatal()ing for others.
Dmitry Belyavskiy 4b21ae
  */
Dmitry Belyavskiy 4b21ae
 static void
Dmitry Belyavskiy 4b21ae
-check_load(int r, const char *path, const char *message)
Dmitry Belyavskiy 4b21ae
+check_load(int r, struct sshkey **k, const char *path, const char *message)
Dmitry Belyavskiy 4b21ae
 {
Dmitry Belyavskiy 4b21ae
 	switch (r) {
Dmitry Belyavskiy 4b21ae
 	case 0:
Dmitry Belyavskiy 4b21ae
+		/* Check RSA keys size and discard if undersized */
Dmitry Belyavskiy 4b21ae
+		if (k != NULL && *k != NULL &&
Dmitry Belyavskiy 4b21ae
+		    (r = sshkey_check_rsa_length(*k,
Dmitry Belyavskiy d4ff0b
+		    options.required_rsa_size)) != 0) {
Dmitry Belyavskiy 4b21ae
+			error_r(r, "load %s \"%s\"", message, path);
Dmitry Belyavskiy 4b21ae
+			free(*k);
Dmitry Belyavskiy 4b21ae
+			*k = NULL;
Dmitry Belyavskiy 4b21ae
+		}
Dmitry Belyavskiy 4b21ae
 		break;
Dmitry Belyavskiy 4b21ae
 	case SSH_ERR_INTERNAL_ERROR:
Dmitry Belyavskiy 4b21ae
 	case SSH_ERR_ALLOC_FAIL:
Dmitry Belyavskiy d4ff0b
@@ -1578,7 +1586,7 @@ main(int ac, char **av)
Dmitry Belyavskiy 4b21ae
 	if ((o) >= sensitive_data.nkeys) \
Dmitry Belyavskiy 4b21ae
 		fatal_f("pubkey out of array bounds"); \
Dmitry Belyavskiy 4b21ae
 	check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \
Dmitry Belyavskiy 4b21ae
-	    p, "pubkey"); \
Dmitry Belyavskiy 4b21ae
+	    &(sensitive_data.keys[o]), p, "pubkey"); \
Dmitry Belyavskiy 4b21ae
 } while (0)
Dmitry Belyavskiy 4b21ae
 #define L_CERT(p,o) do { \
Dmitry Belyavskiy 4b21ae
 	if ((o) >= sensitive_data.nkeys) \
Dmitry Belyavskiy d4ff0b
@@ -1586,7 +1594,8 @@ main(int ac, char **av)
Dmitry Belyavskiy d4ff0b
 #define L_CERT(p,o) do { \
Dmitry Belyavskiy d4ff0b
 	if ((o) >= sensitive_data.nkeys) \
Dmitry Belyavskiy 4b21ae
 		fatal_f("cert out of array bounds"); \
Dmitry Belyavskiy 4b21ae
-	check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), p, "cert"); \
Dmitry Belyavskiy 4b21ae
+	check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), \
Dmitry Belyavskiy 4b21ae
+	    &(sensitive_data.keys[o]), p, "cert"); \
Dmitry Belyavskiy 4b21ae
 } while (0)
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 		if (options.hostbased_authentication == 1) {
Dmitry Belyavskiy 4b21ae
@@ -2244,7 +2253,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
Dmitry Belyavskiy 4b21ae
 		filename = default_client_percent_dollar_expand(cp, cinfo);
Dmitry Belyavskiy 4b21ae
 		free(cp);
Dmitry Belyavskiy 4b21ae
 		check_load(sshkey_load_public(filename, &public, NULL),
Dmitry Belyavskiy 4b21ae
-		    filename, "pubkey");
Dmitry Belyavskiy 4b21ae
+		    &public, filename, "pubkey");
Dmitry Belyavskiy 4b21ae
 		debug("identity file %s type %d", filename,
Dmitry Belyavskiy 4b21ae
 		    public ? public->type : -1);
Dmitry Belyavskiy 4b21ae
 		free(options.identity_files[i]);
Dmitry Belyavskiy d4ff0b
@@ -2284,7 +2293,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
Dmitry Belyavskiy 4b21ae
 			continue;
Dmitry Belyavskiy 4b21ae
 		xasprintf(&cp, "%s-cert", filename);
Dmitry Belyavskiy 4b21ae
 		check_load(sshkey_load_public(cp, &public, NULL),
Dmitry Belyavskiy 4b21ae
-		    filename, "pubkey");
Dmitry Belyavskiy 4b21ae
+		    &public, filename, "pubkey");
Dmitry Belyavskiy 4b21ae
 		debug("identity file %s type %d", cp,
Dmitry Belyavskiy 4b21ae
 		    public ? public->type : -1);
Dmitry Belyavskiy 4b21ae
 		if (public == NULL) {
Dmitry Belyavskiy d4ff0b
@@ -2315,7 +2324,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
Dmitry Belyavskiy 4b21ae
 		free(cp);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 		check_load(sshkey_load_public(filename, &public, NULL),
Dmitry Belyavskiy 4b21ae
-		    filename, "certificate");
Dmitry Belyavskiy 4b21ae
+		    &public, filename, "certificate");
Dmitry Belyavskiy 4b21ae
 		debug("certificate file %s type %d", filename,
Dmitry Belyavskiy 4b21ae
 		    public ? public->type : -1);
Dmitry Belyavskiy 4b21ae
 		free(options.certificate_files[i]);
Dmitry Belyavskiy 4b21ae
diff --git a/sshconnect2.c b/sshconnect2.c
Dmitry Belyavskiy d4ff0b
index f9bd19ea..58fe98db 100644
Dmitry Belyavskiy 4b21ae
--- a/sshconnect2.c
Dmitry Belyavskiy 4b21ae
+++ b/sshconnect2.c
Dmitry Belyavskiy d4ff0b
@@ -96,6 +96,11 @@ static const struct ssh_conn_info *xxx_conn_info;
Dmitry Belyavskiy 4b21ae
 static int
Dmitry Belyavskiy 4b21ae
 verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
Dmitry Belyavskiy 4b21ae
 {
Dmitry Belyavskiy 4b21ae
+	int r;
Dmitry Belyavskiy 4b21ae
+
Dmitry Belyavskiy d4ff0b
+	if ((r = sshkey_check_rsa_length(hostkey,
Dmitry Belyavskiy d4ff0b
+	    options.required_rsa_size)) != 0)
Dmitry Belyavskiy 4b21ae
+		fatal_r(r, "Bad server host key");
Dmitry Belyavskiy 4b21ae
 	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
Dmitry Belyavskiy 4b21ae
 	    xxx_conn_info) == -1)
Dmitry Belyavskiy 4b21ae
 		fatal("Host key verification failed.");
Dmitry Belyavskiy d4ff0b
@@ -1606,6 +1611,13 @@ load_identity_file(Identity *id)
Dmitry Belyavskiy d92560
 			private = NULL;
Dmitry Belyavskiy d92560
 			quit = 1;
Dmitry Belyavskiy d92560
 		}
Dmitry Belyavskiy d4ff0b
+		if (!quit && (r = sshkey_check_rsa_length(private,
Dmitry Belyavskiy d4ff0b
+		    options.required_rsa_size)) != 0) {
Dmitry Belyavskiy d92560
+			debug_fr(r, "Skipping key %s", id->filename);
Dmitry Belyavskiy d92560
+			sshkey_free(private);
Dmitry Belyavskiy d92560
+			private = NULL;
Dmitry Belyavskiy d92560
+			quit = 1;
Dmitry Belyavskiy d92560
+		}
Dmitry Belyavskiy d92560
 		if (!quit && private != NULL && id->agent_fd == -1 &&
Dmitry Belyavskiy d92560
 		    !(id->key && id->isprivate))
Dmitry Belyavskiy d92560
 			maybe_add_key_to_agent(id->filename, private, comment,
Dmitry Belyavskiy d4ff0b
@@ -1752,6 +1764,12 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
Dmitry Belyavskiy 4b21ae
		close(agent_fd);
Dmitry Belyavskiy 4b21ae
	} else {
Dmitry Belyavskiy 4b21ae
 		for (j = 0; j < idlist->nkeys; j++) {
Dmitry Belyavskiy 4b21ae
+			if ((r = sshkey_check_rsa_length(idlist->keys[j],
Dmitry Belyavskiy d4ff0b
+			    options.required_rsa_size)) != 0) {
Dmitry Belyavskiy 4b21ae
+				debug_fr(r, "ignoring %s agent key",
Dmitry Belyavskiy 4b21ae
+				    sshkey_ssh_name(idlist->keys[j]));
Dmitry Belyavskiy 4b21ae
+				continue;
Dmitry Belyavskiy 4b21ae
+			}
Dmitry Belyavskiy 4b21ae
 			found = 0;
Dmitry Belyavskiy 4b21ae
 			TAILQ_FOREACH(id, &files, next) {
Dmitry Belyavskiy 4b21ae
 				/*
Dmitry Belyavskiy 4b21ae
diff --git a/sshd.c b/sshd.c
Dmitry Belyavskiy d4ff0b
index 17eee9d8..395ef493 100644
Dmitry Belyavskiy 4b21ae
--- a/sshd.c
Dmitry Belyavskiy 4b21ae
+++ b/sshd.c
Dmitry Belyavskiy d4ff0b
@@ -1870,6 +1870,13 @@ main(int ac, char **av)
Dmitry Belyavskiy 4b21ae
 				fatal_r(r, "Could not demote key: \"%s\"",
Dmitry Belyavskiy 4b21ae
 				    options.host_key_files[i]);
Dmitry Belyavskiy 4b21ae
 		}
Dmitry Belyavskiy 4b21ae
+		if (pubkey != NULL && (r = sshkey_check_rsa_length(pubkey,
Dmitry Belyavskiy d4ff0b
+		    options.required_rsa_size)) != 0) {
Dmitry Belyavskiy 4b21ae
+			error_fr(r, "Host key %s", options.host_key_files[i]);
Dmitry Belyavskiy 4b21ae
+			sshkey_free(pubkey);
Dmitry Belyavskiy 4b21ae
+			sshkey_free(key);
Dmitry Belyavskiy 4b21ae
+			continue;
Dmitry Belyavskiy 4b21ae
+		}
Dmitry Belyavskiy 4b21ae
 		sensitive_data.host_keys[i] = key;
Dmitry Belyavskiy 4b21ae
 		sensitive_data.host_pubkeys[i] = pubkey;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
diff --git a/sshkey.c b/sshkey.c
Dmitry Belyavskiy d4ff0b
index ed2b5dff..77093235 100644
Dmitry Belyavskiy 4b21ae
--- a/sshkey.c
Dmitry Belyavskiy 4b21ae
+++ b/sshkey.c
Dmitry Belyavskiy d4ff0b
@@ -2365,18 +2365,24 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
Dmitry Belyavskiy 4b21ae
 	return ret;
Dmitry Belyavskiy 4b21ae
 }
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
-#ifdef WITH_OPENSSL
Dmitry Belyavskiy 4b21ae
-static int
Dmitry Belyavskiy 4b21ae
-check_rsa_length(const RSA *rsa)
Dmitry Belyavskiy 4b21ae
+int
Dmitry Belyavskiy 4b21ae
+sshkey_check_rsa_length(const struct sshkey *k, int min_size)
Dmitry Belyavskiy 4b21ae
 {
Dmitry Belyavskiy 4b21ae
+#ifdef WITH_OPENSSL
Dmitry Belyavskiy 4b21ae
 	const BIGNUM *rsa_n;
Dmitry Belyavskiy 4b21ae
+	int nbits;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
-	RSA_get0_key(rsa, &rsa_n, NULL, NULL);
Dmitry Belyavskiy 4b21ae
-	if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
Dmitry Belyavskiy 4b21ae
+	if (k == NULL || k->rsa == NULL ||
Dmitry Belyavskiy 4b21ae
+	    (k->type != KEY_RSA && k->type != KEY_RSA_CERT))
Dmitry Belyavskiy 4b21ae
+		return 0;
Dmitry Belyavskiy 4b21ae
+	RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
Dmitry Belyavskiy 4b21ae
+	nbits = BN_num_bits(rsa_n);
Dmitry Belyavskiy 4b21ae
+	if (nbits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
Dmitry Belyavskiy 4b21ae
+	    (min_size > 0 && nbits < min_size))
Dmitry Belyavskiy 4b21ae
 		return SSH_ERR_KEY_LENGTH;
Dmitry Belyavskiy 4b21ae
+#endif /* WITH_OPENSSL */
Dmitry Belyavskiy 4b21ae
 	return 0;
Dmitry Belyavskiy 4b21ae
 }
Dmitry Belyavskiy 4b21ae
-#endif
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 static int
Dmitry Belyavskiy 4b21ae
 sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
Dmitry Belyavskiy d4ff0b
@@ -2439,7 +2445,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy 4b21ae
 		}
Dmitry Belyavskiy 4b21ae
 		rsa_n = rsa_e = NULL; /* transferred */
Dmitry Belyavskiy 4b21ae
-		if ((ret = check_rsa_length(key->rsa)) != 0)
Dmitry Belyavskiy 4b21ae
+		if ((ret = sshkey_check_rsa_length(key, 0)) != 0)
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy 4b21ae
 #ifdef DEBUG_PK
Dmitry Belyavskiy 4b21ae
 		RSA_print_fp(stderr, key->rsa, 8);
Dmitry Belyavskiy d4ff0b
@@ -3642,7 +3648,7 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy 4b21ae
 		}
Dmitry Belyavskiy 4b21ae
 		rsa_p = rsa_q = NULL; /* transferred */
Dmitry Belyavskiy 4b21ae
-		if ((r = check_rsa_length(k->rsa)) != 0)
Dmitry Belyavskiy 4b21ae
+		if ((r = sshkey_check_rsa_length(k, 0)) != 0)
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy 4b21ae
 		if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy d4ff0b
@@ -4644,7 +4650,7 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
Dmitry Belyavskiy 4b21ae
 			r = SSH_ERR_LIBCRYPTO_ERROR;
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy 4b21ae
 		}
Dmitry Belyavskiy 4b21ae
-		if ((r = check_rsa_length(prv->rsa)) != 0)
Dmitry Belyavskiy 4b21ae
+		if ((r = sshkey_check_rsa_length(prv, 0)) != 0)
Dmitry Belyavskiy 4b21ae
 			goto out;
Dmitry Belyavskiy 4b21ae
 	} else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA &&
Dmitry Belyavskiy 4b21ae
 	    (type == KEY_UNSPEC || type == KEY_DSA)) {
Dmitry Belyavskiy 4b21ae
diff --git a/sshkey.h b/sshkey.h
Dmitry Belyavskiy d4ff0b
index 094815e0..be254e6b 100644
Dmitry Belyavskiy 4b21ae
--- a/sshkey.h
Dmitry Belyavskiy 4b21ae
+++ b/sshkey.h
Dmitry Belyavskiy d4ff0b
@@ -273,6 +273,7 @@ int	sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
Dmitry Belyavskiy 4b21ae
 int	sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob,
Dmitry Belyavskiy 4b21ae
     int type, struct sshkey **pubkeyp);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
+int sshkey_check_rsa_length(const struct sshkey *, int);
Dmitry Belyavskiy 4b21ae
 /* XXX should be internal, but used by ssh-keygen */
Dmitry Belyavskiy 4b21ae
 int ssh_rsa_complete_crt_parameters(struct sshkey *, const BIGNUM *);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
diff --git a/ssh.1 b/ssh.1
Dmitry Belyavskiy d4ff0b
index b4956aec..e255b9b9 100644
Dmitry Belyavskiy 4b21ae
--- a/ssh.1
Dmitry Belyavskiy 4b21ae
+++ b/ssh.1
Dmitry Belyavskiy d4ff0b
@@ -571,6 +571,7 @@ For full details of the options listed below, and their possible values, see
Dmitry Belyavskiy d4ff0b
 .It RemoteCommand
Dmitry Belyavskiy d4ff0b
 .It RemoteForward
Dmitry Belyavskiy d4ff0b
 .It RequestTTY
Dmitry Belyavskiy d4ff0b
+.It RequiredRSASize
Dmitry Belyavskiy d4ff0b
 .It SendEnv
Dmitry Belyavskiy d4ff0b
 .It ServerAliveInterval
Dmitry Belyavskiy d4ff0b
 .It ServerAliveCountMax
Dmitry Belyavskiy 4b21ae
diff --git a/ssh_config.5 b/ssh_config.5
Dmitry Belyavskiy d4ff0b
index 24a46460..d1ede18e 100644
Dmitry Belyavskiy 4b21ae
--- a/ssh_config.5
Dmitry Belyavskiy 4b21ae
+++ b/ssh_config.5
Dmitry Belyavskiy d4ff0b
@@ -1634,6 +1634,17 @@ and
Dmitry Belyavskiy d4ff0b
 .Fl T
Dmitry Belyavskiy d4ff0b
 flags for
Dmitry Belyavskiy d4ff0b
 .Xr ssh 1 .
Dmitry Belyavskiy d4ff0b
+.It Cm RequiredRSASize
Dmitry Belyavskiy d4ff0b
+Specifies the minimum RSA key size (in bits) that
Dmitry Belyavskiy d4ff0b
+.Xr ssh 1
Dmitry Belyavskiy d4ff0b
+will accept.
Dmitry Belyavskiy d4ff0b
+User authentication keys smaller than this limit will be ignored.
Dmitry Belyavskiy d4ff0b
+Servers that present host keys smaller than this limit will cause the
Dmitry Belyavskiy d4ff0b
+connection to be terminated.
Dmitry Belyavskiy d4ff0b
+The default is
Dmitry Belyavskiy d4ff0b
+.Cm 1024
Dmitry Belyavskiy d4ff0b
+bits.
Dmitry Belyavskiy d4ff0b
+Note that this limit may only be raised from the default.
Dmitry Belyavskiy d4ff0b
 .It Cm RevokedHostKeys
Dmitry Belyavskiy d4ff0b
 Specifies revoked host public keys.
Dmitry Belyavskiy d4ff0b
 Keys listed in this file will be refused for host authentication.
Dmitry Belyavskiy 4b21ae
diff --git a/sshd_config.5 b/sshd_config.5
Dmitry Belyavskiy d4ff0b
index 867a747d..f5a06637 100644
Dmitry Belyavskiy 4b21ae
--- a/sshd_config.5
Dmitry Belyavskiy 4b21ae
+++ b/sshd_config.5
Dmitry Belyavskiy d4ff0b
@@ -1596,6 +1596,16 @@ is
Dmitry Belyavskiy d4ff0b
 .Cm default none ,
Dmitry Belyavskiy d4ff0b
 which means that rekeying is performed after the cipher's default amount
Dmitry Belyavskiy d4ff0b
 of data has been sent or received and no time based rekeying is done.
Dmitry Belyavskiy d4ff0b
+.It Cm RequiredRSASize
Dmitry Belyavskiy d4ff0b
+Specifies the minimum RSA key size (in bits) that
Dmitry Belyavskiy d4ff0b
+.Xr sshd 8
Dmitry Belyavskiy d4ff0b
+will accept.
Dmitry Belyavskiy d4ff0b
+User and host-based authentication keys smaller than this limit will be
Dmitry Belyavskiy d4ff0b
+refused.
Dmitry Belyavskiy d4ff0b
+The default is
Dmitry Belyavskiy d4ff0b
+.Cm 1024
Dmitry Belyavskiy d4ff0b
+bits.
Dmitry Belyavskiy d4ff0b
+Note that this limit may only be raised from the default.
Dmitry Belyavskiy d4ff0b
 .It Cm RevokedKeys
Dmitry Belyavskiy d4ff0b
 Specifies revoked public keys file, or
Dmitry Belyavskiy d4ff0b
 .Cm none