Prereq: "3.4.6" diff -ur --new-file /var/tmp/postfix-3.4.6/src/global/mail_version.h ./src/global/mail_version.h --- /var/tmp/postfix-3.4.6/src/global/mail_version.h 2019-06-29 09:57:49.000000000 -0400 +++ ./src/global/mail_version.h 2019-09-21 12:24:58.000000000 -0400 @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20190629" -#define MAIL_VERSION_NUMBER "3.4.6" +#define MAIL_RELEASE_DATE "20190921" +#define MAIL_VERSION_NUMBER "3.4.7" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -ur --new-file /var/tmp/postfix-3.4.6/HISTORY ./HISTORY --- /var/tmp/postfix-3.4.6/HISTORY 2019-06-27 19:36:26.000000000 -0400 +++ ./HISTORY 2019-09-21 11:57:46.000000000 -0400 @@ -24248,3 +24248,48 @@ handshake failure, causing stale numbers to be reported. The command counts are now reset in the function that reports the counts. File: smtpd/smtpd.c. + +20190723 + + Bugfix: the documentation said tls_fast_shutdown_enable, + but the code said tls_fast_shutdown. Viktor Dukhovni. Changed + the code because no-one is expected to override the default. + File: global/mail_params.h. + +20190820 + + Workaround for poor TCP loopback performance on LINUX, where + getsockopt(..., TCP_MAXSEG, ..) reports a TCP maximal segment + size that is 1/2 to 1/3 of the MTU. For example, with kernel + 5.1.16-300.fc30.x86_64 the TCP client and server announce + an mss of 65495 in the TCP handshake, but getsockopt() + returns 32741 (less than half). As a matter of principle, + Postfix won't turn on client-side TCP_NODELAY because that + hides application performance bugs, and because that still + suffers from server-side delayed ACKs. Instead, Postfix + avoids sending "small" writes back-to-back, by choosing a + VSTREAM buffer size that is a multiple of the reported MSS. + This workaround bumps the multiplier from 2x to 4x. File: + util/vstream_tweak.c. + +20190825 + + Bugfix (introduced: 20051222): the Dovecot client could + segfault (null pointer read) or cause an SMTP server assertion + to fail when talking to a fake Dovecot server. The client + now logs a proper error instead. Problem reported by Tim + Düsterhus. File: xsasl/xsasl_dovecot_server.c. + +20190914 + + Bugfix (introduced: Postfix 3.4): don't whitewash OpenSSL + error results after a plaintext output error. The code could + loop, and with some OpenSSL error results could flood the + log with error messages (see below for a specific case). + Problem reported by Andreas Schulze. File: tlsproxy/tlsproxy.c. + + Bitrot: don't invoke SSL_shutdown() when the SSL engine + thinks it is processing a TLS handshake. The commit at + https://github.com/openssl/openssl/commit/64193c8218540499984cd63cda41f3cd491f3f59 + changed the error status, incompatibly, from SSL_ERROR_NONE + into SSL_ERROR_SSL. File: tlsproxy/tlsproxxy.c. diff -ur --new-file /var/tmp/postfix-3.4.6/src/global/mail_params.h ./src/global/mail_params.h --- /var/tmp/postfix-3.4.6/src/global/mail_params.h 2019-06-27 17:38:33.000000000 -0400 +++ ./src/global/mail_params.h 2019-07-23 18:46:37.000000000 -0400 @@ -3333,7 +3333,7 @@ /* * The default is backwards-incompatible. */ -#define VAR_TLS_FAST_SHUTDOWN "tls_fast_shutdown" +#define VAR_TLS_FAST_SHUTDOWN "tls_fast_shutdown_enable" #define DEF_TLS_FAST_SHUTDOWN 1 extern bool var_tls_fast_shutdown; diff -ur --new-file /var/tmp/postfix-3.4.6/src/tlsproxy/tlsproxy.c ./src/tlsproxy/tlsproxy.c --- /var/tmp/postfix-3.4.6/src/tlsproxy/tlsproxy.c 2019-06-29 09:33:40.000000000 -0400 +++ ./src/tlsproxy/tlsproxy.c 2019-09-14 18:43:05.000000000 -0400 @@ -618,11 +618,11 @@ switch (err) { /* - * No error from SSL_read and SSL_write means that the plaintext - * output buffer is full and that the plaintext input buffer is - * empty. Stop read/write events on the ciphertext stream. Keep the - * timer alive as a safety mechanism for the case that the plaintext - * pseudothreads get stuck. + * No error means a successful SSL_accept/connect/shutdown request or + * sequence of SSL_read/write requests. Disable read/write events on + * the ciphertext stream. Keep the ciphertext stream timer alive as a + * safety mechanism for the case that the plaintext pseudothreads get + * stuck. */ case SSL_ERROR_NONE: if (state->ssl_last_err != SSL_ERROR_NONE) { @@ -676,10 +676,23 @@ default: /* - * Allow buffered-up plaintext output to trickle out. + * Allow buffered-up plaintext output to trickle out. Permanently + * disable read/write activity on the ciphertext stream, so that this + * function will no longer be called. Keep the ciphertext stream + * timer alive as a safety mechanism for the case that the plaintext + * pseudothreads get stuck. Return into tlsp_strategy(), which will + * enable plaintext write events. */ - if (state->plaintext_buf && NBBIO_WRITE_PEND(state->plaintext_buf)) +#define TLSP_CAN_TRICKLE_OUT_PLAINTEXT(buf) \ + ((buf) && !NBBIO_ERROR_FLAGS(buf) && NBBIO_WRITE_PEND(buf)) + + if (TLSP_CAN_TRICKLE_OUT_PLAINTEXT(state->plaintext_buf)) { + event_disable_readwrite(ciphertext_fd); + event_request_timer(tlsp_ciphertext_event, (void *) state, + state->timeout); + state->flags |= TLSP_FLAG_NO_MORE_CIPHERTEXT_IO; return (TLSP_STAT_OK); + } tlsp_state_free(state); return (TLSP_STAT_ERR); } @@ -750,6 +763,18 @@ int handshake_err; /* + * This function is called after every ciphertext or plaintext event, to + * schedule new ciphertext or plaintext I/O. + */ + + /* + * Try to make an SSL I/O request. If this fails with SSL_ERROR_WANT_READ + * or SSL_ERROR_WANT_WRITE, enable ciphertext read or write events, and + * retry the SSL I/O request in a later tlsp_strategy() call. + */ + if ((state->flags & TLSP_FLAG_NO_MORE_CIPHERTEXT_IO) == 0) { + + /* * Do not enable plain-text I/O before completing the TLS handshake. * Otherwise the remote peer can prepend plaintext to the optional * TLS_SESS_STATE object. @@ -784,9 +809,8 @@ if (NBBIO_ERROR_FLAGS(plaintext_buf)) { if (NBBIO_ACTIVE_FLAGS(plaintext_buf)) nbbio_disable_readwrite(state->plaintext_buf); - ssl_stat = SSL_shutdown(tls_context->con); - /* XXX Wait for return value 1 if sessions are to be reused? */ - if (ssl_stat < 0) { + if (!SSL_in_init(tls_context->con) + && (ssl_stat = SSL_shutdown(tls_context->con)) < 0) { handshake_err = SSL_get_error(tls_context->con, ssl_stat); tlsp_eval_tls_error(state, handshake_err); /* At this point, state could be a dangling pointer. */ @@ -862,6 +886,19 @@ ssl_write_err : ssl_read_err) < 0) /* At this point, state is a dangling pointer. */ return; + } + + /* + * Destroy state when the ciphertext I/O was permanently disbled and we + * can no longer trickle out plaintext. + */ + else { + plaintext_buf = state->plaintext_buf; + if (!TLSP_CAN_TRICKLE_OUT_PLAINTEXT(plaintext_buf)) { + tlsp_state_free(state); + return; + } + } /* * Try to enable/disable plaintext read/write events. Basically, if we diff -ur --new-file /var/tmp/postfix-3.4.6/src/tlsproxy/tlsproxy.h ./src/tlsproxy/tlsproxy.h --- /var/tmp/postfix-3.4.6/src/tlsproxy/tlsproxy.h 2019-02-08 17:22:24.000000000 -0500 +++ ./src/tlsproxy/tlsproxy.h 2019-09-14 18:43:05.000000000 -0400 @@ -47,6 +47,7 @@ } TLSP_STATE; #define TLSP_FLAG_DO_HANDSHAKE (1<<0) +#define TLSP_FLAG_NO_MORE_CIPHERTEXT_IO (1<<1) /* overrides DO_HANDSHAKE */ extern TLSP_STATE *tlsp_state_create(const char *, VSTREAM *); extern void tlsp_state_free(TLSP_STATE *); diff -ur --new-file /var/tmp/postfix-3.4.6/src/util/vstream_tweak.c ./src/util/vstream_tweak.c --- /var/tmp/postfix-3.4.6/src/util/vstream_tweak.c 2014-12-25 11:47:17.000000000 -0500 +++ ./src/util/vstream_tweak.c 2019-09-08 10:36:14.000000000 -0400 @@ -124,12 +124,20 @@ * stream buffer size to less than VSTREAM_BUFSIZE, when the request is * made before the first stream read or write operation. We don't want to * reduce the buffer size. + * + * As of 20190820 we increase the mss size multipler from 2x to 4x, because + * some LINUX loopback TCP stacks report an MSS of 21845 which is 3x + * smaller than the MTU of 65536. Even with a VSTREAM buffer 2x the + * reported MSS size, performance would suck due to Nagle or delayed ACK + * delays. */ #define EFF_BUFFER_SIZE(fp) (vstream_req_bufsize(fp) ? \ vstream_req_bufsize(fp) : VSTREAM_BUFSIZE) #ifdef CA_VSTREAM_CTL_BUFSIZE - if (mss > EFF_BUFFER_SIZE(fp) / 2) { + if (mss > EFF_BUFFER_SIZE(fp) / 4) { + if (mss < INT_MAX / 2) + mss *= 2; if (mss < INT_MAX / 2) mss *= 2; vstream_control(fp, diff -ur --new-file /var/tmp/postfix-3.4.6/src/xsasl/xsasl_dovecot_server.c ./src/xsasl/xsasl_dovecot_server.c --- /var/tmp/postfix-3.4.6/src/xsasl/xsasl_dovecot_server.c 2016-01-23 19:50:54.000000000 -0500 +++ ./src/xsasl/xsasl_dovecot_server.c 2019-08-27 03:35:11.000000000 -0400 @@ -584,10 +584,20 @@ if (xsasl_dovecot_parse_reply(server, &line) == 0) { /* authentication successful */ xsasl_dovecot_parse_reply_args(server, line, reply, 1); + if (server->username == 0) { + msg_warn("missing Dovecot server %s username field", cmd); + vstring_strcpy(reply, "Authentication backend error"); + return XSASL_AUTH_FAIL; + } return XSASL_AUTH_DONE; } } else if (strcmp(cmd, "CONT") == 0) { if (xsasl_dovecot_parse_reply(server, &line) == 0) { + if (line == 0) { + msg_warn("missing Dovecot server %s reply field", cmd); + vstring_strcpy(reply, "Authentication backend error"); + return XSASL_AUTH_FAIL; + } vstring_strcpy(reply, line); return XSASL_AUTH_MORE; }