1
1

Allow limiting RSA key size used for authentication

Thanks to Harry Sintonen from WithSecure for pointing this out.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
Jakub Jelen 2022-05-09 22:16:12 +02:00
родитель 1c0372e0aa
Коммит b408f5724a
8 изменённых файлов: 210 добавлений и 0 удалений

Просмотреть файл

@ -406,6 +406,7 @@ enum ssh_options_e {
SSH_OPTIONS_PROCESS_CONFIG,
SSH_OPTIONS_REKEY_DATA,
SSH_OPTIONS_REKEY_TIME,
SSH_OPTIONS_RSA_MIN_SIZE,
};
enum {

Просмотреть файл

@ -176,6 +176,7 @@ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key);
ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key);
int ssh_key_algorithm_allowed(ssh_session session, const char *type);
bool ssh_key_size_allowed(ssh_session session, ssh_key key);
/* Return the key size in bits */
int ssh_key_size(ssh_key key);

Просмотреть файл

@ -165,4 +165,5 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
/* URI Function */
int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type);
bool ssh_key_size_allowed_rsa(int min_size, ssh_key key);
#endif /* PKI_PRIV_H_ */

Просмотреть файл

@ -233,6 +233,7 @@ struct ssh_session_struct {
uint8_t options_seen[SOC_MAX];
uint64_t rekey_data;
uint32_t rekey_time;
unsigned int rsa_min_size;
} opts;
/* counters */
ssh_counter socket_counter;

Просмотреть файл

@ -492,6 +492,7 @@ int ssh_userauth_try_publickey(ssh_session session,
{
ssh_string pubkey_s = NULL;
const char *sig_type_c = NULL;
bool allowed;
int rc;
if (session == NULL) {
@ -531,6 +532,13 @@ int ssh_userauth_try_publickey(ssh_session session,
sig_type_c);
return SSH_AUTH_DENIED;
}
allowed = ssh_key_size_allowed(session, pubkey);
if (!allowed) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The '%s' key type of size %d is not allowed by "
"RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
return SSH_AUTH_DENIED;
}
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@ -612,6 +620,7 @@ int ssh_userauth_publickey(ssh_session session,
const ssh_key privkey)
{
ssh_string str = NULL;
bool allowed;
int rc;
const char *sig_type_c = NULL;
enum ssh_keytypes_e key_type;
@ -656,6 +665,13 @@ int ssh_userauth_publickey(ssh_session session,
sig_type_c);
return SSH_AUTH_DENIED;
}
allowed = ssh_key_size_allowed(session, privkey);
if (!allowed) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The '%s' key type of size %d is not allowed by "
"RSA_MIN_SIZE", sig_type_c, ssh_key_size(privkey));
return SSH_AUTH_DENIED;
}
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@ -732,6 +748,7 @@ static int ssh_userauth_agent_publickey(ssh_session session,
ssh_string pubkey_s = NULL;
ssh_string sig_blob = NULL;
const char *sig_type_c = NULL;
bool allowed;
int rc;
switch(session->pending_call_state) {
@ -776,6 +793,14 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
allowed = ssh_key_size_allowed(session, pubkey);
if (!allowed) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The '%s' key type of size %d is not allowed by "
"RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",

Просмотреть файл

@ -465,6 +465,15 @@ int ssh_options_set_algo(ssh_session session,
* in seconds. RFC 4253 Section 9 recommends one hour.
* (uint32_t, 0=off)
*
* - SSH_OPTIONS_RSA_MIN_SIZE
* Set the minimum RSA key size in bits to be accepted by the
* client for both authentication and hostkey verification.
* The values under 768 bits are not accepted even with this
* configuration option as they are considered completely broken.
* Setting 0 will revert the value to defaults.
* Default is 1024 bits or 2048 bits in FIPS mode.
* (unsigned int *)
*
* @param value The value to set. This is a generic pointer and the
* datatype which is used should be set according to the
* type set.
@ -1028,6 +1037,21 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.rekey_time = (*x) * 1000;
}
break;
case SSH_OPTIONS_RSA_MIN_SIZE:
if (value == NULL) {
ssh_set_error_invalid(session);
return -1;
} else {
unsigned int *x = (unsigned int *)value;
if (*x > 0 && *x < 768) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The provided value (%u) for minimal RSA key "
"size is too small. Use at least 768 bits.", *x);
return -1;
}
session->opts.rsa_min_size = *x;
}
break;
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return -1;

Просмотреть файл

@ -404,6 +404,39 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
return ssh_match_group(allowed_list, type);
}
/**
* @brief Check the given key is acceptable in regards to the key size policy
* specified by the configuration
*
* @param[in] session The SSH session
* @param[in] key The SSH key
* @returns true if the key is allowed, false otherwise
*/
bool ssh_key_size_allowed(ssh_session session, ssh_key key)
{
int key_size = ssh_key_size(key);
int min_size = 0;
switch (key->type) {
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA_CERT01:
min_size = session->opts.rsa_min_size;
if (min_size < 768) {
if (ssh_fips_mode()) {
min_size = 2048;
} else {
min_size = 1024;
}
}
if (key_size < min_size) {
return false;
}
/* fall through */
default:
return true;
}
}
/**
* @brief Convert a key type to a hash type. This is usually unambiguous
* for all the key types, unless the SHA2 extension (RFC 8332) is

Просмотреть файл

@ -986,6 +986,124 @@ static void torture_auth_pubkey_types_ed25519_nonblocking(void **state)
} while (rc == SSH_AUTH_AGAIN);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
SSH_KEY_FREE(privkey);
}
static void torture_auth_pubkey_rsa_key_size(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
char bob_ssh_key[1024];
ssh_key privkey = NULL;
struct passwd *pwd;
int rc;
unsigned int limit = 4096;
pwd = getpwnam("bob");
assert_non_null(pwd);
snprintf(bob_ssh_key,
sizeof(bob_ssh_key),
"%s/.ssh/id_rsa",
pwd->pw_dir);
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_ssh_return_code(session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_none(session, NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
/* set unreasonable large minimum key size to trigger the condition */
rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit); /* larger than the test key */
assert_ssh_return_code(session, rc);
/* Import the RSA private key */
rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_publickey(session, NULL, privkey);
assert_int_equal(rc, SSH_AUTH_DENIED);
/* revert to default values which should work also in FIPS mode */
limit = 0;
rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_publickey(session, NULL, privkey);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
SSH_KEY_FREE(privkey);
}
static void torture_auth_pubkey_rsa_key_size_nonblocking(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
char bob_ssh_key[1024];
ssh_key privkey = NULL;
struct passwd *pwd;
int rc;
unsigned int limit = 4096;
pwd = getpwnam("bob");
assert_non_null(pwd);
snprintf(bob_ssh_key,
sizeof(bob_ssh_key),
"%s/.ssh/id_rsa",
pwd->pw_dir);
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_ssh_return_code(session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
ssh_set_blocking(session, 0);
do {
rc = ssh_userauth_none(session, NULL);
} while (rc == SSH_AUTH_AGAIN);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
/* set unreasonable large minimum key size to trigger the condition */
rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit); /* larger than the test key */
assert_ssh_return_code(session, rc);
/* Import the RSA private key */
rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
assert_int_equal(rc, SSH_OK);
do {
rc = ssh_userauth_publickey(session, NULL, privkey);
} while (rc == SSH_AUTH_AGAIN);
assert_int_equal(rc, SSH_AUTH_DENIED);
/* revert to default values which should work also in FIPS mode */
limit = 0;
rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit);
assert_ssh_return_code(session, rc);
do {
rc = ssh_userauth_publickey(session, NULL, privkey);
} while (rc == SSH_AUTH_AGAIN);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
SSH_KEY_FREE(privkey);
}
int torture_run_tests(void) {
@ -1051,6 +1169,12 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ed25519_nonblocking,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size_nonblocking,
pubkey_setup,
session_teardown),
};
ssh_init();