/* * Copyright (C) 2004-2015 Free Software Foundation, Inc. * Copyright (C) 2015-2019 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* Here lies the code of the gnutls_*_set_priority() functions. */ #include "gnutls_int.h" #include "algorithms.h" #include "errors.h" #include #include #include #include #include #include "fips.h" #include "errno.h" #include "ext/srp.h" #include #include "profiles.h" #include "c-strcase.h" #include "inih/ini.h" #include "profiles.h" #include "name_val_array.h" #define MAX_ELEMENTS 64 #define ENABLE_PROFILE(c, profile) do { \ c->additional_verify_flags &= 0x00ffffff; \ c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(profile); \ c->level = _gnutls_profile_to_sec_level(profile); \ } while(0) /* This function is used by the test suite */ char *_gnutls_resolve_priorities(const char* priorities); const char *_gnutls_default_priority_string = DEFAULT_PRIORITY_STRING; static void prio_remove(priority_st * priority_list, unsigned int algo); static void prio_add(priority_st * priority_list, unsigned int algo); static void break_list(char *etag, char *broken_etag[MAX_ELEMENTS], int *size); typedef void (bulk_rmadd_func) (priority_st * priority_list, const int *); inline static void _set_priority(priority_st * st, const int *list) { int num = 0, i; while (list[num] != 0) num++; if (num > MAX_ALGOS) num = MAX_ALGOS; st->num_priorities = num; for (i = 0; i < num; i++) { st->priorities[i] = list[i]; } return; } inline static void _add_priority(priority_st * st, const int *list) { int num, i, j, init; init = i = st->num_priorities; for (num = 0; list[num] != 0; ++num) { if (i + 1 > MAX_ALGOS) { return; } for (j = 0; j < init; j++) { if (st->priorities[j] == (unsigned) list[num]) { break; } } if (j == init) { st->priorities[i++] = list[num]; st->num_priorities++; } } return; } static void _clear_priorities(priority_st * st, const int *list) { memset(st, 0, sizeof(*st)); } static void _clear_given_priorities(priority_st * st, const int *list) { unsigned i; for (i=0;list[i]!=0;i++) { prio_remove(st, list[i]); } } static const int _supported_groups_dh[] = { GNUTLS_GROUP_FFDHE2048, GNUTLS_GROUP_FFDHE3072, GNUTLS_GROUP_FFDHE4096, GNUTLS_GROUP_FFDHE6144, GNUTLS_GROUP_FFDHE8192, 0 }; static const int _supported_groups_ecdh[] = { GNUTLS_GROUP_SECP256R1, GNUTLS_GROUP_SECP384R1, GNUTLS_GROUP_SECP521R1, GNUTLS_GROUP_X25519, /* RFC 8422 */ GNUTLS_GROUP_X448, /* RFC 8422 */ 0 }; static const int _supported_groups_gost[] = { #ifdef ENABLE_GOST GNUTLS_GROUP_GC256A, GNUTLS_GROUP_GC256B, GNUTLS_GROUP_GC256C, GNUTLS_GROUP_GC256D, GNUTLS_GROUP_GC512A, GNUTLS_GROUP_GC512B, GNUTLS_GROUP_GC512C, #endif 0 }; static const int _supported_groups_normal[] = { GNUTLS_GROUP_SECP256R1, GNUTLS_GROUP_SECP384R1, GNUTLS_GROUP_SECP521R1, GNUTLS_GROUP_X25519, /* RFC 8422 */ GNUTLS_GROUP_X448, /* RFC 8422 */ /* These should stay last as our default behavior * is to send key shares for two top types (GNUTLS_KEY_SHARE_TOP2) * and we wouldn't want to have these sent by all clients * by default as they are quite expensive CPU-wise. */ GNUTLS_GROUP_FFDHE2048, GNUTLS_GROUP_FFDHE3072, GNUTLS_GROUP_FFDHE4096, GNUTLS_GROUP_FFDHE6144, GNUTLS_GROUP_FFDHE8192, 0 }; static const int* supported_groups_normal = _supported_groups_normal; static const int _supported_groups_secure128[] = { GNUTLS_GROUP_SECP256R1, GNUTLS_GROUP_SECP384R1, GNUTLS_GROUP_SECP521R1, GNUTLS_GROUP_X25519, /* RFC 8422 */ GNUTLS_GROUP_X448, /* RFC 8422 */ GNUTLS_GROUP_FFDHE2048, GNUTLS_GROUP_FFDHE3072, GNUTLS_GROUP_FFDHE4096, GNUTLS_GROUP_FFDHE6144, GNUTLS_GROUP_FFDHE8192, 0 }; static const int* supported_groups_secure128 = _supported_groups_secure128; static const int _supported_groups_suiteb128[] = { GNUTLS_GROUP_SECP256R1, GNUTLS_GROUP_SECP384R1, 0 }; static const int* supported_groups_suiteb128 = _supported_groups_suiteb128; static const int _supported_groups_suiteb192[] = { GNUTLS_GROUP_SECP384R1, 0 }; static const int* supported_groups_suiteb192 = _supported_groups_suiteb192; static const int _supported_groups_secure192[] = { GNUTLS_GROUP_SECP384R1, GNUTLS_GROUP_SECP521R1, GNUTLS_GROUP_FFDHE8192, 0 }; static const int* supported_groups_secure192 = _supported_groups_secure192; static const int protocol_priority[] = { GNUTLS_TLS1_3, GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_DTLS1_2, GNUTLS_DTLS1_0, 0 }; /* contains all the supported TLS protocols, intended to be used for eliminating them */ static const int stream_protocol_priority[] = { GNUTLS_TLS1_3, GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1_0, 0 }; /* contains all the supported DTLS protocols, intended to be used for eliminating them */ static const int dgram_protocol_priority[] = { GNUTLS_DTLS1_2, GNUTLS_DTLS1_0, GNUTLS_DTLS0_9, 0 }; static const int dtls_protocol_priority[] = { GNUTLS_DTLS1_2, GNUTLS_DTLS1_0, 0 }; static const int _protocol_priority_suiteb[] = { GNUTLS_TLS1_2, 0 }; static const int* protocol_priority_suiteb = _protocol_priority_suiteb; static const int _kx_priority_performance[] = { GNUTLS_KX_RSA, #ifdef ENABLE_ECDHE GNUTLS_KX_ECDHE_ECDSA, GNUTLS_KX_ECDHE_RSA, #endif #ifdef ENABLE_DHE GNUTLS_KX_DHE_RSA, #endif 0 }; static const int* kx_priority_performance = _kx_priority_performance; static const int _kx_priority_pfs[] = { #ifdef ENABLE_ECDHE GNUTLS_KX_ECDHE_ECDSA, GNUTLS_KX_ECDHE_RSA, #endif #ifdef ENABLE_DHE GNUTLS_KX_DHE_RSA, #endif 0 }; static const int* kx_priority_pfs = _kx_priority_pfs; static const int _kx_priority_suiteb[] = { GNUTLS_KX_ECDHE_ECDSA, 0 }; static const int* kx_priority_suiteb = _kx_priority_suiteb; static const int _kx_priority_secure[] = { /* The ciphersuites that offer forward secrecy take * precedence */ #ifdef ENABLE_ECDHE GNUTLS_KX_ECDHE_ECDSA, GNUTLS_KX_ECDHE_RSA, #endif GNUTLS_KX_RSA, /* KX-RSA is now ahead of DHE-RSA and DHE-DSS due to the compatibility * issues the DHE ciphersuites have. That is, one cannot enforce a specific * security level without dropping the connection. */ #ifdef ENABLE_DHE GNUTLS_KX_DHE_RSA, #endif /* GNUTLS_KX_ANON_DH: Man-in-the-middle prone, don't add! */ 0 }; static const int* kx_priority_secure = _kx_priority_secure; static const int _kx_priority_gost[] = { GNUTLS_KX_VKO_GOST_12, 0, }; static const int* kx_priority_gost = _kx_priority_gost; static const int _cipher_priority_performance_default[] = { GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_CHACHA20_POLY1305, GNUTLS_CIPHER_AES_128_CCM, GNUTLS_CIPHER_AES_256_CCM, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; static const int _cipher_priority_performance_no_aesni[] = { GNUTLS_CIPHER_CHACHA20_POLY1305, GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_AES_128_CCM, GNUTLS_CIPHER_AES_256_CCM, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; /* If GCM and AES acceleration is available then prefer * them over anything else. Overall we prioritise AEAD * over legacy ciphers, and 256-bit over 128 (for future * proof). */ static const int _cipher_priority_normal_default[] = { GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_CHACHA20_POLY1305, GNUTLS_CIPHER_AES_256_CCM, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_128_CCM, GNUTLS_CIPHER_AES_128_CBC, 0 }; static const int cipher_priority_performance_fips[] = { GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_128_CCM, GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_AES_256_CCM, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; static const int cipher_priority_normal_fips[] = { GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_AES_256_CCM, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_128_CCM, 0 }; static const int _cipher_priority_suiteb128[] = { GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_AES_128_GCM, 0 }; static const int* cipher_priority_suiteb128 = _cipher_priority_suiteb128; static const int _cipher_priority_suiteb192[] = { GNUTLS_CIPHER_AES_256_GCM, 0 }; static const int* cipher_priority_suiteb192 = _cipher_priority_suiteb192; static const int _cipher_priority_secure128[] = { GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_CHACHA20_POLY1305, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_256_CCM, GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_128_CCM, 0 }; static const int *cipher_priority_secure128 = _cipher_priority_secure128; static const int _cipher_priority_secure192[] = { GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_CHACHA20_POLY1305, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_256_CCM, 0 }; static const int* cipher_priority_secure192 = _cipher_priority_secure192; static const int _sign_priority_default[] = { GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_EDDSA_ED25519, GNUTLS_SIGN_RSA_SHA384, GNUTLS_SIGN_RSA_PSS_SHA384, GNUTLS_SIGN_RSA_PSS_RSAE_SHA384, GNUTLS_SIGN_ECDSA_SHA384, GNUTLS_SIGN_ECDSA_SECP384R1_SHA384, GNUTLS_SIGN_EDDSA_ED448, GNUTLS_SIGN_RSA_SHA512, GNUTLS_SIGN_RSA_PSS_SHA512, GNUTLS_SIGN_RSA_PSS_RSAE_SHA512, GNUTLS_SIGN_ECDSA_SHA512, GNUTLS_SIGN_ECDSA_SECP521R1_SHA512, GNUTLS_SIGN_RSA_SHA1, GNUTLS_SIGN_ECDSA_SHA1, 0 }; static const int* sign_priority_default = _sign_priority_default; static const int _sign_priority_suiteb128[] = { GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_ECDSA_SHA384, GNUTLS_SIGN_ECDSA_SECP384R1_SHA384, 0 }; static const int* sign_priority_suiteb128 = _sign_priority_suiteb128; static const int _sign_priority_suiteb192[] = { GNUTLS_SIGN_ECDSA_SHA384, GNUTLS_SIGN_ECDSA_SECP384R1_SHA384, 0 }; static const int* sign_priority_suiteb192 = _sign_priority_suiteb192; static const int _sign_priority_secure128[] = { GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_EDDSA_ED25519, GNUTLS_SIGN_RSA_SHA384, GNUTLS_SIGN_RSA_PSS_SHA384, GNUTLS_SIGN_RSA_PSS_RSAE_SHA384, GNUTLS_SIGN_ECDSA_SHA384, GNUTLS_SIGN_ECDSA_SECP384R1_SHA384, GNUTLS_SIGN_EDDSA_ED448, GNUTLS_SIGN_RSA_SHA512, GNUTLS_SIGN_RSA_PSS_SHA512, GNUTLS_SIGN_RSA_PSS_RSAE_SHA512, GNUTLS_SIGN_ECDSA_SHA512, GNUTLS_SIGN_ECDSA_SECP521R1_SHA512, 0 }; static const int* sign_priority_secure128 = _sign_priority_secure128; static const int _sign_priority_secure192[] = { GNUTLS_SIGN_RSA_SHA384, GNUTLS_SIGN_RSA_PSS_SHA384, GNUTLS_SIGN_RSA_PSS_RSAE_SHA384, GNUTLS_SIGN_ECDSA_SHA384, GNUTLS_SIGN_ECDSA_SECP384R1_SHA384, GNUTLS_SIGN_RSA_SHA512, GNUTLS_SIGN_RSA_PSS_SHA512, GNUTLS_SIGN_RSA_PSS_RSAE_SHA512, GNUTLS_SIGN_ECDSA_SHA512, GNUTLS_SIGN_ECDSA_SECP521R1_SHA512, 0 }; static const int* sign_priority_secure192 = _sign_priority_secure192; static const int _sign_priority_gost[] = { GNUTLS_SIGN_GOST_256, GNUTLS_SIGN_GOST_512, 0 }; static const int* sign_priority_gost = _sign_priority_gost; static const int mac_priority_normal_default[] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_AEAD, 0 }; static const int mac_priority_normal_fips[] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_AEAD, 0 }; static const int *cipher_priority_performance = _cipher_priority_performance_default; static const int *cipher_priority_normal = _cipher_priority_normal_default; static const int *mac_priority_normal = mac_priority_normal_default; static const int _cipher_priority_gost[] = { GNUTLS_CIPHER_GOST28147_TC26Z_CNT, 0 }; static const int *cipher_priority_gost = _cipher_priority_gost; static const int _mac_priority_gost[] = { GNUTLS_MAC_GOST28147_TC26Z_IMIT, 0 }; static const int *mac_priority_gost = _mac_priority_gost; /* if called with replace the default priorities with the FIPS140 ones */ void _gnutls_priority_update_fips(void) { cipher_priority_performance = cipher_priority_performance_fips; cipher_priority_normal = cipher_priority_normal_fips; mac_priority_normal = mac_priority_normal_fips; } void _gnutls_priority_update_non_aesni(void) { /* if we have no AES acceleration in performance mode * prefer fast stream ciphers */ if (_gnutls_fips_mode_enabled() == 0) { cipher_priority_performance = _cipher_priority_performance_no_aesni; } } static const int _mac_priority_suiteb[] = { GNUTLS_MAC_AEAD, 0 }; static const int* mac_priority_suiteb = _mac_priority_suiteb; static const int _mac_priority_secure128[] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_AEAD, 0 }; static const int* mac_priority_secure128 = _mac_priority_secure128; static const int _mac_priority_secure192[] = { GNUTLS_MAC_AEAD, 0 }; static const int* mac_priority_secure192 = _mac_priority_secure192; static const int cert_type_priority_default[] = { GNUTLS_CRT_X509, 0 }; static const int cert_type_priority_all[] = { GNUTLS_CRT_X509, GNUTLS_CRT_RAWPK, 0 }; typedef void (rmadd_func) (priority_st * priority_list, unsigned int alg); static void prio_remove(priority_st * priority_list, unsigned int algo) { unsigned int i; for (i = 0; i < priority_list->num_priorities; i++) { if (priority_list->priorities[i] == algo) { priority_list->num_priorities--; if ((priority_list->num_priorities - i) > 0) memmove(&priority_list->priorities[i], &priority_list->priorities[i + 1], (priority_list->num_priorities - i) * sizeof(priority_list-> priorities[0])); priority_list->priorities[priority_list-> num_priorities] = 0; break; } } return; } static void prio_add(priority_st * priority_list, unsigned int algo) { unsigned int i, l = priority_list->num_priorities; if (l >= MAX_ALGOS) return; /* can't add it anyway */ for (i = 0; i < l; ++i) { if (algo == priority_list->priorities[i]) return; /* if it exists */ } priority_list->priorities[l] = algo; priority_list->num_priorities++; return; } /** * gnutls_priority_set: * @session: is a #gnutls_session_t type. * @priority: is a #gnutls_priority_t type. * * Sets the priorities to use on the ciphers, key exchange methods, * and macs. Note that this function is expected to be called once * per session; when called multiple times (e.g., before a re-handshake, * the caller should make sure that any new settings are not incompatible * with the original session). * * Returns: %GNUTLS_E_SUCCESS on success, or an error code on error. **/ int gnutls_priority_set(gnutls_session_t session, gnutls_priority_t priority) { int ret; if (priority == NULL || priority->protocol.num_priorities == 0 || priority->cs.size == 0) return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET); /* set the current version to the first in the chain, if this is * the call before the initial handshake. During a re-handshake * we do not set the version to avoid overriding the currently * negotiated version. */ if (!session->internals.handshake_in_progress && !session->internals.initial_negotiation_completed) { ret = _gnutls_set_current_version(session, priority->protocol.priorities[0]); if (ret < 0) return gnutls_assert_val(ret); } /* At this point the provided priorities passed the sanity tests */ if (session->internals.priorities) gnutls_priority_deinit(session->internals.priorities); gnutls_atomic_increment(&priority->usage_cnt); session->internals.priorities = priority; if (priority->no_tickets != 0) { /* when PFS is explicitly requested, disable session tickets */ session->internals.flags |= GNUTLS_NO_TICKETS; } ADD_PROFILE_VFLAGS(session, priority->additional_verify_flags); /* mirror variables */ #undef COPY_TO_INTERNALS #define COPY_TO_INTERNALS(xx) session->internals.xx = priority->_##xx COPY_TO_INTERNALS(allow_large_records); COPY_TO_INTERNALS(allow_small_records); COPY_TO_INTERNALS(no_etm); COPY_TO_INTERNALS(no_ext_master_secret); COPY_TO_INTERNALS(allow_key_usage_violation); COPY_TO_INTERNALS(allow_wrong_pms); COPY_TO_INTERNALS(dumbfw); COPY_TO_INTERNALS(dh_prime_bits); return 0; } #define LEVEL_NONE "NONE" #define LEVEL_NORMAL "NORMAL" #define LEVEL_PFS "PFS" #define LEVEL_PERFORMANCE "PERFORMANCE" #define LEVEL_SECURE128 "SECURE128" #define LEVEL_SECURE192 "SECURE192" #define LEVEL_SECURE256 "SECURE256" #define LEVEL_SUITEB128 "SUITEB128" #define LEVEL_SUITEB192 "SUITEB192" #define LEVEL_LEGACY "LEGACY" struct priority_groups_st { const char *name; const char *alias; const int **proto_list; const int **cipher_list; const int **mac_list; const int **kx_list; const int **sign_list; const int **group_list; unsigned profile; int sec_param; bool no_tickets; }; static const struct priority_groups_st pgroups[] = { {.name = LEVEL_NORMAL, .cipher_list = &cipher_priority_normal, .mac_list = &mac_priority_normal, .kx_list = &kx_priority_secure, .sign_list = &sign_priority_default, .group_list = &supported_groups_normal, .profile = GNUTLS_PROFILE_LOW, .sec_param = GNUTLS_SEC_PARAM_WEAK }, {.name = LEVEL_PFS, .cipher_list = &cipher_priority_normal, .mac_list = &mac_priority_secure128, .kx_list = &kx_priority_pfs, .sign_list = &sign_priority_default, .group_list = &supported_groups_normal, .profile = GNUTLS_PROFILE_LOW, .sec_param = GNUTLS_SEC_PARAM_WEAK, .no_tickets = 1 }, {.name = LEVEL_SECURE128, .alias = "SECURE", .cipher_list = &cipher_priority_secure128, .mac_list = &mac_priority_secure128, .kx_list = &kx_priority_secure, .sign_list = &sign_priority_secure128, .group_list = &supported_groups_secure128, /* The profile should have been HIGH but if we don't allow * SHA-1 (80-bits) as signature algorithm we are not able * to connect anywhere with this level */ .profile = GNUTLS_PROFILE_LOW, .sec_param = GNUTLS_SEC_PARAM_LOW }, {.name = LEVEL_SECURE192, .alias = LEVEL_SECURE256, .cipher_list = &cipher_priority_secure192, .mac_list = &mac_priority_secure192, .kx_list = &kx_priority_secure, .sign_list = &sign_priority_secure192, .group_list = &supported_groups_secure192, .profile = GNUTLS_PROFILE_HIGH, .sec_param = GNUTLS_SEC_PARAM_HIGH }, {.name = LEVEL_SUITEB128, .proto_list = &protocol_priority_suiteb, .cipher_list = &cipher_priority_suiteb128, .mac_list = &mac_priority_suiteb, .kx_list = &kx_priority_suiteb, .sign_list = &sign_priority_suiteb128, .group_list = &supported_groups_suiteb128, .profile = GNUTLS_PROFILE_SUITEB128, .sec_param = GNUTLS_SEC_PARAM_HIGH }, {.name = LEVEL_SUITEB192, .proto_list = &protocol_priority_suiteb, .cipher_list = &cipher_priority_suiteb192, .mac_list = &mac_priority_suiteb, .kx_list = &kx_priority_suiteb, .sign_list = &sign_priority_suiteb192, .group_list = &supported_groups_suiteb192, .profile = GNUTLS_PROFILE_SUITEB192, .sec_param = GNUTLS_SEC_PARAM_ULTRA }, {.name = LEVEL_LEGACY, .cipher_list = &cipher_priority_normal, .mac_list = &mac_priority_normal, .kx_list = &kx_priority_secure, .sign_list = &sign_priority_default, .group_list = &supported_groups_normal, .sec_param = GNUTLS_SEC_PARAM_VERY_WEAK }, {.name = LEVEL_PERFORMANCE, .cipher_list = &cipher_priority_performance, .mac_list = &mac_priority_normal, .kx_list = &kx_priority_performance, .sign_list = &sign_priority_default, .group_list = &supported_groups_normal, .profile = GNUTLS_PROFILE_LOW, .sec_param = GNUTLS_SEC_PARAM_WEAK }, { .name = NULL, } }; #define SET_PROFILE(to_set) \ profile = GNUTLS_VFLAGS_TO_PROFILE(priority_cache->additional_verify_flags); \ if (profile == 0 || profile > to_set) { \ priority_cache->additional_verify_flags &= ~GNUTLS_VFLAGS_PROFILE_MASK; \ priority_cache->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(to_set); \ } #define SET_LEVEL(to_set) \ if (priority_cache->level == 0 || (unsigned)priority_cache->level > (unsigned)to_set) \ priority_cache->level = to_set static int check_level(const char *level, gnutls_priority_t priority_cache, int add) { bulk_rmadd_func *func; unsigned profile = 0; unsigned i; int j; const cipher_entry_st *centry; if (add) func = _add_priority; else func = _set_priority; for (i=0;;i++) { if (pgroups[i].name == NULL) return 0; if (c_strcasecmp(level, pgroups[i].name) == 0 || (pgroups[i].alias != NULL && c_strcasecmp(level, pgroups[i].alias) == 0)) { if (pgroups[i].proto_list != NULL) func(&priority_cache->protocol, *pgroups[i].proto_list); func(&priority_cache->_cipher, *pgroups[i].cipher_list); func(&priority_cache->_kx, *pgroups[i].kx_list); func(&priority_cache->_mac, *pgroups[i].mac_list); func(&priority_cache->_sign_algo, *pgroups[i].sign_list); func(&priority_cache->_supported_ecc, *pgroups[i].group_list); if (pgroups[i].profile != 0) { SET_PROFILE(pgroups[i].profile); /* set certificate level */ } SET_LEVEL(pgroups[i].sec_param); /* set DH params level */ priority_cache->no_tickets = pgroups[i].no_tickets; if (priority_cache->have_cbc == 0) { for (j=0;(*pgroups[i].cipher_list)[j]!=0;j++) { centry = cipher_to_entry((*pgroups[i].cipher_list)[j]); if (centry != NULL && centry->type == CIPHER_BLOCK) { priority_cache->have_cbc = 1; break; } } } return 1; } } } static void enable_compat(gnutls_priority_t c) { ENABLE_PRIO_COMPAT(c); } static void enable_server_key_usage_violations(gnutls_priority_t c) { c->allow_server_key_usage_violation = 1; } static void enable_allow_small_records(gnutls_priority_t c) { c->_allow_small_records = 1; } static void enable_dumbfw(gnutls_priority_t c) { c->_dumbfw = 1; } static void enable_no_extensions(gnutls_priority_t c) { c->no_extensions = 1; } static void enable_no_ext_master_secret(gnutls_priority_t c) { c->_no_ext_master_secret = 1; } static void enable_no_etm(gnutls_priority_t c) { c->_no_etm = 1; } static void enable_force_etm(gnutls_priority_t c) { c->force_etm = 1; } static void enable_no_tickets(gnutls_priority_t c) { c->no_tickets = 1; } static void disable_wildcards(gnutls_priority_t c) { c->additional_verify_flags |= GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS; } static void enable_profile_very_weak(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_VERY_WEAK); } static void enable_profile_low(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_LOW); } static void enable_profile_legacy(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_LEGACY); } static void enable_profile_medium(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_MEDIUM); } static void enable_profile_high(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_HIGH); } static void enable_profile_ultra(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_ULTRA); } static void enable_profile_future(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_FUTURE); } static void enable_profile_suiteb128(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_SUITEB128); } static void enable_profile_suiteb192(gnutls_priority_t c) { ENABLE_PROFILE(c, GNUTLS_PROFILE_SUITEB128); } static void enable_safe_renegotiation(gnutls_priority_t c) { c->sr = SR_SAFE; } static void enable_unsafe_renegotiation(gnutls_priority_t c) { c->sr = SR_UNSAFE; } static void enable_partial_safe_renegotiation(gnutls_priority_t c) { c->sr = SR_PARTIAL; } static void disable_safe_renegotiation(gnutls_priority_t c) { c->sr = SR_DISABLED; } static void enable_fallback_scsv(gnutls_priority_t c) { c->fallback = 1; } static void enable_latest_record_version(gnutls_priority_t c) { c->min_record_version = 0; } static void enable_ssl3_record_version(gnutls_priority_t c) { c->min_record_version = 1; } static void enable_verify_allow_rsa_md5(gnutls_priority_t c) { c->additional_verify_flags |= GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5; } static void enable_verify_allow_sha1(gnutls_priority_t c) { c->additional_verify_flags |= GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1; } static void enable_verify_allow_broken(gnutls_priority_t c) { c->additional_verify_flags |= GNUTLS_VERIFY_ALLOW_BROKEN; } static void disable_crl_checks(gnutls_priority_t c) { c->additional_verify_flags |= GNUTLS_VERIFY_DISABLE_CRL_CHECKS; } static void enable_server_precedence(gnutls_priority_t c) { c->server_precedence = 1; } static void dummy_func(gnutls_priority_t c) { } #include static gnutls_certificate_verification_profiles_t system_wide_verification_profile = GNUTLS_PROFILE_UNKNOWN; static name_val_array_t system_wide_priority_strings = NULL; static unsigned system_wide_priority_strings_init = 0; static unsigned system_wide_default_priority_string = 0; static unsigned fail_on_invalid_config = 0; static unsigned system_wide_disabled_ciphers[MAX_ALGOS+1] = {0}; static unsigned system_wide_disabled_macs[MAX_ALGOS+1] = {0}; static unsigned system_wide_disabled_groups[MAX_ALGOS+1] = {0}; static unsigned system_wide_disabled_kxs[MAX_ALGOS+1] = {0}; static const char *system_priority_file = SYSTEM_PRIORITY_FILE; static time_t system_priority_last_mod = 0; #define CUSTOM_PRIORITY_SECTION "priorities" #define OVERRIDES_SECTION "overrides" #define MAX_ALGO_NAME 2048 static void _clear_default_system_priority(void) { if (system_wide_default_priority_string) { gnutls_free(_gnutls_default_priority_string); _gnutls_default_priority_string = DEFAULT_PRIORITY_STRING; system_wide_default_priority_string = 0; } } gnutls_certificate_verification_profiles_t _gnutls_get_system_wide_verification_profile(void) { return system_wide_verification_profile; } /* removes spaces */ static char *clear_spaces(const char *str, char out[MAX_ALGO_NAME]) { const char *p = str; unsigned i = 0; while (c_isspace(*p)) p++; while (!c_isspace(*p) && *p != 0) { out[i++] = *p; p++; if (i >= MAX_ALGO_NAME-1) break; } out[i] = 0; return out; } /* This function parses a gnutls configuration file and updates internal * settings accordingly. */ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, const char *value) { char *p; int ret, type; unsigned i; char str[MAX_ALGO_NAME]; /* Note that we intentionally overwrite the value above; inih does * not use that value after we handle it. */ /* Parse sections */ if (section == NULL || section[0] == 0 || c_strcasecmp(section, CUSTOM_PRIORITY_SECTION)==0) { if (system_wide_priority_strings_init == 0) { _name_val_array_init(&system_wide_priority_strings); system_wide_priority_strings_init = 1; } _gnutls_debug_log("cfg: adding priority: %s -> %s\n", name, value); ret = _name_val_array_append(&system_wide_priority_strings, name, value); if (ret < 0) return 0; } else if (c_strcasecmp(section, OVERRIDES_SECTION)==0) { if (c_strcasecmp(name, "default-priority-string")==0) { _clear_default_system_priority(); p = clear_spaces(value, str); _gnutls_debug_log("cfg: setting default-priority-string to %s\n", p); if (strlen(p) > 0) { _gnutls_default_priority_string = gnutls_strdup(p); if (!_gnutls_default_priority_string) { _gnutls_default_priority_string = DEFAULT_PRIORITY_STRING; _gnutls_debug_log("cfg: failed setting default-priority-string\n"); return 0; } system_wide_default_priority_string = 1; } else { _gnutls_debug_log("cfg: empty default-priority-string, using default\n"); if (fail_on_invalid_config) return 0; } } else if (c_strcasecmp(name, "insecure-hash")==0) { p = clear_spaces(value, str); _gnutls_debug_log("cfg: marking hash %s as insecure\n", p); ret = _gnutls_digest_mark_insecure(p); if (ret < 0) { _gnutls_debug_log("cfg: found unknown hash %s in %s\n", p, name); if (fail_on_invalid_config) return 0; } } else if (c_strcasecmp(name, "insecure-sig")==0 || c_strcasecmp(name, "insecure-sig-for-cert")==0) { p = clear_spaces(value, str); if (c_strcasecmp(name, "insecure-sig")==0) { type = _INSECURE; _gnutls_debug_log("cfg: marking signature %s as insecure\n", p); } else { _gnutls_debug_log("cfg: marking signature %s as insecure for certs\n", p); type = _INSECURE_FOR_CERTS; } ret = _gnutls_sign_mark_insecure(p, type); if (ret < 0) { _gnutls_debug_log("cfg: found unknown signature algorithm %s in %s\n", p, name); if (fail_on_invalid_config) return 0; } } else if (c_strcasecmp(name, "disabled-version")==0) { p = clear_spaces(value, str); _gnutls_debug_log("cfg: disabling version %s\n", p); ret = _gnutls_version_mark_disabled(p); if (ret < 0) { _gnutls_debug_log("cfg: found unknown version %s in %s\n", p, name); if (fail_on_invalid_config) return 0; } } else if (c_strcasecmp(name, "disabled-curve")==0) { p = clear_spaces(value, str); _gnutls_debug_log("cfg: disabling curve %s\n", p); ret = _gnutls_ecc_curve_mark_disabled(p); if (ret < 0) { _gnutls_debug_log("cfg: found unknown curve %s in %s\n", p, name); if (fail_on_invalid_config) return 0; } } else if (c_strcasecmp(name, "min-verification-profile")==0) { gnutls_certificate_verification_profiles_t profile; profile = gnutls_certificate_verification_profile_get_id(value); if (profile == GNUTLS_PROFILE_UNKNOWN) { _gnutls_debug_log("cfg: found unknown profile %s in %s\n", value, name); if (fail_on_invalid_config) return 0; } system_wide_verification_profile = profile; } else if (c_strcasecmp(name, "tls-disabled-cipher")==0) { unsigned algo; p = clear_spaces(value, str); _gnutls_debug_log("cfg: disabling cipher %s for TLS\n", p); algo = gnutls_cipher_get_id(p); if (algo == 0) { _gnutls_debug_log("cfg: unknown algorithm %s listed at %s\n", p, name); if (fail_on_invalid_config) return 0; } i = 0; while (system_wide_disabled_ciphers[i] != 0) i++; if (i > MAX_ALGOS-1) { _gnutls_debug_log("cfg: too many (%d) disabled ciphers from %s\n", i, name); if (fail_on_invalid_config) return 0; goto exit; } system_wide_disabled_ciphers[i] = algo; system_wide_disabled_ciphers[i+1] = 0; } else if (c_strcasecmp(name, "tls-disabled-mac")==0) { unsigned algo; p = clear_spaces(value, str); _gnutls_debug_log("cfg: disabling MAC %s for TLS\n", p); algo = gnutls_mac_get_id(p); if (algo == 0) { _gnutls_debug_log("cfg: unknown algorithm %s listed at %s\n", p, name); if (fail_on_invalid_config) return 0; goto exit; } i = 0; while (system_wide_disabled_macs[i] != 0) i++; if (i > MAX_ALGOS-1) { _gnutls_debug_log("cfg: too many (%d) disabled MACs from %s\n", i, name); if (fail_on_invalid_config) return 0; goto exit; } system_wide_disabled_macs[i] = algo; system_wide_disabled_macs[i+1] = 0; } else if (c_strcasecmp(name, "tls-disabled-group")==0) { unsigned algo; p = clear_spaces(value, str); if (strlen(p) > 6) p += 6; // skip GROUP- _gnutls_debug_log("cfg: disabling group %s for TLS\n", p); algo = gnutls_group_get_id(p); if (algo == 0) { _gnutls_debug_log("cfg: unknown group %s listed at %s\n", p, name); if (fail_on_invalid_config) return 0; goto exit; } i = 0; while (system_wide_disabled_groups[i] != 0) i++; if (i > MAX_ALGOS-1) { _gnutls_debug_log("cfg: too many (%d) disabled groups from %s\n", i, name); if (fail_on_invalid_config) return 0; goto exit; } system_wide_disabled_groups[i] = algo; system_wide_disabled_groups[i+1] = 0; } else if (c_strcasecmp(name, "tls-disabled-kx")==0) { unsigned algo; p = clear_spaces(value, str); _gnutls_debug_log("cfg: disabling key exchange %s for TLS\n", p); algo = gnutls_kx_get_id(p); if (algo == 0) { _gnutls_debug_log("cfg: unknown key exchange %s listed at %s\n", p, name); if (fail_on_invalid_config) return 0; goto exit; } i = 0; while (system_wide_disabled_kxs[i] != 0) i++; if (i > MAX_ALGOS-1) { _gnutls_debug_log("cfg: too many (%d) disabled key exchanges from %s\n", i, name); if (fail_on_invalid_config) return 0; goto exit; } system_wide_disabled_kxs[i] = algo; system_wide_disabled_kxs[i+1] = 0; } else { _gnutls_debug_log("unknown parameter %s\n", name); if (fail_on_invalid_config) return 0; } } else { _gnutls_debug_log("cfg: unknown section %s\n", section); if (fail_on_invalid_config) return 0; } exit: return 1; } static void _gnutls_update_system_priorities(void) { int ret; struct stat sb; FILE *fp; if (stat(system_priority_file, &sb) < 0) { _gnutls_debug_log("cfg: unable to access: %s: %d\n", system_priority_file, errno); return; } if (system_wide_priority_strings_init != 0 && sb.st_mtime == system_priority_last_mod) { _gnutls_debug_log("cfg: system priority %s has not changed\n", system_priority_file); return; } if (system_wide_priority_strings_init != 0) _name_val_array_clear(&system_wide_priority_strings); fp = fopen(system_priority_file, "re"); if (fp == NULL) { _gnutls_debug_log("cfg: unable to open: %s: %d\n", system_priority_file, errno); return; } ret = ini_parse_file(fp, cfg_ini_handler, NULL); fclose(fp); if (ret != 0) { _gnutls_debug_log("cfg: unable to parse: %s: %d\n", system_priority_file, ret); if (fail_on_invalid_config) exit(1); return; } _gnutls_debug_log("cfg: loaded system priority %s mtime %lld\n", system_priority_file, (unsigned long long)sb.st_mtime); system_priority_last_mod = sb.st_mtime; } void _gnutls_load_system_priorities(void) { const char *p; p = secure_getenv("GNUTLS_SYSTEM_PRIORITY_FILE"); if (p != NULL) system_priority_file = p; p = secure_getenv("GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID"); if (p != NULL && p[0] == '1' && p[1] == 0) fail_on_invalid_config = 1; _gnutls_update_system_priorities(); } void _gnutls_unload_system_priorities(void) { _name_val_array_clear(&system_wide_priority_strings); _clear_default_system_priority(); system_priority_last_mod = 0; } /** * gnutls_get_system_config_file: * * Returns the filename of the system wide configuration * file loaded by the library. The returned pointer is valid * until the library is unloaded. * * Returns: a constant pointer to the config file loaded, or %NULL if none * * Since: 3.6.9 **/ const char *gnutls_get_system_config_file(void) { if (system_wide_priority_strings_init) return system_priority_file; else return NULL; } #define S(str) ((str!=NULL)?str:"") /* Returns the new priorities if a priority string prefixed * with '@' is provided, or just a copy of the provided * priorities, appended with any additional present in * the priorities string. * * The returned string must be released using gnutls_free(). */ char *_gnutls_resolve_priorities(const char* priorities) { const char *p = priorities; char *additional = NULL; char *ret = NULL; const char *ss, *ss_next; unsigned ss_len, ss_next_len; size_t n, n2 = 0; while (c_isspace(*p)) p++; if (*p == '@') { ss = p+1; additional = strchr(ss, ':'); if (additional != NULL) { additional++; } do { ss_next = strchr(ss, ','); if (ss_next != NULL) { if (additional && ss_next > additional) ss_next = NULL; else ss_next++; } if (ss_next) { ss_len = ss_next - ss - 1; ss_next_len = additional - ss_next - 1; } else if (additional) { ss_len = additional - ss - 1; ss_next_len = 0; } else { ss_len = strlen(ss); ss_next_len = 0; } /* Always try to refresh the cached data, to * allow it to be updated without restarting * all applications */ _gnutls_update_system_priorities(); p = _name_val_array_value(system_wide_priority_strings, ss, ss_len); _gnutls_debug_log("resolved '%.*s' to '%s', next '%.*s'\n", ss_len, ss, S(p), ss_next_len, S(ss_next)); ss = ss_next; } while (ss && p == NULL); if (p == NULL) { _gnutls_debug_log("unable to resolve %s\n", priorities); ret = NULL; goto finish; } n = strlen(p); if (additional) n2 = strlen(additional); ret = gnutls_malloc(n+n2+1+1); if (ret == NULL) { goto finish; } memcpy(ret, p, n); if (additional != NULL) { ret[n] = ':'; memcpy(&ret[n+1], additional, n2); ret[n+n2+1] = 0; } else { ret[n] = 0; } } else { return gnutls_strdup(p); } finish: if (ret != NULL) { _gnutls_debug_log("selected priority string: %s\n", ret); } return ret; } static void add_ec(gnutls_priority_t priority_cache) { const gnutls_group_entry_st *ge; unsigned i; for (i = 0; i < priority_cache->_supported_ecc.num_priorities; i++) { ge = _gnutls_id_to_group(priority_cache->_supported_ecc.priorities[i]); if (ge != NULL && priority_cache->groups.size < sizeof(priority_cache->groups.entry)/sizeof(priority_cache->groups.entry[0])) { /* do not add groups which do not correspond to enabled ciphersuites */ if (!ge->curve) continue; priority_cache->groups.entry[priority_cache->groups.size++] = ge; } } } static void add_dh(gnutls_priority_t priority_cache) { const gnutls_group_entry_st *ge; unsigned i; for (i = 0; i < priority_cache->_supported_ecc.num_priorities; i++) { ge = _gnutls_id_to_group(priority_cache->_supported_ecc.priorities[i]); if (ge != NULL && priority_cache->groups.size < sizeof(priority_cache->groups.entry)/sizeof(priority_cache->groups.entry[0])) { /* do not add groups which do not correspond to enabled ciphersuites */ if (!ge->prime) continue; priority_cache->groups.entry[priority_cache->groups.size++] = ge; priority_cache->groups.have_ffdhe = 1; } } } /* This function was originally precalculating ciphersuite-specific items, however * it has now extended to much more than that. It provides a consistency check to * set parameters, and in cases it applies policy specific items. */ static int set_ciphersuite_list(gnutls_priority_t priority_cache) { unsigned i, j, z; const gnutls_cipher_suite_entry_st *ce; const gnutls_sign_entry_st *se; unsigned have_ec = 0; unsigned have_dh = 0; unsigned tls_sig_sem = 0; const version_entry_st *tlsmax = NULL, *vers; const version_entry_st *dtlsmax = NULL; const version_entry_st *tlsmin = NULL; const version_entry_st *dtlsmin = NULL; unsigned have_tls13 = 0, have_srp = 0; unsigned have_pre_tls12 = 0, have_tls12 = 0; unsigned have_psk = 0, have_null = 0, have_rsa_psk = 0; /* have_psk indicates that a PSK key exchange compatible * with TLS1.3 is enabled. */ priority_cache->cs.size = 0; priority_cache->sigalg.size = 0; priority_cache->groups.size = 0; priority_cache->groups.have_ffdhe = 0; /* disable key exchanges which are globally disabled */ z = 0; while (system_wide_disabled_kxs[z] != 0) { for (i = j = 0; i < priority_cache->_kx.num_priorities; i++) { if (priority_cache->_kx.priorities[i] != system_wide_disabled_kxs[z]) priority_cache->_kx.priorities[j++] = priority_cache->_kx.priorities[i]; } priority_cache->_kx.num_priorities = j; z++; } /* disable groups which are globally disabled */ z = 0; while (system_wide_disabled_groups[z] != 0) { for (i = j = 0; i < priority_cache->_supported_ecc.num_priorities; i++) { if (priority_cache->_supported_ecc.priorities[i] != system_wide_disabled_groups[z]) priority_cache->_supported_ecc.priorities[j++] = priority_cache->_supported_ecc.priorities[i]; } priority_cache->_supported_ecc.num_priorities = j; z++; } /* disable ciphers which are globally disabled */ z = 0; while (system_wide_disabled_ciphers[z] != 0) { for (i = j = 0; i < priority_cache->_cipher.num_priorities; i++) { if (priority_cache->_cipher.priorities[i] != system_wide_disabled_ciphers[z]) priority_cache->_cipher.priorities[j++] = priority_cache->_cipher.priorities[i]; } priority_cache->_cipher.num_priorities = j; z++; } /* disable MACs which are globally disabled */ z = 0; while (system_wide_disabled_macs[z] != 0) { for (i = j = 0; i < priority_cache->_mac.num_priorities; i++) { if (priority_cache->_mac.priorities[i] != system_wide_disabled_macs[z]) priority_cache->_mac.priorities[j++] = priority_cache->_mac.priorities[i]; } priority_cache->_mac.num_priorities = j; z++; } for (j=0;j_cipher.num_priorities;j++) { if (priority_cache->_cipher.priorities[j] == GNUTLS_CIPHER_NULL) { have_null = 1; break; } } for (i = 0; i < priority_cache->_kx.num_priorities; i++) { if (IS_SRP_KX(priority_cache->_kx.priorities[i])) { have_srp = 1; } else if (_gnutls_kx_is_psk(priority_cache->_kx.priorities[i])) { if (priority_cache->_kx.priorities[i] == GNUTLS_KX_RSA_PSK) have_rsa_psk = 1; else have_psk = 1; } } /* disable TLS versions which are added but are unsupported */ for (i = j = 0; i < priority_cache->protocol.num_priorities; i++) { vers = version_to_entry(priority_cache->protocol.priorities[i]); if (!vers || vers->supported) priority_cache->protocol.priorities[j++] = priority_cache->protocol.priorities[i]; } priority_cache->protocol.num_priorities = j; /* if we have NULL ciphersuites, SRP, or RSA-PSK enabled remove TLS1.3+ * protocol versions; they cannot be negotiated under TLS1.3. */ if (have_null || have_srp || have_rsa_psk || priority_cache->no_extensions) { for (i = j = 0; i < priority_cache->protocol.num_priorities; i++) { vers = version_to_entry(priority_cache->protocol.priorities[i]); if (!vers || !vers->tls13_sem) priority_cache->protocol.priorities[j++] = priority_cache->protocol.priorities[i]; } priority_cache->protocol.num_priorities = j; } for (i = 0; i < priority_cache->protocol.num_priorities; i++) { vers = version_to_entry(priority_cache->protocol.priorities[i]); if (!vers) continue; if (vers->transport == GNUTLS_STREAM) { /* TLS */ tls_sig_sem |= vers->tls_sig_sem; if (vers->tls13_sem) have_tls13 = 1; if (vers->id == GNUTLS_TLS1_2) have_tls12 = 1; else if (vers->id < GNUTLS_TLS1_2) have_pre_tls12 = 1; if (tlsmax == NULL || vers->age > tlsmax->age) tlsmax = vers; if (tlsmin == NULL || vers->age < tlsmin->age) tlsmin = vers; } else { /* dtls */ tls_sig_sem |= vers->tls_sig_sem; /* we need to introduce similar handling to above * when DTLS1.3 is supported */ if (dtlsmax == NULL || vers->age > dtlsmax->age) dtlsmax = vers; if (dtlsmin == NULL || vers->age < dtlsmin->age) dtlsmin = vers; } } /* DTLS or TLS protocols must be present */ if ((!tlsmax || !tlsmin) && (!dtlsmax || !dtlsmin)) return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET); priority_cache->have_psk = have_psk; /* if we are have TLS1.3+ do not enable any key exchange algorithms, * the protocol doesn't require any. */ if (tlsmin && tlsmin->tls13_sem && !have_psk) { if (!dtlsmin || (dtlsmin && dtlsmin->tls13_sem)) priority_cache->_kx.num_priorities = 0; } /* Add TLS 1.3 ciphersuites (no KX) */ for (j=0;j_cipher.num_priorities;j++) { for (z=0;z_mac.num_priorities;z++) { ce = cipher_suite_get( 0, priority_cache->_cipher.priorities[j], priority_cache->_mac.priorities[z]); if (ce != NULL && priority_cache->cs.size < MAX_CIPHERSUITE_SIZE) { priority_cache->cs.entry[priority_cache->cs.size++] = ce; } } } for (i = 0; i < priority_cache->_kx.num_priorities; i++) { for (j=0;j_cipher.num_priorities;j++) { for (z=0;z_mac.num_priorities;z++) { ce = cipher_suite_get( priority_cache->_kx.priorities[i], priority_cache->_cipher.priorities[j], priority_cache->_mac.priorities[z]); if (ce != NULL && priority_cache->cs.size < MAX_CIPHERSUITE_SIZE) { priority_cache->cs.entry[priority_cache->cs.size++] = ce; if (!have_ec && (_gnutls_kx_is_ecc(ce->kx_algorithm) || _gnutls_kx_is_vko_gost(ce->kx_algorithm))) { have_ec = 1; add_ec(priority_cache); } if (!have_dh && _gnutls_kx_is_dhe(ce->kx_algorithm)) { have_dh = 1; add_dh(priority_cache); } } } } } if (have_tls13 && (!have_ec || !have_dh)) { /* scan groups to determine have_ec and have_dh */ for (i=0; i < priority_cache->_supported_ecc.num_priorities; i++) { const gnutls_group_entry_st *ge; ge = _gnutls_id_to_group(priority_cache->_supported_ecc.priorities[i]); if (ge) { if (ge->curve && !have_ec) { add_ec(priority_cache); have_ec = 1; } else if (ge->prime && !have_dh) { add_dh(priority_cache); have_dh = 1; } if (have_dh && have_ec) break; } } } for (i = 0; i < priority_cache->_sign_algo.num_priorities; i++) { se = _gnutls_sign_to_entry(priority_cache->_sign_algo.priorities[i]); if (se != NULL && priority_cache->sigalg.size < sizeof(priority_cache->sigalg.entry)/sizeof(priority_cache->sigalg.entry[0])) { /* if the signature algorithm semantics are not compatible with * the protocol's, then skip. */ if ((se->aid.tls_sem & tls_sig_sem) == 0) continue; priority_cache->sigalg.entry[priority_cache->sigalg.size++] = se; } } _gnutls_debug_log("added %d protocols, %d ciphersuites, %d sig algos and %d groups into priority list\n", priority_cache->protocol.num_priorities, priority_cache->cs.size, priority_cache->sigalg.size, priority_cache->groups.size); if (priority_cache->sigalg.size == 0) { /* no signature algorithms; eliminate TLS 1.2 or DTLS 1.2 and later */ priority_st newp; newp.num_priorities = 0; /* we need to eliminate TLS 1.2 or DTLS 1.2 and later protocols */ for (i = 0; i < priority_cache->protocol.num_priorities; i++) { if (priority_cache->protocol.priorities[i] < GNUTLS_TLS1_2) { newp.priorities[newp.num_priorities++] = priority_cache->protocol.priorities[i]; } else if (priority_cache->protocol.priorities[i] >= GNUTLS_DTLS_VERSION_MIN && priority_cache->protocol.priorities[i] < GNUTLS_DTLS1_2) { newp.priorities[newp.num_priorities++] = priority_cache->protocol.priorities[i]; } } memcpy(&priority_cache->protocol, &newp, sizeof(newp)); } if (unlikely(priority_cache->protocol.num_priorities == 0)) return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET); #ifndef ENABLE_SSL3 else if (unlikely(priority_cache->protocol.num_priorities == 1 && priority_cache->protocol.priorities[0] == GNUTLS_SSL3)) return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET); #endif if (unlikely(priority_cache->cs.size == 0)) return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET); /* when TLS 1.3 is available we must have groups set; additionally * we require TLS1.2 to be enabled if TLS1.3 is asked for, and * a pre-TLS1.2 protocol is there; that is because servers which * do not support TLS1.3 will negotiate TLS1.2 if seen a TLS1.3 handshake */ if (unlikely((!have_psk && tlsmax && tlsmax->id >= GNUTLS_TLS1_3 && priority_cache->groups.size == 0)) || (!have_tls12 && have_pre_tls12 && have_tls13)) { for (i = j = 0; i < priority_cache->protocol.num_priorities; i++) { vers = version_to_entry(priority_cache->protocol.priorities[i]); if (!vers || vers->transport != GNUTLS_STREAM || !vers->tls13_sem) priority_cache->protocol.priorities[j++] = priority_cache->protocol.priorities[i]; } priority_cache->protocol.num_priorities = j; } /* ensure that the verification profile is not lower from the configured */ if (system_wide_verification_profile) { gnutls_sec_param_t level = priority_cache->level; gnutls_sec_param_t system_wide_level = _gnutls_profile_to_sec_level(system_wide_verification_profile); if (level < system_wide_level) { ENABLE_PROFILE(priority_cache, system_wide_verification_profile); } } return 0; } /** * gnutls_priority_init2: * @priority_cache: is a #gnutls_prioritity_t type. * @priorities: is a string describing priorities (may be %NULL) * @err_pos: In case of an error this will have the position in the string the error occurred * @flags: zero or %GNUTLS_PRIORITY_INIT_DEF_APPEND * * Sets priorities for the ciphers, key exchange methods, and macs. * The @priority_cache should be deinitialized * using gnutls_priority_deinit(). * * The #priorities option allows you to specify a colon * separated list of the cipher priorities to enable. * Some keywords are defined to provide quick access * to common preferences. * * When @flags is set to %GNUTLS_PRIORITY_INIT_DEF_APPEND then the @priorities * specified will be appended to the default options. * * Unless there is a special need, use the "NORMAL" keyword to * apply a reasonable security level, or "NORMAL:%%COMPAT" for compatibility. * * "PERFORMANCE" means all the "secure" ciphersuites are enabled, * limited to 128 bit ciphers and sorted by terms of speed * performance. * * "LEGACY" the NORMAL settings for GnuTLS 3.2.x or earlier. There is * no verification profile set, and the allowed DH primes are considered * weak today. * * "NORMAL" means all "secure" ciphersuites. The 256-bit ciphers are * included as a fallback only. The ciphers are sorted by security * margin. * * "PFS" means all "secure" ciphersuites that support perfect forward secrecy. * The 256-bit ciphers are included as a fallback only. * The ciphers are sorted by security margin. * * "SECURE128" means all "secure" ciphersuites of security level 128-bit * or more. * * "SECURE192" means all "secure" ciphersuites of security level 192-bit * or more. * * "SUITEB128" means all the NSA SuiteB ciphersuites with security level * of 128. * * "SUITEB192" means all the NSA SuiteB ciphersuites with security level * of 192. * * "NONE" means nothing is enabled. This disables everything, including protocols. * * "@@KEYWORD1,KEYWORD2,..." The system administrator imposed settings. * The provided keyword(s) will be expanded from a configuration-time * provided file - default is: /etc/gnutls/config. * Any attributes that follow it, will be appended to the expanded * string. If multiple keywords are provided, separated by commas, * then the first keyword that exists in the configuration file * will be used. At least one of the keywords must exist, or this * function will return an error. Typical usage would be to specify * an application specified keyword first, followed by "SYSTEM" as * a default fallback. e.g., "@LIBVIRT,SYSTEM:!-VERS-SSL3.0" will * first try to find a config file entry matching "LIBVIRT", but if * that does not exist will use the entry for "SYSTEM". If "SYSTEM" * does not exist either, an error will be returned. In all cases, * the SSL3.0 protocol will be disabled. The system priority file * entries should be formatted as "KEYWORD=VALUE", e.g., * "SYSTEM=NORMAL:+ARCFOUR-128". * * Special keywords are "!", "-" and "+". * "!" or "-" appended with an algorithm will remove this algorithm. * "+" appended with an algorithm will add this algorithm. * * Check the GnuTLS manual section "Priority strings" for detailed * information. * * Examples: * * "NONE:+VERS-TLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL" * * "NORMAL:+ARCFOUR-128" means normal ciphers plus ARCFOUR-128. * * "SECURE128:-VERS-SSL3.0" means that only secure ciphers are * and enabled, SSL3.0 is disabled. * * "NONE:+VERS-TLS-ALL:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1", * * "NONE:+VERS-TLS-ALL:+AES-128-CBC:+ECDHE-RSA:+SHA1:+COMP-NULL:+SIGN-RSA-SHA1:+CURVE-SECP256R1", * * "SECURE256:+SECURE128", * * Note that "NORMAL:%%COMPAT" is the most compatible mode. * * A %NULL @priorities string indicates the default priorities to be * used (this is available since GnuTLS 3.3.0). * * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned, * %GNUTLS_E_SUCCESS on success, or an error code. * * Since: 3.6.3 **/ int gnutls_priority_init2(gnutls_priority_t * priority_cache, const char *priorities, const char **err_pos, unsigned flags) { gnutls_buffer_st buf; const char *ep; int ret; if (flags & GNUTLS_PRIORITY_INIT_DEF_APPEND) { if (priorities == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); if (err_pos) *err_pos = priorities; _gnutls_buffer_init(&buf); ret = _gnutls_buffer_append_str(&buf, _gnutls_default_priority_string); if (ret < 0) { _gnutls_buffer_clear(&buf); return gnutls_assert_val(ret); } ret = _gnutls_buffer_append_str(&buf, ":"); if (ret < 0) { _gnutls_buffer_clear(&buf); return gnutls_assert_val(ret); } ret = _gnutls_buffer_append_str(&buf, priorities); if (ret < 0) { _gnutls_buffer_clear(&buf); return gnutls_assert_val(ret); } ret = gnutls_priority_init(priority_cache, (const char*)buf.data, &ep); if (ret < 0 && ep != (const char*)buf.data && ep != NULL) { ptrdiff_t diff = (ptrdiff_t)ep-(ptrdiff_t)buf.data; unsigned hlen = strlen(_gnutls_default_priority_string)+1; if (err_pos && diff > hlen) { *err_pos = priorities + diff - hlen; } } _gnutls_buffer_clear(&buf); return ret; } else { return gnutls_priority_init(priority_cache, priorities, err_pos); } } #define PRIO_MATCH(name) c_strncasecmp(&broken_list[i][1], name, sizeof(name) - 1) /** * gnutls_priority_init: * @priority_cache: is a #gnutls_prioritity_t type. * @priorities: is a string describing priorities (may be %NULL) * @err_pos: In case of an error this will have the position in the string the error occurred * * For applications that do not modify their crypto settings per release, consider * using gnutls_priority_init2() with %GNUTLS_PRIORITY_INIT_DEF_APPEND flag * instead. We suggest to use centralized crypto settings handled by the GnuTLS * library, and applications modifying the default settings to their needs. * * This function is identical to gnutls_priority_init2() with zero * flags. * * A %NULL @priorities string indicates the default priorities to be * used (this is available since GnuTLS 3.3.0). * * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned, * %GNUTLS_E_SUCCESS on success, or an error code. **/ int gnutls_priority_init(gnutls_priority_t * priority_cache, const char *priorities, const char **err_pos) { char *broken_list[MAX_ELEMENTS]; int broken_list_size = 0, i = 0, j; char *darg = NULL; unsigned ikeyword_set = 0; int algo; int ret; rmadd_func *fn; bulk_rmadd_func *bulk_fn; bulk_rmadd_func *bulk_given_fn; const cipher_entry_st *centry; unsigned resolved_match = 1; if (err_pos) *err_pos = priorities; *priority_cache = gnutls_calloc(1, sizeof(struct gnutls_priority_st)); if (*priority_cache == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } /* for now unsafe renegotiation is default on everyone. To be removed * when we make it the default. */ (*priority_cache)->sr = SR_PARTIAL; (*priority_cache)->min_record_version = 1; gnutls_atomic_init(&(*priority_cache)->usage_cnt); if (priorities == NULL) { priorities = _gnutls_default_priority_string; resolved_match = 0; } darg = _gnutls_resolve_priorities(priorities); if (darg == NULL) { gnutls_assert(); goto error; } if (strcmp(darg, priorities) != 0) resolved_match = 0; break_list(darg, broken_list, &broken_list_size); /* This is our default set of protocol version, certificate types. */ if (c_strcasecmp(broken_list[0], LEVEL_NONE) != 0) { _set_priority(&(*priority_cache)->protocol, protocol_priority); _set_priority(&(*priority_cache)->client_ctype, cert_type_priority_default); _set_priority(&(*priority_cache)->server_ctype, cert_type_priority_default); _set_priority(&(*priority_cache)->_sign_algo, sign_priority_default); _set_priority(&(*priority_cache)->_supported_ecc, supported_groups_normal); i = 0; } else { ikeyword_set = 1; i = 1; } for (; i < broken_list_size; i++) { if (check_level(broken_list[i], *priority_cache, ikeyword_set) != 0) { ikeyword_set = 1; continue; } else if (broken_list[i][0] == '!' || broken_list[i][0] == '+' || broken_list[i][0] == '-') { if (broken_list[i][0] == '+') { fn = prio_add; bulk_fn = _add_priority; bulk_given_fn = _add_priority; } else { fn = prio_remove; bulk_fn = _clear_priorities; bulk_given_fn = _clear_given_priorities; } if (broken_list[i][0] == '+' && check_level(&broken_list[i][1], *priority_cache, 1) != 0) { continue; } else if ((algo = gnutls_mac_get_id(&broken_list[i][1])) != GNUTLS_MAC_UNKNOWN) { fn(&(*priority_cache)->_mac, algo); } else if ((centry = cipher_name_to_entry(&broken_list[i][1])) != NULL) { if (_gnutls_cipher_exists(centry->id)) { fn(&(*priority_cache)->_cipher, centry->id); if (centry->type == CIPHER_BLOCK) (*priority_cache)->have_cbc = 1; } } else if ((algo = _gnutls_kx_get_id(&broken_list[i][1])) != GNUTLS_KX_UNKNOWN) { if (algo != GNUTLS_KX_INVALID) fn(&(*priority_cache)->_kx, algo); } else if (PRIO_MATCH("VERS-") == 0) { if (PRIO_MATCH("VERS-TLS-ALL") == 0) { bulk_given_fn(&(*priority_cache)-> protocol, stream_protocol_priority); } else if (PRIO_MATCH("VERS-DTLS-ALL") == 0) { bulk_given_fn(&(*priority_cache)-> protocol, (bulk_given_fn==_add_priority)?dtls_protocol_priority:dgram_protocol_priority); } else if (PRIO_MATCH("VERS-ALL") == 0) { bulk_fn(&(*priority_cache)-> protocol, protocol_priority); } else { if ((algo = gnutls_protocol_get_id (&broken_list[i][6])) != GNUTLS_VERSION_UNKNOWN) { fn(&(*priority_cache)-> protocol, algo); } else goto error; } } /* now check if the element is something like -ALGO */ else if (PRIO_MATCH("COMP-") == 0) { /* ignore all compression methods */ continue; } /* now check if the element is something like -ALGO */ else if (PRIO_MATCH("CURVE-") == 0) { if (PRIO_MATCH("CURVE-ALL") == 0) { bulk_fn(&(*priority_cache)-> _supported_ecc, supported_groups_normal); } else { if ((algo = gnutls_ecc_curve_get_id (&broken_list[i][7])) != GNUTLS_ECC_CURVE_INVALID) fn(&(*priority_cache)-> _supported_ecc, algo); else goto error; } } else if (PRIO_MATCH("GROUP-") == 0) { if (PRIO_MATCH("GROUP-ALL") == 0) { bulk_fn(&(*priority_cache)-> _supported_ecc, supported_groups_normal); } else if (PRIO_MATCH("GROUP-DH-ALL") == 0) { bulk_given_fn(&(*priority_cache)-> _supported_ecc, _supported_groups_dh); } else if (PRIO_MATCH("GROUP-EC-ALL") == 0) { bulk_given_fn(&(*priority_cache)-> _supported_ecc, _supported_groups_ecdh); } else if (PRIO_MATCH("GROUP-GOST-ALL") == 0) { bulk_given_fn(&(*priority_cache)-> _supported_ecc, _supported_groups_gost); } else { if ((algo = gnutls_group_get_id (&broken_list[i][7])) != GNUTLS_GROUP_INVALID) fn(&(*priority_cache)-> _supported_ecc, algo); else goto error; } } else if (PRIO_MATCH("CTYPE-") == 0) { // Certificate types if (PRIO_MATCH("CTYPE-ALL") == 0) { // Symmetric cert types, all types allowed bulk_fn(&(*priority_cache)->client_ctype, cert_type_priority_all); bulk_fn(&(*priority_cache)->server_ctype, cert_type_priority_all); } else if (PRIO_MATCH("CTYPE-CLI-") == 0) { // Client certificate types if (PRIO_MATCH("CTYPE-CLI-ALL") == 0) { // All client cert types allowed bulk_fn(&(*priority_cache)->client_ctype, cert_type_priority_all); } else if ((algo = gnutls_certificate_type_get_id (&broken_list[i][11])) != GNUTLS_CRT_UNKNOWN) { // Specific client cert type allowed fn(&(*priority_cache)->client_ctype, algo); } else goto error; } else if (PRIO_MATCH("CTYPE-SRV-") == 0) { // Server certificate types if (PRIO_MATCH("CTYPE-SRV-ALL") == 0) { // All server cert types allowed bulk_fn(&(*priority_cache)->server_ctype, cert_type_priority_all); } else if ((algo = gnutls_certificate_type_get_id (&broken_list[i][11])) != GNUTLS_CRT_UNKNOWN) { // Specific server cert type allowed fn(&(*priority_cache)->server_ctype, algo); } else goto error; } else { // Symmetric certificate type if ((algo = gnutls_certificate_type_get_id (&broken_list[i][7])) != GNUTLS_CRT_UNKNOWN) { fn(&(*priority_cache)->client_ctype, algo); fn(&(*priority_cache)->server_ctype, algo); } else if (PRIO_MATCH("CTYPE-OPENPGP") == 0) { /* legacy openpgp option - ignore */ continue; } else goto error; } } else if (PRIO_MATCH("SIGN-") == 0) { if (PRIO_MATCH("SIGN-ALL") == 0) { bulk_fn(&(*priority_cache)-> _sign_algo, sign_priority_default); } else if (PRIO_MATCH("SIGN-GOST-ALL") == 0) { bulk_fn(&(*priority_cache)-> _sign_algo, sign_priority_gost); } else { if ((algo = gnutls_sign_get_id (&broken_list[i][6])) != GNUTLS_SIGN_UNKNOWN) fn(&(*priority_cache)-> _sign_algo, algo); else goto error; } } else if (PRIO_MATCH("MAC-") == 0) { if (PRIO_MATCH("MAC-ALL") == 0) { bulk_fn(&(*priority_cache)->_mac, mac_priority_normal); } else if (PRIO_MATCH("MAC-GOST-ALL") == 0) { bulk_fn(&(*priority_cache)->_mac, mac_priority_gost); } } else if (PRIO_MATCH("CIPHER-") == 0) { if (PRIO_MATCH("CIPHER-ALL") == 0) { bulk_fn(&(*priority_cache)->_cipher, cipher_priority_normal); } else if (PRIO_MATCH("CIPHER-GOST-ALL") == 0) { bulk_fn(&(*priority_cache)->_cipher, cipher_priority_gost); } } else if (PRIO_MATCH("KX-") == 0) { if (PRIO_MATCH("KX-ALL") == 0) { bulk_fn(&(*priority_cache)->_kx, kx_priority_secure); } else if (PRIO_MATCH("KX-GOST-ALL") == 0) { bulk_fn(&(*priority_cache)->_kx, kx_priority_gost); } } else if (PRIO_MATCH("GOST") == 0) { bulk_given_fn(&(*priority_cache)->_supported_ecc, _supported_groups_gost); bulk_fn(&(*priority_cache)->_sign_algo, sign_priority_gost); bulk_fn(&(*priority_cache)->_mac, mac_priority_gost); bulk_fn(&(*priority_cache)->_cipher, cipher_priority_gost); bulk_fn(&(*priority_cache)->_kx, kx_priority_gost); } else goto error; } else if (broken_list[i][0] == '%') { const struct priority_options_st * o; /* to add a new option modify * priority_options.gperf */ o = in_word_set(&broken_list[i][1], strlen(&broken_list[i][1])); if (o == NULL) { goto error; } o->func(*priority_cache); } else goto error; } ret = set_ciphersuite_list(*priority_cache); if (ret < 0) { if (err_pos) *err_pos = priorities; goto error_cleanup; } gnutls_free(darg); return 0; error: if (err_pos != NULL && i < broken_list_size && resolved_match) { *err_pos = priorities; for (j = 0; j < i; j++) { (*err_pos) += strlen(broken_list[j]) + 1; } } ret = GNUTLS_E_INVALID_REQUEST; error_cleanup: free(darg); gnutls_priority_deinit(*priority_cache); *priority_cache = NULL; return ret; } /** * gnutls_priority_deinit: * @priority_cache: is a #gnutls_prioritity_t type. * * Deinitializes the priority cache. **/ void gnutls_priority_deinit(gnutls_priority_t priority_cache) { if (priority_cache == NULL) return; /* Note that here we care about the following two cases: * 1. Multiple sessions or different threads holding a reference + a global reference * 2. One session holding a reference with a possible global reference * * As such, it will never be that two threads reach the * zero state at the same time, unless the global reference * is cleared too, which is invalid state. */ if (gnutls_atomic_val(&priority_cache->usage_cnt) == 0) { gnutls_atomic_deinit(&priority_cache->usage_cnt); gnutls_free(priority_cache); return; } else { gnutls_atomic_decrement(&priority_cache->usage_cnt); } } /** * gnutls_priority_set_direct: * @session: is a #gnutls_session_t type. * @priorities: is a string describing priorities * @err_pos: In case of an error this will have the position in the string the error occurred * * Sets the priorities to use on the ciphers, key exchange methods, * and macs. This function avoids keeping a * priority cache and is used to directly set string priorities to a * TLS session. For documentation check the gnutls_priority_init(). * * To use a reasonable default, consider using gnutls_set_default_priority(), * or gnutls_set_default_priority_append() instead of this function. * * Returns: On syntax error %GNUTLS_E_INVALID_REQUEST is returned, * %GNUTLS_E_SUCCESS on success, or an error code. **/ int gnutls_priority_set_direct(gnutls_session_t session, const char *priorities, const char **err_pos) { gnutls_priority_t prio; int ret; ret = gnutls_priority_init(&prio, priorities, err_pos); if (ret < 0) { gnutls_assert(); return ret; } ret = gnutls_priority_set(session, prio); if (ret < 0) { gnutls_assert(); return ret; } /* ensure that the session holds the only reference for the struct */ gnutls_priority_deinit(prio); return 0; } /* Breaks a list of "xxx", "yyy", to a character array, of * MAX_COMMA_SEP_ELEMENTS size; Note that the given string is modified. */ static void break_list(char *list, char *broken_list[MAX_ELEMENTS], int *size) { char *p = list; *size = 0; do { broken_list[*size] = p; (*size)++; p = strchr(p, ':'); if (p) { *p = 0; p++; /* move to next entry and skip white * space. */ while (*p == ' ') p++; } } while (p != NULL && *size < MAX_ELEMENTS); } /** * gnutls_set_default_priority: * @session: is a #gnutls_session_t type. * * Sets the default priority on the ciphers, key exchange methods, * and macs. This is the recommended method of * setting the defaults, in order to promote consistency between applications * using GnuTLS, and to allow GnuTLS using applications to update settings * in par with the library. For client applications which require * maximum compatibility consider calling gnutls_session_enable_compatibility_mode() * after this function. * * For an application to specify additional options to priority string * consider using gnutls_set_default_priority_append(). * * To allow a user to override the defaults (e.g., when a user interface * or configuration file is available), the functions * gnutls_priority_set_direct() or gnutls_priority_set() can * be used. * * Returns: %GNUTLS_E_SUCCESS on success, or an error code. * * Since: 2.1.4 **/ int gnutls_set_default_priority(gnutls_session_t session) { return gnutls_priority_set_direct(session, NULL, NULL); } /** * gnutls_set_default_priority_append: * @session: is a #gnutls_session_t type. * @add_prio: is a string describing priorities to be appended to default * @err_pos: In case of an error this will have the position in the string the error occurred * @flags: must be zero * * Sets the default priority on the ciphers, key exchange methods, * and macs with the additional options in @add_prio. This is the recommended method of * setting the defaults when only few additional options are to be added. This promotes * consistency between applications using GnuTLS, and allows GnuTLS using applications * to update settings in par with the library. * * The @add_prio string should start as a normal priority string, e.g., * '-VERS-TLS-ALL:+VERS-TLS1.3:%%COMPAT' or '%%FORCE_ETM'. That is, it must not start * with ':'. * * To allow a user to override the defaults (e.g., when a user interface * or configuration file is available), the functions * gnutls_priority_set_direct() or gnutls_priority_set() can * be used. * * Returns: %GNUTLS_E_SUCCESS on success, or an error code. * * Since: 3.6.3 **/ int gnutls_set_default_priority_append(gnutls_session_t session, const char *add_prio, const char **err_pos, unsigned flags) { gnutls_priority_t prio; int ret; ret = gnutls_priority_init2(&prio, add_prio, err_pos, GNUTLS_PRIORITY_INIT_DEF_APPEND); if (ret < 0) { gnutls_assert(); return ret; } ret = gnutls_priority_set(session, prio); if (ret < 0) { gnutls_assert(); return ret; } /* ensure that the session holds the only reference for the struct */ gnutls_priority_deinit(prio); return 0; } /** * gnutls_priority_ecc_curve_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available elliptic curves in the priority * structure. * * Deprecated: This function has been replaced by * gnutls_priority_group_list() since 3.6.0. * * Returns: the number of items, or an error code. * * Since: 3.0 **/ int gnutls_priority_ecc_curve_list(gnutls_priority_t pcache, const unsigned int **list) { unsigned i; if (pcache->_supported_ecc.num_priorities == 0) return 0; *list = pcache->_supported_ecc.priorities; /* to ensure we don't confuse the caller, we do not include * any FFDHE groups. This may return an incomplete list. */ for (i=0;i_supported_ecc.num_priorities;i++) if (pcache->_supported_ecc.priorities[i] > GNUTLS_ECC_CURVE_MAX) return i; return pcache->_supported_ecc.num_priorities; } /** * gnutls_priority_group_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available groups in the priority * structure. * * Returns: the number of items, or an error code. * * Since: 3.6.0 **/ int gnutls_priority_group_list(gnutls_priority_t pcache, const unsigned int **list) { if (pcache->_supported_ecc.num_priorities == 0) return 0; *list = pcache->_supported_ecc.priorities; return pcache->_supported_ecc.num_priorities; } /** * gnutls_priority_kx_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available key exchange methods in the priority * structure. * * Returns: the number of items, or an error code. * Since: 3.2.3 **/ int gnutls_priority_kx_list(gnutls_priority_t pcache, const unsigned int **list) { if (pcache->_kx.num_priorities == 0) return 0; *list = pcache->_kx.priorities; return pcache->_kx.num_priorities; } /** * gnutls_priority_cipher_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available ciphers in the priority * structure. * * Returns: the number of items, or an error code. * Since: 3.2.3 **/ int gnutls_priority_cipher_list(gnutls_priority_t pcache, const unsigned int **list) { if (pcache->_cipher.num_priorities == 0) return 0; *list = pcache->_cipher.priorities; return pcache->_cipher.num_priorities; } /** * gnutls_priority_mac_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available MAC algorithms in the priority * structure. * * Returns: the number of items, or an error code. * Since: 3.2.3 **/ int gnutls_priority_mac_list(gnutls_priority_t pcache, const unsigned int **list) { if (pcache->_mac.num_priorities == 0) return 0; *list = pcache->_mac.priorities; return pcache->_mac.num_priorities; } /** * gnutls_priority_compression_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available compression method in the priority * structure. * * Returns: the number of methods, or an error code. * Since: 3.0 **/ int gnutls_priority_compression_list(gnutls_priority_t pcache, const unsigned int **list) { static const unsigned int priority[1] = {GNUTLS_COMP_NULL}; *list = priority; return 1; } /** * gnutls_priority_protocol_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available TLS version numbers in the priority * structure. * * Returns: the number of protocols, or an error code. * Since: 3.0 **/ int gnutls_priority_protocol_list(gnutls_priority_t pcache, const unsigned int **list) { if (pcache->protocol.num_priorities == 0) return 0; *list = pcache->protocol.priorities; return pcache->protocol.num_priorities; } /** * gnutls_priority_sign_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available signature algorithms in the priority * structure. * * Returns: the number of algorithms, or an error code. * Since: 3.0 **/ int gnutls_priority_sign_list(gnutls_priority_t pcache, const unsigned int **list) { if (pcache->_sign_algo.num_priorities == 0) return 0; *list = pcache->_sign_algo.priorities; return pcache->_sign_algo.num_priorities; } /** * gnutls_priority_certificate_type_list: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list * * Get a list of available certificate types in the priority * structure. * * As of version 3.6.4 this function is an alias for * gnutls_priority_certificate_type_list2 with the target parameter * set to: * - GNUTLS_CTYPE_SERVER, if the %SERVER_PRECEDENCE option is set * - GNUTLS_CTYPE_CLIENT, otherwise. * * Returns: the number of certificate types, or an error code. * Since: 3.0 **/ int gnutls_priority_certificate_type_list(gnutls_priority_t pcache, const unsigned int **list) { gnutls_ctype_target_t target = pcache->server_precedence ? GNUTLS_CTYPE_SERVER : GNUTLS_CTYPE_CLIENT; return gnutls_priority_certificate_type_list2(pcache, list, target); } /** * gnutls_priority_certificate_type_list2: * @pcache: is a #gnutls_prioritity_t type. * @list: will point to an integer list. * @target: is a #gnutls_ctype_target_t type. Valid arguments are * GNUTLS_CTYPE_CLIENT and GNUTLS_CTYPE_SERVER * * Get a list of available certificate types for the given target * in the priority structure. * * Returns: the number of certificate types, or an error code. * * Since: 3.6.4 **/ int gnutls_priority_certificate_type_list2(gnutls_priority_t pcache, const unsigned int **list, gnutls_ctype_target_t target) { switch (target) { case GNUTLS_CTYPE_CLIENT: if(pcache->client_ctype.num_priorities > 0) { *list = pcache->client_ctype.priorities; return pcache->client_ctype.num_priorities; } break; case GNUTLS_CTYPE_SERVER: if(pcache->server_ctype.num_priorities > 0) { *list = pcache->server_ctype.priorities; return pcache->server_ctype.num_priorities; } break; default: // Invalid target given gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } // Found a matching target but non of them had any ctypes set return 0; } /** * gnutls_priority_string_list: * @iter: an integer counter starting from zero * @flags: one of %GNUTLS_PRIORITY_LIST_INIT_KEYWORDS, %GNUTLS_PRIORITY_LIST_SPECIAL * * Can be used to iterate all available priority strings. * Due to internal implementation details, there are cases where this * function can return the empty string. In that case that string should be ignored. * When no strings are available it returns %NULL. * * Returns: a priority string * Since: 3.4.0 **/ const char * gnutls_priority_string_list(unsigned iter, unsigned int flags) { if (flags & GNUTLS_PRIORITY_LIST_INIT_KEYWORDS) { if (iter >= (sizeof(pgroups)/sizeof(pgroups[0]))-1) return NULL; return pgroups[iter].name; } else if (flags & GNUTLS_PRIORITY_LIST_SPECIAL) { if (iter >= (sizeof(wordlist)/sizeof(wordlist[0]))-1) return NULL; return wordlist[iter].name; } return NULL; }