jonathancammack / rpms / openssh

Forked from rpms/openssh 8 months ago
Clone
Dmitry Belyavskiy 4b21ae
diff --git a/auth2-hostbased.c b/auth2-hostbased.c
Dmitry Belyavskiy 4b21ae
index 2ab222ed6..4e9437912 100644
Dmitry Belyavskiy 4b21ae
--- a/auth2-hostbased.c
Dmitry Belyavskiy 4b21ae
+++ b/auth2-hostbased.c
Dmitry Belyavskiy 4b21ae
@@ -118,6 +118,10 @@ 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 4b21ae
+	if ((r = sshkey_check_rsa_length(key, options.rsa_min_size)) != 0) {
Dmitry Belyavskiy 4b21ae
+		logit("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 4b21ae
index daa756a01..68e7dea1f 100644
Dmitry Belyavskiy 4b21ae
--- a/auth2-pubkey.c
Dmitry Belyavskiy 4b21ae
+++ b/auth2-pubkey.c
Dmitry Belyavskiy 4b21ae
@@ -172,6 +172,10 @@ 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 4b21ae
+	if ((r = sshkey_check_rsa_length(key, options.rsa_min_size)) != 0) {
Dmitry Belyavskiy 4b21ae
+		logit("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 4b21ae
index 5b5afa8e3..5e17abd41 100644
Dmitry Belyavskiy 4b21ae
--- a/readconf.c
Dmitry Belyavskiy 4b21ae
+++ b/readconf.c
Dmitry Belyavskiy 4b21ae
@@ -160,7 +160,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 4b21ae
+	oSecurityKeyProvider, oKnownHostsCommand, oRSAMinSize,
Dmitry Belyavskiy 4b21ae
 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
Dmitry Belyavskiy 4b21ae
 } OpCodes;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
@@ -306,6 +306,7 @@ static struct {
Dmitry Belyavskiy 4b21ae
 	{ "proxyjump", oProxyJump },
Dmitry Belyavskiy 4b21ae
 	{ "securitykeyprovider", oSecurityKeyProvider },
Dmitry Belyavskiy 4b21ae
 	{ "knownhostscommand", oKnownHostsCommand },
Dmitry Belyavskiy 4b21ae
+	{ "rsaminsize", oRSAMinSize },
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	{ NULL, oBadOption }
Dmitry Belyavskiy 4b21ae
 };
Dmitry Belyavskiy 4b21ae
@@ -2162,6 +2163,10 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
Dmitry Belyavskiy 4b21ae
 			*charptr = xstrdup(arg);
Dmitry Belyavskiy 4b21ae
 		break;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
+	case oRSAMinSize:
Dmitry Belyavskiy 4b21ae
+		intptr = &options->rsa_min_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 4b21ae
@@ -2409,6 +2414,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 4b21ae
+	options->rsa_min_size = -1;
Dmitry Belyavskiy 4b21ae
 }
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 /*
Dmitry Belyavskiy 4b21ae
@@ -2598,6 +2604,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 4b21ae
+	if (options->rsa_min_size == -1)
Dmitry Belyavskiy 4b21ae
+		options->rsa_min_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 4b21ae
@@ -3287,6 +3295,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 4b21ae
+	dump_cfg_int(oRSAMinSize, o->rsa_min_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 4b21ae
index f647bd42a..29db353ab 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 4b21ae
+	int	rsa_min_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 4b21ae
index f7317a5cb..362ff5b67 100644
Dmitry Belyavskiy 4b21ae
--- a/servconf.c
Dmitry Belyavskiy 4b21ae
+++ b/servconf.c
Dmitry Belyavskiy 4b21ae
@@ -177,6 +177,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 4b21ae
+	options->rsa_min_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 4b21ae
@@ -416,6 +417,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 4b21ae
+	if (options->rsa_min_size == -1)
Dmitry Belyavskiy 4b21ae
+		options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 	assemble_algorithms(options);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
@@ -489,6 +492,7 @@ typedef enum {
Dmitry Belyavskiy 4b21ae
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
Dmitry Belyavskiy 4b21ae
 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
Dmitry Belyavskiy 4b21ae
 	sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
Dmitry Belyavskiy 4b21ae
+	sRSAMinSize,
Dmitry Belyavskiy 4b21ae
 	sDeprecated, sIgnore, sUnsupported
Dmitry Belyavskiy 4b21ae
 } ServerOpCodes;
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
@@ -632,6 +636,7 @@ 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 4b21ae
+	{ "rsaminsize", sRSAMinSize, SSHCFG_ALL },
Dmitry Belyavskiy 4b21ae
 	{ NULL, sBadOption, 0 }
Dmitry Belyavskiy 4b21ae
 };
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
@@ -2377,6 +2382,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 4b21ae
+	case sRSAMinSize:
Dmitry Belyavskiy 4b21ae
+		intptr = &options->rsa_min_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 4b21ae
@@ -2549,6 +2558,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 4b21ae
+	M_CP_INTOPT(rsa_min_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 4b21ae
@@ -2810,6 +2820,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 4b21ae
+	dump_cfg_int(sRSAMinSize, o->rsa_min_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 4b21ae
index 115db1e79..2e3486906 100644
Dmitry Belyavskiy 4b21ae
--- a/servconf.h
Dmitry Belyavskiy 4b21ae
+++ b/servconf.h
Dmitry Belyavskiy 4b21ae
@@ -227,6 +227,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 4b21ae
+	int	rsa_min_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 4b21ae
index a926cc007..cd13fb879 100644
Dmitry Belyavskiy 4b21ae
--- a/ssh.c
Dmitry Belyavskiy 4b21ae
+++ b/ssh.c
Dmitry Belyavskiy 4b21ae
@@ -500,14 +500,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 4b21ae
+		    options.rsa_min_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 4b21ae
@@ -1557,12 +1565,13 @@ 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 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 4b21ae
@@ -2263,7 +2272,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 4b21ae
@@ -2294,7 +2303,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 4b21ae
index 67f8e0309..d050c1656 100644
Dmitry Belyavskiy 4b21ae
--- a/sshconnect2.c
Dmitry Belyavskiy 4b21ae
+++ b/sshconnect2.c
Dmitry Belyavskiy 4b21ae
@@ -91,6 +91,10 @@ 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 4b21ae
+	if ((r = sshkey_check_rsa_length(hostkey, options.rsa_min_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 d92560
@@ -1762,6 +1762,12 @@ load_identity_file(Identity *id)
Dmitry Belyavskiy d92560
 			private = NULL;
Dmitry Belyavskiy d92560
 			quit = 1;
Dmitry Belyavskiy d92560
 		}
Dmitry Belyavskiy d92560
+		if (r = sshkey_check_rsa_length(private, options.rsa_min_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 4b21ae
@@ -1747,6 +1751,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 4b21ae
+			    options.rsa_min_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 4b21ae
index d26eb86ae..5f36905a1 100644
Dmitry Belyavskiy 4b21ae
--- a/sshd.c
Dmitry Belyavskiy 4b21ae
+++ b/sshd.c
Dmitry Belyavskiy 4b21ae
@@ -1746,6 +1746,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 4b21ae
+		    options.rsa_min_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 4b21ae
index 47864e6d8..8bad6bd99 100644
Dmitry Belyavskiy 4b21ae
--- a/sshkey.c
Dmitry Belyavskiy 4b21ae
+++ b/sshkey.c
Dmitry Belyavskiy 4b21ae
@@ -2319,18 +2319,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 4b21ae
@@ -2391,7 +2397,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 4b21ae
@@ -3580,7 +3586,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 4b21ae
@@ -4566,7 +4572,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 4b21ae
index 125cadb64..52e879456 100644
Dmitry Belyavskiy 4b21ae
--- a/sshkey.h
Dmitry Belyavskiy 4b21ae
+++ b/sshkey.h
Dmitry Belyavskiy 4b21ae
@@ -267,6 +267,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 4b21ae
index b4956aec..b1a40ebd 100644
Dmitry Belyavskiy 4b21ae
--- a/ssh.1
Dmitry Belyavskiy 4b21ae
+++ b/ssh.1
Dmitry Belyavskiy 4b21ae
@@ -554,6 +554,7 @@ For full details of the options listed below, and their possible values, see
Dmitry Belyavskiy 4b21ae
 .It LogLevel
Dmitry Belyavskiy 4b21ae
 .It MACs
Dmitry Belyavskiy 4b21ae
 .It Match
Dmitry Belyavskiy 4b21ae
+.It RSAMinSize
Dmitry Belyavskiy 4b21ae
 .It NoHostAuthenticationForLocalhost
Dmitry Belyavskiy 4b21ae
 .It NumberOfPasswordPrompts
Dmitry Belyavskiy 4b21ae
 .It PasswordAuthentication
Dmitry Belyavskiy 4b21ae
diff --git a/ssh_config.5 b/ssh_config.5
Dmitry Belyavskiy 4b21ae
index 24a46460..68771e4b 100644
Dmitry Belyavskiy 4b21ae
--- a/ssh_config.5
Dmitry Belyavskiy 4b21ae
+++ b/ssh_config.5
Dmitry Belyavskiy 4b21ae
@@ -1322,6 +1322,10 @@ The argument to this keyword must be
Dmitry Belyavskiy 4b21ae
 or
Dmitry Belyavskiy 4b21ae
 .Cm no
Dmitry Belyavskiy 4b21ae
 (the default).
Dmitry Belyavskiy 4b21ae
+.It Cm RSAMinSize
Dmitry Belyavskiy 4b21ae
+Provides a minimal bits requirement for RSA keys when used for signature and
Dmitry Belyavskiy 4b21ae
+verification but not for the key generation. The default value is 1024 and
Dmitry Belyavskiy 4b21ae
+can't be reduced.
Dmitry Belyavskiy 4b21ae
 .It Cm NumberOfPasswordPrompts
Dmitry Belyavskiy 4b21ae
 Specifies the number of password prompts before giving up.
Dmitry Belyavskiy 4b21ae
 The argument to this keyword must be an integer.
Dmitry Belyavskiy 4b21ae
diff --git a/sshd_config.5 b/sshd_config.5
Dmitry Belyavskiy 4b21ae
index 867a747d..e08811ca 100644
Dmitry Belyavskiy 4b21ae
--- a/sshd_config.5
Dmitry Belyavskiy 4b21ae
+++ b/sshd_config.5
Dmitry Belyavskiy 4b21ae
@@ -1266,6 +1266,10 @@ will refuse connection attempts with a probability of rate/100 (30%)
Dmitry Belyavskiy 4b21ae
 if there are currently start (10) unauthenticated connections.
Dmitry Belyavskiy 4b21ae
 The probability increases linearly and all connection attempts
Dmitry Belyavskiy 4b21ae
 are refused if the number of unauthenticated connections reaches full (60).
Dmitry Belyavskiy 4b21ae
+.It Cm RSAMinSize
Dmitry Belyavskiy 4b21ae
+Provides a minimal bits requirement for RSA keys when used for signature and
Dmitry Belyavskiy 4b21ae
+verification but not for the key generation. The default value is 1024 and
Dmitry Belyavskiy 4b21ae
+can't be reduced.
Dmitry Belyavskiy 4b21ae
 .It Cm ModuliFile
Dmitry Belyavskiy 4b21ae
 Specifies the
Dmitry Belyavskiy 4b21ae
 .Xr moduli 5
Dmitry Belyavskiy 4b21ae
diff --git a/sshkey.h b/sshkey.h
Dmitry Belyavskiy 4b21ae
index 094815e0..2bb8cb90 100644
Dmitry Belyavskiy 4b21ae
--- a/sshkey.h
Dmitry Belyavskiy 4b21ae
+++ b/sshkey.h
Dmitry Belyavskiy 4b21ae
@@ -286,6 +286,8 @@ int	 sshkey_private_serialize_maxsign(struct sshkey *key,
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
 void	 sshkey_sig_details_free(struct sshkey_sig_details *);
Dmitry Belyavskiy 4b21ae
 
Dmitry Belyavskiy 4b21ae
+int ssh_set_rsa_min_bits(int minbits);
Dmitry Belyavskiy 4b21ae
+
Dmitry Belyavskiy 4b21ae
 #ifdef SSHKEY_INTERNAL
Dmitry Belyavskiy 4b21ae
 int ssh_rsa_sign(const struct sshkey *key,
Dmitry Belyavskiy 4b21ae
     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,