diff --git a/m4/acx_getaddrinfo.m4 b/m4/acx_getaddrinfo.m4 index e88e1ddd..a4af9e1d 100644 --- a/m4/acx_getaddrinfo.m4 +++ b/m4/acx_getaddrinfo.m4 @@ -32,12 +32,12 @@ dnl see if on windows if test "$ac_cv_header_windows_h" = "yes"; then AC_DEFINE(USE_WINSOCK, 1, [Whether the windows socket API is used]) USE_WINSOCK="1" - LIBS="$LIBS -lws2_32" + LIBS="$LIBS -lws2_32 -lcrypt32" fi ], dnl no quick getaddrinfo, try mingw32 and winsock2 library. ORIGLIBS="$LIBS" -LIBS="$LIBS -lws2_32" +LIBS="$LIBS -lws2_32 -lcrypt32" AC_LINK_IFELSE( [AC_LANG_PROGRAM( [ @@ -62,7 +62,7 @@ AC_LINK_IFELSE( )], [ ac_cv_func_getaddrinfo="yes" -dnl already: LIBS="$LIBS -lws2_32" +dnl already: LIBS="$LIBS -lws2_32 -lcrypt32" AC_DEFINE(USE_WINSOCK, 1, [Whether the windows socket API is used]) USE_WINSOCK="1" ], diff --git a/src/context.c b/src/context.c index 6527333e..342ac485 100644 --- a/src/context.c +++ b/src/context.c @@ -44,6 +44,14 @@ #include #include typedef unsigned short in_port_t; + +#include +#include +#include + +#include +#include +#include #endif #include @@ -130,6 +138,86 @@ static void set_ub_edns_maximum_udp_payload_size(struct getdns_context*, /* Stuff to make it compile pedantically */ #define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code; + +#ifdef USE_WINSOCK +/* For windows, the CA trust store is not read by openssl. + Add code to open the trust store using wincrypt API and add + the root certs into openssl trust store */ +static int +add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx) +{ + HCERTSTORE hSystemStore; + PCCERT_CONTEXT pTargetCert = NULL; + + /* load just once per context lifetime for this version of getdns + TODO: dynamically update CA trust changes as they are available */ + if (!tls_ctx) + return 0; + + /* Call wincrypt's CertOpenStore to open the CA root store. */ + + if ((hSystemStore = CertOpenStore( + CERT_STORE_PROV_SYSTEM, + 0, + 0, + /* NOTE: mingw does not have this const: replace with 1 << 16 from code + CERT_SYSTEM_STORE_CURRENT_USER, */ + 1 << 16, + L"root")) == 0) + { + return 0; + } + + X509_STORE* store = SSL_CTX_get_cert_store(tls_ctx); + if (!store) + return 0; + + /* failure if the CA store is empty or the call fails */ + if ((pTargetCert = CertEnumCertificatesInStore( + hSystemStore, pTargetCert)) == 0) { + DEBUG_STUB("*** %s(%s %d:%s)\n", __FUNCTION__, + "CA certificate store for Windows is empty."); + return 0; + } + /* iterate over the windows cert store and add to openssl store */ + do + { + X509 *cert1 = d2i_X509(NULL, + (const unsigned char **)&pTargetCert->pbCertEncoded, + pTargetCert->cbCertEncoded); + if (!cert1) { + /* return error if a cert fails */ + DEBUG_STUB("*** %s(%s %d:%s)\n", __FUNCTION__, + "unable to parse certificate in memory", + ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); + return 0; + } + else { + /* return error if a cert add to store fails */ + if (X509_STORE_add_cert(store, cert1) == 0) { + DEBUG_STUB("*** %s(%s %d:%s)\n", __FUNCTION__, + "error adding certificate", ERR_get_error(), + ERR_error_string(ERR_get_error(), NULL)); + return 0; + } + X509_free(cert1); + } + } while ((pTargetCert = CertEnumCertificatesInStore( + hSystemStore, pTargetCert)) != 0); + + /* Clean up memory and quit. */ + if (pTargetCert) + CertFreeCertificateContext(pTargetCert); + if (hSystemStore) + { + if (!CertCloseStore( + hSystemStore, 0)) + return 0; + } + return 1; +} +#endif + static void destroy_local_host(_getdns_rbnode_t * node, void *arg) { getdns_context *context = (getdns_context *)arg; @@ -864,8 +952,9 @@ set_os_defaults(struct getdns_context *context) *token = 0; (void) strlcpy(domain, parse, sizeof(domain)); - - } else if (strncmp(parse, "search", 6) == 0) { + continue; + } + if (strncmp(parse, "search", 6) == 0) { parse += 6; do { parse += strspn(parse, " \t"); @@ -879,8 +968,9 @@ set_os_defaults(struct getdns_context *context) *token = prev_ch; parse = token; } while (*parse); - - } else if (strncmp(parse, "nameserver", 10) != 0) + continue; + } + if (strncmp(parse, "nameserver", 10) != 0) continue; parse += 10; @@ -2658,30 +2748,7 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context, /* Transport can in theory be set per query in stub mode */ if (context->resolution_type == GETDNS_RESOLUTION_STUB && tls_is_in_transports_list(context) == 1) { - if (context->tls_ctx == NULL) { -#ifdef HAVE_TLS_v1_2 - /* Create client context, use TLS v1.2 only for now */ - context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); - if(context->tls_ctx == NULL) -#ifndef USE_WINSOCK - return GETDNS_RETURN_BAD_CONTEXT; -#else - printf("Warning! Bad TLS context, check openssl version on Windows!\n");; -#endif - /* Be strict and only use the cipher suites recommended in RFC7525 - Unless we later fallback to opportunistic. */ - const char* const PREFERRED_CIPHERS = "EECDH+aRSA+AESGCM:EECDH+aECDSA+AESGCM:EDH+aRSA+AESGCM"; - if (!SSL_CTX_set_cipher_list(context->tls_ctx, PREFERRED_CIPHERS)) - return GETDNS_RETURN_BAD_CONTEXT; - if (!SSL_CTX_set_default_verify_paths(context->tls_ctx)) - return GETDNS_RETURN_BAD_CONTEXT; -#else - if (tls_only_is_in_transports_list(context) == 1) - return GETDNS_RETURN_BAD_CONTEXT; - /* A null tls_ctx will make TLS fail and fallback to the other - transports will kick-in.*/ -#endif - } + /* Check minimum require authentication level*/ if (tls_only_is_in_transports_list(context) == 1 && context->tls_auth == GETDNS_AUTHENTICATION_REQUIRED) { context->tls_auth_min = GETDNS_AUTHENTICATION_REQUIRED; @@ -2690,6 +2757,35 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context, else { context->tls_auth_min = GETDNS_AUTHENTICATION_NONE; } + + if (context->tls_ctx == NULL) { +#ifdef HAVE_TLS_v1_2 + /* Create client context, use TLS v1.2 only for now */ + context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); + if(context->tls_ctx == NULL) + return GETDNS_RETURN_BAD_CONTEXT; + /* Be strict and only use the cipher suites recommended in RFC7525 + Unless we later fallback to opportunistic. */ + const char* const PREFERRED_CIPHERS = "EECDH+aRSA+AESGCM:EECDH+aECDSA+AESGCM:EDH+aRSA+AESGCM"; + if (!SSL_CTX_set_cipher_list(context->tls_ctx, PREFERRED_CIPHERS)) + 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)*/ +#ifndef USE_WINSOCK + if (!SSL_CTX_set_default_verify_paths(context->tls_ctx)) { +#else + if (!add_WIN_cacerts_to_openssl_store(context->tls_ctx)) { +#endif /* USE_WINSOCK */ + if (context->tls_auth_min == GETDNS_AUTHENTICATION_REQUIRED) + return GETDNS_RETURN_BAD_CONTEXT; + } +#else /* HAVE_TLS_v1_2 */ + if (tls_only_is_in_transports_list(context) == 1) + return GETDNS_RETURN_BAD_CONTEXT; + /* A null tls_ctx will make TLS fail and fallback to the other + transports will kick-in.*/ +#endif /* HAVE_TLS_v1_2 */ + } } /* Block use of TLS ONLY in recursive mode as it won't work */ diff --git a/src/pubkey-pinning.c b/src/pubkey-pinning.c index 6281f2f8..9e0f3844 100644 --- a/src/pubkey-pinning.c +++ b/src/pubkey-pinning.c @@ -99,7 +99,7 @@ getdns_dict* getdns_pubkey_pin_create_from_string( getdns_dict* out = NULL; /* we only do sha256 right now, make sure this is well-formed */ - if (strncmp(PIN_PREFIX, str, PIN_PREFIX_LENGTH)) + if (!str || strncmp(PIN_PREFIX, str, PIN_PREFIX_LENGTH)) return NULL; for (i = PIN_PREFIX_LENGTH; i < PIN_PREFIX_LENGTH + B64_ENCODED_SHA256_LENGTH - 1; i++) if (!((str[i] >= 'a' && str[i] <= 'z') ||