X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/e3dd1d67d7e5bfd0b123d6cb84e00a570415bdea..e51c7be22dfccad376659a1a46cee93c9979bbf7:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index db2544c03..1d6b91470 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -23,7 +23,7 @@ functions from the OpenSSL library. */ #include #include #ifdef EXPERIMENTAL_OCSP -#include +# include #endif #ifdef EXPERIMENTAL_OCSP @@ -32,7 +32,7 @@ functions from the OpenSSL library. */ #endif #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) -#define EXIM_HAVE_OPENSSL_TLSEXT +# define EXIM_HAVE_OPENSSL_TLSEXT #endif /* Structure for collecting random data for seeding. */ @@ -107,6 +107,10 @@ typedef struct tls_ext_ctx_cb { uschar *server_cipher_list; /* only passed down to tls_error: */ host_item *host; + +#ifdef EXPERIMENTAL_CERTNAMES + uschar * verify_cert_hostnames; +#endif } tls_ext_ctx_cb; /* should figure out a cleanup of API to handle state preserved per @@ -262,35 +266,35 @@ Returns: 1 if verified, 0 if not */ static int -verify_callback(int state, X509_STORE_CTX *x509ctx, tls_support *tlsp, BOOL *calledp, BOOL *optionalp) +verify_callback(int state, X509_STORE_CTX *x509ctx, + tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { +X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); static uschar txt[256]; -X509_NAME_oneline(X509_get_subject_name(x509ctx->current_cert), - CS txt, sizeof(txt)); +X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); if (state == 0) { log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s", - x509ctx->error_depth, - X509_verify_cert_error_string(x509ctx->error), + X509_STORE_CTX_get_error_depth(x509ctx), + X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), txt); tlsp->certificate_verified = FALSE; *calledp = TRUE; if (!*optionalp) { - tlsp->peercert = X509_dup(x509ctx->current_cert); + tlsp->peercert = X509_dup(cert); return 0; /* reject */ } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); - return 1; /* accept */ } -if (x509ctx->error_depth != 0) +else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0) { - DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d cert=%s\n", - x509ctx->error_depth, txt); + DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", + X509_STORE_CTX_get_error_depth(x509ctx), txt); #ifdef EXPERIMENTAL_OCSP if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store) { /* client, wanting stapling */ @@ -298,28 +302,55 @@ if (x509ctx->error_depth != 0) for the verification of the OCSP stapled information. */ if (!X509_STORE_add_cert(client_static_cbinfo->u_ocsp.client.verify_store, - x509ctx->current_cert)) + cert)) ERR_clear_error(); } #endif } else { - DEBUG(D_tls) debug_printf("SSL%s peer: %s\n", - *calledp ? "" : " authenticated", txt); +#ifdef EXPERIMENTAL_CERTNAMES + uschar * verify_cert_hostnames; +#endif + tlsp->peerdn = txt; - tlsp->peercert = X509_dup(x509ctx->current_cert); - } + tlsp->peercert = X509_dup(cert); -/*XXX JGH: this looks bogus - we set "verified" first time through, which -will be for the root CS cert (calls work down the chain). Why should it -not be on the last call, where we're setting peerdn? +#ifdef EXPERIMENTAL_CERTNAMES + if ( tlsp == &tls_out + && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames))) + /* client, wanting hostname check */ -To test: set up a chain anchored by a good root-CA but with a bad server cert. -Does certificate_verified get set? -*/ -if (!*calledp) tlsp->certificate_verified = TRUE; -*calledp = TRUE; +# if OPENSSL_VERSION_NUMBER >= 0x010100000L || OPENSSL_VERSION_NUMBER >= 0x010002000L + { + int sep = 0; + uschar * list = verify_cert_hostnames; + uschar * name; + while (name = string_nextinlist(&list, &sep, NULL, 0)) + if (X509_check_host(cert, name, 0, 0)) + break; + if (!name) + { + log_write(0, LOG_MAIN, + "SSL verify error: certificate name mismatch: \"%s\"\n", txt); + return 0; /* reject */ + } + } +# else + if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) + { + log_write(0, LOG_MAIN, + "SSL verify error: certificate name mismatch: \"%s\"\n", txt); + return 0; /* reject */ + } +# endif +#endif + + DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", + *calledp ? "" : " authenticated", txt); + if (!*calledp) tlsp->certificate_verified = TRUE; + *calledp = TRUE; + } return 1; /* accept */ } @@ -791,11 +822,8 @@ const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; uschar *response_der; int response_der_len; -if (log_extra_selector & LX_tls_cipher) - log_write(0, LOG_MAIN, "[%s] Recieved OCSP stapling req;%s responding", - sender_host_address, cbinfo->u_ocsp.server.response ? "":" not"); -else - DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response.", +DEBUG(D_tls) + debug_printf("Received TLS status request (OCSP stapling); %s response.", cbinfo->u_ocsp.server.response ? "have" : "lack"); tls_in.ocsp = OCSP_NOT_RESP; @@ -965,8 +993,8 @@ return i; * Initialize for TLS * *************************************************/ -/* Called from both server and client code, to do preliminary initialization of -the library. +/* Called from both server and client code, to do preliminary initialization +of the library. We allocate and return a context structure. Arguments: host connected host, if client; NULL if server @@ -975,6 +1003,7 @@ Arguments: privatekey private key ocsp_file file of stapling info (server); flag for require ocsp (client) addr address if client; NULL if server (for some randomness) + cbp place to put allocated context Returns: OK/DEFER/FAIL */ @@ -1128,6 +1157,10 @@ else /* client */ # endif #endif +#ifdef EXPERIMENTAL_CERTNAMES +cbinfo->verify_cert_hostnames = NULL; +#endif + /* Set up the RSA callback */ SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback); @@ -1555,6 +1588,7 @@ if (expciphers != NULL) /* stick to the old behaviour for compatibility if tls_verify_certificates is set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only the specified host patterns if one of them is defined */ + if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) || (verify_check_host(&ob->tls_verify_hosts) == OK)) { @@ -1562,6 +1596,19 @@ if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) || ob->tls_crl, host, FALSE, verify_callback_client)) != OK) return rc; client_verify_optional = FALSE; + +#ifdef EXPERIMENTAL_CERTNAMES + if (ob->tls_verify_cert_hostnames) + { + if (!expand_check(ob->tls_verify_cert_hostnames, + US"tls_verify_cert_hostnames", + &client_static_cbinfo->verify_cert_hostnames)) + return FAIL; + if (client_static_cbinfo->verify_cert_hostnames) + DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", + client_static_cbinfo->verify_cert_hostnames); + } +#endif } else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) {