client remained in the "accept this message" state, skipping
Milter policy enforcement for the second message.
+20260805
+
+ Isolation: stamp Postfix SMTP server TLS session tickets
+ with their master.cf service name. With this, an SMTP server
+ defined in master.cf will no longer accept tickets issued
+ by a different SMTP server defined in the same master.cf
+ file. Fix by OpenAI security. File: tls_server.c.
+
+ Bug (defect introduced: Postfix 3.4, date: 20180805): SMTP
+ server command history memory exhaustion with a large number
+ of very small BDAT requests. Reported by OpenAI security.
+ File: smtpd.c.
+
+ Bug (defect introduced: Postfix 3.4, date: 20180303): the
+ MySQL client setting "tls_verify_cert = yes" had no effect
+ with Oracle MySQL 8 and later. Report and fix by OpenAI
+ Security. File: dict_mysql.c.
+
TODO
Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
<p>
The maximal number of lines in the Postfix SMTP server command history
-before it is flushed upon receipt of EHLO, RSET, or end of DATA.
+before it is flushed upon receipt of EHLO, RSET, or end of DATA. With
+Postfix ≥ 3.4 releases after 20260805, the history is flushed
+whenever it reaches the threshold. The <a href="postconf.5.html#notify_classes">notify_classes</a> parameter
+controls what command history may be reported in postmaster
+notification email.
</p>
This feature is available in Postfix >= 3.10.
.SH smtpd_history_flush_threshold (default: 100)
The maximal number of lines in the Postfix SMTP server command history
-before it is flushed upon receipt of EHLO, RSET, or end of DATA.
+before it is flushed upon receipt of EHLO, RSET, or end of DATA. With
+Postfix >= 3.4 releases after 20260805, the history is flushed
+whenever it reaches the threshold. The notify_classes parameter
+controls what command history may be reported in postmaster
+notification email.
.SH smtpd_junk_command_limit (default: normal: 100, overload: 1)
The number of junk commands (NOOP, VRFY, ETRN or RSET) that a remote
SMTP client can send before the Postfix SMTP server starts to
<p>
The maximal number of lines in the Postfix SMTP server command history
-before it is flushed upon receipt of EHLO, RSET, or end of DATA.
+before it is flushed upon receipt of EHLO, RSET, or end of DATA. With
+Postfix ≥ 3.4 releases after 20260805, the history is flushed
+whenever it reaches the threshold. The notify_classes parameter
+controls what command history may be reported in postmaster
+notification email.
</p>
%PARAM smtpd_noop_commands
resloop
tokenized
fortra
+OpenAI
+stateful
support File smtpd smtpd c
sendmail sendmail c
postqueue postqueue c
+ OpenAI Security File smtpd smtpd c
+ by OpenAI Security File smtpd smtpd c
unitext
esmtpargs
uncoded
+desynchronization
+xffffffff
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50023
#define DICT_MYSQL_SSL_VERIFY_SERVER_CERT MYSQL_OPT_SSL_VERIFY_SERVER_CERT
#elif MYSQL_VERSION_ID >= 80000
-#define DICT_MYSQL_SSL_VERIFY_SERVER_CERT MYSQL_OPT_SSL_MODE
+/* 202607 OpenAI: MYSQL_OPT_SSL_MODE is an enum, not a bool. */
+#define DICT_MYSQL_SSL_MODE MYSQL_OPT_SSL_MODE
#endif
/*
char *tls_CAfile;
char *tls_CApath;
char *tls_ciphers;
-#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
+#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT) || defined(DICT_MYSQL_SSL_MODE)
int tls_verify_cert;
#endif
int require_result_set;
*/
static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
{
+#if defined(DICT_MYSQL_SSL_MODE)
+ enum mysql_ssl_mode ssl_mode;
+
+#endif
+
if ((host->db = mysql_init(NULL)) == NULL)
msg_fatal("dict_mysql: insufficient memory");
if (dict_mysql->option_file)
dict_mysql->tls_CAfile, dict_mysql->tls_CApath,
dict_mysql->tls_ciphers);
#endif
-#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
+#if defined(DICT_MYSQL_SSL_MODE)
+ if (dict_mysql->tls_verify_cert != -1) {
+ /* MYSQL_OPT_SSL_MODE enum values are not Boolean values. */
+ ssl_mode = dict_mysql->tls_verify_cert ? SSL_MODE_VERIFY_IDENTITY :
+ SSL_MODE_PREFERRED;
+ if (mysql_options(host->db, DICT_MYSQL_SSL_MODE, &ssl_mode) != 0) {
+ msg_warn("dict_mysql: could not set TLS mode for host %s: %s",
+ host->hostname, mysql_error(host->db));
+ plmysql_down_host(host, dict_mysql->retry_interval);
+ return;
+ }
+ }
+#elif defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
if (dict_mysql->tls_verify_cert != -1)
mysql_options(host->db, DICT_MYSQL_SSL_VERIFY_SERVER_CERT,
&dict_mysql->tls_verify_cert);
dict_mysql->tls_CAfile = cfg_get_str(p, "tls_CAfile", NULL, 0, 0);
dict_mysql->tls_CApath = cfg_get_str(p, "tls_CApath", NULL, 0, 0);
dict_mysql->tls_ciphers = cfg_get_str(p, "tls_ciphers", NULL, 0, 0);
-#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
+#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT) || defined(DICT_MYSQL_SSL_MODE)
dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1);
#endif
dict_mysql->require_result_set = cfg_get_bool(p, "require_result_set", 1);
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20260804"
+#define MAIL_RELEASE_DATE "20260805"
#define MAIL_VERSION_NUMBER "3.12"
#ifdef SNAPSHOT
for (;;) {
if (state->flags & SMTPD_FLAG_HANGUP)
break;
+ /* Flush the command history if it becomes large. */
+ chat_reset(state, var_smtpd_hist_thrsh);
smtp_stream_setup(state->client, var_smtpd_tmout,
var_smtpd_req_deadline, 0);
if (state->error_count >= var_smtpd_hard_erlim) {
const char *cipher_list;
TLS_APPL_STATE *app_ctx = props->ctx;
int log_mask;
+ /* 202607 OpenAI: isolate session tickets by master.cf service name. */
+ EVP_MD_CTX *sessid_ctx_md = 0;
+ unsigned char sessid_ctx[EVP_MAX_MD_SIZE];
+ unsigned int sessid_ctx_len;
+ static const char sessid_ctx_label[] = "Postfix/TLS serverid";
/*
* Convert user loglevel to internal logmask.
TLScontext->stream = props->stream;
TLScontext->mdalg = props->mdalg;
+ /*
+ * The serverid already partitions stateful sessions in the tlsmgr
+ * session cache. Apply the same partition to stateless sessions; all
+ * services in one Postfix instance share the tlsmgr ticket keys.
+ */
+ if (tls_digest_byname(LN_sha256, &sessid_ctx_md) == 0
+ || EVP_DigestUpdate(sessid_ctx_md, sessid_ctx_label,
+ sizeof(sessid_ctx_label)) != 1
+ || EVP_DigestUpdate(sessid_ctx_md, props->serverid,
+ strlen(props->serverid) + 1) != 1
+ || EVP_DigestFinal_ex(sessid_ctx_md, sessid_ctx, &sessid_ctx_len) != 1
+ || sessid_ctx_len > SSL_MAX_SID_CTX_LENGTH
+ || SSL_set_session_id_context(TLScontext->con, sessid_ctx,
+ sessid_ctx_len) != 1) {
+ msg_warn("Could not set TLS session ID context");
+ tls_print_errors();
+ if (sessid_ctx_md)
+ EVP_MD_CTX_destroy(sessid_ctx_md);
+ tls_free_context(TLScontext);
+ return (0);
+ }
+ EVP_MD_CTX_destroy(sessid_ctx_md);
+
if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
msg_warn("Could not set application data for 'TLScontext->con'");
tls_print_errors();