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:
parent
1e2ac172f1
commit
b800a42bb8
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user