diff --git a/ChangeLog b/ChangeLog index 05c1d9c3..74e977dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,9 @@ * 2017-12-??: Version 1.3.0 + * Specify default available cipher suites for authenticated TLS + upstreams with getdns_context_set_tls_ciphers_list() + An upstream specific available cipher suite may also be given + with the tls_cipher_list setting in the upstream dict with + getdns_context_set_upstream_recursive_servers() * PR #366: Add support for TLS 1.3 and Chacha20-Poly1305 Thanks Pascal Ernster * Bugfix #356: Do Zero configuration DNSSEC meta queries over on the diff --git a/src/const-info.c b/src/const-info.c index 41d1c091..28f6ff60 100644 --- a/src/const-info.c +++ b/src/const-info.c @@ -91,6 +91,7 @@ static struct const_info consts_info[] = { { 630, "GETDNS_CONTEXT_CODE_HOSTS", GETDNS_CONTEXT_CODE_HOSTS_TEXT }, { 631, "GETDNS_CONTEXT_CODE_CAPATH", GETDNS_CONTEXT_CODE_CAPATH_TEXT }, { 632, "GETDNS_CONTEXT_CODE_CAFILE", GETDNS_CONTEXT_CODE_CAFILE_TEXT }, + { 633, "GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST", GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST_TEXT }, { 700, "GETDNS_CALLBACK_COMPLETE", GETDNS_CALLBACK_COMPLETE_TEXT }, { 701, "GETDNS_CALLBACK_CANCEL", GETDNS_CALLBACK_CANCEL_TEXT }, { 702, "GETDNS_CALLBACK_TIMEOUT", GETDNS_CALLBACK_TIMEOUT_TEXT }, @@ -186,6 +187,7 @@ static struct const_name_info consts_name_info[] = { { "GETDNS_CONTEXT_CODE_TIMEOUT", 616 }, { "GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION", 618 }, { "GETDNS_CONTEXT_CODE_TLS_BACKOFF_TIME", 623 }, + { "GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST", 633 }, { "GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES", 624 }, { "GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE", 620 }, { "GETDNS_CONTEXT_CODE_TRUST_ANCHORS_URL", 625 }, diff --git a/src/context.c b/src/context.c index 7f7d393a..b50dff1a 100644 --- a/src/context.c +++ b/src/context.c @@ -165,6 +165,17 @@ static void set_ub_dnssec_allowed_skew(struct getdns_context*, uint32_t); /* Stuff to make it compile pedantically */ #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; +static char * +_getdns_strdup2(const struct mem_funcs *mfs, const getdns_bindata *s) +{ + char *r; + if (!s || !(r = GETDNS_XMALLOC(*mfs, char, s->size + 1))) + return NULL; + else { + r[s->size] = '\0'; + return memcpy(r, s, s->size); + } +} #ifdef USE_WINSOCK /* For windows, the CA trust store is not read by openssl. @@ -723,6 +734,8 @@ _getdns_upstreams_dereference(getdns_upstreams *upstreams) pin = nextpin; } upstream->tls_pubkey_pinset = NULL; + if (upstream->tls_cipher_list) + GETDNS_FREE(upstreams->mf, upstream->tls_cipher_list); } GETDNS_FREE(upstreams->mf, upstreams); } @@ -1006,6 +1019,7 @@ upstream_init(getdns_upstream *upstream, upstream->fd = -1; upstream->tls_obj = NULL; upstream->tls_session = NULL; + upstream->tls_cipher_list = NULL; upstream->transport = GETDNS_TRANSPORT_TCP; upstream->tls_hs_state = GETDNS_HS_NONE; upstream->tls_auth_name[0] = '\0'; @@ -1377,11 +1391,11 @@ static void _getdns_check_expired_pending_netreqs_cb(void *arg) _getdns_check_expired_pending_netreqs((getdns_context *)arg, &now_ms); } -static const char *_getdns_default_trust_anchors_url = +static char const * const _getdns_default_trust_anchors_url = "http://data.iana.org/root-anchors/root-anchors.xml"; /* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */ -static const char *_getdns_default_trust_anchors_verify_CA = +static char const * const _getdns_default_trust_anchors_verify_CA = "-----BEGIN CERTIFICATE-----\n" "MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n" "TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n" @@ -1404,9 +1418,12 @@ static const char *_getdns_default_trust_anchors_verify_CA = "j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n" "-----END CERTIFICATE-----\n"; -static const char *_getdns_default_trust_anchors_verify_email = +static char const * const _getdns_default_trust_anchors_verify_email = "dnssec@iana.org"; +static char const * const _getdns_default_tls_cipher_list = + "TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:" + "TLS13-CHACHA20-POLY1305-SHA256:EECDH+AESGCM:EECDH+CHACHA20"; /* * getdns_context_create @@ -1515,6 +1532,7 @@ getdns_context_create_with_extended_memory_functions( result->appdata_dir = NULL; result->CApath = NULL; result->CAfile = NULL; + result->tls_cipher_list = NULL; (void) memset(&result->root_ksk, 0, sizeof(result->root_ksk)); @@ -1783,7 +1801,8 @@ getdns_context_destroy(struct getdns_context *context) GETDNS_FREE(context->mf, context->CApath); if (context->CAfile) GETDNS_FREE(context->mf, context->CAfile); - + if (context->tls_cipher_list) + GETDNS_FREE(context->mf, context->tls_cipher_list); #ifdef USE_WINSOCK WSACleanup(); @@ -2972,16 +2991,19 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, upstream->addr.ss_family = addr.ss_family; upstream_init(upstream, upstreams, ai); upstream->transport = getdns_upstream_transports[j]; - if (getdns_upstream_transports[j] == GETDNS_TRANSPORT_TLS) { + if (dict && getdns_upstream_transports[j] == GETDNS_TRANSPORT_TLS) { getdns_list *pubkey_pinset = NULL; - if (dict && (r = getdns_dict_get_bindata( + getdns_bindata *tls_cipher_list = NULL; + + if ((r = getdns_dict_get_bindata( dict, "tls_auth_name", &tls_auth_name)) == GETDNS_RETURN_GOOD) { if (tls_auth_name->size >= sizeof(upstream->tls_auth_name)) { - /* tls_auth_name's are just - * domain names and should - * thus not be larger than 256 - * bytes. + /* tls_auth_name's are + * domain names in presentation + * format and, taking escaping + * into account, should not + * be larger than 1024 bytes. */ goto invalid_parameter; } @@ -2991,7 +3013,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, upstream->tls_auth_name [tls_auth_name->size] = '\0'; } - if (dict && (r = getdns_dict_get_list(dict, "tls_pubkey_pinset", + if ((r = getdns_dict_get_list(dict, "tls_pubkey_pinset", &pubkey_pinset)) == GETDNS_RETURN_GOOD) { /* TODO: what if the user supplies tls_pubkey_pinset with * something other than a list? */ @@ -3001,6 +3023,12 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, if (r != GETDNS_RETURN_GOOD) goto invalid_parameter; } + (void) getdns_dict_get_bindata( + dict, "tls_cipher_list", &tls_cipher_list); + upstream->tls_cipher_list = tls_cipher_list + ? _getdns_strdup2(&upstreams->mf + , tls_cipher_list) + : NULL; } if ((upstream->tsig_alg = tsig_alg)) { if (tsig_name) { @@ -3574,8 +3602,9 @@ _getdns_context_prepare_for_resolution(getdns_context *context) # endif /* Be strict and only use the cipher suites recommended in RFC7525 Unless we later fallback to opportunistic. */ - const char* const PREFERRED_CIPHERS = "TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:TLS13-CHACHA20-POLY1305-SHA256:EECDH+AESGCM:EECDH+CHACHA20"; - if (!SSL_CTX_set_cipher_list(context->tls_ctx, PREFERRED_CIPHERS)) + if (!SSL_CTX_set_cipher_list(context->tls_ctx, + context->tls_cipher_list ? context->tls_cipher_list + : _getdns_default_tls_cipher_list)) return GETDNS_RETURN_BAD_CONTEXT; /* For strict authentication, we must have local root certs available Set up is done only when the tls_ctx is created (per getdns_context)*/ @@ -3625,7 +3654,7 @@ _getdns_context_prepare_for_resolution(getdns_context *context) return r; } /* _getdns_context_prepare_for_resolution */ -char * +static char * _getdns_strdup(const struct mem_funcs *mfs, const char *s) { size_t sz; @@ -3891,6 +3920,8 @@ _get_context_settings(getdns_context* context) (void) getdns_dict_util_set_string(result, "CApath", str_value); if (!getdns_context_get_CAfile(context, &str_value) && str_value) (void) getdns_dict_util_set_string(result, "CAfile", str_value); + if (!getdns_context_get_tls_cipher_list(context, &str_value) && str_value) + (void) getdns_dict_util_set_string(result, "tls_cipher_list", str_value); /* Default settings for extensions */ (void)getdns_dict_set_int( @@ -4475,6 +4506,11 @@ getdns_context_get_upstream_recursive_servers(getdns_context *context, break; } } + if (upstream->tls_cipher_list) { + (void) getdns_dict_util_set_string( + d, "tls_cipher_list", + upstream->tls_cipher_list); + } } } if (!r) @@ -4683,6 +4719,7 @@ _getdns_context_config_setting(getdns_context *context, CONTEXT_SETTING_STRING(hosts) CONTEXT_SETTING_STRING(CApath) CONTEXT_SETTING_STRING(CAfile) + CONTEXT_SETTING_STRING(tls_cipher_list) /**************************************/ /**** ****/ @@ -5233,4 +5270,34 @@ getdns_context_get_CAfile(getdns_context *context, const char **CAfile) return GETDNS_RETURN_GOOD; } +getdns_return_t +getdns_context_set_tls_cipher_list( + getdns_context *context, const char *tls_cipher_list) +{ + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + if (context->tls_cipher_list) + GETDNS_FREE(context->mf, context->tls_cipher_list); + context->tls_cipher_list = tls_cipher_list + ? _getdns_strdup(&context->mf, tls_cipher_list) + : NULL; + + dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_tls_cipher_list( + getdns_context *context, const char **tls_cipher_list) +{ + if (!context || !tls_cipher_list) + return GETDNS_RETURN_INVALID_PARAMETER; + + *tls_cipher_list = context->tls_cipher_list + ? context->tls_cipher_list + : _getdns_default_tls_cipher_list; + return GETDNS_RETURN_GOOD; +} + + /* context.c */ diff --git a/src/context.h b/src/context.h index 12c29356..86f40b03 100644 --- a/src/context.h +++ b/src/context.h @@ -205,6 +205,7 @@ typedef struct getdns_upstream { getdns_tls_hs_state_t tls_hs_state; getdns_auth_state_t tls_auth_state; unsigned tls_fallback_ok : 1; + char *tls_cipher_list; /* Auth credentials*/ char tls_auth_name[256]; sha256_pin_t *tls_pubkey_pinset; @@ -345,6 +346,7 @@ struct getdns_context { char *CApath; char *CAfile; + char *tls_cipher_list; getdns_upstreams *upstreams; uint16_t limit_outstanding_queries; @@ -527,8 +529,6 @@ void _getdns_context_cancel_request(getdns_dns_req *dnsreq); */ void _getdns_context_request_timed_out(getdns_dns_req *dnsreq); -char *_getdns_strdup(const struct mem_funcs *mfs, const char *str); - struct getdns_bindata *_getdns_bindata_copy( struct mem_funcs *mfs, size_t size, const uint8_t *data); diff --git a/src/getdns/getdns.h.in b/src/getdns/getdns.h.in index 4732a4a3..788f0d94 100644 --- a/src/getdns/getdns.h.in +++ b/src/getdns/getdns.h.in @@ -1687,6 +1687,8 @@ getdns_context_set_dnssec_allowed_skew(getdns_context *context, * - `value` A SHA256 hash of the `SubjectPublicKeyInfo` * of the upstream, which will be used to authenticate * it. + * - `tls_cipher_list` (a bindata) that is the string + * of available ciphers specific for this upstream. * @return GETDNS_RETURN_GOOD when successful. * @return GETDNS_RETURN_INVALID_PARAMETER when `context` or `upstream_list` was `NULL` * @return GETDNS_RETURN_CONTEXT_UPDATE_FAIL when there were problems parsing diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index 1c88813b..fd353ef4 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -98,6 +98,8 @@ extern "C" { #define GETDNS_CONTEXT_CODE_CAPATH_TEXT "Change related to getdns_context_set_CApath" #define GETDNS_CONTEXT_CODE_CAFILE 632 #define GETDNS_CONTEXT_CODE_CAFILE_TEXT "Change related to getdns_context_set_CAfile" +#define GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST 633 +#define GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST_TEXT "Change related to getdns_context_set_tls_cipher_list" /** @} */ @@ -739,6 +741,18 @@ getdns_context_set_CApath(getdns_context *context, const char *CApath); getdns_return_t getdns_context_set_CAfile(getdns_context *context, const char *CAfile); +/** + * Sets the list of available ciphers for authenticated TLS upstreams. + * @see getdns_context_get_tls_cipher_list + * @param[in] context The context to configure + * @param[in] cipher_list The cipher list + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_set_tls_cipher_list( + getdns_context *context, const char *cipher_list); + /** * Get the current resolution type setting from this context. * @see getdns_context_set_resolution_type @@ -1246,6 +1260,19 @@ getdns_context_get_CApath(getdns_context *context, const char **CApath); getdns_return_t getdns_context_get_CAfile(getdns_context *context, const char **CAfile); +/** + * Get the list of available ciphers for authenticated TLS upstreams. + * @see getdns_context_set_tls_cipher_list + * @param[in] context The context configure + * @param[out] cipher_list The cipher list + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_get_tls_cipher_list( + getdns_context *context, const char **cipher_list); + + /** @} */ diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index 1dadfa45..2c67564f 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -35,6 +35,7 @@ getdns_context_get_suffix getdns_context_get_timeout getdns_context_get_tls_authentication getdns_context_get_tls_backoff_time +getdns_context_get_tls_cipher_list getdns_context_get_tls_connection_retries getdns_context_get_tls_query_padding_blocksize getdns_context_get_trust_anchors_url @@ -77,6 +78,7 @@ getdns_context_set_suffix getdns_context_set_timeout getdns_context_set_tls_authentication getdns_context_set_tls_backoff_time +getdns_context_set_tls_cipher_list getdns_context_set_tls_connection_retries getdns_context_set_tls_query_padding_blocksize getdns_context_set_trust_anchors_url diff --git a/src/stub.c b/src/stub.c index d8da9a9c..07f22fcd 100644 --- a/src/stub.c +++ b/src/stub.c @@ -986,9 +986,12 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) SSL_set_cipher_list(ssl, "DEFAULT"); DEBUG_STUB("%s %-35s: WARNING: Using Oppotunistic TLS (fallback allowed)!\n", STUB_DEBUG_SETUP_TLS, __FUNC__); - } else + } else { + if (upstream->tls_cipher_list) + SSL_set_cipher_list(ssl, upstream->tls_cipher_list); DEBUG_STUB("%s %-35s: Using Strict TLS \n", STUB_DEBUG_SETUP_TLS, __FUNC__); + } SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_callback); SSL_set_connect_state(ssl);