X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=541c45a9435c1e9be3298e04076a48a99194cd08;hb=5c03403d88afcde2bb3f543296b0fca6f05c9f2c;hp=e47df7cb29f3f739eac6982fa2b5f408c3a2b23b;hpb=e570d1363603ca4a58401008541408d74cd0ce40;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e47df7cb2..541c45a94 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -108,6 +108,12 @@ change this guard and punt the issue for a while longer. */ # define DISABLE_OCSP #endif +#ifdef EXPERIMENTAL_TLS_RESUME +# if OPENSSL_VERSION_NUMBER < 0x0101010L +# error OpenSSL version too old for session-resumption +# endif +#endif + #ifdef EXIM_HAVE_OPENSSL_CHECKHOST # include #endif @@ -142,7 +148,7 @@ Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev static exim_openssl_option exim_openssl_options[] = { /* KEEP SORTED ALPHABETICALLY! */ #ifdef SSL_OP_ALL - { US"all", SSL_OP_ALL }, + { US"all", (long) SSL_OP_ALL }, #endif #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION }, @@ -309,7 +315,7 @@ static SSL_CTX *server_sni = NULL; static char ssl_errstring[256]; -static int ssl_session_timeout = 3600; +static int ssl_session_timeout = 7200; /* Two hours */ static BOOL client_verify_optional = FALSE; static BOOL server_verify_optional = FALSE; @@ -366,7 +372,7 @@ static int tls_server_stapling_cb(SSL *s, void *arg); -/* Daemon-called key create/rotate */ +/* Daemon-called, before every connection, key create/rotate */ #ifdef EXPERIMENTAL_TLS_RESUME static void tk_init(void); static int tls_exdata_idx = -1; @@ -520,6 +526,7 @@ if (ev) } DEBUG(D_tls) debug_printf("Event-action verify failure overridden " "(host in tls_try_verify_hosts)\n"); + tlsp->verify_override = TRUE; } X509_free(tlsp->peercert); tlsp->peercert = old_cert; @@ -579,6 +586,7 @@ if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn))) } dn[sizeof(dn)-1] = '\0'; +tlsp->verify_override = FALSE; if (preverify_ok == 0) { uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])", @@ -597,6 +605,7 @@ if (preverify_ok == 0) } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); + tlsp->verify_override = TRUE; } else if (depth != 0) @@ -673,8 +682,9 @@ else tlsp->peercert = X509_dup(cert); /* record failing cert */ return 0; /* reject */ } - DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " + DEBUG(D_tls) debug_printf("SSL verify name failure overridden (host in " "tls_try_verify_hosts)\n"); + tlsp->verify_override = TRUE; } } @@ -685,7 +695,6 @@ else DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", *calledp ? "" : " authenticated", dn); - if (!*calledp) tlsp->certificate_verified = TRUE; *calledp = TRUE; } @@ -742,7 +751,7 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n", if (preverify_ok == 1) { - tls_out.dane_verified = tls_out.certificate_verified = TRUE; + tls_out.dane_verified = TRUE; #ifndef DISABLE_OCSP if (client_static_cbinfo->u_ocsp.client.verify_store) { /* client, wanting stapling */ @@ -834,25 +843,24 @@ typedef struct { /* Session ticket encryption key */ uschar name[16]; const EVP_CIPHER * aes_cipher; - uschar aes_key[16]; /* size needed depends on cipher. aes_128 implies 128/8 = 16? */ + uschar aes_key[32]; /* size needed depends on cipher. aes_128 implies 128/8 = 16? */ const EVP_MD * hmac_hash; uschar hmac_key[16]; time_t renew; time_t expire; } exim_stek; -/*XXX for now just always create/find the one key. -Worry about rotation and overlap later. */ - -static exim_stek exim_tk; -static exim_stek exim_tk_old; +static exim_stek exim_tk; /* current key */ +static exim_stek exim_tk_old; /* previous key */ static void tk_init(void) { +time_t t = time(NULL); + if (exim_tk.name[0]) { - if (exim_tk.renew >= time(NULL)) return; + if (exim_tk.renew >= t) return; exim_tk_old = exim_tk; } @@ -864,10 +872,10 @@ if (RAND_bytes(exim_tk.hmac_key, sizeof(exim_tk.hmac_key)) <= 0) return; if (RAND_bytes(exim_tk.name+1, sizeof(exim_tk.name)-1) <= 0) return; exim_tk.name[0] = 'E'; -exim_tk.aes_cipher = EVP_aes_128_cbc(); +exim_tk.aes_cipher = EVP_aes_256_cbc(); exim_tk.hmac_hash = EVP_sha256(); -exim_tk.expire = time(NULL) + ssl_session_timeout; -exim_tk.renew = exim_tk.expire - ssl_session_timeout/2; +exim_tk.expire = t + ssl_session_timeout; +exim_tk.renew = t + ssl_session_timeout/2; } static exim_stek * @@ -904,7 +912,7 @@ if (enc) if (!(key = tk_current())) /* current key doesn't exist or isn't valid */ return 0; /* key couldn't be created */ memcpy(key_name, key->name, 16); - DEBUG(D_tls) debug_printf("STEK expire %ld\n", key->expire - time(NULL)); + DEBUG(D_tls) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - time(NULL)); /*XXX will want these dependent on the ssl session strength */ HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key), @@ -926,7 +934,7 @@ else DEBUG(D_tls) { debug_printf("ticket not usable (%s)\n", key ? "expired" : "not found"); - if (key) debug_printf("STEK expire %ld\n", key->expire - now); + if (key) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - now); } return 0; } @@ -935,7 +943,13 @@ else key->hmac_hash, NULL); EVP_DecryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv); - DEBUG(D_tls) debug_printf("ticket usable, STEK expire %ld\n", key->expire - now); + DEBUG(D_tls) debug_printf("ticket usable, STEK expire " TIME_T_FMT "\n", key->expire - now); + + /* The ticket lifetime and renewal are the same as the STEK lifetime and + renewal, which is overenthusiastic. A factor of, say, 3x longer STEK would + be better. To do that we'd have to encode ticket lifetime in the name as + we don't yet see the restored session. Could check posthandshake for TLS1.3 + and trigger a new ticket then, but cannot do that for TLS1.2 */ return key->renew < now ? 2 : 1; } } @@ -1556,7 +1570,7 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername, /* Make the extension value available for expansion */ store_pool = POOL_PERM; -tls_in.sni = string_copy(US servername); +tls_in.sni = string_copy_taint(US servername, TRUE); store_pool = old_pool; if (!reexpand_tls_files_for_sni) @@ -1701,17 +1715,17 @@ if(!p) return cbinfo->u_ocsp.client.verify_required ? 0 : 1; } -if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) - { - tls_out.ocsp = OCSP_FAILED; +if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) + { + tls_out.ocsp = OCSP_FAILED; /*XXX should use tlsp-> to permit concurrent outbound */ if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); else DEBUG(D_tls) debug_printf(" parse error\n"); return 0; - } + } -if(!(bs = OCSP_response_get1_basic(rsp))) +if (!(bs = OCSP_response_get1_basic(rsp))) { tls_out.ocsp = OCSP_FAILED; if (LOGGING(tls_cipher)) @@ -2148,8 +2162,24 @@ if (tlsp->peercert) { DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n"); } else { - peerdn[siz-1] = '\0'; - tlsp->peerdn = peerdn; /*XXX a static buffer... */ + int oldpool = store_pool; + + peerdn[siz-1] = '\0'; /* paranoia */ + store_pool = POOL_PERM; + tlsp->peerdn = string_copy(peerdn); + store_pool = oldpool; + + /* We used to set CV in the cert-verify callbacks (either plain or dane) + but they don't get called on session-resumption. So use the official + interface, which uses the resumed value. Unfortunately this claims verified + when it actually failed but we're in try-verify mode, due to us wanting the + knowlege that it failed so needing to have the callback and forcing a + permissive return. If we don't force it, the TLS startup is failed. + The extra bit of information is set in verify_override in the cb, stashed + for resumption next to the TLS session, and used here. */ + + if (!tlsp->verify_override) + tlsp->certificate_verified = SSL_get_verify_result(ssl) == X509_V_OK; } } @@ -2539,7 +2569,7 @@ DEBUG(D_tls) #ifdef EXIM_HAVE_SESSION_TICKET { SSL_SESSION * ss = SSL_get_session(server_ssl); - if (SSL_SESSION_has_ticket(ss)) + if (SSL_SESSION_has_ticket(ss)) /* 1.1.0 */ debug_printf("The session has a ticket, life %lu seconds\n", SSL_SESSION_get_ticket_lifetime_hint(ss)); } @@ -2709,6 +2739,14 @@ if (tlsp->host_resumable) debug_printf("decoding session: %s\n", ssl_errstring); } } +#ifdef EXIM_HAVE_SESSION_TICKET + else if ( SSL_SESSION_get_ticket_lifetime_hint(ss) + dt->time_stamp + < time(NULL)) + { + DEBUG(D_tls) debug_printf("session expired\n"); + dbfn_delete(dbm_file, key); + } +#endif else if (!SSL_set_session(ssl, ss)) { DEBUG(D_tls) @@ -2722,6 +2760,8 @@ if (tlsp->host_resumable) { DEBUG(D_tls) debug_printf("good session\n"); tlsp->resumption |= RESUME_CLIENT_SUGGESTED; + tlsp->verify_override = dt->verify_override; + tlsp->ocsp = dt->ocsp; } } else @@ -2744,20 +2784,22 @@ DEBUG(D_tls) debug_printf("tls_save_session_cb\n"); if (!cbinfo || !(tlsp = cbinfo->tlsp)->host_resumable) return 0; -# ifdef EXIM_HAVE_SESSION_TICKET - -if (SSL_SESSION_is_resumable(ss)) +# ifdef OPENSSL_HAVE_NUM_TICKETS +if (SSL_SESSION_is_resumable(ss)) /* 1.1.1 */ +# endif { int len = i2d_SSL_SESSION(ss, NULL); int dlen = sizeof(dbdata_tls_session) + len; - dbdata_tls_session * dt = store_get(dlen); + dbdata_tls_session * dt = store_get(dlen, TRUE); uschar * s = dt->session; open_db dbblock, * dbm_file; DEBUG(D_tls) debug_printf("session is resumable\n"); tlsp->resumption |= RESUME_SERVER_TICKET; /* server gave us a ticket */ - len = i2d_SSL_SESSION(ss, &s); /* s gets bumped to end */ + dt->verify_override = tlsp->verify_override; + dt->ocsp = tlsp->ocsp; + (void) i2d_SSL_SESSION(ss, &s); /* s gets bumped to end */ if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE))) { @@ -2769,7 +2811,6 @@ if (SSL_SESSION_is_resumable(ss)) (unsigned)dlen); } } -# endif return 1; } @@ -2867,7 +2908,7 @@ BOOL require_ocsp = FALSE; rc = store_pool; store_pool = POOL_PERM; -exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx)); +exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE); exim_client_ctx->corked = NULL; store_pool = rc; @@ -2987,12 +3028,6 @@ if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx))) } SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx)); -#ifdef EXPERIMENTAL_TLS_RESUME -if (!tls_client_ssl_resume_prehandshake(exim_client_ctx->ssl, tlsp, host, - errstr)) - return FALSE; -#endif - SSL_set_fd(exim_client_ctx->ssl, cctx->sock); SSL_set_connect_state(exim_client_ctx->ssl); @@ -3052,6 +3087,12 @@ if (request_ocsp) } #endif +#ifdef EXPERIMENTAL_TLS_RESUME +if (!tls_client_ssl_resume_prehandshake(exim_client_ctx->ssl, tlsp, host, + errstr)) + return FALSE; +#endif + #ifndef DISABLE_EVENT client_static_cbinfo->event_action = tb ? tb->event_action : NULL; #endif @@ -3148,32 +3189,10 @@ switch(error) case SSL_ERROR_ZERO_RETURN: DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); - receive_getc = smtp_getc; - receive_getbuf = smtp_getbuf; - receive_get_cache = smtp_get_cache; - receive_ungetc = smtp_ungetc; - receive_feof = smtp_feof; - receive_ferror = smtp_ferror; - receive_smtp_buffered = smtp_buffered; - if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN) SSL_shutdown(server_ssl); -#ifndef DISABLE_OCSP - sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free); - server_static_cbinfo->verify_stack = NULL; -#endif - SSL_free(server_ssl); - SSL_CTX_free(server_ctx); - server_ctx = NULL; - server_ssl = NULL; - tls_in.active.sock = -1; - tls_in.active.tls_ctx = NULL; - tls_in.bits = 0; - tls_in.cipher = NULL; - tls_in.peerdn = NULL; - tls_in.sni = NULL; - + tls_close(NULL, TLS_NO_SHUTDOWN); return FALSE; /* Handle genuine errors */ @@ -3354,14 +3373,14 @@ a store reset there, so use POOL_PERM. */ if ((more || corked)) { -#ifdef EXPERIMENTAL_PIPE_CONNECT +#ifdef SUPPORT_PIPE_CONNECT int save_pool = store_pool; store_pool = POOL_PERM; #endif corked = string_catn(corked, buff, len); -#ifdef EXPERIMENTAL_PIPE_CONNECT +#ifdef SUPPORT_PIPE_CONNECT store_pool = save_pool; #endif @@ -3462,14 +3481,25 @@ if (shutdown) } } -#ifndef DISABLE_OCSP if (!o_ctx) /* server side */ { +#ifndef DISABLE_OCSP sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free); server_static_cbinfo->verify_stack = NULL; - } #endif + receive_getc = smtp_getc; + receive_getbuf = smtp_getbuf; + receive_get_cache = smtp_get_cache; + receive_ungetc = smtp_ungetc; + receive_feof = smtp_feof; + receive_ferror = smtp_ferror; + receive_smtp_buffered = smtp_buffered; + tls_in.active.tls_ctx = NULL; + tls_in.sni = NULL; + /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */ + } + SSL_CTX_free(*ctxp); SSL_free(*sslp); *ctxp = NULL;