X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/ac35befe29020ebd38c62a7dd48f7977a0856048..254f38d1c5ada5e4df0bccb385dc466549620c71:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index f04cbf813..d37c78970 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -70,6 +70,8 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_CHECKHOST # define EXIM_HAVE_OPENSSL_DH_BITS # define EXIM_HAVE_OPENSSL_TLS_METHOD +# define EXIM_HAVE_OPENSSL_KEYLOG +# define EXIM_HAVE_OPENSSL_CIPHER_GET_ID # else # define EXIM_NEED_OPENSSL_INIT # endif @@ -91,6 +93,14 @@ change this guard and punt the issue for a while longer. */ # endif #endif +#ifndef LIBRESSL_VERSION_NUMBER +# if OPENSSL_VERSION_NUMBER >= 0x010101000L +# define OPENSSL_HAVE_KEYLOG_CB +# define OPENSSL_HAVE_NUM_TICKETS +# define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME +# endif +#endif + #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile" # define DISABLE_OCSP @@ -100,6 +110,13 @@ change this guard and punt the issue for a while longer. */ # include #endif +#ifndef EXIM_HAVE_OPENSSL_CIPHER_STD_NAME +# ifndef EXIM_HAVE_OPENSSL_CIPHER_GET_ID +# define SSL_CIPHER_get_id(c) (c->id) +# endif +# include "tls-cipher-stdname.c" +#endif + /************************************************* * OpenSSL option parse * *************************************************/ @@ -774,6 +791,14 @@ DEBUG(D_tls) } } +#ifdef OPENSSL_HAVE_KEYLOG_CB +static void +keylog_callback(const SSL *ssl, const char *line) +{ +DEBUG(D_tls) debug_printf("%.200s\n", line); +} +#endif + /************************************************* @@ -1768,6 +1793,9 @@ if (!RAND_status()) level. */ DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); +#ifdef OPENSSL_HAVE_KEYLOG_CB +DEBUG(D_tls) SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback); +#endif /* Automatically re-try reads/writes after renegotiation. */ (void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); @@ -1794,6 +1822,10 @@ if (init_options) else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); +#ifdef OPENSSL_HAVE_NUM_TICKETS +SSL_CTX_set_num_tickets(ctx, 0); /* send no TLS1.3 stateful-tickets */ +#endif + /* We'd like to disable session cache unconditionally, but foolish Outlook Express clients then give up the first TLS connection and make a second one (which works). Only when there is an IMAP service on the same machine. @@ -1888,28 +1920,46 @@ return OK; /* Argument: pointer to an SSL structure for the connection - buffer to use for answer - size of buffer pointer to number of bits for cipher -Returns: nothing +Returns: pointer to allocated string in perm-pool */ -static void -construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits) +static uschar * +construct_cipher_name(SSL * ssl, int * bits) { +int pool = store_pool; /* With OpenSSL 1.0.0a, 'c' needs to be const but the documentation doesn't yet reflect that. It should be a safe change anyway, even 0.9.8 versions have the accessor functions use const in the prototype. */ const uschar * ver = CUS SSL_get_version(ssl); const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl); +uschar * s; SSL_CIPHER_get_bits(c, bits); -string_format(cipherbuf, bsize, "%s:%s:%u", ver, - SSL_CIPHER_get_name(c), *bits); +store_pool = POOL_PERM; +s = string_sprintf("%s:%s:%u", ver, SSL_CIPHER_get_name(c), *bits); +store_pool = pool; +DEBUG(D_tls) debug_printf("Cipher: %s\n", s); +return s; +} + + +/* Get IETF-standard name for ciphersuite. +Argument: pointer to an SSL structure for the connection +Returns: pointer to string +*/ -DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf); +static const uschar * +cipher_stdname_ssl(SSL * ssl) +{ +#ifdef EXIM_HAVE_OPENSSL_CIPHER_STD_NAME +return CUS SSL_CIPHER_standard_name(SSL_get_current_cipher(ssl)); +#else +ushort id = 0xffff & SSL_CIPHER_get_id(SSL_get_current_cipher(ssl)); +return cipher_stdname(id >> 8, id & 0xff); +#endif } @@ -2156,7 +2206,6 @@ int rc; uschar * expciphers; tls_ext_ctx_cb * cbinfo; static uschar peerdn[256]; -static uschar cipherbuf[256]; /* Check for previous activation */ @@ -2284,14 +2333,26 @@ and initialize things. */ peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn)); -construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits); -tls_in.cipher = cipherbuf; +tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits); +tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl); DEBUG(D_tls) { uschar buf[2048]; - if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)) != NULL) + if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf))) debug_printf("Shared ciphers: %s\n", buf); + +#ifdef EXIM_HAVE_OPENSSL_KEYLOG + { + BIO * bp = BIO_new(BIO_s_mem()); + uschar * s; + int len; + SSL_SESSION_print_keylog(bp, SSL_get_session(server_ssl)); + len = (int) BIO_get_mem_data(bp, CSS &s); + debug_printf("%.*s", len, s); + BIO_free(bp); + } +#endif } /* Record the certificate we presented */ @@ -2429,34 +2490,30 @@ return DEFER; /* Called from the smtp transport after STARTTLS has been accepted. -Argument: - fd the fd of the connection - host connected host (for messages and option-tests) - addr the first address (for some randomness; can be NULL) - tb transport (always smtp) - tlsa_dnsa tlsa lookup, if DANE, else null - tlsp record details of channel configuration here; must be non-NULL - errstr error string pointer - -Returns: Pointer to TLS session context, or NULL on error +Arguments: + cctx connection context + conn_args connection details + cookie datum for randomness; can be NULL + tlsp record details of TLS channel configuration here; must be non-NULL + errstr error string pointer + +Returns: TRUE for success with TLS session context set in connection context, + FALSE on error */ -void * -tls_client_start(int fd, host_item *host, address_item *addr, - transport_instance * tb, -#ifdef SUPPORT_DANE - dns_answer * tlsa_dnsa, -#endif - tls_support * tlsp, uschar ** errstr) +BOOL +tls_client_start(client_conn_ctx * cctx, smtp_connect_args * conn_args, + void * cookie, tls_support * tlsp, uschar ** errstr) { +host_item * host = conn_args->host; /* for msgs and option-tests */ +transport_instance * tb = conn_args->tblock; /* always smtp or NULL */ smtp_transport_options_block * ob = tb ? (smtp_transport_options_block *)tb->options_block : &smtp_transport_option_defaults; exim_openssl_client_tls_ctx * exim_client_ctx; -static uschar peerdn[256]; uschar * expciphers; int rc; -static uschar cipherbuf[256]; +static uschar peerdn[256]; #ifndef DISABLE_OCSP BOOL request_ocsp = FALSE; @@ -2475,7 +2532,7 @@ tlsp->tlsa_usage = 0; #ifndef DISABLE_OCSP { # ifdef SUPPORT_DANE - if ( tlsa_dnsa + if ( conn_args->dane && ob->hosts_request_ocsp[0] == '*' && ob->hosts_request_ocsp[1] == '\0' ) @@ -2505,22 +2562,22 @@ rc = tls_init(&exim_client_ctx->ctx, host, NULL, #ifndef DISABLE_OCSP (void *)(long)request_ocsp, #endif - addr, &client_static_cbinfo, errstr); -if (rc != OK) return NULL; + cookie, &client_static_cbinfo, errstr); +if (rc != OK) return FALSE; tlsp->certificate_verified = FALSE; client_verify_callback_called = FALSE; expciphers = NULL; #ifdef SUPPORT_DANE -if (tlsa_dnsa) +if (conn_args->dane) { /* We fall back to tls_require_ciphers if unset, empty or forced failure, but other failures should be treated as problems. */ if (ob->dane_require_tls_ciphers && !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers", &expciphers, errstr)) - return NULL; + return FALSE; if (expciphers && *expciphers == '\0') expciphers = NULL; } @@ -2528,7 +2585,7 @@ if (tlsa_dnsa) if (!expciphers && !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr)) - return NULL; + return FALSE; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they are separated by underscores. So that I can use either form in my tests, and @@ -2542,12 +2599,12 @@ if (expciphers) if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers)) { tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr); - return NULL; + return FALSE; } } #ifdef SUPPORT_DANE -if (tlsa_dnsa) +if (conn_args->dane) { SSL_CTX_set_verify(exim_client_ctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, @@ -2556,12 +2613,12 @@ if (tlsa_dnsa) if (!DANESSL_library_init()) { tls_error(US"library init", host, NULL, errstr); - return NULL; + return FALSE; } if (DANESSL_CTX_init(exim_client_ctx->ctx) <= 0) { tls_error(US"context init", host, NULL, errstr); - return NULL; + return FALSE; } } else @@ -2570,21 +2627,21 @@ else if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob, client_static_cbinfo, errstr) != OK) - return NULL; + return FALSE; if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx))) { tls_error(US"SSL_new", host, NULL, errstr); - return NULL; + return FALSE; } SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx)); -SSL_set_fd(exim_client_ctx->ssl, fd); +SSL_set_fd(exim_client_ctx->ssl, cctx->sock); SSL_set_connect_state(exim_client_ctx->ssl); if (ob->tls_sni) { if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr)) - return NULL; + return FALSE; if (!tlsp->sni) { DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); @@ -2604,9 +2661,9 @@ if (ob->tls_sni) } #ifdef SUPPORT_DANE -if (tlsa_dnsa) - if (dane_tlsa_load(exim_client_ctx->ssl, host, tlsa_dnsa, errstr) != OK) - return NULL; +if (conn_args->dane) + if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK) + return FALSE; #endif #ifndef DISABLE_OCSP @@ -2650,22 +2707,41 @@ rc = SSL_connect(exim_client_ctx->ssl); ALARM_CLR(0); #ifdef SUPPORT_DANE -if (tlsa_dnsa) +if (conn_args->dane) DANESSL_cleanup(exim_client_ctx->ssl); #endif if (rc <= 0) { tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr); - return NULL; + return FALSE; } -DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); +DEBUG(D_tls) + { + debug_printf("SSL_connect succeeded\n"); +#ifdef EXIM_HAVE_OPENSSL_KEYLOG + { + BIO * bp = BIO_new(BIO_s_mem()); + if (bp) + { + uschar * s; + int len; + SSL_SESSION_print_keylog(bp, SSL_get_session(exim_client_ctx->ssl)); + len = (int) BIO_get_mem_data(bp, CSS &s); + debug_printf("%.*s", len, s); + BIO_free(bp); + } + else + debug_printf("(alloc failure for keylog)\n"); + } +#endif + } peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn)); -construct_cipher_name(exim_client_ctx->ssl, cipherbuf, sizeof(cipherbuf), &tlsp->bits); -tlsp->cipher = cipherbuf; +tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits); +tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); /* Record the certificate we presented */ { @@ -2673,9 +2749,10 @@ tlsp->cipher = cipherbuf; tlsp->ourcert = crt ? X509_dup(crt) : NULL; } -tlsp->active.sock = fd; +tlsp->active.sock = cctx->sock; tlsp->active.tls_ctx = exim_client_ctx; -return exim_client_ctx; +cctx->tls_ctx = exim_client_ctx; +return TRUE; }