/*
* Copyright (C) 2004-2012 Free Software Foundation, 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 "gnutls_errors.h"
#include
static void
break_comma_list(char *etag,
char **broken_etag, int *elements, int max_elements,
char sep);
/**
* gnutls_cipher_set_priority:
* @session: is a #gnutls_session_t structure.
* @list: is a 0 terminated list of gnutls_cipher_algorithm_t elements.
*
* Sets the priority on the ciphers supported by gnutls. Priority is
* higher for elements specified before others. After specifying the
* ciphers you want, you must append a 0. Note that the priority is
* set on the client. The server does not use the algorithm's
* priority except for disabling algorithms that were not specified.
*
* Returns: %GNUTLS_E_SUCCESS (0) on success, or a negative error code.
**/
int gnutls_cipher_set_priority(gnutls_session_t session, const int *list)
{
int num = 0, i;
while (list[num] != 0)
num++;
if (num > MAX_ALGOS)
num = MAX_ALGOS;
session->internals.priorities.cipher.algorithms = num;
for (i = 0; i < num; i++) {
session->internals.priorities.cipher.priority[i] = list[i];
}
return 0;
}
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->algorithms = num;
for (i = 0; i < num; i++) {
st->priority[i] = list[i];
}
return;
}
inline static void _add_priority(priority_st * st, const int *list)
{
int num, i, j, init;
init = i = st->algorithms;
for (num = 0; list[num] != 0; ++num) {
if (i + 1 > MAX_ALGOS) {
return;
}
for (j = 0; j < init; j++) {
if (st->priority[j] == (unsigned) list[num]) {
break;
}
}
if (j == init) {
st->priority[i++] = list[num];
st->algorithms++;
}
}
return;
}
static void _clear_priorities(priority_st * st, const int *list)
{
memset(st, 0, sizeof(*st));
}
/**
* gnutls_kx_set_priority:
* @session: is a #gnutls_session_t structure.
* @list: is a 0 terminated list of gnutls_kx_algorithm_t elements.
*
* Sets the priority on the key exchange algorithms supported by
* gnutls. Priority is higher for elements specified before others.
* After specifying the algorithms you want, you must append a 0.
* Note that the priority is set on the client. The server does not
* use the algorithm's priority except for disabling algorithms that
* were not specified.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int gnutls_kx_set_priority(gnutls_session_t session, const int *list)
{
_set_priority(&session->internals.priorities.kx, list);
return 0;
}
/**
* gnutls_mac_set_priority:
* @session: is a #gnutls_session_t structure.
* @list: is a 0 terminated list of gnutls_mac_algorithm_t elements.
*
* Sets the priority on the mac algorithms supported by gnutls.
* Priority is higher for elements specified before others. After
* specifying the algorithms you want, you must append a 0. Note
* that the priority is set on the client. The server does not use
* the algorithm's priority except for disabling algorithms that were
* not specified.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int gnutls_mac_set_priority(gnutls_session_t session, const int *list)
{
_set_priority(&session->internals.priorities.mac, list);
return 0;
}
/**
* gnutls_compression_set_priority:
* @session: is a #gnutls_session_t structure.
* @list: is a 0 terminated list of gnutls_compression_method_t elements.
*
* Sets the priority on the compression algorithms supported by
* gnutls. Priority is higher for elements specified before others.
* After specifying the algorithms you want, you must append a 0.
* Note that the priority is set on the client. The server does not
* use the algorithm's priority except for disabling algorithms that
* were not specified.
*
* TLS 1.0 does not define any compression algorithms except
* NULL. Other compression algorithms are to be considered as gnutls
* extensions.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int
gnutls_compression_set_priority(gnutls_session_t session, const int *list)
{
_set_priority(&session->internals.priorities.compression, list);
return 0;
}
/**
* gnutls_protocol_set_priority:
* @session: is a #gnutls_session_t structure.
* @list: is a 0 terminated list of gnutls_protocol_t elements.
*
* Sets the priority on the protocol versions supported by gnutls.
* This function actually enables or disables protocols. Newer protocol
* versions always have highest priority.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int gnutls_protocol_set_priority(gnutls_session_t session, const int *list)
{
_set_priority(&session->internals.priorities.protocol, list);
/* set the current version to the first in the chain.
* This will be overridden later.
*/
if (list)
_gnutls_set_current_version(session, list[0]);
return 0;
}
/**
* gnutls_certificate_type_set_priority:
* @session: is a #gnutls_session_t structure.
* @list: is a 0 terminated list of gnutls_certificate_type_t elements.
*
* Sets the priority on the certificate types supported by gnutls.
* Priority is higher for elements specified before others.
* After specifying the types you want, you must append a 0.
* Note that the certificate type priority is set on the client.
* The server does not use the cert type priority except for disabling
* types that were not specified.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int
gnutls_certificate_type_set_priority(gnutls_session_t session,
const int *list)
{
#ifdef ENABLE_OPENPGP
_set_priority(&session->internals.priorities.cert_type, list);
return 0;
#else
return GNUTLS_E_UNIMPLEMENTED_FEATURE;
#endif
}
static const int supported_ecc_normal[] = {
GNUTLS_ECC_CURVE_SECP192R1,
GNUTLS_ECC_CURVE_SECP224R1,
GNUTLS_ECC_CURVE_SECP256R1,
GNUTLS_ECC_CURVE_SECP384R1,
GNUTLS_ECC_CURVE_SECP521R1,
0
};
static const int supported_ecc_secure128[] = {
GNUTLS_ECC_CURVE_SECP256R1,
GNUTLS_ECC_CURVE_SECP384R1,
GNUTLS_ECC_CURVE_SECP521R1,
0
};
static const int supported_ecc_suiteb128[] = {
GNUTLS_ECC_CURVE_SECP256R1,
GNUTLS_ECC_CURVE_SECP384R1,
0
};
static const int supported_ecc_suiteb192[] = {
GNUTLS_ECC_CURVE_SECP384R1,
0
};
static const int supported_ecc_secure192[] = {
GNUTLS_ECC_CURVE_SECP384R1,
GNUTLS_ECC_CURVE_SECP521R1,
0
};
static const int protocol_priority[] = {
GNUTLS_TLS1_2,
GNUTLS_TLS1_1,
GNUTLS_TLS1_0,
GNUTLS_SSL3,
GNUTLS_DTLS1_2,
GNUTLS_DTLS1_0,
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 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,
GNUTLS_KX_DHE_DSS,
#endif
0
};
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,
GNUTLS_KX_DHE_DSS,
#endif
0
};
static const int kx_priority_suiteb[] = {
GNUTLS_KX_ECDHE_ECDSA,
0
};
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,
GNUTLS_KX_DHE_DSS,
#endif
/* GNUTLS_KX_ANON_DH: Man-in-the-middle prone, don't add!
*/
0
};
/* If GCM and AES acceleration is available then prefer
* them over anything else.
*/
static const int cipher_priority_performance[] = {
GNUTLS_CIPHER_ARCFOUR_128,
GNUTLS_CIPHER_AES_128_GCM,
GNUTLS_CIPHER_AES_256_GCM,
GNUTLS_CIPHER_CAMELLIA_128_GCM,
GNUTLS_CIPHER_CAMELLIA_256_GCM,
GNUTLS_CIPHER_AES_128_CBC,
GNUTLS_CIPHER_AES_256_CBC,
GNUTLS_CIPHER_CAMELLIA_128_CBC,
GNUTLS_CIPHER_CAMELLIA_256_CBC,
GNUTLS_CIPHER_3DES_CBC,
0
};
static const int cipher_priority_normal[] = {
GNUTLS_CIPHER_AES_128_GCM,
GNUTLS_CIPHER_AES_256_GCM,
GNUTLS_CIPHER_CAMELLIA_128_GCM,
GNUTLS_CIPHER_CAMELLIA_256_GCM,
GNUTLS_CIPHER_AES_128_CBC,
GNUTLS_CIPHER_AES_256_CBC,
GNUTLS_CIPHER_CAMELLIA_128_CBC,
GNUTLS_CIPHER_CAMELLIA_256_CBC,
GNUTLS_CIPHER_3DES_CBC,
GNUTLS_CIPHER_ARCFOUR_128,
0
};
static const int cipher_priority_suiteb128[] = {
GNUTLS_CIPHER_AES_128_GCM,
GNUTLS_CIPHER_AES_256_GCM,
0
};
static const int cipher_priority_suiteb192[] = {
GNUTLS_CIPHER_AES_256_GCM,
0
};
static const int cipher_priority_secure128[] = {
GNUTLS_CIPHER_AES_128_GCM,
GNUTLS_CIPHER_CAMELLIA_128_GCM,
GNUTLS_CIPHER_AES_256_GCM,
GNUTLS_CIPHER_CAMELLIA_256_GCM,
GNUTLS_CIPHER_AES_128_CBC,
GNUTLS_CIPHER_CAMELLIA_128_CBC,
GNUTLS_CIPHER_AES_256_CBC,
GNUTLS_CIPHER_CAMELLIA_256_CBC,
0
};
static const int cipher_priority_secure192[] = {
GNUTLS_CIPHER_AES_256_GCM,
GNUTLS_CIPHER_CAMELLIA_256_GCM,
GNUTLS_CIPHER_AES_256_CBC,
GNUTLS_CIPHER_CAMELLIA_256_CBC,
0
};
static const int comp_priority[] = {
/* compression should be explicitly requested to be enabled */
GNUTLS_COMP_NULL,
0
};
static const int sign_priority_default[] = {
GNUTLS_SIGN_RSA_SHA256,
GNUTLS_SIGN_DSA_SHA256,
GNUTLS_SIGN_ECDSA_SHA256,
GNUTLS_SIGN_RSA_SHA384,
GNUTLS_SIGN_ECDSA_SHA384,
GNUTLS_SIGN_RSA_SHA512,
GNUTLS_SIGN_ECDSA_SHA512,
GNUTLS_SIGN_RSA_SHA224,
GNUTLS_SIGN_DSA_SHA224,
GNUTLS_SIGN_ECDSA_SHA224,
GNUTLS_SIGN_RSA_SHA1,
GNUTLS_SIGN_DSA_SHA1,
GNUTLS_SIGN_ECDSA_SHA1,
0
};
static const int sign_priority_suiteb128[] = {
GNUTLS_SIGN_ECDSA_SHA256,
GNUTLS_SIGN_ECDSA_SHA384,
0
};
static const int sign_priority_suiteb192[] = {
GNUTLS_SIGN_ECDSA_SHA384,
0
};
static const int sign_priority_secure128[] = {
GNUTLS_SIGN_RSA_SHA256,
GNUTLS_SIGN_DSA_SHA256,
GNUTLS_SIGN_ECDSA_SHA256,
GNUTLS_SIGN_RSA_SHA384,
GNUTLS_SIGN_ECDSA_SHA384,
GNUTLS_SIGN_RSA_SHA512,
GNUTLS_SIGN_ECDSA_SHA512,
0
};
static const int sign_priority_secure192[] = {
GNUTLS_SIGN_RSA_SHA384,
GNUTLS_SIGN_ECDSA_SHA384,
GNUTLS_SIGN_RSA_SHA512,
GNUTLS_SIGN_ECDSA_SHA512,
0
};
static const int mac_priority_normal[] = {
GNUTLS_MAC_SHA1,
GNUTLS_MAC_SHA256,
GNUTLS_MAC_SHA384,
GNUTLS_MAC_AEAD,
GNUTLS_MAC_MD5,
0
};
static const int mac_priority_suiteb128[] = {
GNUTLS_MAC_AEAD,
0
};
static const int mac_priority_suiteb192[] = {
GNUTLS_MAC_AEAD,
0
};
static const int mac_priority_secure128[] = {
GNUTLS_MAC_SHA1,
GNUTLS_MAC_SHA256,
GNUTLS_MAC_SHA384,
GNUTLS_MAC_AEAD,
0
};
static const int mac_priority_secure192[] = {
GNUTLS_MAC_SHA256,
GNUTLS_MAC_SHA384,
GNUTLS_MAC_AEAD,
0
};
static const int cert_type_priority_default[] = {
GNUTLS_CRT_X509,
0
};
static const int cert_type_priority_all[] = {
GNUTLS_CRT_X509,
GNUTLS_CRT_OPENPGP,
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->algorithms; i++) {
if (priority_list->priority[i] == algo) {
priority_list->algorithms--;
if ((priority_list->algorithms - i) > 0)
memmove(&priority_list->priority[i],
&priority_list->priority[i + 1],
(priority_list->algorithms -
i) *
sizeof(priority_list->
priority[0]));
priority_list->priority[priority_list->
algorithms] = 0;
break;
}
}
return;
}
static void prio_add(priority_st * priority_list, unsigned int algo)
{
unsigned int i, l = priority_list->algorithms;
if (l >= MAX_ALGOS)
return; /* can't add it anyway */
for (i = 0; i < l; ++i) {
if (algo == priority_list->priority[i])
return; /* if it exists */
}
priority_list->priority[l] = algo;
priority_list->algorithms++;
return;
}
/**
* gnutls_priority_set:
* @session: is a #gnutls_session_t structure.
* @priority: is a #gnutls_priority_t structure.
*
* Sets the priorities to use on the ciphers, key exchange methods,
* macs and compression methods.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int
gnutls_priority_set(gnutls_session_t session, gnutls_priority_t priority)
{
if (priority == NULL) {
gnutls_assert();
return GNUTLS_E_NO_CIPHER_SUITES;
}
memcpy(&session->internals.priorities, priority,
sizeof(struct gnutls_priority_st));
/* set the current version to the first in the chain.
* This will be overridden later.
*/
if (session->internals.priorities.protocol.algorithms > 0)
_gnutls_set_current_version(session,
session->internals.priorities.
protocol.priority[0]);
if (session->internals.priorities.protocol.algorithms == 0 ||
session->internals.priorities.cipher.algorithms == 0 ||
session->internals.priorities.mac.algorithms == 0 ||
session->internals.priorities.kx.algorithms == 0 ||
session->internals.priorities.compression.algorithms == 0)
return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
return 0;
}
#define MAX_ELEMENTS 48
#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_EXPORT "EXPORT"
static
int check_level(const char *level, gnutls_priority_t priority_cache,
int add)
{
bulk_rmadd_func *func;
if (add)
func = _add_priority;
else
func = _set_priority;
if (strcasecmp(level, LEVEL_PERFORMANCE) == 0) {
func(&priority_cache->cipher, cipher_priority_performance);
func(&priority_cache->kx, kx_priority_performance);
func(&priority_cache->mac, mac_priority_normal);
func(&priority_cache->sign_algo, sign_priority_default);
func(&priority_cache->supported_ecc, supported_ecc_normal);
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_VERY_WEAK;
return 1;
} else if (strcasecmp(level, LEVEL_NORMAL) == 0) {
func(&priority_cache->cipher, cipher_priority_normal);
func(&priority_cache->kx, kx_priority_secure);
func(&priority_cache->mac, mac_priority_normal);
func(&priority_cache->sign_algo, sign_priority_default);
func(&priority_cache->supported_ecc, supported_ecc_normal);
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_VERY_WEAK;
return 1;
} else if (strcasecmp(level, LEVEL_PFS) == 0) {
func(&priority_cache->cipher, cipher_priority_normal);
func(&priority_cache->kx, kx_priority_pfs);
func(&priority_cache->mac, mac_priority_normal);
func(&priority_cache->sign_algo, sign_priority_default);
func(&priority_cache->supported_ecc, supported_ecc_normal);
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_VERY_WEAK;
return 1;
} else if (strcasecmp(level, LEVEL_SECURE256) == 0
|| strcasecmp(level, LEVEL_SECURE192) == 0) {
func(&priority_cache->cipher, cipher_priority_secure192);
func(&priority_cache->kx, kx_priority_secure);
func(&priority_cache->mac, mac_priority_secure192);
func(&priority_cache->sign_algo, sign_priority_secure192);
func(&priority_cache->supported_ecc,
supported_ecc_secure192);
/* be conservative for now. Set the bits to correspond to 96-bit level */
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_LEGACY;
return 1;
} else if (strcasecmp(level, LEVEL_SECURE128) == 0
|| strcasecmp(level, "SECURE") == 0) {
func(&priority_cache->cipher, cipher_priority_secure128);
func(&priority_cache->kx, kx_priority_secure);
func(&priority_cache->mac, mac_priority_secure128);
func(&priority_cache->sign_algo, sign_priority_secure128);
func(&priority_cache->supported_ecc,
supported_ecc_secure128);
/* be conservative for now. Set the bits to correspond to an 72-bit level */
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_WEAK;
return 1;
} else if (strcasecmp(level, LEVEL_SUITEB128) == 0) {
func(&priority_cache->protocol, protocol_priority_suiteb);
func(&priority_cache->cipher, cipher_priority_suiteb128);
func(&priority_cache->kx, kx_priority_suiteb);
func(&priority_cache->mac, mac_priority_suiteb128);
func(&priority_cache->sign_algo, sign_priority_suiteb128);
func(&priority_cache->supported_ecc,
supported_ecc_suiteb128);
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_HIGH;
return 1;
} else if (strcasecmp(level, LEVEL_SUITEB192) == 0) {
func(&priority_cache->protocol, protocol_priority_suiteb);
func(&priority_cache->cipher, cipher_priority_suiteb192);
func(&priority_cache->kx, kx_priority_suiteb);
func(&priority_cache->mac, mac_priority_suiteb192);
func(&priority_cache->sign_algo, sign_priority_suiteb192);
func(&priority_cache->supported_ecc,
supported_ecc_suiteb192);
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_ULTRA;
return 1;
} else if (strcasecmp(level, LEVEL_EXPORT) == 0) {
func(&priority_cache->cipher, cipher_priority_performance);
func(&priority_cache->kx, kx_priority_performance);
func(&priority_cache->mac, mac_priority_secure128);
func(&priority_cache->sign_algo, sign_priority_default);
func(&priority_cache->supported_ecc, supported_ecc_normal);
if (priority_cache->level == 0)
priority_cache->level = GNUTLS_SEC_PARAM_EXPORT;
return 1;
}
return 0;
}
/**
* gnutls_priority_init:
* @priority_cache: is a #gnutls_prioritity_t structure.
* @priorities: is a string describing priorities
* @err_pos: In case of an error this will have the position in the string the error occured
*
* Sets priorities for the ciphers, key exchange methods, macs and
* compression methods.
*
* 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.
*
* Unless there is a special need, using "NORMAL" or "NORMAL:%COMPAT" for compatibility
* is recommended.
*
* "PERFORMANCE" means all the "secure" ciphersuites are enabled,
* limited to 128 bit ciphers and sorted by terms of speed
* performance.
*
* "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.
*
* "EXPORT" means all ciphersuites are enabled, including the
* low-security 40 bit ciphers.
*
* "NONE" means nothing is enabled. This disables even protocols and
* compression methods.
*
* 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 except for ARCFOUR-128.
*
* "SECURE128:-VERS-SSL3.0:+COMP-DEFLATE" means that only secure ciphers are
* enabled, SSL3.0 is disabled, and libz compression enabled.
*
* "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.
*
* 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;
int algo;
rmadd_func *fn;
bulk_rmadd_func *bulk_fn;
*priority_cache =
gnutls_calloc(1, sizeof(struct gnutls_priority_st));
if (*priority_cache == NULL) {
gnutls_assert();
return GNUTLS_E_MEMORY_ERROR;
}
if (err_pos)
*err_pos = priorities;
/* for now unsafe renegotiation is default on everyone. To be removed
* when we make it the default.
*/
(*priority_cache)->sr = SR_PARTIAL;
(*priority_cache)->ssl3_record_version = 1;
(*priority_cache)->max_empty_records = DEFAULT_MAX_EMPTY_RECORDS;
if (priorities == NULL)
priorities = LEVEL_NORMAL;
darg = gnutls_strdup(priorities);
if (darg == NULL) {
gnutls_assert();
goto error;
}
break_comma_list(darg, broken_list, &broken_list_size,
MAX_ELEMENTS, ':');
/* This is our default set of protocol version, certificate types and
* compression methods.
*/
if (strcasecmp(broken_list[0], LEVEL_NONE) != 0) {
_set_priority(&(*priority_cache)->protocol,
protocol_priority);
_set_priority(&(*priority_cache)->compression,
comp_priority);
_set_priority(&(*priority_cache)->cert_type,
cert_type_priority_default);
_set_priority(&(*priority_cache)->sign_algo,
sign_priority_default);
_set_priority(&(*priority_cache)->supported_ecc,
supported_ecc_normal);
i = 0;
} else {
i = 1;
}
for (; i < broken_list_size; i++) {
if (check_level(broken_list[i], *priority_cache, 0) != 0) {
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;
} else {
fn = prio_remove;
bulk_fn = _clear_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 ((algo =
gnutls_cipher_get_id(&broken_list[i][1]))
!= GNUTLS_CIPHER_UNKNOWN)
fn(&(*priority_cache)->cipher, algo);
else if ((algo =
gnutls_kx_get_id(&broken_list[i][1])) !=
GNUTLS_KX_UNKNOWN)
fn(&(*priority_cache)->kx, algo);
else if (strncasecmp
(&broken_list[i][1], "VERS-", 5) == 0) {
if (strncasecmp
(&broken_list[i][1], "VERS-TLS-ALL",
12) == 0) {
bulk_fn(&(*priority_cache)->
protocol,
protocol_priority);
} else
if (strncasecmp
(&broken_list[i][1],
"VERS-DTLS-ALL", 13) == 0) {
bulk_fn(&(*priority_cache)->
protocol,
dtls_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 (strncasecmp
(&broken_list[i][1], "COMP-", 5) == 0) {
if (strncasecmp
(&broken_list[i][1], "COMP-ALL",
8) == 0) {
bulk_fn(&(*priority_cache)->
compression,
comp_priority);
} else {
if ((algo =
gnutls_compression_get_id
(&broken_list[i][6])) !=
GNUTLS_COMP_UNKNOWN)
fn(&(*priority_cache)->
compression, algo);
else
goto error;
}
} /* now check if the element is something like -ALGO */
else if (strncasecmp
(&broken_list[i][1], "CURVE-", 6) == 0) {
if (strncasecmp
(&broken_list[i][1], "CURVE-ALL",
9) == 0) {
bulk_fn(&(*priority_cache)->
supported_ecc,
supported_ecc_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;
}
} /* now check if the element is something like -ALGO */
else if (strncasecmp
(&broken_list[i][1], "CTYPE-", 6) == 0) {
if (strncasecmp
(&broken_list[i][1], "CTYPE-ALL",
9) == 0) {
bulk_fn(&(*priority_cache)->
cert_type,
cert_type_priority_all);
} else {
if ((algo =
gnutls_certificate_type_get_id
(&broken_list[i][7])) !=
GNUTLS_CRT_UNKNOWN)
fn(&(*priority_cache)->
cert_type, algo);
else
goto error;
}
} /* now check if the element is something like -ALGO */
else if (strncasecmp
(&broken_list[i][1], "SIGN-", 5) == 0) {
if (strncasecmp
(&broken_list[i][1], "SIGN-ALL",
8) == 0) {
bulk_fn(&(*priority_cache)->
sign_algo,
sign_priority_default);
} 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 (strncasecmp
(&broken_list[i][1], "MAC-ALL", 7) == 0) {
bulk_fn(&(*priority_cache)->mac,
mac_priority_normal);
} else
if (strncasecmp
(&broken_list[i][1], "CIPHER-ALL",
10) == 0) {
bulk_fn(&(*priority_cache)->cipher,
cipher_priority_normal);
} else
if (strncasecmp
(&broken_list[i][1], "KX-ALL", 6) == 0) {
bulk_fn(&(*priority_cache)->kx,
kx_priority_secure);
} else
goto error;
} else if (broken_list[i][0] == '%') {
if (strcasecmp(&broken_list[i][1], "COMPAT") == 0) {
ENABLE_COMPAT((*priority_cache));
} else
if (strcasecmp(&broken_list[i][1], "DUMBFW") == 0) {
(*priority_cache)->dumbfw = 1;
} else
if (strcasecmp
(&broken_list[i][1],
"NO_EXTENSIONS") == 0) {
(*priority_cache)->no_extensions = 1;
} else
if (strcasecmp
(&broken_list[i][1],
"STATELESS_COMPRESSION") == 0) {
(*priority_cache)->stateless_compression =
1;
} else
if (strcasecmp
(&broken_list[i][1],
"VERIFY_ALLOW_SIGN_RSA_MD5") == 0) {
prio_add(&(*priority_cache)->sign_algo,
GNUTLS_SIGN_RSA_MD5);
(*priority_cache)->
additional_verify_flags |=
GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5;
} else
if (strcasecmp
(&broken_list[i][1],
"VERIFY_DISABLE_CRL_CHECKS") == 0) {
(*priority_cache)->
additional_verify_flags |=
GNUTLS_VERIFY_DISABLE_CRL_CHECKS;
} else
if (strcasecmp
(&broken_list[i][1],
"SSL3_RECORD_VERSION") == 0)
(*priority_cache)->ssl3_record_version = 1;
else if (strcasecmp(&broken_list[i][1],
"LATEST_RECORD_VERSION") == 0)
(*priority_cache)->ssl3_record_version = 0;
else if (strcasecmp(&broken_list[i][1],
"VERIFY_ALLOW_X509_V1_CA_CRT")
== 0)
(*priority_cache)->
additional_verify_flags |=
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT;
else if (strcasecmp
(&broken_list[i][1],
"UNSAFE_RENEGOTIATION") == 0) {
(*priority_cache)->sr = SR_UNSAFE;
} else
if (strcasecmp
(&broken_list[i][1],
"SAFE_RENEGOTIATION") == 0) {
(*priority_cache)->sr = SR_SAFE;
} else if (strcasecmp(&broken_list[i][1],
"PARTIAL_RENEGOTIATION") ==
0) {
(*priority_cache)->sr = SR_PARTIAL;
} else if (strcasecmp(&broken_list[i][1],
"DISABLE_SAFE_RENEGOTIATION")
== 0) {
(*priority_cache)->sr = SR_DISABLED;
} else if (strcasecmp(&broken_list[i][1],
"SERVER_PRECEDENCE") == 0) {
(*priority_cache)->server_precedence = 1;
} else if (strcasecmp(&broken_list[i][1],
"NEW_PADDING") == 0) {
(*priority_cache)->new_record_padding = 1;
} else
goto error;
} else
goto error;
}
gnutls_free(darg);
return 0;
error:
if (err_pos != NULL && i < broken_list_size) {
*err_pos = priorities;
for (j = 0; j < i; j++) {
(*err_pos) += strlen(broken_list[j]) + 1;
}
}
gnutls_free(darg);
gnutls_free(*priority_cache);
return GNUTLS_E_INVALID_REQUEST;
}
/**
* gnutls_priority_deinit:
* @priority_cache: is a #gnutls_prioritity_t structure.
*
* Deinitializes the priority cache.
**/
void gnutls_priority_deinit(gnutls_priority_t priority_cache)
{
gnutls_free(priority_cache);
}
/**
* gnutls_priority_set_direct:
* @session: is a #gnutls_session_t structure.
* @priorities: is a string describing priorities
* @err_pos: In case of an error this will have the position in the string the error occured
*
* Sets the priorities to use on the ciphers, key exchange methods,
* macs and compression methods. 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().
*
* 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;
}
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_comma_list(char *etag,
char **broken_etag, int *elements, int max_elements,
char sep)
{
char *p = etag;
if (sep == 0)
sep = ',';
*elements = 0;
do {
broken_etag[*elements] = p;
(*elements)++;
p = strchr(p, sep);
if (p) {
*p = 0;
p++; /* move to next entry and skip white
* space.
*/
while (*p == ' ')
p++;
}
}
while (p != NULL && *elements < max_elements);
}
/**
* gnutls_set_default_priority:
* @session: is a #gnutls_session_t structure.
*
* Sets some default priority on the ciphers, key exchange methods,
* macs and compression methods.
*
* This is the same as calling:
*
* gnutls_priority_set_direct (session, "NORMAL", NULL);
*
* This function is kept around for backwards compatibility, but
* because of its wide use it is still fully supported. If you wish
* to allow users to provide a string that specify which ciphers to
* use (which is recommended), you should use
* gnutls_priority_set_direct() or gnutls_priority_set() instead.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int gnutls_set_default_priority(gnutls_session_t session)
{
return gnutls_priority_set_direct(session, "NORMAL", NULL);
}
/**
* gnutls_set_default_export_priority:
* @session: is a #gnutls_session_t structure.
*
* Sets some default priority on the ciphers, key exchange methods, macs
* and compression methods. This function also includes weak algorithms.
*
* This is the same as calling:
*
* gnutls_priority_set_direct (session, "EXPORT", NULL);
*
* This function is kept around for backwards compatibility, but
* because of its wide use it is still fully supported. If you wish
* to allow users to provide a string that specify which ciphers to
* use (which is recommended), you should use
* gnutls_priority_set_direct() or gnutls_priority_set() instead.
*
* Returns: %GNUTLS_E_SUCCESS on success, or an error code.
**/
int gnutls_set_default_export_priority(gnutls_session_t session)
{
return gnutls_priority_set_direct(session, "EXPORT", NULL);
}
/**
* gnutls_priority_ecc_curve_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @list: will point to an integer list
*
* Get a list of available elliptic curves in the priority
* structure.
*
* Returns: the number of curves, or an error code.
* Since: 3.0
**/
int
gnutls_priority_ecc_curve_list(gnutls_priority_t pcache,
const unsigned int **list)
{
if (pcache->supported_ecc.algorithms == 0)
return 0;
*list = pcache->supported_ecc.priority;
return pcache->supported_ecc.algorithms;
}
/**
* gnutls_priority_kx_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @list: will point to an integer list
*
* Get a list of available key exchange methods in the priority
* structure.
*
* Returns: the number of curves, or an error code.
* Since: 3.2.3
**/
int
gnutls_priority_kx_list(gnutls_priority_t pcache,
const unsigned int **list)
{
if (pcache->kx.algorithms == 0)
return 0;
*list = pcache->kx.priority;
return pcache->kx.algorithms;
}
/**
* gnutls_priority_cipher_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @list: will point to an integer list
*
* Get a list of available ciphers in the priority
* structure.
*
* Returns: the number of curves, or an error code.
* Since: 3.2.3
**/
int
gnutls_priority_cipher_list(gnutls_priority_t pcache,
const unsigned int **list)
{
if (pcache->cipher.algorithms == 0)
return 0;
*list = pcache->cipher.priority;
return pcache->cipher.algorithms;
}
/**
* gnutls_priority_mac_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @list: will point to an integer list
*
* Get a list of available MAC algorithms in the priority
* structure.
*
* Returns: the number of curves, or an error code.
* Since: 3.2.3
**/
int
gnutls_priority_mac_list(gnutls_priority_t pcache,
const unsigned int **list)
{
if (pcache->mac.algorithms == 0)
return 0;
*list = pcache->mac.priority;
return pcache->mac.algorithms;
}
/**
* gnutls_priority_compression_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @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)
{
if (pcache->compression.algorithms == 0)
return 0;
*list = pcache->compression.priority;
return pcache->compression.algorithms;
}
/**
* gnutls_priority_protocol_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @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.algorithms == 0)
return 0;
*list = pcache->protocol.priority;
return pcache->protocol.algorithms;
}
/**
* gnutls_priority_sign_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @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.algorithms == 0)
return 0;
*list = pcache->sign_algo.priority;
return pcache->sign_algo.algorithms;
}
/**
* gnutls_priority_certificate_type_list:
* @pcache: is a #gnutls_prioritity_t structure.
* @list: will point to an integer list
*
* Get a list of available certificate types in the priority
* structure.
*
* 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)
{
if (pcache->cert_type.algorithms == 0)
return 0;
*list = pcache->cert_type.priority;
return pcache->cert_type.algorithms;
}