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>
Этот коммит содержится в:
родитель
1c0372e0aa
Коммит
b408f5724a
@ -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;
|
||||
|
25
src/auth.c
25
src/auth.c
@ -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;
|
||||
|
33
src/pki.c
33
src/pki.c
@ -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();
|
||||
|
Загрузка…
Ссылка в новой задаче
Block a user