From b10c87b38c2345d15d30da5c18c823355ac506a9 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 2 May 2019 17:16:05 +0100 Subject: [PATCH] TLS: Session resumption, under the EXPERIMENTAL_TLS_RESUME build option. --- doc/doc-docbook/spec.xfpt | 4 +- doc/doc-txt/ChangeLog | 2 + doc/doc-txt/NewStuff | 3 + doc/doc-txt/experimental-spec.txt | 44 +++ src/OS/Makefile-Base | 4 +- src/src/acl.c | 2 +- src/src/config.h.defaults | 1 + src/src/daemon.c | 5 + src/src/dbfn.c | 7 +- src/src/dbfunctions.h | 2 +- src/src/dbstuff.h | 6 + src/src/deliver.c | 23 +- src/src/enq.c | 4 +- src/src/exim.c | 3 + src/src/exim_dbutil.c | 33 +- src/src/expand.c | 8 +- src/src/functions.h | 1 + src/src/globals.c | 39 +-- src/src/globals.h | 8 + src/src/macro_predef.c | 5 +- src/src/macro_predef.h | 2 +- src/src/macros.h | 22 +- src/src/readconf.c | 3 + src/src/receive.c | 6 + src/src/retry.c | 4 +- src/src/smtp_in.c | 6 + src/src/spool_in.c | 5 + src/src/spool_out.c | 3 + src/src/structs.h | 3 + src/src/tls-gnu.c | 339 ++++++++++++++---- src/src/tls-openssl.c | 399 +++++++++++++++++++++- src/src/tls.c | 6 +- src/src/transport.c | 4 +- src/src/transports/smtp.c | 18 +- src/src/transports/smtp.h | 3 + src/src/verify.c | 8 +- test/aux-var-src/tls_conf_prefix | 2 +- test/confs/5890 | 94 +++++ test/confs/5891 | 94 +++++ test/log/2102 | 2 +- test/log/5890 | 116 +++++++ test/log/5891 | 130 +++++++ test/runtest | 14 +- test/scripts/5890-Resume-GnuTLS/5890 | 62 ++++ test/scripts/5890-Resume-GnuTLS/REQUIRES | 3 + test/scripts/5891-Resume-OpenSSL/5891 | 57 ++++ test/scripts/5891-Resume-OpenSSL/REQUIRES | 3 + test/stderr/5890 | 6 + test/stderr/5891 | 6 + test/stdout/0572 | 4 +- test/stdout/0577 | 2 +- test/stdout/5890 | 6 + test/stdout/5891 | 6 + 53 files changed, 1470 insertions(+), 172 deletions(-) create mode 100644 test/confs/5890 create mode 100644 test/confs/5891 create mode 100644 test/log/5890 create mode 100644 test/log/5891 create mode 100644 test/scripts/5890-Resume-GnuTLS/5890 create mode 100644 test/scripts/5890-Resume-GnuTLS/REQUIRES create mode 100644 test/scripts/5891-Resume-OpenSSL/5891 create mode 100644 test/scripts/5891-Resume-OpenSSL/REQUIRES create mode 100644 test/stderr/5890 create mode 100644 test/stderr/5891 create mode 100644 test/stdout/5890 create mode 100644 test/stdout/5891 diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 7a7608bd6..783aeb429 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -16169,7 +16169,7 @@ harm. This option overrides the &%pipe_as_creator%& option of the &(pipe)& transport driver. -.option openssl_options main "string list" "+no_sslv2 +single_dh_use +no_ticket" +.option openssl_options main "string list" "+no_sslv2 +no_sslv3 +single_dh_use +no_ticket" .cindex "OpenSSL "compatibility options" This option allows an administrator to adjust the SSL options applied by OpenSSL to connections. It is given as a space-separated list of items, @@ -28319,7 +28319,7 @@ There is no current way to staple a proof for a client certificate. -.section "Configuring an Exim client to use TLS" "SECID185" +.section "Configuring an Exim client to use TLS" "SECTclientTLS" .cindex "cipher" "logging" .cindex "log" "TLS cipher" .cindex "log" "distinguished name" diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index a85841af6..59a025b2a 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -89,6 +89,8 @@ JH/16 GnuTLS: rework ciphersuite strings under recent library versions. Thanks This affects log line X= elements, the $tls_{in,out}_cipher variables, and the use of specific cipher names in the encrypted= ACL condition. +JH/17 OpenSSL: the default openssl_options now disables ssl_v3. + Exim version 4.92 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index e776a4f95..352833c4b 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -20,6 +20,9 @@ Version 4.93 5. A case_insensitive option for verify=not_blind. + 6. EXPERIMENTAL_TLS_RESUME optional build feature. See the experimental.spec + file. + Version 4.92 -------------- diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt index 2f1e5c591..a2861c4a9 100644 --- a/doc/doc-txt/experimental-spec.txt +++ b/doc/doc-txt/experimental-spec.txt @@ -951,6 +951,50 @@ Transport configurations should be checked for this. An example avoidance: +TLS Session Resumption +---------------------- +TLS Session Resumption for TLS 1.2 and TLS1.3 connections can be used (defined +in RFC 5077 for 1.2). The support for this can be included by building with +EXPERIMENTAL_TLS_RESUME defined. + +Session resumption (this is the "stateless" variant) involves the server sending +a "session ticket" to the client on one connection, which can be stored by the +client and used for a later session. The ticket contains sufficient state for +the server to reconstruct the TLS session, avoiding some expensive crypto +calculation and one full packet roundtrip time. + +Operational cost/benefit: + The extra data being transmitted costs a minor amount, and the client has +extra costs in storing and retrieving the data. + +In the Exim/Gnutls implementation the extra cost on an initial connection +which is TLS1.2 over a loopback path is about 6ms on 2017-laptop class hardware. +The saved cost on a subsequent connection is about 4ms; three or more +connections become a net win. On longer network paths, two or more +connections will have an average lower startup time thanks to the one +saved packet roundtrip. TLS1.3 will save the crypto cpu costs but not any +packet roundtrips. + +Security aspects: + The session ticket is encrypted, but is obviously an additional security +vulnarability surface. An attacker able to decrypt it would have access +all connections using the resumed session. +The session ticket encryption key is not committed to storage by the server +and is rotated regularly. Tickets have limited lifetime. + +There is a question-mark over the security of the Diffie-Helman parameters +used for session negotiation. TBD. q-value; cf bug 1895 + +Observability: + New log_selector "tls_resumption", appends an asterisk to the tls_cipher "X=" +element. + +Variables $tls_{in,out}_resumption have bit 0-4 indicating respectively +support built, client requested ticket, client offered session, +server issued ticket, resume used. A suitable decode list is provided +in the builtin macro _RESUME_DECODE for ${listextract {}{}}. + + -------------------------------------------------------------- End of file -------------------------------------------------------------- diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 0fbee9d03..8ca9a485c 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -123,7 +123,7 @@ config.h: Makefile buildconfig ../src/config.h.defaults $(EDITME) # Build the builtin-macros data struct -MACRO_HSRC = macro_predef.h os.h globals.h config.h \ +MACRO_HSRC = macro_predef.h os.h globals.h config.h macros.h \ routers/accept.h routers/dnslookup.h routers/ipliteral.h \ routers/iplookup.h routers/manualroute.h routers/queryprogram.h \ routers/redirect.h @@ -158,7 +158,7 @@ macro-transport.o: transport.c macro-drtables.o : drtables.c @echo "$(CC) -DMACRO_PREDEF drtables.c" $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ drtables.c -macro-tls.o: tls.c +macro-tls.o: tls.c tls-gnu.c tls-openssl.c @echo "$(CC) -DMACRO_PREDEF tls.c" $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ tls.c macro-appendfile.o : transports/appendfile.c diff --git a/src/src/acl.c b/src/src/acl.c index fdd32b8e7..19938affa 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2406,7 +2406,7 @@ if ((t = tree_search(*anchor, key))) /* We aren't using a pre-computed rate, so get a previously recorded rate from the database, which will be updated and written back if required. */ -if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE))) +if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE, TRUE))) { store_pool = old_pool; sender_rate = NULL; diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index a27ad3fd2..c5d5389ba 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -203,6 +203,7 @@ Do not put spaces between # and the 'define'. #define EXPERIMENTAL_PIPE_CONNECT #define EXPERIMENTAL_QUEUEFILE #define EXPERIMENTAL_SRS +#define EXPERIMENTAL_TLS_RESUME /* For developers */ diff --git a/src/src/daemon.c b/src/src/daemon.c index 4addf0ac7..cf5e09201 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1985,6 +1985,11 @@ for (;;) handle_ending_processes(); errno = select_errno; +#ifdef SUPPORT_TLS + /* Create or rotate any required keys */ + tls_daemon_init(); +#endif + /* Loop for all the sockets that are currently ready to go. If select actually failed, we have set the count to 1 and select_failed=TRUE, so as to use the common error code for select/accept below. */ diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 5555c710b..a60775681 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -72,6 +72,7 @@ Arguments: dbblock Points to an open_db block to be filled in. lof If TRUE, write to the log for actual open failures (locking failures are always logged). + panic If TRUE, panic on failure to create the db directory Returns: NULL if the open failed, or the locking failed. After locking failures, errno is zero. @@ -85,7 +86,7 @@ moment I haven't changed them. */ open_db * -dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof) +dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic) { int rc, save_errno; BOOL read_only = flags == O_RDONLY; @@ -114,7 +115,7 @@ snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name); if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) { created = TRUE; - (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, TRUE); + (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic); dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); } @@ -536,7 +537,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) } start = clock(); - odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE); + odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE); stop = clock(); if (odb) diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index 93d12efc3..2e18e0eff 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -10,7 +10,7 @@ void dbfn_close(open_db *); int dbfn_delete(open_db *, const uschar *); -open_db *dbfn_open(uschar *, int, open_db *, BOOL); +open_db *dbfn_open(uschar *, int, open_db *, BOOL, BOOL); void *dbfn_read_with_length(open_db *, const uschar *, int *); uschar *dbfn_scan(open_db *, BOOL, EXIM_CURSOR **); int dbfn_write(open_db *, const uschar *, void *, int); diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h index 02cfa1446..6b1ae0ebb 100644 --- a/src/src/dbstuff.h +++ b/src/src/dbstuff.h @@ -804,5 +804,11 @@ typedef struct { } dbdata_ehlo_resp; #endif +typedef struct { + time_t time_stamp; + /*************/ + uschar session[1]; +} dbdata_tls_session; + /* End of dbstuff.h */ diff --git a/src/src/deliver.c b/src/src/deliver.c index 696effdee..f79522d8e 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -803,12 +803,18 @@ return g; #ifdef SUPPORT_TLS static gstring * -d_tlslog(gstring * s, address_item * addr) +d_tlslog(gstring * g, address_item * addr) { if (LOGGING(tls_cipher) && addr->cipher) - s = string_append(s, 2, US" X=", addr->cipher); + { + g = string_append(g, 2, US" X=", addr->cipher); +#ifdef EXPERIMENTAL_TLS_RESUME + if (LOGGING(tls_resumption) && testflag(addr, af_tls_resume)) + g = string_catn(g, US"*", 1); +#endif + } if (LOGGING(tls_certificate_verified) && addr->cipher) - s = string_append(s, 2, US" CV=", + g = string_append(g, 2, US" CV=", testflag(addr, af_cert_verified) ? #ifdef SUPPORT_DANE @@ -819,8 +825,8 @@ if (LOGGING(tls_certificate_verified) && addr->cipher) "yes" : "no"); if (LOGGING(tls_peerdn) && addr->peerdn) - s = string_append(s, 3, US" DN=\"", string_printing(addr->peerdn), US"\""); -return s; + g = string_append(g, 3, US" DN=\"", string_printing(addr->peerdn), US"\""); +return g; } #endif @@ -2900,7 +2906,7 @@ while (addr_local) of these checks, rather than for all local deliveries, because some local deliveries (e.g. to pipes) can take a substantial time. */ - if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) + if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE))) { DEBUG(D_deliver|D_retry|D_hints_lookup) debug_printf("no retry data available\n"); @@ -4794,6 +4800,9 @@ all pipes, so I do not see a reason to use non-blocking IO here #ifdef SUPPORT_DANE if (tls_out.dane_verified) setflag(addr, af_dane_verified); #endif +# ifdef EXPERIMENTAL_TLS_RESUME + if (tls_out.resumption & RESUME_USED) setflag(addr, af_tls_resume); +# endif /* Use an X item only if there's something to send */ #ifdef SUPPORT_TLS @@ -6321,7 +6330,7 @@ while (addr_new) /* Loop until all addresses dealt with */ /* Failure to open the retry database is treated the same as if it does not exist. In both cases, dbm_file is NULL. */ - if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) + if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE))) DEBUG(D_deliver|D_retry|D_route|D_hints_lookup) debug_printf("no retry data available\n"); diff --git a/src/src/enq.c b/src/src/enq.c index 573fc00ca..7feba5531 100644 --- a/src/src/enq.c +++ b/src/src/enq.c @@ -47,7 +47,7 @@ deliberate; the dbfn_open() function - which is an Exim function - always tries to create if it can't open a read/write file. It expects only O_RDWR or O_RDONLY as its argument. */ -if (!(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) +if (!(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) return FALSE; /* See if there is a record for this host or queue run; if there is, we cannot @@ -101,7 +101,7 @@ dbdata_serialize *serial_record; DEBUG(D_transport) debug_printf("end serialized: %s\n", key); -if ( !(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)) +if ( !(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)) || !(serial_record = dbfn_read(dbm_file, key)) ) return; diff --git a/src/src/exim.c b/src/src/exim.c index df76de15e..1952d91a4 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -934,6 +934,9 @@ fprintf(fp, "Support for:"); #ifdef EXPERIMENTAL_PIPE_CONNECT fprintf(fp, " Experimental_PIPE_CONNECT"); #endif +#ifdef EXPERIMENTAL_TLS_RESUME + fprintf(fp, " Experimental_TLS_resume"); +#endif fprintf(fp, "\n"); fprintf(fp, "Lookups (built-in):"); diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index a0514f009..2c7aad63b 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -20,6 +20,7 @@ argument is the name of the database file. The available names are: misc: miscellaneous hints data wait-: message waiting information; is a transport name callout: callout verification cache + tls: TLS session resumption cache There are a number of common subroutines, followed by three main programs, whose inclusion is controlled by -D on the compilation command. */ @@ -35,6 +36,7 @@ whose inclusion is controlled by -D on the compilation command. */ #define type_misc 3 #define type_callout 4 #define type_ratelimit 5 +#define type_tls 6 /* This is used by our cut-down dbfn_open(). */ @@ -91,7 +93,7 @@ static void usage(uschar *name, uschar *options) { printf("Usage: exim_%s%s \n", name, options); -printf(" = retry | misc | wait- | callout | ratelimit\n"); +printf(" = retry | misc | wait- | callout | ratelimit | tls\n"); exit(1); } @@ -114,6 +116,7 @@ if (argc == 3) if (Ustrncmp(argv[2], "wait-", 5) == 0) return type_wait; if (Ustrcmp(argv[2], "callout") == 0) return type_callout; if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit; + if (Ustrcmp(argv[2], "tls") == 0) return type_tls; } usage(name, options); return -1; /* Never obeyed */ @@ -240,6 +243,7 @@ Arguments: flags O_RDONLY or O_RDWR dbblock Points to an open_db block to be filled in. lof Unused. + panic Unused Returns: NULL if the open failed, or the locking failed. On success, dbblock is returned. This contains the dbm pointer and @@ -247,7 +251,7 @@ Returns: NULL if the open failed, or the locking failed. */ open_db * -dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof) +dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic) { int rc; struct flock lock_data; @@ -525,7 +529,7 @@ uschar keybuffer[1024]; dbdata_type = check_args(argc, argv, US"dumpdb", US""); spool_directory = argv[1]; -if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE))) +if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE, TRUE))) exit(1); /* Scan the file, formatting the information for each entry. Note @@ -541,6 +545,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); dbdata_callout_cache *callout; dbdata_ratelimit *ratelimit; dbdata_ratelimit_unique *rate_unique; + dbdata_tls_session *session; int count_bad = 0; int length; uschar *t; @@ -673,6 +678,11 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); keybuffer); } break; + + case type_tls: + session = (dbdata_tls_session *)value; + printf(" %s %.*s\n", keybuffer, length, session->session); + break; } store_reset(value); } @@ -745,6 +755,7 @@ for(;;) dbdata_callout_cache *callout; dbdata_ratelimit *ratelimit; dbdata_ratelimit_unique *rate_unique; + dbdata_tls_session *session; int oldlength; uschar *t; uschar field[256], value[256]; @@ -783,7 +794,7 @@ for(;;) int verify = 1; spool_directory = argv[1]; - if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE))) + if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE))) continue; if (Ustrcmp(field, "d") == 0) @@ -925,6 +936,10 @@ for(;;) break; } break; + + case type_tls: + printf("Can't change contents of tls database record\n"); + break; } dbfn_write(dbm, name, record, oldlength); @@ -949,7 +964,7 @@ for(;;) /* Handle a read request, or verify after an update. */ spool_directory = argv[1]; - if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE))) + if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE, TRUE))) continue; if (!(record = dbfn_read_with_length(dbm, name, &oldlength))) @@ -1036,6 +1051,12 @@ for(;;) printf("5 add element to filter\n"); } break; + + case type_tls: + session = (dbdata_tls_session *)value; + printf("0 time stamp: %s\n", print_time(session->time_stamp)); + printf("1 session: .%s\n", session->session); + break; } } @@ -1134,7 +1155,7 @@ oldest = time(NULL) - maxkeep; printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]); spool_directory = argv[1]; -if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE))) +if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE))) exit(1); /* Prepare for building file names */ diff --git a/src/src/expand.c b/src/src/expand.c index ff1b72615..d8ea87dee 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -751,6 +751,9 @@ static var_entry var_table[] = { { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, +#ifdef EXPERIMENTAL_TLS_RESUME + { "tls_in_resumption", vtype_int, &tls_in.resumption }, +#endif #if defined(SUPPORT_TLS) { "tls_in_sni", vtype_stringptr, &tls_in.sni }, #endif @@ -765,6 +768,9 @@ static var_entry var_table[] = { { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, { "tls_out_peercert", vtype_cert, &tls_out.peercert }, { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn }, +#ifdef EXPERIMENTAL_TLS_RESUME + { "tls_out_resumption", vtype_int, &tls_out.resumption }, +#endif #if defined(SUPPORT_TLS) { "tls_out_sni", vtype_stringptr, &tls_out.sni }, #endif @@ -5039,7 +5045,7 @@ while (*s != 0) port = ntohs(service_info->s_port); } - /*XXX we trust that the request is idempotent. Hmm. */ + /*XXX we trust that the request is idempotent for TFO. Hmm. */ cctx.sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port, timeout, &host, &expand_string_message, do_tls ? NULL : &reqstr); diff --git a/src/src/functions.h b/src/src/functions.h index 2eb74579f..87953c413 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -51,6 +51,7 @@ extern BOOL tls_client_start(client_conn_ctx *, smtp_connect_args *, extern void tls_close(void *, int); extern BOOL tls_could_read(void); +extern void tls_daemon_init(void); extern int tls_export_cert(uschar *, size_t, void *); extern int tls_feof(void); extern int tls_ferror(void); diff --git a/src/src/globals.c b/src/src/globals.c index acabeb8c3..a2fa032bc 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -102,38 +102,12 @@ them. Also, the tls_ variables are now always visible. Note that these are only used for smtp connections, not for service-daemon access. */ tls_support tls_in = { - .active = {.sock = -1}, - .bits = 0, - .certificate_verified = FALSE, -#ifdef SUPPORT_DANE - .dane_verified = FALSE, - .tlsa_usage = 0, -#endif - .cipher = NULL, - .on_connect = FALSE, - .on_connect_ports = NULL, - .ourcert = NULL, - .peercert = NULL, - .peerdn = NULL, - .sni = NULL, - .ocsp = OCSP_NOT_REQ + .active = {.sock = -1} + /* all other elements zero */ }; tls_support tls_out = { .active = {.sock = -1}, - .bits = 0, - .certificate_verified = FALSE, -#ifdef SUPPORT_DANE - .dane_verified = FALSE, - .tlsa_usage = 0, -#endif - .cipher = NULL, - .on_connect = FALSE, - .on_connect_ports = NULL, - .ourcert = NULL, - .peercert = NULL, - .peerdn = NULL, - .sni = NULL, - .ocsp = OCSP_NOT_REQ + /* all other elements zero */ }; uschar *dsn_envid = NULL; @@ -161,6 +135,9 @@ uschar *tls_ocsp_file = NULL; uschar *tls_privatekey = NULL; BOOL tls_remember_esmtp = FALSE; uschar *tls_require_ciphers = NULL; +# ifdef EXPERIMENTAL_TLS_RESUME +uschar *tls_resumption_hosts = NULL; +# endif uschar *tls_try_verify_hosts = NULL; uschar *tls_verify_certificates= US"system"; uschar *tls_verify_hosts = NULL; @@ -1047,7 +1024,8 @@ uschar *log_file_path = US LOG_FILE_PATH int log_notall[] = { -1 }; -bit_table log_options[] = { /* must be in alphabetical order */ +bit_table log_options[] = { /* must be in alphabetical order, + with definitions from enum logbit. */ BIT_TABLE(L, 8bitmime), BIT_TABLE(L, acl_warn_skipped), BIT_TABLE(L, address_rewrite), @@ -1105,6 +1083,7 @@ bit_table log_options[] = { /* must be in alphabetical order */ BIT_TABLE(L, tls_certificate_verified), BIT_TABLE(L, tls_cipher), BIT_TABLE(L, tls_peerdn), + BIT_TABLE(L, tls_resumption), BIT_TABLE(L, tls_sni), BIT_TABLE(L, unknown_in_list), }; diff --git a/src/src/globals.h b/src/src/globals.h index a0c1977a2..1aacaf7e6 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -103,6 +103,11 @@ typedef struct { OCSP_FAILED, /* verify failed */ OCSP_VFIED /* verified */ } ocsp; /* Stapled OCSP status */ +#ifdef EXPERIMENTAL_TLS_RESUME + unsigned resumption; /* Session resumption */ + BOOL host_resumable:1; + BOOL ticket_received:1; +#endif } tls_support; extern tls_support tls_in; extern tls_support tls_out; @@ -124,6 +129,9 @@ extern uschar *tls_ocsp_file; /* OCSP stapling proof file */ extern uschar *tls_privatekey; /* Private key file */ extern BOOL tls_remember_esmtp; /* For YAEB */ extern uschar *tls_require_ciphers; /* So some can be avoided */ +# ifdef EXPERIMENTAL_TLS_RESUME +extern uschar *tls_resumption_hosts; /* TLS session resumption */ +# endif extern uschar *tls_try_verify_hosts; /* Optional client verification */ extern uschar *tls_verify_certificates;/* Path for certificates to check */ extern uschar *tls_verify_hosts; /* Mandatory client verification */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 86be52f54..33249133a 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -200,6 +200,9 @@ due to conflicts with other common macros. */ #ifdef EXPERIMENTAL_PIPE_CONNECT builtin_macro_create(US"_HAVE_PIPE_CONNECT"); #endif +#ifdef EXPERIMENTAL_TLS_RESUME + builtin_macro_create(US"_HAVE_TLS_RESUME"); +#endif #ifdef LOOKUP_LSEARCH builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH"); @@ -287,7 +290,7 @@ options_routers(); options_transports(); options_auths(); options_logging(); -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +#if defined(SUPPORT_TLS) options_tls(); #endif } diff --git a/src/src/macro_predef.h b/src/src/macro_predef.h index f265750ca..79a8d6f15 100644 --- a/src/src/macro_predef.h +++ b/src/src/macro_predef.h @@ -20,7 +20,7 @@ extern void options_transports(void); extern void options_auths(void); extern void options_logging(void); extern void params_dkim(void); -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +#if defined(SUPPORT_TLS) extern void options_tls(void); #endif diff --git a/src/src/macros.h b/src/src/macros.h index f98d5e6bd..e3f1f4c28 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -435,11 +435,12 @@ enum { /* Options bits for logging. Those that have values < BITWORDSIZE can be used in calls to log_write(). The others are put into later words in log_selector and are only ever tested independently, so they do not need bit mask -declarations. The Li_all value is recognized specially by decode_bits(). */ +declarations. The Li_all value is recognized specially by decode_bits(). +Add also to log_options[] when creating new ones. */ #define LOG_BIT(name) Li_##name = IOTA(Li_iota), L_##name = BIT(Li_##name) -enum { +enum logbit { Li_all = -1, Li_iota = IOTA_INIT(0), @@ -495,6 +496,7 @@ enum { Li_tls_certificate_verified, Li_tls_cipher, Li_tls_peerdn, + Li_tls_resumption, Li_tls_sni, Li_unknown_in_list, @@ -1077,5 +1079,21 @@ should not be one active. */ #define AUTH_ITEM_IGN64 BIT(2) +/* Flags for tls_{in,out}_resumption */ +#define RESUME_SUPPORTED BIT(0) +#define RESUME_CLIENT_REQUESTED BIT(1) +#define RESUME_CLIENT_SUGGESTED BIT(2) +#define RESUME_SERVER_TICKET BIT(3) +#define RESUME_USED BIT(4) + +#define RESUME_DECODE_STRING \ + US"not requested or offered : 0x02 :client requested, no server ticket" \ + ": 0x04 : 0x05 : 0x06 :client offered session, no server action" \ + ": 0x08 :no client request: 0x0A :client requested new ticket, server provided" \ + ": 0x0C :client offered session, not used: 0x0E :client offered session, server only provided new ticket" \ + ": 0x10 :session resumed unasked: 0x12 :session resumed unasked" \ + ": 0x14 : 0x15 : 0x16 :session resumed" \ + ": 0x18 :session resumed unasked: 0x1A :session resumed unasked" \ + ": 0x1C :session resumed: 0x1E :session resumed, also new ticket" /* End of macros.h */ diff --git a/src/src/readconf.c b/src/src/readconf.c index 150d7973d..cac8fe5c2 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -367,6 +367,9 @@ static optionlist optionlist_config[] = { { "tls_privatekey", opt_stringptr, &tls_privatekey }, { "tls_remember_esmtp", opt_bool, &tls_remember_esmtp }, { "tls_require_ciphers", opt_stringptr, &tls_require_ciphers }, +# ifdef EXPERIMENTAL_TLS_RESUME + { "tls_resumption_hosts", opt_stringptr, &tls_resumption_hosts }, +# endif { "tls_try_verify_hosts", opt_stringptr, &tls_try_verify_hosts }, { "tls_verify_certificates", opt_stringptr, &tls_verify_certificates }, { "tls_verify_hosts", opt_stringptr, &tls_verify_hosts }, diff --git a/src/src/receive.c b/src/src/receive.c index 701d540b0..fbd32c8fa 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3980,7 +3980,13 @@ g = add_host_info_for_log(g); #ifdef SUPPORT_TLS if (LOGGING(tls_cipher) && tls_in.cipher) + { g = string_append(g, 2, US" X=", tls_in.cipher); +# ifdef EXPERIMENTAL_TLS_RESUME + if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED) + g = string_catn(g, US"*", 1); +# endif + } if (LOGGING(tls_certificate_verified) && tls_in.cipher) g = string_append(g, 2, US" CV=", tls_in.certificate_verified ? "yes":"no"); if (LOGGING(tls_peerdn) && tls_in.peerdn) diff --git a/src/src/retry.c b/src/src/retry.c index dc39813fb..509de123c 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -170,7 +170,7 @@ if ((node = tree_search(tree_unusable, host_key))) /* Open the retry database, giving up if there isn't one. Otherwise, search for the retry records, and then close the database again. */ -if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) +if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE))) { DEBUG(D_deliver|D_retry|D_hints_lookup) debug_printf("no retry data available\n"); @@ -580,7 +580,7 @@ for (int i = 0; i < 3; i++) reached their retry next try time. */ if (!dbm_file) - dbm_file = dbfn_open(US"retry", O_RDWR, &dbblock, TRUE); + dbm_file = dbfn_open(US"retry", O_RDWR, &dbblock, TRUE, TRUE); if (!dbm_file) { diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b46f3e876..40fd3083b 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1786,7 +1786,13 @@ static gstring * s_tlslog(gstring * g) { if (LOGGING(tls_cipher) && tls_in.cipher) + { g = string_append(g, 2, US" X=", tls_in.cipher); +#ifdef EXPERIMENTAL_TLS_RESUME + if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED) + g = string_catn(g, US"*", 1); +#endif + } if (LOGGING(tls_certificate_verified) && tls_in.cipher) g = string_append(g, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); if (LOGGING(tls_peerdn) && tls_in.peerdn) diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 786eb514e..95004c103 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -667,6 +667,11 @@ for (;;) tls_in.sni = string_unprinting(string_copy(big_buffer + 9)); else if (Ustrncmp(q, "ocsp", 4) == 0) tls_in.ocsp = big_buffer[10] - '0'; +# ifdef EXPERIMENTAL_TLS_RESUME + else if (Ustrncmp(q, "resumption", 10) == 0) + tls_in.resumption = big_buffer[16] - 'A'; +# endif + } break; #endif diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 46a490a93..d14914f94 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -249,6 +249,9 @@ if (tls_in.ourcert) fprintf(fp, "-tls_ourcert %s\n", CS big_buffer); } if (tls_in.ocsp) fprintf(fp, "-tls_ocsp %d\n", tls_in.ocsp); +# ifdef EXPERIMENTAL_TLS_RESUME +fprintf(fp, "-tls_resumption %c\n", 'A' + tls_in.resumption); +# endif #endif #ifdef SUPPORT_I18N diff --git a/src/src/structs.h b/src/src/structs.h index 7fb32777c..349aa38e8 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -636,6 +636,9 @@ typedef struct address_item { #endif #ifdef SUPPORT_I18N BOOL af_utf8_downcvt:1; /* downconvert was done for delivery */ +#endif +#ifdef EXPERIMENTAL_TLS_RESUME + BOOL af_tls_resume:1; /* TLS used a resumed session */ #endif } flags; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 91ddc579b..32625d6d8 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -99,6 +99,17 @@ require current GnuTLS, then we'll drop support for the ancient libraries). #include "tls-cipher-stdname.c" +#ifdef MACRO_PREDEF +void +options_tls(void) +{ +# ifdef EXPERIMENTAL_TLS_RESUME +builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING ); +# endif +} +#else + + /* GnuTLS 2 vs 3 GnuTLS 3 only: @@ -174,45 +185,9 @@ typedef struct exim_gnutls_state { } exim_gnutls_state_st; static const exim_gnutls_state_st exim_gnutls_state_init = { - .session = NULL, - .x509_cred = NULL, - .priority_cache = NULL, - .verify_requirement = VERIFY_NONE, + /* all elements not explicitly intialised here get 0/NULL/FALSE */ .fd_in = -1, .fd_out = -1, - .peer_cert_verified = FALSE, - .peer_dane_verified = FALSE, - .trigger_sni_changes =FALSE, - .have_set_peerdn = FALSE, - .host = NULL, - .peercert = NULL, - .peerdn = NULL, - .ciphersuite = NULL, - .received_sni = NULL, - - .tls_certificate = NULL, - .tls_privatekey = NULL, - .tls_sni = NULL, - .tls_verify_certificates = NULL, - .tls_crl = NULL, - .tls_require_ciphers =NULL, - - .exp_tls_certificate = NULL, - .exp_tls_privatekey = NULL, - .exp_tls_verify_certificates = NULL, - .exp_tls_crl = NULL, - .exp_tls_require_ciphers = NULL, - .exp_tls_verify_cert_hostnames = NULL, -#ifndef DISABLE_EVENT - .event_action = NULL, -#endif - .tlsp = NULL, - - .xfer_buffer = NULL, - .xfer_buffer_lwm = 0, - .xfer_buffer_hwm = 0, - .xfer_eof = FALSE, - .xfer_error = FALSE, }; /* Not only do we have our own APIs which don't pass around state, assuming @@ -234,9 +209,7 @@ don't want to repeat this. */ static gnutls_dh_params_t dh_server_params = NULL; -/* No idea how this value was chosen; preserving it. Default is 3600. */ - -static const int ssl_session_timeout = 200; +static int ssl_session_timeout = 3600; /* One hour */ static const uschar * const exim_default_gnutls_priority = US"NORMAL"; @@ -248,6 +221,9 @@ static BOOL exim_gnutls_base_init_done = FALSE; static BOOL gnutls_buggy_ocsp = FALSE; #endif +#ifdef EXPERIMENTAL_TLS_RESUME +static gnutls_datum_t server_sessticket_key; +#endif /* ------------------------------------------------------------------------ */ /* macros */ @@ -261,7 +237,7 @@ setuid binaries, making it useless - "GNUTLS_DEBUG_LEVEL". Allegedly the testscript line "GNUTLS_DEBUG_LEVEL=9 sudo exim ..." would work, but the env var must be added to /etc/sudoers too. */ #ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL -# define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 +# define EXIM_GNUTLS_LIBRARY_LOG_LEVEL 9 #endif #ifndef EXIM_CLIENT_DH_MIN_BITS @@ -312,6 +288,24 @@ static int server_ocsp_stapling_cb(gnutls_session_t session, void * ptr, +/* Daemon one-time initialisation */ +void +tls_daemon_init(void) +{ +#ifdef EXPERIMENTAL_TLS_RESUME +/* We are dependent on the GnuTLS implementation of the Session Ticket +encryption; both the strength and the key rotation period. We hope that +the strength at least matches that of the ciphersuite (but GnuTLS does not +document this). */ + +static BOOL once = FALSE; +if (once) return; +once = TRUE; +gnutls_session_ticket_key_generate(&server_sessticket_key); /* >= 2.10.0 */ +if (f.running_in_test_harness) ssl_session_timeout = 6; +#endif +} + /* ------------------------------------------------------------------------ */ /* Static functions */ @@ -463,7 +457,6 @@ Argument: static void extract_exim_vars_from_tls_state(exim_gnutls_state_st * state) { -gnutls_cipher_algorithm_t cipher; #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING int old_pool; int rc; @@ -474,12 +467,6 @@ tls_support * tlsp = state->tlsp; tlsp->active.sock = state->fd_out; tlsp->active.tls_ctx = state; -cipher = gnutls_cipher_get(state->session); -/* returns size in "bytes" */ -tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8; - -tlsp->cipher = state->ciphersuite; - DEBUG(D_tls) debug_printf("cipher: %s\n", state->ciphersuite); tlsp->certificate_verified = state->peer_cert_verified; @@ -1423,6 +1410,9 @@ if ((rc = gnutls_priority_init(&state->priority_cache, CCS p, &errpos))) if ((rc = gnutls_priority_set(state->session, state->priority_cache))) return tls_error_gnu(US"gnutls_priority_set", rc, host, errstr); +/* This also sets the server ticket expiration time to the same, and +the STEK rotation time to 3x. */ + gnutls_db_set_cache_expiration(state->session, ssl_session_timeout); /* Reduce security in favour of increased compatibility, if the admin @@ -1492,9 +1482,10 @@ Returns: OK/DEFER/FAIL */ static int -peer_status(exim_gnutls_state_st *state, uschar ** errstr) +peer_status(exim_gnutls_state_st * state, uschar ** errstr) { -const gnutls_datum_t *cert_list; +gnutls_session_t session = state->session; +const gnutls_datum_t * cert_list; int old_pool, rc; unsigned int cert_list_size = 0; gnutls_protocol_t protocol; @@ -1503,7 +1494,7 @@ gnutls_kx_algorithm_t kx; gnutls_mac_algorithm_t mac; gnutls_certificate_type_t ct; gnutls_x509_crt_t crt; -uschar *dn_buf; +uschar * dn_buf; size_t sz; if (state->have_set_peerdn) @@ -1513,23 +1504,24 @@ state->have_set_peerdn = TRUE; state->peerdn = NULL; /* tls_cipher */ -cipher = gnutls_cipher_get(state->session); -protocol = gnutls_protocol_get_version(state->session); -mac = gnutls_mac_get(state->session); +cipher = gnutls_cipher_get(session); +protocol = gnutls_protocol_get_version(session); +mac = gnutls_mac_get(session); kx = #ifdef GNUTLS_TLS1_3 protocol >= GNUTLS_TLS1_3 ? 0 : #endif - gnutls_kx_get(state->session); + gnutls_kx_get(session); old_pool = store_pool; { + tls_support * tlsp = state->tlsp; store_pool = POOL_PERM; #ifdef SUPPORT_GNUTLS_SESS_DESC { gstring * g = NULL; - uschar * s = US gnutls_session_get_desc(state->session), c; + uschar * s = US gnutls_session_get_desc(session), c; /* Nikos M suggests we use this by preference. It returns like: (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) @@ -1568,15 +1560,15 @@ old_pool = store_pool; /* debug_printf("peer_status: ciphersuite %s\n", state->ciphersuite); */ - state->tlsp->cipher = state->ciphersuite; - state->tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8; + tlsp->cipher = state->ciphersuite; + tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8; - state->tlsp->cipher_stdname = cipher_stdname_kcm(kx, cipher, mac); + tlsp->cipher_stdname = cipher_stdname_kcm(kx, cipher, mac); } store_pool = old_pool; /* tls_peerdn */ -cert_list = gnutls_certificate_get_peers(state->session, &cert_list_size); +cert_list = gnutls_certificate_get_peers(session, &cert_list_size); if (!cert_list || cert_list_size == 0) { @@ -1588,7 +1580,7 @@ if (!cert_list || cert_list_size == 0) return OK; } -if ((ct = gnutls_certificate_type_get(state->session)) != GNUTLS_CRT_X509) +if ((ct = gnutls_certificate_type_get(session)) != GNUTLS_CRT_X509) { const uschar * ctn = US gnutls_certificate_type_get_name(ct); DEBUG(D_tls) @@ -1660,13 +1652,14 @@ verify_certificate(exim_gnutls_state_st * state, uschar ** errstr) int rc; uint verify; -if (state->verify_requirement == VERIFY_NONE) - return TRUE; - DEBUG(D_tls) debug_printf("TLS: checking peer certificate\n"); *errstr = NULL; +rc = peer_status(state, errstr); -if ((rc = peer_status(state, errstr)) != OK || !state->peerdn) +if (state->verify_requirement == VERIFY_NONE) + return TRUE; + +if (rc != OK || !state->peerdn) { verify = GNUTLS_CERT_INVALID; *errstr = US"certificate not supplied"; @@ -2065,7 +2058,6 @@ return g; static void post_handshake_debug(exim_gnutls_state_st * state) { -debug_printf("gnutls_handshake was successful\n"); #ifdef SUPPORT_GNUTLS_SESS_DESC debug_printf("%s\n", gnutls_session_get_desc(state->session)); #endif @@ -2094,6 +2086,63 @@ else #endif } + +#ifdef EXPERIMENTAL_TLS_RESUME +static int +tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, + unsigned incoming, const gnutls_datum_t * msg) +{ +DEBUG(D_tls) debug_printf("newticket cb\n"); +tls_in.resumption |= RESUME_CLIENT_REQUESTED; +return 0; +} + +static void +tls_server_resume_prehandshake(exim_gnutls_state_st * state) +{ +/* Should the server offer session resumption? */ +tls_in.resumption = RESUME_SUPPORTED; +if (verify_check_host(&tls_resumption_hosts) == OK) + { + int rc; + /* GnuTLS appears to not do ticket overlap, but does emit a fresh ticket when + an offered resumption is unacceptable. We lose one resumption per ticket + lifetime, and sessions cannot be indefinitely re-used. There seems to be no + way (3.6.7) of changing the default number of 2 TLS1.3 tickets issued, but at + least they go out in a single packet. */ + + if (!(rc = gnutls_session_ticket_enable_server(state->session, + &server_sessticket_key))) + tls_in.resumption |= RESUME_SERVER_TICKET; + else + DEBUG(D_tls) + debug_printf("enabling session tickets: %s\n", US gnutls_strerror(rc)); + + /* Try to tell if we see a ticket request */ + gnutls_handshake_set_hook_function(state->session, + GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, GNUTLS_HOOK_POST, tls_server_ticket_cb); + } +} + +static void +tls_server_resume_posthandshake(exim_gnutls_state_st * state) +{ +if (gnutls_session_resumption_requested(state->session)) + { + /* This tells us the client sent a full ticket. We use a + callback on session-ticket request, elsewhere, to tell + if a client asked for a ticket. */ + + tls_in.resumption |= RESUME_CLIENT_SUGGESTED; + DEBUG(D_tls) debug_printf("client requested resumption\n"); + } +if (gnutls_session_is_resumed(state->session)) + { + tls_in.resumption |= RESUME_USED; + DEBUG(D_tls) debug_printf("Session resumed\n"); + } +} +#endif /* ------------------------------------------------------------------------ */ /* Exported functions */ @@ -2141,6 +2190,10 @@ if ((rc = tls_init(NULL, tls_certificate, tls_privatekey, NULL, tls_verify_certificates, tls_crl, require_ciphers, &state, &tls_in, errstr)) != OK) return rc; +#ifdef EXPERIMENTAL_TLS_RESUME +tls_server_resume_prehandshake(state); +#endif + /* If this is a host for which certificate verification is mandatory or optional, set up appropriately. */ @@ -2240,6 +2293,10 @@ if (rc != GNUTLS_E_SUCCESS) return FAIL; } +#ifdef EXPERIMENTAL_TLS_RESUME +tls_server_resume_posthandshake(state); +#endif + DEBUG(D_tls) post_handshake_debug(state); /* Verify after the fact */ @@ -2256,10 +2313,6 @@ if (!verify_certificate(state, errstr)) *errstr); } -/* Figure out peer DN, and if authenticated, etc. */ - -if ((rc = peer_status(state, NULL)) != OK) return rc; - /* Sets various Exim expansion variables; always safe within server */ extract_exim_vars_from_tls_state(state); @@ -2372,6 +2425,140 @@ return TRUE; +#ifdef EXPERIMENTAL_TLS_RESUME +/* On the client, get any stashed session for the given IP from hints db +and apply it to the ssl-connection for attempted resumption. Although +there is a gnutls_session_ticket_enable_client() interface it is +documented as unnecessary (as of 3.6.7) as "session tickets are emabled +by deafult". There seems to be no way to disable them, so even hosts not +enabled by the transport option will be sent a ticket request. We will +however avoid storing and retrieving session information. */ + +static void +tls_retrieve_session(tls_support * tlsp, gnutls_session_t session, + host_item * host, smtp_transport_options_block * ob) +{ +tlsp->resumption = RESUME_SUPPORTED; +if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK) + { + dbdata_tls_session * dt; + int len, rc; + open_db dbblock, * dbm_file; + + DEBUG(D_tls) + debug_printf("check for resumable session for %s\n", host->address); + tlsp->host_resumable = TRUE; + tlsp->resumption |= RESUME_CLIENT_REQUESTED; + if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE))) + { + /* key for the db is the IP */ + if ((dt = dbfn_read_with_length(dbm_file, host->address, &len))) + if (!(rc = gnutls_session_set_data(session, + CUS dt->session, (size_t)len - sizeof(dbdata_tls_session)))) + { + DEBUG(D_tls) debug_printf("good session\n"); + tlsp->resumption |= RESUME_CLIENT_SUGGESTED; + } + else DEBUG(D_tls) debug_printf("setting session resumption data: %s\n", + US gnutls_strerror(rc)); + dbfn_close(dbm_file); + } + } +} + + +static void +tls_save_session(tls_support * tlsp, gnutls_session_t session, const host_item * host) +{ +/* TLS 1.2 - we get both the callback and the direct posthandshake call, +but this flag is not set until the second. TLS 1.3 it's the other way about. +Keep both calls as the session data cannot be extracted before handshake +completes. */ + +if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET) + { + gnutls_datum_t tkt; + int rc; + + DEBUG(D_tls) debug_printf("server offered session ticket\n"); + tlsp->ticket_received = TRUE; + tlsp->resumption |= RESUME_SERVER_TICKET; + + if (tlsp->host_resumable) + if (!(rc = gnutls_session_get_data2(session, &tkt))) + { + open_db dbblock, * dbm_file; + int dlen = sizeof(dbdata_tls_session) + tkt.size; + dbdata_tls_session * dt = store_get(dlen); + + DEBUG(D_tls) debug_printf("session data size %u\n", (unsigned)tkt.size); + memcpy(dt->session, tkt.data, tkt.size); + gnutls_free(tkt.data); + + if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE))) + { + /* key for the db is the IP */ + dbfn_delete(dbm_file, host->address); + dbfn_write(dbm_file, host->address, dt, dlen); + dbfn_close(dbm_file); + + DEBUG(D_tls) + debug_printf("wrote session db (len %u)\n", (unsigned)dlen); + } + } + else DEBUG(D_tls) + debug_printf("extract session data: %s\n", US gnutls_strerror(rc)); + } +} + + +/* With a TLS1.3 session, the ticket(s) are not seen until +the first data read is attempted. And there's often two of them. +Pick them up with this callback. We are also called for 1.2 +but we do nothing. +*/ +static int +tls_client_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, + unsigned incoming, const gnutls_datum_t * msg) +{ +exim_gnutls_state_st * state = gnutls_session_get_ptr(sess); +tls_support * tlsp = state->tlsp; + +DEBUG(D_tls) debug_printf("newticket cb\n"); + +if (!tlsp->ticket_received) + tls_save_session(tlsp, sess, state->host); +return 0; +} + + +static void +tls_client_resume_prehandshake(exim_gnutls_state_st * state, + tls_support * tlsp, host_item * host, + smtp_transport_options_block * ob) +{ +gnutls_session_set_ptr(state->session, state); +gnutls_handshake_set_hook_function(state->session, + GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, GNUTLS_HOOK_POST, tls_client_ticket_cb); + +tls_retrieve_session(tlsp, state->session, host, ob); +} + +static void +tls_client_resume_posthandshake(exim_gnutls_state_st * state, + tls_support * tlsp, host_item * host) +{ +if (gnutls_session_is_resumed(state->session)) + { + DEBUG(D_tls) debug_printf("Session resumed\n"); + tlsp->resumption |= RESUME_USED; + } + +tls_save_session(tlsp, state->session, host); +} +#endif /* EXPERIMENTAL_TLS_RESUME */ + + /************************************************* * Start a TLS session in a client * *************************************************/ @@ -2512,6 +2699,10 @@ if (request_ocsp) } #endif +#ifdef EXPERIMENTAL_TLS_RESUME +tls_client_resume_prehandshake(state, tlsp, host, ob); +#endif + #ifndef DISABLE_EVENT if (tb && tb->event_action) { @@ -2589,10 +2780,9 @@ if (require_ocsp) } #endif -/* Figure out peer DN, and if authenticated, etc. */ - -if (peer_status(state, errstr) != OK) - return FALSE; +#ifdef EXPERIMENTAL_TLS_RESUME +tls_client_resume_posthandshake(state, tlsp, host); +#endif /* Sets various Exim expansion variables; may need to adjust for ACL callouts */ @@ -3090,6 +3280,7 @@ fprintf(f, "Library version: GnuTLS: Compile: %s\n" gnutls_check_version(NULL)); } +#endif /*!MACRO_PREDEF*/ /* vi: aw ai sw=2 */ /* End of tls-gnu.c */ diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index c7b22a0db..06008decc 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) University of Cambridge 1995 - 2019 */ /* See the file NOTICE for conditions of use and distribution. */ /* Portions Copyright (c) The OpenSSL Project 1999 */ @@ -72,6 +72,7 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_TLS_METHOD # define EXIM_HAVE_OPENSSL_KEYLOG # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID +# define EXIM_HAVE_SESSION_TICKET # else # define EXIM_NEED_OPENSSL_INIT # endif @@ -249,6 +250,10 @@ for (struct exim_openssl_option * o = exim_openssl_options; spf(buf, sizeof(buf), US"_OPT_OPENSSL_%T_X", o->name); builtin_macro_create(buf); } + +# ifdef EXPERIMENTAL_TLS_RESUME +builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING ); +# endif } #else @@ -303,7 +308,7 @@ static SSL_CTX *server_sni = NULL; static char ssl_errstring[256]; -static int ssl_session_timeout = 200; +static int ssl_session_timeout = 3600; static BOOL client_verify_optional = FALSE; static BOOL server_verify_optional = FALSE; @@ -311,6 +316,7 @@ static BOOL reexpand_tls_files_for_sni = FALSE; typedef struct tls_ext_ctx_cb { + tls_support * tlsp; uschar *certificate; uschar *privatekey; BOOL is_server; @@ -342,7 +348,7 @@ typedef struct tls_ext_ctx_cb { /* should figure out a cleanup of API to handle state preserved per implementation, for various reasons, which can be void * in the APIs. For now, we hack around it. */ -tls_ext_ctx_cb *client_static_cbinfo = NULL; +tls_ext_ctx_cb *client_static_cbinfo = NULL; /*XXX should not use static; multiple concurrent clients! */ tls_ext_ctx_cb *server_static_cbinfo = NULL; static int @@ -358,6 +364,23 @@ static int tls_server_stapling_cb(SSL *s, void *arg); #endif + +/* Daemon-called key create/rotate */ +#ifdef EXPERIMENTAL_TLS_RESUME +static void tk_init(void); +static int tls_exdata_idx = -1; +#endif + +void +tls_daemon_init(void) +{ +#ifdef EXPERIMENTAL_TLS_RESUME +tk_init(); +#endif +return; +} + + /************************************************* * Handle TLS error * *************************************************/ @@ -803,6 +826,121 @@ DEBUG(D_tls) debug_printf("%.200s\n", line); #endif +#ifdef EXPERIMENTAL_TLS_RESUME +/* Manage the keysets used for encrypting the session tickets, on the server. */ + +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? */ + 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 void +tk_init(void) +{ +if (exim_tk.name[0]) + { + if (exim_tk.renew >= time(NULL)) return; + exim_tk_old = exim_tk; + } + +if (f.running_in_test_harness) ssl_session_timeout = 6; + +DEBUG(D_tls) debug_printf("OpenSSL: %s STEK\n", exim_tk.name[0] ? "rotating" : "creating"); +if (RAND_bytes(exim_tk.aes_key, sizeof(exim_tk.aes_key)) <= 0) return; +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.hmac_hash = EVP_sha256(); +exim_tk.expire = time(NULL) + ssl_session_timeout; +exim_tk.renew = exim_tk.expire - ssl_session_timeout/2; +} + +static exim_stek * +tk_current(void) +{ +if (!exim_tk.name[0]) return NULL; +return &exim_tk; +} + +static exim_stek * +tk_find(const uschar * name) +{ +return memcmp(name, exim_tk.name, sizeof(exim_tk.name)) == 0 ? &exim_tk + : memcmp(name, exim_tk_old.name, sizeof(exim_tk_old.name)) == 0 ? &exim_tk_old + : NULL; +} + +/* Callback for session tickets, on server */ +static int +ticket_key_callback(SSL * ssl, uschar key_name[16], + uschar * iv, EVP_CIPHER_CTX * ctx, HMAC_CTX * hctx, int enc) +{ +tls_support * tlsp = server_static_cbinfo->tlsp; +exim_stek * key; + +if (enc) + { + DEBUG(D_tls) debug_printf("ticket_key_callback: create new session\n"); + tlsp->resumption |= RESUME_CLIENT_REQUESTED; + + if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0) + return -1; /* insufficient random */ + + 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)); + + /*XXX will want these dependent on the ssl session strength */ + HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key), + key->hmac_hash, NULL); + EVP_EncryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv); + + DEBUG(D_tls) debug_printf("ticket created\n"); + return 1; + } +else + { + time_t now = time(NULL); + + DEBUG(D_tls) debug_printf("ticket_key_callback: retrieve session\n"); + tlsp->resumption |= RESUME_CLIENT_SUGGESTED; + + if (!(key = tk_find(key_name)) || key->expire < now) + { + 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); + } + return 0; + } + + HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key), + 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); + return key->renew < now ? 2 : 1; + } +} +#endif + + /************************************************* * Initialize for DH * @@ -1394,6 +1532,9 @@ Arguments: arg Callback of "our" registered data Returns: SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK} + +XXX might need to change to using ClientHello callback, +per https://www.openssl.org/docs/manmaster/man3/SSL_client_hello_cb_fn.html */ #ifdef EXIM_HAVE_OPENSSL_TLSEXT @@ -1714,7 +1855,9 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, #ifndef DISABLE_OCSP uschar *ocsp_file, /*XXX stack, in server*/ #endif - address_item *addr, tls_ext_ctx_cb ** cbp, uschar ** errstr) + address_item *addr, tls_ext_ctx_cb ** cbp, + tls_support * tlsp, + uschar ** errstr) { SSL_CTX * ctx; long init_options; @@ -1722,6 +1865,7 @@ int rc; tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); +cbinfo->tlsp = tlsp; cbinfo->certificate = certificate; cbinfo->privatekey = privatekey; cbinfo->is_server = host==NULL; @@ -1795,10 +1939,16 @@ if (!RAND_status()) /* Set up the information callback, which outputs if debugging is at a suitable level. */ -DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); +DEBUG(D_tls) + { + SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); +#ifndef OPENSSL_NO_SSL_TRACE /* this needs a debug build of OpenSSL */ + SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace); +#endif #ifdef OPENSSL_HAVE_KEYLOG_CB -DEBUG(D_tls) SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback); + 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); @@ -1815,8 +1965,22 @@ availability of the option value macros from OpenSSL. */ if (!tls_openssl_options_parse(openssl_options, &init_options)) return tls_error(US"openssl_options parsing failed", host, NULL, errstr); +#ifdef EXPERIMENTAL_TLS_RESUME +tlsp->resumption = RESUME_SUPPORTED; +#endif if (init_options) { +#ifdef EXPERIMENTAL_TLS_RESUME + /* Should the server offer session resumption? */ + if (!host && verify_check_host(&tls_resumption_hosts) == OK) + { + DEBUG(D_tls) debug_printf("tls_resumption_hosts overrides openssl_options\n"); + init_options &= ~SSL_OP_NO_TICKET; + tlsp->resumption |= RESUME_SERVER_TICKET; /* server will give ticket on request */ + tlsp->host_resumable = TRUE; + } +#endif + DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); if (!(SSL_CTX_set_options(ctx, init_options))) return tls_error(string_sprintf( @@ -1825,10 +1989,6 @@ 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. @@ -1903,7 +2063,8 @@ cbinfo->verify_cert_hostnames = NULL; SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback); #endif -/* Finally, set the timeout, and we are done */ +/* Finally, set the session cache timeout, and we are done. +The period appears to be also used for (server-generated) session tickets */ SSL_CTX_set_timeout(ctx, ssl_session_timeout); DEBUG(D_tls) debug_printf("Initialized TLS\n"); @@ -2226,7 +2387,7 @@ rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey, #ifndef DISABLE_OCSP tls_ocsp_file, /*XXX stack*/ #endif - NULL, &server_static_cbinfo, errstr); + NULL, &server_static_cbinfo, &tls_in, errstr); if (rc != OK) return rc; cbinfo = server_static_cbinfo; @@ -2244,8 +2405,7 @@ TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 if (expciphers) { - uschar * s = expciphers; - while (*s != 0) { if (*s == '_') *s = '-'; s++; } + for (uschar * s = expciphers; *s; s++ ) if (*s == '_') *s = '-'; DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); if (!SSL_CTX_set_cipher_list(server_ctx, CS expciphers)) return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr); @@ -2276,6 +2436,19 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK) server_verify_optional = TRUE; } +#ifdef EXPERIMENTAL_TLS_RESUME +SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_key_callback); +/* despite working, appears to always return failure, so ignoring */ +#endif +#ifdef OPENSSL_HAVE_NUM_TICKETS +# ifdef EXPERIMENTAL_TLS_RESUME +SSL_CTX_set_num_tickets(server_ctx, tls_in.host_resumable ? 1 : 0); +# else +SSL_CTX_set_num_tickets(server_ctx, 0); /* send no TLS1.3 stateful-tickets */ +# endif +#endif + + /* Prepare for new connection */ if (!(server_ssl = SSL_new(server_ctx))) @@ -2329,7 +2502,15 @@ if (rc <= 0) DEBUG(D_tls) debug_printf("SSL_accept was successful\n"); ERR_clear_error(); /* Even success can leave errors in the stack. Seen with - anon-authentication ciphersuite negociated. */ + anon-authentication ciphersuite negotiated. */ + +#ifdef EXPERIMENTAL_TLS_RESUME +if (SSL_session_reused(server_ssl)) + { + tls_in.resumption |= RESUME_USED; + DEBUG(D_tls) debug_printf("Session reused\n"); + } +#endif /* TLS has been set up. Adjust the input functions to read via TLS, and initialize things. */ @@ -2352,6 +2533,15 @@ DEBUG(D_tls) BIO_free(bp); } #endif + +#ifdef EXIM_HAVE_SESSION_TICKET + { + SSL_SESSION * ss = SSL_get_session(server_ssl); + if (SSL_SESSION_has_ticket(ss)) + debug_printf("The session has a ticket, life %lu seconds\n", + SSL_SESSION_get_ticket_lifetime_hint(ss)); + } +#endif } /* Record the certificate we presented */ @@ -2483,6 +2673,160 @@ return DEFER; +#ifdef EXPERIMENTAL_TLS_RESUME +/* On the client, get any stashed session for the given IP from hints db +and apply it to the ssl-connection for attempted resumption. */ + +static void +tls_retrieve_session(tls_support * tlsp, SSL * ssl, const uschar * key) +{ +tlsp->resumption |= RESUME_SUPPORTED; +if (tlsp->host_resumable) + { + dbdata_tls_session * dt; + int len; + open_db dbblock, * dbm_file; + + tlsp->resumption |= RESUME_CLIENT_REQUESTED; + DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key); + if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE))) + { + /* key for the db is the IP */ + if ((dt = dbfn_read_with_length(dbm_file, key, &len))) + { + SSL_SESSION * ss = NULL; + const uschar * sess_asn1 = dt->session; + + len -= sizeof(dbdata_tls_session); + if (!(d2i_SSL_SESSION(&ss, &sess_asn1, (long)len))) + { + DEBUG(D_tls) + { + ERR_error_string_n(ERR_get_error(), + ssl_errstring, sizeof(ssl_errstring)); + debug_printf("decoding session: %s\n", ssl_errstring); + } + } + else if (!SSL_set_session(ssl, ss)) + { + DEBUG(D_tls) + { + ERR_error_string_n(ERR_get_error(), + ssl_errstring, sizeof(ssl_errstring)); + debug_printf("applying session to ssl: %s\n", ssl_errstring); + } + } + else + { + DEBUG(D_tls) debug_printf("good session\n"); + tlsp->resumption |= RESUME_CLIENT_SUGGESTED; + } + } + else + DEBUG(D_tls) debug_printf("no session record\n"); + dbfn_close(dbm_file); + } + } +} + + +/* On the client, save the session for later resumption */ + +static int +tls_save_session_cb(SSL * ssl, SSL_SESSION * ss) +{ +tls_ext_ctx_cb * cbinfo = SSL_get_ex_data(ssl, tls_exdata_idx); +tls_support * tlsp; + +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)) + { + int len = i2d_SSL_SESSION(ss, NULL); + int dlen = sizeof(dbdata_tls_session) + len; + dbdata_tls_session * dt = store_get(dlen); + 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 */ + + if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE))) + { + const uschar * key = cbinfo->host->address; + dbfn_delete(dbm_file, key); + dbfn_write(dbm_file, key, dt, dlen); + dbfn_close(dbm_file); + DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n", + (unsigned)dlen); + } + } +# endif +return 1; +} + + +static void +tls_client_ctx_resume_prehandshake( + exim_openssl_client_tls_ctx * exim_client_ctx, tls_support * tlsp, + smtp_transport_options_block * ob, host_item * host) +{ +/* Should the client request a session resumption ticket? */ +if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK) + { + tlsp->host_resumable = TRUE; + + SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx, + SSL_SESS_CACHE_CLIENT + | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR); + SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb); + } +} + +static BOOL +tls_client_ssl_resume_prehandshake(SSL * ssl, tls_support * tlsp, + host_item * host, uschar ** errstr) +{ +if (tlsp->host_resumable) + { + DEBUG(D_tls) + debug_printf("tls_resumption_hosts overrides openssl_options, enabling tickets\n"); + SSL_clear_options(ssl, SSL_OP_NO_TICKET); + + tls_exdata_idx = SSL_get_ex_new_index(0, 0, 0, 0, 0); + if (!SSL_set_ex_data(ssl, tls_exdata_idx, client_static_cbinfo)) + { + tls_error(US"set ex_data", host, NULL, errstr); + return FALSE; + } + debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_cbinfo); + } + +tlsp->resumption = RESUME_SUPPORTED; +/* Pick up a previous session, saved on an old ticket */ +tls_retrieve_session(tlsp, ssl, host->address); +return TRUE; +} + +static void +tls_client_resume_posthandshake(exim_openssl_client_tls_ctx * exim_client_ctx, + tls_support * tlsp) +{ +if (SSL_session_reused(exim_client_ctx->ssl)) + { + DEBUG(D_tls) debug_printf("The session was reused\n"); + tlsp->resumption |= RESUME_USED; + } +} +#endif /* EXPERIMENTAL_TLS_RESUME */ + + /************************************************* * Start a TLS session in a client * *************************************************/ @@ -2562,7 +2906,7 @@ rc = tls_init(&exim_client_ctx->ctx, host, NULL, #ifndef DISABLE_OCSP (void *)(long)request_ocsp, #endif - cookie, &client_static_cbinfo, errstr); + cookie, &client_static_cbinfo, tlsp, errstr); if (rc != OK) return FALSE; tlsp->certificate_verified = FALSE; @@ -2629,12 +2973,24 @@ else client_static_cbinfo, errstr) != OK) return FALSE; +#ifdef EXPERIMENTAL_TLS_RESUME +tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host); +#endif + + if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx))) { tls_error(US"SSL_new", host, NULL, errstr); return FALSE; } 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); @@ -2729,6 +3085,10 @@ DEBUG(D_tls) #endif } +#ifdef EXPERIMENTAL_TLS_RESUME +tls_client_resume_posthandshake(exim_client_ctx, tlsp); +#endif + peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn)); tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits); @@ -3377,12 +3737,17 @@ uschar *end; uschar keep_c; BOOL adding, item_parsed; +/* Server: send no (<= TLS1.2) session tickets */ result = SSL_OP_NO_TICKET; + /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed * from default because it increases BEAST susceptibility. */ #ifdef SSL_OP_NO_SSLv2 result |= SSL_OP_NO_SSLv2; #endif +#ifdef SSL_OP_NO_SSLv3 +result |= SSL_OP_NO_SSLv3; +#endif #ifdef SSL_OP_SINGLE_DH_USE result |= SSL_OP_SINGLE_DH_USE; #endif @@ -3393,7 +3758,7 @@ if (!option_spec) return TRUE; } -for (uschar * s = option_spec; *s != '\0'; /**/) +for (uschar * s = option_spec; *s; /**/) { while (isspace(*s)) ++s; if (*s == '\0') diff --git a/src/src/tls.c b/src/src/tls.c index 23e9d4111..7b8d7a2a3 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -20,8 +20,10 @@ functions from the OpenSSL or GNU TLS libraries. */ #include "transports/smtp.h" #if defined(MACRO_PREDEF) && defined(SUPPORT_TLS) -# ifndef USE_GNUTLS -# include "macro_predef.h" +# include "macro_predef.h" +# ifdef USE_GNUTLS +# include "tls-gnu.c" +# else # include "tls-openssl.c" # endif #endif diff --git a/src/src/transport.c b/src/src/transport.c index f34db0914..fb74dfdcd 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1475,7 +1475,7 @@ DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname); /* Open the database for this transport */ if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname), - O_RDWR, &dbblock, TRUE))) + O_RDWR, &dbblock, TRUE, TRUE))) return; /* Scan the list of hosts for which this message is waiting, and ensure @@ -1648,7 +1648,7 @@ if (local_message_max > 0 && continue_sequence >= local_message_max) /* Open the waiting information database. */ if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name), - O_RDWR, &dbblock, TRUE))) + O_RDWR, &dbblock, TRUE, TRUE))) return FALSE; /* See if there is a record for this host; if not, there's nothing to do. */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 806c41fed..f76b4f730 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -183,6 +183,10 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, tls_privatekey) }, { "tls_require_ciphers", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) }, +# ifdef EXPERIMENTAL_TLS_RESUME + { "tls_resumption_hosts", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, tls_resumption_hosts) }, +# endif { "tls_sni", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_sni) }, { "tls_tempfail_tryclear", opt_bool, @@ -293,6 +297,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { .tls_verify_certificates = US"system", .tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS, .tls_tempfail_tryclear = TRUE, +# ifdef EXPERIMENTAL_TLS_RESUME + .tls_resumption_hosts = NULL, +# endif .tls_verify_hosts = NULL, .tls_try_verify_hosts = US"*", .tls_verify_cert_hostnames = US"*", @@ -825,7 +832,7 @@ write_ehlo_cache_entry(const smtp_context * sx) { open_db dbblock, * dbm_file; -if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) +if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) { uschar * ehlo_resp_key = ehlo_cache_key(sx); dbdata_ehlo_resp er = { .data = sx->ehlo_resp }; @@ -845,7 +852,7 @@ invalidate_ehlo_cache_entry(smtp_context * sx) open_db dbblock, * dbm_file; if ( sx->early_pipe_active - && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) + && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) { uschar * ehlo_resp_key = ehlo_cache_key(sx); dbfn_delete(dbm_file, ehlo_resp_key); @@ -859,7 +866,7 @@ read_ehlo_cache_entry(smtp_context * sx) open_db dbblock; open_db * dbm_file; -if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE))) +if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE, TRUE))) { DEBUG(D_transport) debug_printf("ehlo-cache: no misc DB\n"); } else { @@ -872,7 +879,7 @@ else { DEBUG(D_transport) debug_printf("ehlo-resp record too old\n"); dbfn_close(dbm_file); - if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) + if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) dbfn_delete(dbm_file, ehlo_resp_key); } else @@ -2016,6 +2023,9 @@ tls_out.peerdn = NULL; tls_out.sni = NULL; #endif tls_out.ocsp = OCSP_NOT_REQ; +#ifdef EXPERIMENTAL_TLS_RESUME +tls_out.resumption = 0; +#endif /* Flip the legacy TLS-related variables over to the outbound set in case they're used in the context of the transport. Don't bother resetting diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 056efbcf7..ab0e93ff8 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -83,6 +83,9 @@ typedef struct { uschar *tls_crl; uschar *tls_privatekey; uschar *tls_require_ciphers; +# ifdef EXPERIMENTAL_TLS_RESUME + uschar *tls_resumption_hosts; +# endif uschar *tls_sni; uschar *tls_verify_certificates; int tls_dh_min_bits; diff --git a/src/src/verify.c b/src/src/verify.c index 528a668b8..720f85d8c 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -140,7 +140,7 @@ if (options & vopt_callout_no_cache) { HDEBUG(D_verify) debug_printf("callout cache: disabled by no_cache\n"); } -else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE))) +else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE))) { HDEBUG(D_verify) debug_printf("callout cache: not available\n"); } @@ -313,7 +313,7 @@ implying some kind of I/O error. We don't want to write the cache in that case. Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */ if (dom_rec->result != ccache_unknown) - if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE))) + if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE))) { HDEBUG(D_verify) debug_printf("callout cache: not available\n"); } @@ -335,7 +335,7 @@ is disabled. */ if (done && addr_rec->result != ccache_unknown) { if (!dbm_file) - dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE); + dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE); if (!dbm_file) { HDEBUG(D_verify) debug_printf("no callout cache available\n"); @@ -3245,7 +3245,7 @@ int verify_check_host(uschar **listptr) { return verify_check_this_host(CUSS listptr, sender_host_cache, NULL, - (sender_host_address == NULL)? US"" : sender_host_address, NULL); + sender_host_address ? sender_host_address : US"", NULL); } diff --git a/test/aux-var-src/tls_conf_prefix b/test/aux-var-src/tls_conf_prefix index 965bc8b24..1c464f63e 100644 --- a/test/aux-var-src/tls_conf_prefix +++ b/test/aux-var-src/tls_conf_prefix @@ -1,4 +1,4 @@ -keep_environment = PATH +keep_environment = PATH:SSLKEYLOGFILE exim_path = EXIM_PATH host_lookup_order = bydns spool_directory = DIR/spool diff --git a/test/confs/5890 b/test/confs/5890 new file mode 100644 index 000000000..6daf59667 --- /dev/null +++ b/test/confs/5890 @@ -0,0 +1,94 @@ +# Exim test configuration 5890 + +SERVER = +OPTION = NORMAL + +.include DIR/aux-var/tls_conf_prefix + +primary_hostname = myhost.test.ex + +# ----- Main settings ----- + +domainlist local_domains = test.ex : *.test.ex + +acl_smtp_helo = check_helo +acl_smtp_rcpt = check_recipient +log_selector = +received_recipients +tls_resumption + +tls_advertise_hosts = * + +# Set certificate only if server + +tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} + +tls_require_ciphers = OPTION +tls_resumption_hosts = 127.0.0.1 + + +# ------ ACL ------ + +begin acl + +check_helo: + accept condition = ${if def:tls_in_cipher} + logwrite = tls_in_resumption ${listextract {$tls_in_resumption} {_RESUME_DECODE}} + accept + +check_recipient: + accept domains = +local_domains + deny message = relay not permitted + +log_resumption: + accept condition = ${if def:tls_out_cipher} + condition = ${if eq {$event_name}{tcp:close}} + logwrite = tls_out_resumption ${listextract {$tls_out_resumption} {_RESUME_DECODE}} + + +# ----- Routers ----- + +begin routers + +client: + driver = accept + condition = ${if eq {SERVER}{server}{no}{yes}} + retry_use_local_part + transport = send_to_server${if eq{$local_part}{abcd}{2}{1}} + +server: + driver = redirect + data = :blackhole: + +# ----- Transports ----- + +begin transports + +send_to_server1: + driver = smtp + allow_localhost + hosts = 127.0.0.1 + port = PORT_D + helo_data = helo.data.changed +.ifdef VALUE + tls_resumption_hosts = * +.else + tls_resumption_hosts = : +.endif + event_action = ${acl {log_resumption}} + +send_to_server2: + driver = smtp + allow_localhost + hosts = HOSTIPV4 + port = PORT_D + event_action = ${acl {log_resumption}} + + +# ----- Retry ----- + + +begin retry + +* * F,5d,10s + + +# End diff --git a/test/confs/5891 b/test/confs/5891 new file mode 100644 index 000000000..78d22f7ff --- /dev/null +++ b/test/confs/5891 @@ -0,0 +1,94 @@ +# Exim test configuration 5891 + +SERVER = +OPTION = + +.include DIR/aux-var/tls_conf_prefix + +primary_hostname = myhost.test.ex + +# ----- Main settings ----- + +domainlist local_domains = test.ex : *.test.ex + +acl_smtp_helo = check_helo +acl_smtp_rcpt = check_recipient +log_selector = +received_recipients +tls_resumption + +openssl_options = +no_sslv2 +no_sslv3 +single_dh_use OPTION +tls_advertise_hosts = * + +# Set certificate only if server + +tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail} + +tls_resumption_hosts = 127.0.0.1 + + +# ------ ACL ------ + +begin acl + +check_helo: + accept condition = ${if def:tls_in_cipher} + logwrite = tls_in_resumption ${listextract {$tls_in_resumption} {_RESUME_DECODE}} + accept + +check_recipient: + accept domains = +local_domains + deny message = relay not permitted + +log_resumption: + accept condition = ${if def:tls_out_cipher} + condition = ${if eq {$event_name}{tcp:close}} + logwrite = tls_out_resumption ${listextract {$tls_out_resumption} {_RESUME_DECODE}} + + +# ----- Routers ----- + +begin routers + +client: + driver = accept + condition = ${if eq {SERVER}{server}{no}{yes}} + retry_use_local_part + transport = send_to_server${if eq{$local_part}{abcd}{2}{1}} + +server: + driver = redirect + data = :blackhole: + +# ----- Transports ----- + +begin transports + +send_to_server1: + driver = smtp + allow_localhost + hosts = 127.0.0.1 + port = PORT_D + helo_data = helo.data.changed +.ifdef VALUE + tls_resumption_hosts = * +.else + tls_resumption_hosts = : +.endif + event_action = ${acl {log_resumption}} + +send_to_server2: + driver = smtp + allow_localhost + hosts = HOSTIPV4 + port = PORT_D + event_action = ${acl {log_resumption}} + + +# ----- Retry ----- + + +begin retry + +* * F,5d,10s + + +# End diff --git a/test/log/2102 b/test/log/2102 index 5ec2c1cb9..215bbe243 100644 --- a/test/log/2102 +++ b/test/log/2102 @@ -40,7 +40,7 @@ 1999-03-02 09:44:33 sha256 fingerprint E251FA7D0372CB784294CF92B243DCE53FDDABD9F58A1B89226586C07C82CAC6 1999-03-02 09:44:33 der_b64 MIIDuDCCAqCgAwIBAgICAMkwDQYJKoZIhvcNAQELBQAwNzEUMBIGA1UEChMLZXhhbXBsZS5jb20xHzAdBgNVBAMTFmNsaWNhIFNpZ25pbmcgQ2VydCByc2EwHhcNMTIxMTAxMTI0MDA0WhcNMzcxMjAxMTI0MDA0WjAeMRwwGgYDVQQDExNzZXJ2ZXIyLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA52Rfiv2Igy0NiaKN5gc0VPLbEoHngkdJWv3wEORp+iFl6skQRbsCylT8djJ2pvHstFpnzSodF3Wwjj2/EDuj3iKBzN9HeXJOvJz8j9Si1xkgCxJeUjPGgYcvKdxybaZAOpi9l3xwPCCEXN4JBq/WaQQ9+eP1PczeMNfvFtXma+VcHXG743ttPOv7eSMr0JxQl3zjQvYGOhFP/KAw6jh/N6YPqii9kV0cC/ubeVzpqJ5/+hndx5YrmAu39N5qzwWujhDPkFNSgCJUhfkEiMaQiPxFxDTbUzWnQ5jpAQ5El4WJVkGWkqxose1bOjSSNzFPJt59YtxxJC3IWN3UtGODTwIDAQABo4HmMIHjMA4GA1UdDwEB/wQEAwIE8DAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTgYDVR0jBEcwRYANQUFidHdDeGNYZ2IwUaExpC8wLTEUMBIGA1UEChMLZXhhbXBsZS5jb20xFTATBgNVBAMTDGNsaWNhIENBIHJzYYIBQjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vc2NwLmV4YW1wbGUuY29tLzApBgNVHREEIjAgghNzZXJ2ZXIyLmV4YW1wbGUuY29tggkqLnRlc3QuZXgwDQYJKoZIhvcNAQELBQADggEBALHOkZkvHLpNm0QSof09vmmdNFE6/+0TCIoPExeqqSOsy4NsF+Ha46WttjJRSVtbhRxF8jxEU7btPiFgQUaOcJZTwQPDhmQSOPNO8GS46oJ57aQ7U7O+X3M1sVS5Pa2IzE6vrJSh349/CNbTA8WPQdWLlxVJhJXAcZNtaEu6lCsZuDSMTpAsW5I4+snyrm3yvP5t0eD28K5LgCKePX962drkAOP6XGQ51VnbMQ7b1TSdQedtYKIpR3VKUvG5Ky/+0c+Rmwfi2aQ8oXXwekzJyS5jvovdVVsdhO68It+Rz/zursN5Pn+Gj1YuQNUs2nDrGHN+VIIFpgWUjLZO4bcJctY= 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes DN="/CN=server2.example.com" S=sss -1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D 1999-03-02 09:44:33 Our cert SN: 1999-03-02 09:44:33 Peer did not present a cert 1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:ke-ECDSA-AES256-SHAnnn:xxx CV=no S=sss diff --git a/test/log/5890 b/test/log/5890 new file mode 100644 index 000000000..9d098e526 --- /dev/null +++ b/test/log/5890 @@ -0,0 +1,116 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for getticket@test.ex +1999-03-02 09:44:33 10HmaX-0005vi-00 tls_out_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmaX-0005vi-00 => getticket@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmaY-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for resume@test.ex abcd@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmaZ-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmaZ-0005vi-00 tls_out_resumption not requested or offered +1999-03-02 09:44:33 10HmaZ-0005vi-00 => resume@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 -> xyz@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => abcd@test.ex R=client T=send_to_server2 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbB-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for renewal@test.ex +1999-03-02 09:44:33 10HmbC-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmbC-0005vi-00 => renewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbD-0005vi-00" +1999-03-02 09:44:33 10HmbC-0005vi-00 Completed +1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for postrenewal@test.ex +1999-03-02 09:44:33 10HmbE-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmbE-0005vi-00 => postrenewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbF-0005vi-00" +1999-03-02 09:44:33 10HmbE-0005vi-00 Completed +1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for timeout@test.ex +1999-03-02 09:44:33 10HmbG-0005vi-00 tls_out_resumption client offered session, server only provided new ticket +1999-03-02 09:44:33 10HmbG-0005vi-00 => timeout@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbH-0005vi-00" +1999-03-02 09:44:33 10HmbG-0005vi-00 Completed +1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for notreq@test.ex +1999-03-02 09:44:33 10HmbI-0005vi-00 tls_out_resumption no client request +1999-03-02 09:44:33 10HmbI-0005vi-00 => notreq@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbJ-0005vi-00" +1999-03-02 09:44:33 10HmbI-0005vi-00 Completed +1999-03-02 09:44:33 10HmbK-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for getticket@test.ex +1999-03-02 09:44:33 10HmbK-0005vi-00 tls_out_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbK-0005vi-00 => getticket@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbL-0005vi-00" +1999-03-02 09:44:33 10HmbK-0005vi-00 Completed +1999-03-02 09:44:33 10HmbM-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for resume@test.ex abcd@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmbM-0005vi-00 tls_out_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbM-0005vi-00 tls_out_resumption not requested or offered +1999-03-02 09:44:33 10HmbM-0005vi-00 => resume@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbN-0005vi-00" +1999-03-02 09:44:33 10HmbM-0005vi-00 -> xyz@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbN-0005vi-00" +1999-03-02 09:44:33 10HmbM-0005vi-00 => abcd@test.ex R=client T=send_to_server2 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbO-0005vi-00" +1999-03-02 09:44:33 10HmbM-0005vi-00 Completed +1999-03-02 09:44:33 10HmbP-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for renewal@test.ex +1999-03-02 09:44:33 10HmbP-0005vi-00 tls_out_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbP-0005vi-00 => renewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbQ-0005vi-00" +1999-03-02 09:44:33 10HmbP-0005vi-00 Completed +1999-03-02 09:44:33 10HmbR-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for postrenewal@test.ex +1999-03-02 09:44:33 10HmbR-0005vi-00 tls_out_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbR-0005vi-00 => postrenewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbS-0005vi-00" +1999-03-02 09:44:33 10HmbR-0005vi-00 Completed +1999-03-02 09:44:33 10HmbT-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for timeout@test.ex +1999-03-02 09:44:33 10HmbT-0005vi-00 tls_out_resumption client offered session, server only provided new ticket +1999-03-02 09:44:33 10HmbT-0005vi-00 => timeout@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbU-0005vi-00" +1999-03-02 09:44:33 10HmbT-0005vi-00 Completed +1999-03-02 09:44:33 10HmbV-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for notreq@test.ex +1999-03-02 09:44:33 10HmbV-0005vi-00 tls_out_resumption no client request +1999-03-02 09:44:33 10HmbV-0005vi-00 => notreq@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbW-0005vi-00" +1999-03-02 09:44:33 10HmbV-0005vi-00 Completed + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex for getticket@test.ex +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no S=sss id=E10HmaZ-0005vi-00@myhost.test.ex for resume@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption not requested or offered +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaZ-0005vi-00@myhost.test.ex for abcd@test.ex +1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed +1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbC-0005vi-00@myhost.test.ex for renewal@test.ex +1999-03-02 09:44:33 10HmbD-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbD-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed +1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke--AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbE-0005vi-00@myhost.test.ex for postrenewal@test.ex +1999-03-02 09:44:33 10HmbF-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbF-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption client offered session, server only provided new ticket +1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbG-0005vi-00@myhost.test.ex for timeout@test.ex +1999-03-02 09:44:33 10HmbH-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbH-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbJ-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbI-0005vi-00@myhost.test.ex for notreq@test.ex +1999-03-02 09:44:33 10HmbJ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbJ-0005vi-00 Completed +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbL-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbK-0005vi-00@myhost.test.ex for getticket@test.ex +1999-03-02 09:44:33 10HmbL-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbL-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbN-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbM-0005vi-00@myhost.test.ex for resume@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmbN-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbN-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbN-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption not requested or offered +1999-03-02 09:44:33 10HmbO-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbM-0005vi-00@myhost.test.ex for abcd@test.ex +1999-03-02 09:44:33 10HmbO-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbO-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbQ-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbP-0005vi-00@myhost.test.ex for renewal@test.ex +1999-03-02 09:44:33 10HmbQ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbQ-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbS-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-PSK-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbR-0005vi-00@myhost.test.ex for postrenewal@test.ex +1999-03-02 09:44:33 10HmbS-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbS-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbU-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbT-0005vi-00@myhost.test.ex for timeout@test.ex +1999-03-02 09:44:33 10HmbU-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbU-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbW-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbV-0005vi-00@myhost.test.ex for notreq@test.ex +1999-03-02 09:44:33 10HmbW-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbW-0005vi-00 Completed diff --git a/test/log/5891 b/test/log/5891 new file mode 100644 index 000000000..8131404e5 --- /dev/null +++ b/test/log/5891 @@ -0,0 +1,130 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for getticket@test.ex +1999-03-02 09:44:33 10HmaX-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmaX-0005vi-00 [127.0.0.1] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="127.0.0.1" +1999-03-02 09:44:33 10HmaX-0005vi-00 tls_out_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmaX-0005vi-00 => getticket@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmaY-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for resume@test.ex abcd@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmaZ-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmaZ-0005vi-00 [ip4.ip4.ip4.ip4] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmaZ-0005vi-00 [ip4.ip4.ip4.ip4] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="ip4.ip4.ip4.ip4" +1999-03-02 09:44:33 10HmaZ-0005vi-00 tls_out_resumption not requested or offered +1999-03-02 09:44:33 10HmaZ-0005vi-00 => resume@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 -> xyz@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => abcd@test.ex R=client T=send_to_server2 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbB-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for renewal@test.ex +1999-03-02 09:44:33 10HmbC-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmbC-0005vi-00 => renewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbD-0005vi-00" +1999-03-02 09:44:33 10HmbC-0005vi-00 Completed +1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for postrenewal@test.ex +1999-03-02 09:44:33 10HmbE-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmbE-0005vi-00 => postrenewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbF-0005vi-00" +1999-03-02 09:44:33 10HmbE-0005vi-00 Completed +1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for timeout@test.ex +1999-03-02 09:44:33 10HmbG-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmbG-0005vi-00 [127.0.0.1] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="127.0.0.1" +1999-03-02 09:44:33 10HmbG-0005vi-00 tls_out_resumption client offered session, server only provided new ticket +1999-03-02 09:44:33 10HmbG-0005vi-00 => timeout@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbH-0005vi-00" +1999-03-02 09:44:33 10HmbG-0005vi-00 Completed +1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for notreq@test.ex +1999-03-02 09:44:33 10HmbI-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmbI-0005vi-00 [127.0.0.1] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="127.0.0.1" +1999-03-02 09:44:33 10HmbI-0005vi-00 tls_out_resumption not requested or offered +1999-03-02 09:44:33 10HmbI-0005vi-00 => notreq@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbJ-0005vi-00" +1999-03-02 09:44:33 10HmbI-0005vi-00 Completed +1999-03-02 09:44:33 10HmbK-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for getticket@test.ex +1999-03-02 09:44:33 10HmbK-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmbK-0005vi-00 [127.0.0.1] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="127.0.0.1" +1999-03-02 09:44:33 10HmbK-0005vi-00 tls_out_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbK-0005vi-00 => getticket@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbL-0005vi-00" +1999-03-02 09:44:33 10HmbK-0005vi-00 Completed +1999-03-02 09:44:33 10HmbM-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for resume@test.ex abcd@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmbM-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmbM-0005vi-00 [ip4.ip4.ip4.ip4] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmbM-0005vi-00 [ip4.ip4.ip4.ip4] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="ip4.ip4.ip4.ip4" +1999-03-02 09:44:33 10HmbM-0005vi-00 tls_out_resumption not requested or offered +1999-03-02 09:44:33 10HmbM-0005vi-00 => resume@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbN-0005vi-00" +1999-03-02 09:44:33 10HmbM-0005vi-00 -> xyz@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbN-0005vi-00" +1999-03-02 09:44:33 10HmbM-0005vi-00 => abcd@test.ex R=client T=send_to_server2 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbO-0005vi-00" +1999-03-02 09:44:33 10HmbM-0005vi-00 Completed +1999-03-02 09:44:33 10HmbP-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for renewal@test.ex +1999-03-02 09:44:33 10HmbP-0005vi-00 tls_out_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbP-0005vi-00 => renewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbQ-0005vi-00" +1999-03-02 09:44:33 10HmbP-0005vi-00 Completed +1999-03-02 09:44:33 10HmbR-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for postrenewal@test.ex +1999-03-02 09:44:33 10HmbR-0005vi-00 tls_out_resumption session resumed +1999-03-02 09:44:33 10HmbR-0005vi-00 => postrenewal@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbS-0005vi-00" +1999-03-02 09:44:33 10HmbR-0005vi-00 Completed +1999-03-02 09:44:33 10HmbT-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for timeout@test.ex +1999-03-02 09:44:33 10HmbT-0005vi-00 tls_out_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbT-0005vi-00 => timeout@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no C="250 OK id=10HmbU-0005vi-00" +1999-03-02 09:44:33 10HmbT-0005vi-00 Completed +1999-03-02 09:44:33 10HmbV-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for notreq@test.ex +1999-03-02 09:44:33 10HmbV-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock +1999-03-02 09:44:33 10HmbV-0005vi-00 [127.0.0.1] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="127.0.0.1" +1999-03-02 09:44:33 10HmbV-0005vi-00 tls_out_resumption not requested or offered +1999-03-02 09:44:33 10HmbV-0005vi-00 => notreq@test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 OK id=10HmbW-0005vi-00" +1999-03-02 09:44:33 10HmbV-0005vi-00 Completed + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex for getticket@test.ex +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmaZ-0005vi-00@myhost.test.ex for resume@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption not requested or offered +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaZ-0005vi-00@myhost.test.ex for abcd@test.ex +1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbB-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbC-0005vi-00@myhost.test.ex for renewal@test.ex +1999-03-02 09:44:33 10HmbD-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbD-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbE-0005vi-00@myhost.test.ex for postrenewal@test.ex +1999-03-02 09:44:33 10HmbF-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbF-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption client offered session, server only provided new ticket +1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbG-0005vi-00@myhost.test.ex for timeout@test.ex +1999-03-02 09:44:33 10HmbH-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbH-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption no client request +1999-03-02 09:44:33 10HmbJ-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbI-0005vi-00@myhost.test.ex for notreq@test.ex +1999-03-02 09:44:33 10HmbJ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbJ-0005vi-00 Completed +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbL-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbK-0005vi-00@myhost.test.ex for getticket@test.ex +1999-03-02 09:44:33 10HmbL-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbL-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed +1999-03-02 09:44:33 10HmbN-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbM-0005vi-00@myhost.test.ex for resume@test.ex xyz@test.ex +1999-03-02 09:44:33 10HmbN-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbN-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbN-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption not requested or offered +1999-03-02 09:44:33 10HmbO-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbM-0005vi-00@myhost.test.ex for abcd@test.ex +1999-03-02 09:44:33 10HmbO-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbO-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbQ-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbP-0005vi-00@myhost.test.ex for renewal@test.ex +1999-03-02 09:44:33 10HmbQ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbQ-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed +1999-03-02 09:44:33 10HmbS-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbR-0005vi-00@myhost.test.ex for postrenewal@test.ex +1999-03-02 09:44:33 10HmbS-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbS-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption session resumed, also new ticket +1999-03-02 09:44:33 10HmbU-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx* CV=no S=sss id=E10HmbT-0005vi-00@myhost.test.ex for timeout@test.ex +1999-03-02 09:44:33 10HmbU-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbU-0005vi-00 Completed +1999-03-02 09:44:33 tls_in_resumption client requested new ticket, server provided +1999-03-02 09:44:33 10HmbW-0005vi-00 <= CALLER@myhost.test.ex H=(helo.data.changed) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbV-0005vi-00@myhost.test.ex for notreq@test.ex +1999-03-02 09:44:33 10HmbW-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbW-0005vi-00 Completed diff --git a/test/runtest b/test/runtest index 6566579ef..a9927475e 100755 --- a/test/runtest +++ b/test/runtest @@ -615,9 +615,10 @@ RESET_AFTER_EXTRA_LINE_READ: # TLS1.2:ECDHE_SECP256R1__RSA_SHA256__AES_256_GCM:256 # TLS1.2:ECDHE_SECP256R1__RSA_SHA256__AES_128_CBC__SHA256:128 # TLS1.2:ECDHE_SECP256R1__ECDSA_SHA512__AES_256_GCM:256 - # TLS1.2:ECDHE_RSA_SECP256R1__AES_256_GCM:256 (! 3.5.18 !) - # TLS1.2:RSA__CAMELLIA_256_GCM:256 (leave the cipher name) - # TLS1.2-PKIX:RSA__AES_128_GCM__AEAD:128 (the -PKIX seems to be a 3.1.20 thing) + # TLS1.2:ECDHE_SECP256R1__AES_256_GCM:256 (3.6.7 resumption) + # TLS1.2:ECDHE_RSA_SECP256R1__AES_256_GCM:256 (! 3.5.18 !) + # TLS1.2:RSA__CAMELLIA_256_GCM:256 (leave the cipher name) + # TLS1.2-PKIX:RSA__AES_128_GCM__AEAD:128 (the -PKIX seems to be a 3.1.20 thing) # TLS1.2-PKIX:ECDHE_RSA_SECP521R1__AES_256_GCM__AEAD:256 # # X=TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256 @@ -1000,6 +1001,10 @@ RESET_AFTER_EXTRA_LINE_READ: # ARC is not always supported by the build next if /^arc_sign =/; + + # TLS resumption is not always supported by the build + next if /^tls_resumption_hosts =/; + next if /^-tls_resumption/; } # ======== stderr ======== @@ -1225,6 +1230,9 @@ RESET_AFTER_EXTRA_LINE_READ: # Not all builds include DMARC next if /^DMARC: no (dmarc_tld_file|sender_host_address)$/ ; + # TLS resumption is not always supported by the build + next if /in tls_resumption_hosts\?/; + # When Exim is checking the size of directories for maildir, it uses # the check_dir_size() function to scan directories. Of course, the order # of the files that are obtained using readdir() varies from system to diff --git a/test/scripts/5890-Resume-GnuTLS/5890 b/test/scripts/5890-Resume-GnuTLS/5890 new file mode 100644 index 000000000..9db5403b9 --- /dev/null +++ b/test/scripts/5890-Resume-GnuTLS/5890 @@ -0,0 +1,62 @@ +# TLS session resumption +gnutls +# +# For keying info: +# (requires SSLKEYLOGFILE added to /etc/sudoers) +# SSLKEYLOGFILE=/home/jgh/git/exim/test/foo sudo exim -DSERVER=server -bd -oX PORT_D +# +### TLS1.2 +exim -DSERVER=server -DOPTION=NORMAL:!VERS-TLS1.3 -bd -oX PORT_D +**** +exim -DVALUE=resume -odf getticket@test.ex +Test message. Contains FF: ÿ +**** +exim -DVALUE=resume -odf resume@test.ex abcd@test.ex xyz@test.ex +Test message to two different hosts, one does not support resume +**** +# allow time for ticket to hit renewal time +sleep 3 +exim -DVALUE=resume -odf renewal@test.ex +Test message. +**** +exim -DVALUE=resume -odf postrenewal@test.ex +Test message. +**** +sleep 3 +exim -DVALUE=resume -odf timeout@test.ex +Test message. +**** +exim -odf notreq@test.ex +Test message, not requesting resumption. +**** +killdaemon +sleep 1 +sudo rm DIR/spool/db/tls +# +# +### TLS1.3 +exim -DSERVER=server -DOPTION=NORMAL -bd -oX PORT_D +**** +exim -DVALUE=resume -odf getticket@test.ex +Test message. Contains FF: ÿ +**** +exim -DVALUE=resume -odf resume@test.ex abcd@test.ex xyz@test.ex +Test message to two different hosts, one does not support resume +**** +# allow time for ticket to hit renewal time +sleep 3 +exim -DVALUE=resume -odf renewal@test.ex +Test message. +**** +exim -DVALUE=resume -odf postrenewal@test.ex +Test message. +**** +sleep 3 +exim -DVALUE=resume -odf timeout@test.ex +Test message. +**** +exim -odf notreq@test.ex +Test message, not requesting resumption. +**** +killdaemon +no_msglog_check diff --git a/test/scripts/5890-Resume-GnuTLS/REQUIRES b/test/scripts/5890-Resume-GnuTLS/REQUIRES new file mode 100644 index 000000000..2f12f2742 --- /dev/null +++ b/test/scripts/5890-Resume-GnuTLS/REQUIRES @@ -0,0 +1,3 @@ +support GnuTLS +running IPv4 +support Experimental_TLS_resume diff --git a/test/scripts/5891-Resume-OpenSSL/5891 b/test/scripts/5891-Resume-OpenSSL/5891 new file mode 100644 index 000000000..116f5cfe9 --- /dev/null +++ b/test/scripts/5891-Resume-OpenSSL/5891 @@ -0,0 +1,57 @@ +# TLS session resumption +# +### TLS1.2 +exim -DSERVER=server -DOPTION=+no_tlsv1_3 -bd -oX PORT_D +**** +exim -DVALUE=resume -odf getticket@test.ex +Test message. Contains FF: ÿ +**** +exim -DVALUE=resume -odf resume@test.ex abcd@test.ex xyz@test.ex +Test message to two different hosts, one does not support resume +**** +# allow time for ticket to hit renewal time +sleep 3 +exim -DVALUE=resume -odf renewal@test.ex +Test message. +**** +exim -DVALUE=resume -odf postrenewal@test.ex +Test message. +**** +sleep 3 +exim -DVALUE=resume -odf timeout@test.ex +Test message. +**** +exim -odf notreq@test.ex +Test message, not requesting resumption. +**** +killdaemon +sleep 1 +sudo rm DIR/spool/db/tls +# +# +### TLS1.3 +exim -DSERVER=server -bd -oX PORT_D +**** +exim -DVALUE=resume -odf getticket@test.ex +Test message. Contains FF: ÿ +**** +exim -DVALUE=resume -odf resume@test.ex abcd@test.ex xyz@test.ex +Test message to two different hosts, one does not support resume +**** +# allow time for ticket to hit renewal time +sleep 3 +exim -DVALUE=resume -odf renewal@test.ex +Test message. +**** +exim -DVALUE=resume -odf postrenewal@test.ex +Test message. +**** +sleep 3 +exim -DVALUE=resume -odf timeout@test.ex +Test message. +**** +exim -odf notreq@test.ex +Test message, not requesting resumption. +**** +killdaemon +no_msglog_check diff --git a/test/scripts/5891-Resume-OpenSSL/REQUIRES b/test/scripts/5891-Resume-OpenSSL/REQUIRES new file mode 100644 index 000000000..027b4dcab --- /dev/null +++ b/test/scripts/5891-Resume-OpenSSL/REQUIRES @@ -0,0 +1,3 @@ +support OpenSSL +running IPv4 +support Experimental_TLS_resume diff --git a/test/stderr/5890 b/test/stderr/5890 new file mode 100644 index 000000000..6b5c4341e --- /dev/null +++ b/test/stderr/5890 @@ -0,0 +1,6 @@ +### TLS1.2 +### TLS1.3 + +******** SERVER ******** +### TLS1.2 +### TLS1.3 diff --git a/test/stderr/5891 b/test/stderr/5891 new file mode 100644 index 000000000..6b5c4341e --- /dev/null +++ b/test/stderr/5891 @@ -0,0 +1,6 @@ +### TLS1.2 +### TLS1.3 + +******** SERVER ******** +### TLS1.2 +### TLS1.3 diff --git a/test/stdout/0572 b/test/stdout/0572 index 272aa06a1..0ea381190 100644 --- a/test/stdout/0572 +++ b/test/stdout/0572 @@ -78,7 +78,7 @@ OPT = # 1 "TESTSUITE/aux-var/std_conf_prefix" # 1 "TESTSUITE/aux-var/std_conf_prefix" # 1 "TESTSUITE/aux-var/tls_conf_prefix" -keep_environment = PATH +keep_environment = PATH:SSLKEYLOGFILE exim_path = TESTSUITE/eximdir/exim host_lookup_order = bydns spool_directory = TESTSUITE/spool @@ -118,7 +118,7 @@ OPT = # 1 "TESTSUITE/aux-var/std_conf_prefix" # 1 "TESTSUITE/aux-var/std_conf_prefix" # 1 "TESTSUITE/aux-var/tls_conf_prefix" -keep_environment = PATH +keep_environment = PATH:SSLKEYLOGFILE exim_path = TESTSUITE/eximdir/exim host_lookup_order = bydns spool_directory = TESTSUITE/spool diff --git a/test/stdout/0577 b/test/stdout/0577 index ef918a86b..f08550f6f 100644 --- a/test/stdout/0577 +++ b/test/stdout/0577 @@ -4,7 +4,7 @@ # 1 "TESTSUITE/aux-var/std_conf_prefix" # 1 "TESTSUITE/aux-var/std_conf_prefix" # 1 "TESTSUITE/aux-var/tls_conf_prefix" -keep_environment = PATH +keep_environment = PATH:SSLKEYLOGFILE exim_path = TESTSUITE/eximdir/exim host_lookup_order = bydns spool_directory = TESTSUITE/spool diff --git a/test/stdout/5890 b/test/stdout/5890 new file mode 100644 index 000000000..6b5c4341e --- /dev/null +++ b/test/stdout/5890 @@ -0,0 +1,6 @@ +### TLS1.2 +### TLS1.3 + +******** SERVER ******** +### TLS1.2 +### TLS1.3 diff --git a/test/stdout/5891 b/test/stdout/5891 new file mode 100644 index 000000000..6b5c4341e --- /dev/null +++ b/test/stdout/5891 @@ -0,0 +1,6 @@ +### TLS1.2 +### TLS1.3 + +******** SERVER ******** +### TLS1.2 +### TLS1.3 -- 2.30.2