From: Pascal Knecht Date: Mon, 12 Oct 2020 16:58:53 +0000 (+0200) Subject: tls-server: Consider supported signature algorithms when selecting key/certificate X-Git-Tag: 5.9.2rc1~23^2~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9803fb82f448c7eae03aa4505b0f24a26a8f1948;p=thirdparty%2Fstrongswan.git tls-server: Consider supported signature algorithms when selecting key/certificate This won't work if the client doesn't send a `signature_algorithms` extension. But since the default is SHA1/RSA, most will send it to at least announce stronger hash algorithms if not ECDSA. --- diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index fc74d31f2e..cb4ed9c530 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -23,6 +23,7 @@ #include #include +#include ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, @@ -2423,3 +2424,87 @@ tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group) } return 0; } + +/** + * See header. + */ +key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig) +{ + int i; + + for (i = 0; i < countof(schemes); i++) + { + if (schemes[i].sig == sig) + { + return key_type_from_signature_scheme(schemes[i].params.scheme); + } + } + return 0; +} + +/** + * Hashtable hash function + */ +static u_int hash_key_type(key_type_t *type) +{ + return chunk_hash(chunk_from_thing(*type)); +} + +/** + * Hashtable equals function + */ +static bool equals_key_type(key_type_t *key1, key_type_t *key2) +{ + return *key1 == *key2; +} + +CALLBACK(filter_key_types, bool, + void *data, enumerator_t *orig, va_list args) +{ + key_type_t *key_type, *out; + + VA_ARGS_VGET(args, out); + + if (orig->enumerate(orig, NULL, &key_type)) + { + *out = *key_type; + return TRUE; + } + return FALSE; +} + +CALLBACK(destroy_key_types, void, + hashtable_t *ht) +{ + ht->destroy_function(ht, (void*)free); +} + +/* + * See header. + */ +enumerator_t *tls_get_supported_key_types(tls_version_t min_version, + tls_version_t max_version) +{ + hashtable_t *ht; + key_type_t *type, lookup; + int i; + + ht = hashtable_create((hashtable_hash_t)hash_key_type, + (hashtable_equals_t)equals_key_type, 4); + for (i = 0; i < countof(schemes); i++) + { + if (schemes[i].min_version <= max_version && + schemes[i].max_version >= min_version) + { + lookup = key_type_from_signature_scheme(schemes[i].params.scheme); + if (!ht->get(ht, &lookup)) + { + type = malloc_thing(key_type_t); + *type = lookup; + ht->put(ht, type, type); + } + } + } + return enumerator_create_filter(ht->create_enumerator(ht), + filter_key_types, ht, destroy_key_types); +} diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 9da07ce750..0e9d454f73 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -691,4 +691,24 @@ int tls_crypto_get_supported_groups(diffie_hellman_group_t **groups); */ tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group); +/** + * Get the key type from a TLS signature scheme + * + * @param sig TLS signature algorithm scheme + * @return type of a key + */ +key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig); + +/** + * Create an enumerator over supported key types within a specific TLS version range + * + * Enumerates over key_type_t + * + * @param min_version minimum negotiated TLS version + * @param max_version maximum negotiated TLS version + * @return hashtable of key types + */ +enumerator_t *tls_get_supported_key_types(tls_version_t min_version, + tls_version_t max_version); + #endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 4957cba898..f5e15615bb 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -169,71 +169,109 @@ struct private_tls_server_t { bool curves_received; }; +/** + * Create an array of an intersection of server and peer supported key types + */ +static array_t *create_common_key_types(chunk_t hashsig, + tls_version_t version_min, + tls_version_t version_max) +{ + array_t *key_types; + enumerator_t *enumerator; + key_type_t v, lookup; + uint16_t sig_scheme; + + key_types = array_create(sizeof(key_type_t), 8); + enumerator = tls_get_supported_key_types(version_min, version_max); + while (enumerator->enumerate(enumerator, &v)) + { + bio_reader_t *reader; + + reader = bio_reader_create(hashsig); + while (reader->remaining(reader) && + reader->read_uint16(reader, &sig_scheme)) + { + lookup = tls_signature_scheme_to_key_type(sig_scheme); + if (v == lookup) + { + array_insert(key_types, ARRAY_TAIL, &lookup); + break; + } + } + reader->destroy(reader); + } + enumerator->destroy(enumerator); + return key_types; +} + /** * Find a cipher suite and a server key */ static bool select_suite_and_key(private_tls_server_t *this, tls_cipher_suite_t *suites, int count) { + array_t *key_types; + tls_version_t version_min, version_max; private_key_t *key; key_type_t type; - key = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->server, - this->server_auth); + version_min = this->tls->get_version_min(this->tls); + version_max = this->tls->get_version_max(this->tls); + key_types = create_common_key_types(this->hashsig, version_min, version_max); + if (!array_count(key_types)) + { + DBG1(DBG_TLS, "no common signature algorithms found"); + array_destroy(key_types); + return FALSE; + } + while (array_remove(key_types, ARRAY_HEAD, &type)) + { + key = lib->credmgr->get_private(lib->credmgr, type, this->server, + this->server_auth); + if (key) + { + break; + } + } if (!key) { DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", this->server); + array_destroy(key_types); return FALSE; } - if (this->tls->get_version_max(this->tls) >= TLS_1_3) + if (version_max >= TLS_1_3) { - /* currently no support to derive key based on client supported - * signature schemes */ this->suite = this->crypto->select_cipher_suite(this->crypto, suites, count, KEY_ANY); - if (!this->suite) - { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; - } } else { this->suite = this->crypto->select_cipher_suite(this->crypto, suites, - count, - key->get_type(key)); - if (!this->suite) - { /* no match for this key, try to find another type */ - if (key->get_type(key) == KEY_ECDSA) - { - type = KEY_RSA; - } - else - { - type = KEY_ECDSA; - } - key->destroy(key); - - this->suite = this->crypto->select_cipher_suite(this->crypto, suites, - count, type); - if (!this->suite) - { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; - } + count, type); + while (!this->suite && array_remove(key_types, ARRAY_HEAD, &type)) + { /* find a key and cipher suite for one of the remaining key types */ + DESTROY_IF(key); this->server_auth->destroy(this->server_auth); this->server_auth = auth_cfg_create(); key = lib->credmgr->get_private(lib->credmgr, type, this->server, this->server_auth); - if (!key) + if (key) { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, + type); } } } + array_destroy(key_types); + if (!this->suite || !key) + { + DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable"); + return FALSE; + } + DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key)); this->private = key; return TRUE; }