diff --git a/include/libssh/kex.h b/include/libssh/kex.h index 19a67c81..3a1f4a6f 100644 --- a/include/libssh/kex.h +++ b/include/libssh/kex.h @@ -39,11 +39,13 @@ int ssh_set_client_kex(ssh_session session); int ssh_kex_select_methods(ssh_session session); int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name); char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list); +char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list); char **ssh_space_tokenize(const char *chain); int ssh_get_kex1(ssh_session session); char *ssh_find_matching(const char *in_d, const char *what_d); const char *ssh_kex_get_supported_method(uint32_t algo); const char *ssh_kex_get_default_methods(uint32_t algo); +const char *ssh_kex_get_fips_methods(uint32_t algo); const char *ssh_kex_get_description(uint32_t algo); char *ssh_client_select_hostkeys(ssh_session session); int ssh_send_rekex(ssh_session session); diff --git a/src/kex.c b/src/kex.c index 321187d9..d24237c1 100644 --- a/src/kex.c +++ b/src/kex.c @@ -136,11 +136,55 @@ #define KEY_EXCHANGE_SUPPORTED \ GEX_SHA1 \ KEY_EXCHANGE + #define KEX_METHODS_SIZE 10 /* RFC 8308 */ #define KEX_EXTENSION_CLIENT "ext-info-c" +/* Allowed algorithms in FIPS mode */ +#define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\ + "aes256-ctr,"\ + "aes256-cbc,"\ + "aes128-gcm@openssh.com,"\ + "aes128-ctr,"\ + "aes128-cbc" + +#define FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS "ecdsa-sha2-nistp521,"\ + "ecdsa-sha2-nistp384,"\ + "ecdsa-sha2-nistp256,"\ + "rsa-sha2-512,"\ + "rsa-sha2-256" + +#define FIPS_ALLOWED_KEX "ecdh-sha2-nistp256,"\ + "ecdh-sha2-nistp384,"\ + "ecdh-sha2-nistp521,"\ + "diffie-hellman-group-exchange-sha256,"\ + "diffie-hellman-group16-sha512,"\ + "diffie-hellman-group18-sha512" + +#define FIPS_ALLOWED_MACS "hmac-sha2-256-etm@openssh.com,"\ + "hmac-sha1-etm@openssh.com,"\ + "hmac-sha2-512-etm@openssh.com,"\ + "hmac-sha2-256,"\ + "hmac-sha1,"\ + "hmac-sha2-512" + +/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ +static const char *fips_methods[] = { + FIPS_ALLOWED_KEX, + FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS, + FIPS_ALLOWED_CIPHERS, + FIPS_ALLOWED_CIPHERS, + FIPS_ALLOWED_MACS, + FIPS_ALLOWED_MACS, + ZLIB, + ZLIB, + "", + "", + NULL +}; + /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ static const char *default_methods[] = { KEY_EXCHANGE, @@ -211,6 +255,14 @@ const char *ssh_kex_get_description(uint32_t algo) { return ssh_kex_descriptions[algo]; } +const char *ssh_kex_get_fips_methods(uint32_t algo) { + if (algo >= KEX_METHODS_SIZE) { + return NULL; + } + + return fips_methods[algo]; +} + /** * @internal * @brief returns whether the first client key exchange algorithm or @@ -600,8 +652,13 @@ int ssh_set_client_kex(ssh_session session) for (i = 0; i < KEX_METHODS_SIZE; i++) { wanted = session->opts.wanted_methods[i]; - if (wanted == NULL) - wanted = default_methods[i]; + if (wanted == NULL) { + if (ssh_fips_mode()) { + wanted = fips_methods[i]; + } else { + wanted = default_methods[i]; + } + } client->methods[i] = strdup(wanted); if (client->methods[i] == NULL) { ssh_set_error_oom(session); @@ -823,6 +880,27 @@ char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list) return ssh_find_all_matching(supported_methods[algo], list); } +/** + * @internal + * + * @brief Return a new allocated string containing only the FIPS allowed + * algorithms from the list. + * + * @param[in] algo The type of the methods to filter + * @param[in] list The list to be filtered + * + * @return A new allocated list containing only the FIPS allowed algorithms from + * the list; NULL in case of error. + */ +char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list) +{ + if (algo > SSH_LANG_S_C) { + return NULL; + } + + return ssh_find_all_matching(fips_methods[algo], list); +} + int ssh_make_sessionid(ssh_session session) { ssh_string num = NULL; diff --git a/src/options.c b/src/options.c index 9af7b22b..672735a4 100644 --- a/src/options.c +++ b/src/options.c @@ -223,10 +223,15 @@ int ssh_options_set_algo(ssh_session session, { char *p = NULL; - p = ssh_keep_known_algos(algo, list); + if (ssh_fips_mode()) { + p = ssh_keep_fips_algos(algo, list); + } else { + p = ssh_keep_known_algos(algo, list); + } + if (p == NULL) { ssh_set_error(session, SSH_REQUEST_DENIED, - "Setting method: no algorithm for method \"%s\" (%s)", + "Setting method: no allowed algorithm for method \"%s\" (%s)", ssh_kex_get_description(algo), list); return -1; } @@ -796,7 +801,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, ssh_set_error_invalid(session); return -1; } else { - p = ssh_keep_known_algos(SSH_HOSTKEYS, v); + if (ssh_fips_mode()) { + p = ssh_keep_fips_algos(SSH_HOSTKEYS, v); + } else { + p = ssh_keep_known_algos(SSH_HOSTKEYS, v); + } if (p == NULL) { ssh_set_error(session, SSH_REQUEST_DENIED, "Setting method: no known public key algorithm (%s)", @@ -1503,7 +1512,11 @@ static int ssh_bind_set_algo(ssh_bind sshbind, { char *p = NULL; - p = ssh_keep_known_algos(algo, list); + if (ssh_fips_mode()) { + p = ssh_keep_fips_algos(algo, list); + } else { + p = ssh_keep_known_algos(algo, list); + } if (p == NULL) { ssh_set_error(sshbind, SSH_REQUEST_DENIED, "Setting method: no algorithm for method \"%s\" (%s)", @@ -1938,7 +1951,11 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, ssh_set_error_invalid(sshbind); return -1; } else { - p = ssh_keep_known_algos(SSH_HOSTKEYS, v); + if (ssh_fips_mode()) { + p = ssh_keep_fips_algos(SSH_HOSTKEYS, v); + } else { + p = ssh_keep_known_algos(SSH_HOSTKEYS, v); + } if (p == NULL) { ssh_set_error(sshbind, SSH_REQUEST_DENIED, "Setting method: no known public key algorithm (%s)", diff --git a/src/pki.c b/src/pki.c index a65e8a48..d49eaa19 100644 --- a/src/pki.c +++ b/src/pki.c @@ -323,7 +323,11 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type) if (session->client) { allowed_list = session->opts.pubkey_accepted_types; if (allowed_list == NULL) { - allowed_list = ssh_kex_get_default_methods(SSH_HOSTKEYS); + if (ssh_fips_mode()) { + allowed_list = ssh_kex_get_fips_methods(SSH_HOSTKEYS); + } else { + allowed_list = ssh_kex_get_default_methods(SSH_HOSTKEYS); + } } } #ifdef WITH_SERVER @@ -2111,13 +2115,26 @@ int pki_key_check_hash_compatible(ssh_key key, case SSH_KEYTYPE_DSS_CERT01: case SSH_KEYTYPE_DSS: if (hash_type == SSH_DIGEST_SHA1) { - return SSH_OK; + if (ssh_fips_mode()) { + SSH_LOG(SSH_LOG_WARN, "SHA1 is not allowed in FIPS mode"); + return SSH_ERROR; + } else { + return SSH_OK; + } } break; case SSH_KEYTYPE_RSA_CERT01: case SSH_KEYTYPE_RSA: - if (hash_type == SSH_DIGEST_SHA1 || - hash_type == SSH_DIGEST_SHA256 || + if (hash_type == SSH_DIGEST_SHA1) { + if (ssh_fips_mode()) { + SSH_LOG(SSH_LOG_WARN, "SHA1 is not allowed in FIPS mode"); + return SSH_ERROR; + } else { + return SSH_OK; + } + } + + if (hash_type == SSH_DIGEST_SHA256 || hash_type == SSH_DIGEST_SHA512) { return SSH_OK; diff --git a/src/server.c b/src/server.c index 1fb68118..f5a12bdf 100644 --- a/src/server.c +++ b/src/server.c @@ -142,7 +142,11 @@ int server_set_kex(ssh_session session) if (session->opts.wanted_methods[SSH_HOSTKEYS]) { allowed = session->opts.wanted_methods[SSH_HOSTKEYS]; } else { - allowed = ssh_kex_get_default_methods(SSH_HOSTKEYS); + if (ssh_fips_mode()) { + allowed = ssh_kex_get_fips_methods(SSH_HOSTKEYS); + } else { + allowed = ssh_kex_get_default_methods(SSH_HOSTKEYS); + } } /* It is expected for the list of allowed hostkeys to be ordered by @@ -163,8 +167,13 @@ int server_set_kex(ssh_session session) } for (i = 0; i < 10; i++) { - if ((wanted = session->opts.wanted_methods[i]) == NULL) { - wanted = ssh_kex_get_default_methods(i); + wanted = session->opts.wanted_methods[i]; + if (wanted == NULL) { + if (ssh_fips_mode()) { + wanted = ssh_kex_get_fips_methods(i); + } else { + wanted = ssh_kex_get_default_methods(i); + } } server->methods[i] = strdup(wanted); if (server->methods[i] == NULL) {