2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

postfix-3.10-20250120-nonprod

This commit is contained in:
Wietse Z Venema 2025-01-20 00:00:00 -05:00 committed by Viktor Dukhovni
parent 1e2ac172f1
commit b800a42bb8
14 changed files with 149 additions and 53 deletions

View File

@ -28881,40 +28881,55 @@ Apologies for any names omitted.
20250119
Feature: REQUIRETLS verb in SMTP. According to RFC 8689 this
not only requires TLS, but also requires server certificate
matching. TODO: parameters, defaults, files.
Feature: support for the REQUIRETLS verb in SMTP. According
to RFC 8689, this requires TLS server certificate matching.
Files: cleanup/cleanup_api.c, global/cleanup_strflags.c,
global/post_mail.c, global/post_mail.c, global/ehlo_mask.[hc],
global/ehlo_mask_test.c, local/forward.c, smtpd/smtpd.c,
smtp/smtp_connect.c, smtp/smtp_proto.c.
20250120
Added a configuration parameter "tls_required_enable (default:
yes) to control support for the "TLS-Required: no" message
header. Files: global/mail_params.[hc], cleanup/cleanup_message.c.
Added a configuration parameter "requiretls_enable" (default:
yes). Files: cleanup/cleanup_api.c, global/cleanup_strflags.c,
global/post_mail.c, global/post_mail.c, global/ehlo_mask.[hc],
global/ehlo_mask_test.c, local/forward.c, smtpd/smtpd.c,
smtp/smtp_connect.c, smtp/smtp_proto.c.
REQUIRETLS support: After a certificate check fails, or a
remote SMTP server does not announce REQUIRETLS support,
the Postfix SMTP client will override the RFC 8689 5.x.x.
status and treat it as a soft error, until there are no
more alternate MX servers to try. Files: smtp/smtp.h,
smtp/smtp_proto.c, smtp/smtp_trouble.c.
Completed: when a message received with REQUIRETLS is
returned in a delivery status notification, return the
message headers only, and do not request delivery with
REQUIRETLS. Files: bounce/bounce_notify_service.c,
bounce/bounce_one_service.c, bounce/bounce_trace_service.c,
bounce/bounce_verp_service.c, bounce/bounce_warn_service.c.
TODO:
If the TLS policy, or remote server, does not support
REQUIRETLS, try alternate MX hosts before returning the
message as undeliverable. Code: smtp_connect.c,
smtp_proto.c (ehlo response and post-TLS handshake check).
The RFC says that REQUIRETLS applies to LMTP. Dovecot supports
TLS, but how common is it for Postfix to verify a Dovecot
server certificate? Should we add a 'cheat' setting that does
not enforce REQUIRETLS?
If a message contains "TLS-Required: no", should a bounce message
also contain this header?
If a message with REQUIRETLS is bounced, and we return a
headers-only bounce (NOTIFY-HDRS) does the bounce still
need REQUIRETLS?
If the Postfix SMTP server accepted REQUIRETLS, should that stay
in effect if, before the message is forwarded, the configuration
is changed to "requiretls_enable = no"? Same for "postsuper -r".
Same question for "TLS-Required: no".
Ditto for "tls_required_enable = no" and "TLS-Required: no".
Problem. After a certificate check fails, we want the SMTP
client to try other MX servers. This requires a 4.x.x status
code. But then, the Postfix SMTP client will defer the
message.
How does the delivery_status_filter solve that?
With "smtp_tls_security_level = secure" and "smtp_mx_session_limit
= 2" the Postfix SMTP client will make three connections.
Simplify cleanup_envelope_test. Write the initial SIZE record
Simplify the cleanup_envelope_test. Write the initial SIZE record
to /dev/null, don't call cleanup_final(), and verify the value
of state->sendopts.

View File

@ -159,3 +159,5 @@ proto proto socketmap_table
qmgr qmgr_deliver c qmgr qmgr_message c qmqpd qmqpd c
smtp smtp_proto c smtpd smtpd c verify verify c
operations Files cleanup cleanup h cleanup cleanup_message c
global ehlo_mask_test c local forward c smtpd smtpd c
more alternate MX servers to try Files smtp smtp h

View File

@ -98,6 +98,15 @@ int bounce_notify_service(int flags, char *service, char *queue_name,
char *postmaster;
int count;
/*
* If the original sender requested REQUIRETLS, return headers only, and
* do not enforce REQUIRETLS for the delivery status notification.
*/
if ((sendopts & SOPT_REQUIRETLS_ESMTP) != 0) {
dsn_ret = DSN_RET_HDRS;
sendopts &= ~SOPT_REQUIRETLS_ESMTP;
}
/*
* Initialize. Open queue file, bounce log, etc.
*
@ -196,7 +205,8 @@ int bounce_notify_service(int flags, char *service, char *queue_name,
&& bounce_header_dsn(bounce, bounce_info) == 0
&& bounce_diagnostic_dsn(bounce, bounce_info,
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_FULL);
bounce_original(bounce, bounce_info, dsn_ret ?
dsn_ret : DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
if (bounce_status == 0)
msg_info("%s: postmaster non-delivery notification: %s",

View File

@ -533,6 +533,14 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
post_mail_fprintf(bounce, "In-Reply-To: %s", STR(bounce_info->orig_msgid));
}
/*
* Trade confidentiality against availability.
*/
#if 0
if (var_tls_required_enable)
post_mail_fprintf(bounce, "TLS-Required: no");
#endif
/*
* Auto-Submitted header, as per RFC 3834.
*/

View File

@ -111,6 +111,15 @@ int bounce_notify_verp(int flags, char *service, char *queue_name,
if (strcasecmp_utf8(recipient, mail_addr_double_bounce()) == 0)
msg_panic("%s: attempt to bounce a double bounce", myname);
/*
* If the original sender requested REQUIRETLS, return headers only, and
* do not enforce REQUIRETLS for the delivery status notification.
*/
if ((sendopts & SOPT_REQUIRETLS_ESMTP) != 0) {
dsn_ret = DSN_RET_HDRS;
sendopts &= ~SOPT_REQUIRETLS_ESMTP;
}
/*
* Initialize. Open queue file, bounce log, etc.
*/

View File

@ -96,6 +96,15 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id,
var_notify_classes);
VSTRING *new_id = vstring_alloc(10);
/*
* If the original sender requested REQUIRETLS, return headers only, and
* do not enforce REQUIRETLS for the delivery status notification.
*/
if ((sendopts & SOPT_REQUIRETLS_ESMTP) != 0) {
dsn_ret = DSN_RET_HDRS;
sendopts &= ~SOPT_REQUIRETLS_ESMTP;
}
/*
* Initialize. Open queue file, bounce log, etc.
*/
@ -162,7 +171,8 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id,
&& bounce_recipient_log(bounce, bounce_info) == 0
&& bounce_header_dsn(bounce, bounce_info) == 0
&& bounce_recipient_dsn(bounce, bounce_info) == 0)
bounce_original(bounce, bounce_info, DSN_RET_FULL);
bounce_original(bounce, bounce_info, dsn_ret ?
dsn_ret : DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
if (bounce_status == 0)
msg_info("%s: postmaster non-delivery notification: %s",

View File

@ -95,6 +95,15 @@ int bounce_trace_service(int flags, char *service, char *queue_name,
int count;
const char *sender;
/*
* If the original sender requested REQUIRETLS, do not enforce REQUIRETLS
* for the delivery status notification. The trace service always returns
* headers only.
*/
if ((sendopts & SOPT_REQUIRETLS_ESMTP) != 0) {
sendopts &= ~SOPT_REQUIRETLS_ESMTP;
}
/*
* For consistency with fail/delay notifications, send notification for a
* non-bounce message as a single-bounce message, send notification for a

View File

@ -98,6 +98,15 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name,
char *postmaster;
int count;
/*
* If the original sender requested REQUIRETLS, return headers only, and
* do not enforce REQUIRETLS for the delivery status notification.
*/
if ((sendopts & SOPT_REQUIRETLS_ESMTP) != 0) {
dsn_ret = DSN_RET_HDRS;
sendopts &= ~SOPT_REQUIRETLS_ESMTP;
}
/*
* Initialize. Open queue file, bounce log, etc.
*
@ -185,7 +194,8 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name,
&& bounce_header_dsn(bounce, bounce_info) == 0
&& bounce_diagnostic_dsn(bounce, bounce_info,
DSN_NOTIFY_OVERRIDE) > 0) {
bounce_original(bounce, bounce_info, DSN_RET_FULL);
bounce_original(bounce, bounce_info, dsn_ret ?
dsn_ret : DSN_RET_FULL);
bounce_status = post_mail_fclose(bounce);
if (bounce_status == 0)
msg_info("%s: postmaster delay notification: %s",

View File

@ -653,7 +653,7 @@ static void cleanup_header_callback(void *context, int header_class,
if (state->hop_count == 1)
argv_add(state->auto_hdrs, vstring_str(header_buf), ARGV_END);
}
if (hdr_opts->type == HDR_TLS_REQUIRED) {
if (hdr_opts->type == HDR_TLS_REQUIRED && var_tls_required_enable) {
char *cp = vstring_str(header_buf) + strlen(hdr_opts->name) + 1;
while (ISSPACE(*cp))

View File

@ -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 "20250119"
#define MAIL_RELEASE_DATE "20250120"
#define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT

View File

@ -638,8 +638,9 @@ extern void smtp_rcpt_done(SMTP_STATE *, SMTP_RESP *, RECIPIENT *);
/*
* smtp_trouble.c
*/
#define SMTP_THROTTLE 1
#define SMTP_NOTHROTTLE 0
#define SMTP_MISC_FAIL_NONE 0
#define SMTP_MISC_FAIL_THROTTLE (1<<0)
#define SMTP_MISC_FAIL_SOFT_NON_FINAL (1<<1)
extern int smtp_sess_fail(SMTP_STATE *);
extern int PRINTFLIKE(5, 6) smtp_misc_fail(SMTP_STATE *, int, const char *,
SMTP_RESP *, const char *,...);
@ -649,9 +650,9 @@ extern void PRINTFLIKE(5, 6) smtp_rcpt_fail(SMTP_STATE *, RECIPIENT *,
extern int smtp_stream_except(SMTP_STATE *, int, const char *);
#define smtp_site_fail(state, mta, resp, ...) \
smtp_misc_fail((state), SMTP_THROTTLE, (mta), (resp), __VA_ARGS__)
smtp_misc_fail((state), SMTP_MISC_FAIL_THROTTLE, (mta), (resp), __VA_ARGS__)
#define smtp_mesg_fail(state, mta, resp, ...) \
smtp_misc_fail((state), SMTP_NOTHROTTLE, (mta), (resp), __VA_ARGS__)
smtp_misc_fail((state), SMTP_MISC_FAIL_NONE, (mta), (resp), __VA_ARGS__)
/*
* smtp_unalias.c

View File

@ -685,18 +685,20 @@ int smtp_helo(SMTP_STATE *state)
/*
* Require that the server announces REQUIRETLS when the sender required
* REQUIRETLS. TODO(wietse) try alternative MX hosts before returning the
* message as undeliverable.
* REQUIRETLS. Return the message as undeliverable only when there are no
* more alternative MX hosts.
*/
#ifdef USE_TLS
if ((state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0
&& (session->features & SMTP_FEATURE_REQUIRETLS) == 0
&& (request->sendopts & SOPT_REQUIRETLS_ESMTP) != 0)
return (smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "5.7.30"),
"REQUIRETLS is required, "
"but was not offered by host %s",
session->namaddr));
"Sender requires authenticated TLS, but no "
"mail server was found with REQUIRETLS "
"support. The last attempted server "
"was %s", session->namaddr));
#endif
/*
@ -1163,7 +1165,7 @@ static int smtp_start_tls(SMTP_STATE *state)
if (PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE)
RETRY_AS_PLAINTEXT;
return (smtp_misc_fail(state, state->tls->level == TLS_LEV_MAY ?
SMTP_NOTHROTTLE : SMTP_THROTTLE,
SMTP_MISC_FAIL_NONE : SMTP_MISC_FAIL_THROTTLE,
DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),
"Cannot start TLS: handshake failure"));
@ -1211,15 +1213,18 @@ static int smtp_start_tls(SMTP_STATE *state)
/*
* Require a server certificate match when the sender requested
* REQUIRETLS. TODO(wietse) try alternative MX hosts before
* returning the message as undeliverable.
* REQUIRETLS. Return the message as undeliverable only when
* there are no more alternative MX hosts.
*/
if ((state->request->sendopts & SOPT_REQUIRETLS_ESMTP)) {
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "5.7.10"),
"Sender requires a TLS server "
"certificate match, but the remote "
"server certificate did not match"));
"certificate match, but no matching "
"mail server was found. The last "
"attempted server was %s",
session->namaddr));
}
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),

View File

@ -33,9 +33,9 @@
/* int exception;
/* const char *description;
/* AUXILIARY FUNCTIONS
/* int smtp_misc_fail(state, throttle, mta_name, resp, format, ...)
/* int smtp_misc_fail(state, flags, mta_name, resp, format, ...)
/* SMTP_STATE *state;
/* int throttle;
/* int flags;
/* const char *mta_name;
/* SMTP_RESP *resp;
/* const char *format;
@ -91,8 +91,11 @@
/*
/* smtp_misc_fail() provides a more detailed interface than
/* smtp_site_fail() and smtp_mesg_fail(), which are convenience
/* wrappers around smtp_misc_fail(). The throttle argument
/* is either SMTP_THROTTLE or SMTP_NOTHROTTLE; it is used only
/* wrappers around smtp_misc_fail(). The flags argument is either
/* SMTP_MISC_FAIL_NONE or the bitwise OR of SMTP_MISC_FAIL_THROTTLE
/* (throttle the destination) and/or SMTP_MISC_FAIL_SOFT_NON_FINAL
/* (if the server was not the last one to try, treat a hard error
/* as a soft error); SMTP_MISC_FAIL_THROTTLE is used only
/* in the "soft error, final server" policy, and determines
/* whether a destination will be marked as problematic.
/*
@ -210,7 +213,7 @@ static void smtp_check_code(SMTP_SESSION *session, int code)
/* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */
static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
static int smtp_bulk_fail(SMTP_STATE *state, int flags)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
@ -220,8 +223,21 @@ static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
int aggregate_status;
int soft_error = (STR(why->status)[0] == '4');
int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce);
int throttle_queue = (flags & SMTP_MISC_FAIL_THROTTLE);
int nrcpt;
/*
* Sanity check.
*/
if ((flags & SMTP_MISC_FAIL_SOFT_NON_FINAL) != 0) {
if (soft_error) {
msg_warn("smtp_bulk_fail: ignoring SMTP_MISC_FAIL_SOFT_NON_FINAL "
"for a soft error");
} else {
soft_error = (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0;
}
}
/*
* Don't defer the recipients just yet when this error qualifies them for
* delivery to a backup server. Just log something informative to show
@ -302,7 +318,7 @@ int smtp_sess_fail(SMTP_STATE *state)
* because this error information is collected by a routine that
* terminates BEFORE the error is reported.
*/
return (smtp_bulk_fail(state, SMTP_THROTTLE));
return (smtp_bulk_fail(state, SMTP_MISC_FAIL_THROTTLE));
}
/* vsmtp_fill_dsn - fill in temporary DSN structure */
@ -342,7 +358,7 @@ static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name,
/* smtp_misc_fail - maybe throttle queue; skip/defer/bounce all recipients */
int smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name,
int smtp_misc_fail(SMTP_STATE *state, int flags, const char *mta_name,
SMTP_RESP *resp, const char *format,...)
{
va_list ap;
@ -360,7 +376,7 @@ int smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name,
/*
* Skip, defer or bounce recipients, and throttle this queue.
*/
return (smtp_bulk_fail(state, throttle));
return (smtp_bulk_fail(state, flags));
}
/* smtp_rcpt_fail - skip, defer, or bounce recipient */
@ -472,5 +488,5 @@ int smtp_stream_except(SMTP_STATE *state, int code, const char *description)
* falling back to plaintext, because RETRY_AS_PLAINTEXT clears the
* FINAL_SERVER flag.
*/
return (smtp_bulk_fail(state, SMTP_THROTTLE));
return (smtp_bulk_fail(state, SMTP_MISC_FAIL_THROTTLE));
}

View File

@ -2687,6 +2687,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
/* Already processed early. */ ;
#ifdef USE_TLS
} else if (var_requiretls_enable
&& state->tls_context != 0
&& (state->ehlo_discard_mask & EHLO_MASK_REQUIRETLS) == 0
&& strcasecmp(arg, "REQUIRETLS") == 0) { /* RFC 8689 */
state->flags |= SMTPD_FLAG_REQUIRETLS;