diff --git a/postfix/HISTORY b/postfix/HISTORY index b93da842b..d1545c53a 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -26779,6 +26779,7 @@ Apologies for any names omitted. warning message tls.tls_dh.c. 20230115 + Workaround for a breaking change in OpenSSL 3: always turn on SSL_OP_IGNORE_UNEXPECTED_EOF, to avoid warning messages and missed opportunities for TLS session reuse. This is @@ -26786,3 +26787,24 @@ Apologies for any names omitted. framing, and is therefore not affected by TLS truncation attacks. Fix by Viktor Dukhovni. Files: tls/tls.h, tls_client.c, tls/tls_server.c. + +20230121 + + Documentation: describe when Postfix and Milters inspect + SMTP commands or header/body content. File: + proto/MILTER_README.html. + +20230127 + + Bugfix (introduced: Postfix 3.4): the posttls-finger command + failed to detect that a connection was resumed in the case + that a server did not return a certificate. Viktor Dukhovni. + File: posttls-finger/posttls-finger.c. + + Workaround: OpenSSL 3.x EVP_get_cipherbyname() can return + lazily-bound handles. Postfix now checks that the expected + functionality will be available instead of failing later. + Fix by Viktor Dukhovni. File: tls/tls_server.c. + + Portability: MacOS support for the postfix-env.sh test + script. diff --git a/postfix/README_FILES/MILTER_README b/postfix/README_FILES/MILTER_README index fc52c6e8d..4ace86821 100644 --- a/postfix/README_FILES/MILTER_README +++ b/postfix/README_FILES/MILTER_README @@ -24,6 +24,7 @@ implementations. This document provides information on the following topics: * How Milter applications plug into Postfix + * When Postfix and Milters inspect an SMTP session * Building Milter applications * Running Milter applications * Configuring Postfix @@ -80,6 +81,41 @@ Postfix architecture). Local -> sendmail(1) +WWhheenn PPoossttffiixx aanndd MMiilltteerrss iinnssppeecctt aann SSMMTTPP sseessssiioonn + +Generally, Postfix inspects information first, then the first configured +Milter, the second configured Milter, and so on. + + * With most SMTP commands: Postfix reviews one SMTP command, and if Postfix + does not reject it, Postfix passes the command to the first configured + Milter. If the first Milter does not reject the command, Postfix passes it + to the second configured Milter, and so on. This includes commands with an + envelope sender (MAIL FROM) or envelope recipient (RCPT TO). Postfix stores + the same envelope records in a queue file as when no Milters are + configured, including rewritten envelope addresses, expanded virtual + aliases, BCC addresses from sender/recipient_bcc_maps, and so on. + + * With header/body content: Postfix may rewrite or reject header/body content + before it stores that content in the queue file; Postfix stores the same + header/body content as when no Milters are configured. If Postfix does not + reject the header/body content, Postfix passes it to the first configured + Milter which may modify or reject that content or may modify the stored + envelope. If the first Milter does not reject the header/body content, + Postfix passes it to the second configured Milter, and so on. + +Details: + + * Postfix hides its own Postfix-prepended Received: header, for compatibility + with Sendmail. Postfix does not hide other headers that Postfix or Milters + added or modified. + + * When the Postfix SMTP server receives a sequence of one or more valid BDAT + commands, it generates one DATA command for the Milters. + + * The Milter API does not support inspection of SMTP commands such as QUIT, + NOOP, or VRFY; the API supports only commands that are needed for email + delivery. + BBuuiillddiinngg MMiilltteerr aapppplliiccaattiioonnss Milter applications have been written in C, Haskell, Java, Perl, Python, Rust, diff --git a/postfix/html/MILTER_README.html b/postfix/html/MILTER_README.html index 2f8c99412..bcef3b947 100644 --- a/postfix/html/MILTER_README.html +++ b/postfix/html/MILTER_README.html @@ -48,6 +48,9 @@ document for differences between Postfix and Sendmail implementations.
  • How Milter applications plug into Postfix +
  • When Postfix and Milters inspect an +SMTP session +
  • Building Milter applications
  • Running Milter applications @@ -192,6 +195,54 @@ href="QSHAPE_README.html#incoming_queue"> incoming +

    When Postfix and Milters inspect an SMTP +session

    + +

    Generally, Postfix inspects information first, then the first +configured Milter, the second configured Milter, and so on.

    + + + +

    Details:

    + + +

    Building Milter applications

    Milter applications have been written in C, Haskell, Java, Perl, diff --git a/postfix/postfix-env.sh b/postfix/postfix-env.sh index 9c0fe44cf..35f317534 100644 --- a/postfix/postfix-env.sh +++ b/postfix/postfix-env.sh @@ -2,4 +2,4 @@ # Run a program with the new shared libraries instead of the installed ones. -LD_LIBRARY_PATH=`pwd`/lib exec "$@" +LD_LIBRARY_PATH=`pwd`/lib DYLD_LIBRARY_PATH=`pwd`/lib exec "$@" diff --git a/postfix/proto/MILTER_README.html b/postfix/proto/MILTER_README.html index 1f2868d27..e5ec60e5a 100644 --- a/postfix/proto/MILTER_README.html +++ b/postfix/proto/MILTER_README.html @@ -48,6 +48,9 @@ document for differences between Postfix and Sendmail implementations.

  • How Milter applications plug into Postfix +
  • When Postfix and Milters inspect an +SMTP session +
  • Building Milter applications
  • Running Milter applications @@ -192,6 +195,54 @@ href="QSHAPE_README.html#incoming_queue"> incoming +

    When Postfix and Milters inspect an SMTP +session

    + +

    Generally, Postfix inspects information first, then the first +configured Milter, the second configured Milter, and so on.

    + + + +

    Details:

    + + +

    Building Milter applications

    Milter applications have been written in C, Haskell, Java, Perl, diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 07663c44a..fcf78608e 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20230121" +#define MAIL_RELEASE_DATE "20230128" #define MAIL_VERSION_NUMBER "3.8" #ifdef SNAPSHOT diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index cdb0b4f9e..5526c408d 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -942,9 +942,9 @@ static int starttls(STATE *state) print_trust_info(state); state->log_mask &= ~(TLS_LOG_CERTMATCH | TLS_LOG_PEERCERT | TLS_LOG_VERBOSE | TLS_LOG_UNTRUSTED); - state->log_mask |= TLS_LOG_CACHE | TLS_LOG_SUMMARY; - tls_update_app_logmask(state->tls_ctx, state->log_mask); } + state->log_mask |= TLS_LOG_CACHE | TLS_LOG_SUMMARY; + tls_update_app_logmask(state->tls_ctx, state->log_mask); } return (0); } diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index b76cfbc70..dcf232e84 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -167,6 +167,13 @@ */ static const char server_session_id_context[] = "Postfix/TLS"; +#ifndef OPENSSL_NO_TLSEXT + /* + * We retain the cipher handle for the lifetime of the process. + */ +static const EVP_CIPHER *tkt_cipher; +#endif + #define GET_SID(s, v, lptr) ((v) = SSL_SESSION_get_id((s), (lptr))) typedef const unsigned char *session_id_t; @@ -293,7 +300,7 @@ static int new_server_session_cb(SSL *ssl, SSL_SESSION *session) #define TLS_TKT_ACCEPT 1 /* Ticket decryptable and re-usable */ #define TLS_TKT_REISSUE 2 /* Ticket decryptable, not re-usable */ -#if defined(SSL_OP_NO_TICKET) && !defined(OPENSSL_NO_TLSEXT) +#if !defined(OPENSSL_NO_TLSEXT) #if OPENSSL_VERSION_PREREQ(3,0) @@ -303,13 +310,11 @@ static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], EVP_CIPHER_CTX *ctx, EVP_MAC_CTX *hctx, int create) { OSSL_PARAM params[3]; - static const EVP_CIPHER *ciph; TLS_TICKET_KEY *key; TLS_SESS_STATE *TLScontext = SSL_get_ex_data(con, TLScontext_index); int timeout = ((int) SSL_CTX_get_timeout(SSL_get_SSL_CTX(con))) / 2; - if ((!ciph && (ciph = EVP_get_cipherbyname(var_tls_tkt_cipher)) == 0) - || (key = tls_mgr_key(create ? 0 : name, timeout)) == 0 + if ((key = tls_mgr_key(create ? 0 : name, timeout)) == 0 || (create && RAND_bytes(iv, TLS_TICKET_IVLEN) <= 0)) return (create ? TLS_TKT_NOKEYS : TLS_TKT_STALE); @@ -323,13 +328,13 @@ static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], return (create ? TLS_TKT_NOKEYS : TLS_TKT_STALE); if (create) { - EVP_EncryptInit_ex(ctx, ciph, NOENGINE, key->bits, iv); + EVP_EncryptInit_ex(ctx, tkt_cipher, NOENGINE, key->bits, iv); memcpy((void *) name, (void *) key->name, TLS_TICKET_NAMELEN); if (TLScontext->log_mask & TLS_LOG_CACHE) msg_info("%s: Issuing session ticket, key expiration: %ld", TLScontext->namaddr, (long) key->tout); } else { - EVP_DecryptInit_ex(ctx, ciph, NOENGINE, key->bits, iv); + EVP_DecryptInit_ex(ctx, tkt_cipher, NOENGINE, key->bits, iv); if (TLScontext->log_mask & TLS_LOG_CACHE) msg_info("%s: Decrypting session ticket, key expiration: %ld", TLScontext->namaddr, (long) key->tout); @@ -346,13 +351,11 @@ static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int create) { static const EVP_MD *sha256; - static const EVP_CIPHER *ciph; TLS_TICKET_KEY *key; TLS_SESS_STATE *TLScontext = SSL_get_ex_data(con, TLScontext_index); int timeout = ((int) SSL_CTX_get_timeout(SSL_get_SSL_CTX(con))) / 2; if ((!sha256 && (sha256 = EVP_sha256()) == 0) - || (!ciph && (ciph = EVP_get_cipherbyname(var_tls_tkt_cipher)) == 0) || (key = tls_mgr_key(create ? 0 : name, timeout)) == 0 || (create && RAND_bytes(iv, TLS_TICKET_IVLEN) <= 0)) return (create ? TLS_TKT_NOKEYS : TLS_TKT_STALE); @@ -360,13 +363,13 @@ static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], HMAC_Init_ex(hctx, key->hmac, TLS_TICKET_MACLEN, sha256, NOENGINE); if (create) { - EVP_EncryptInit_ex(ctx, ciph, NOENGINE, key->bits, iv); + EVP_EncryptInit_ex(ctx, tkt_cipher, NOENGINE, key->bits, iv); memcpy((void *) name, (void *) key->name, TLS_TICKET_NAMELEN); if (TLScontext->log_mask & TLS_LOG_CACHE) msg_info("%s: Issuing session ticket, key expiration: %ld", TLScontext->namaddr, (long) key->tout); } else { - EVP_DecryptInit_ex(ctx, ciph, NOENGINE, key->bits, iv); + EVP_DecryptInit_ex(ctx, tkt_cipher, NOENGINE, key->bits, iv); if (TLScontext->log_mask & TLS_LOG_CACHE) msg_info("%s: Decrypting session ticket, key expiration: %ld", TLScontext->namaddr, (long) key->tout); @@ -530,18 +533,20 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) * Add SSL_OP_NO_TICKET when the timeout is zero or library support is * incomplete. */ -#ifdef SSL_OP_NO_TICKET #ifndef OPENSSL_NO_TLSEXT ticketable = (*var_tls_tkt_cipher && scache_timeout > 0 && !(off & SSL_OP_NO_TICKET)); if (ticketable) { - const EVP_CIPHER *ciph; - - if ((ciph = EVP_get_cipherbyname(var_tls_tkt_cipher)) == 0 - || EVP_CIPHER_mode(ciph) != EVP_CIPH_CBC_MODE - || EVP_CIPHER_iv_length(ciph) != TLS_TICKET_IVLEN - || EVP_CIPHER_key_length(ciph) < TLS_TICKET_IVLEN - || EVP_CIPHER_key_length(ciph) > TLS_TICKET_KEYLEN) { +#if OPENSSL_VERSION_PREREQ(3,0) + tkt_cipher = EVP_CIPHER_fetch(NULL, var_tls_tkt_cipher, NULL); +#else + tkt_cipher = EVP_get_cipherbyname(var_tls_tkt_cipher); +#endif + if (tkt_cipher == 0 + || EVP_CIPHER_mode(tkt_cipher) != EVP_CIPH_CBC_MODE + || EVP_CIPHER_iv_length(tkt_cipher) != TLS_TICKET_IVLEN + || EVP_CIPHER_key_length(tkt_cipher) < TLS_TICKET_IVLEN + || EVP_CIPHER_key_length(tkt_cipher) > TLS_TICKET_KEYLEN) { msg_warn("%s: invalid value: %s; session tickets disabled", VAR_TLS_TKT_CIPHER, var_tls_tkt_cipher); ticketable = 0; @@ -571,7 +576,6 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) #endif if (!ticketable) off |= SSL_OP_NO_TICKET; -#endif SSL_CTX_set_options(server_ctx, off);