2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-31 06:05:37 +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 20250119
Feature: REQUIRETLS verb in SMTP. According to RFC 8689 this Feature: support for the REQUIRETLS verb in SMTP. According
not only requires TLS, but also requires server certificate to RFC 8689, this requires TLS server certificate matching.
matching. TODO: parameters, defaults, files. 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: TODO:
If the TLS policy, or remote server, does not support The RFC says that REQUIRETLS applies to LMTP. Dovecot supports
REQUIRETLS, try alternate MX hosts before returning the TLS, but how common is it for Postfix to verify a Dovecot
message as undeliverable. Code: smtp_connect.c, server certificate? Should we add a 'cheat' setting that does
smtp_proto.c (ehlo response and post-TLS handshake check). not enforce REQUIRETLS?
If a message contains "TLS-Required: no", should a bounce message If a message contains "TLS-Required: no", should a bounce message
also contain this header? 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 If the Postfix SMTP server accepted REQUIRETLS, should that stay
in effect if, before the message is forwarded, the configuration in effect if, before the message is forwarded, the configuration
is changed to "requiretls_enable = no"? Same for "postsuper -r". 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 Simplify the cleanup_envelope_test. Write the initial SIZE record
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
to /dev/null, don't call cleanup_final(), and verify the value to /dev/null, don't call cleanup_final(), and verify the value
of state->sendopts. of state->sendopts.

View File

@@ -159,3 +159,5 @@ proto proto socketmap_table
qmgr qmgr_deliver c qmgr qmgr_message c qmqpd qmqpd c qmgr qmgr_deliver c qmgr qmgr_message c qmqpd qmqpd c
smtp smtp_proto c smtpd smtpd c verify verify c smtp smtp_proto c smtpd smtpd c verify verify c
operations Files cleanup cleanup h cleanup cleanup_message 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; char *postmaster;
int count; 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. * 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_header_dsn(bounce, bounce_info) == 0
&& bounce_diagnostic_dsn(bounce, bounce_info, && bounce_diagnostic_dsn(bounce, bounce_info,
DSN_NOTIFY_OVERRIDE) > 0) { 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); bounce_status = post_mail_fclose(bounce);
if (bounce_status == 0) if (bounce_status == 0)
msg_info("%s: postmaster non-delivery notification: %s", 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)); 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. * 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) if (strcasecmp_utf8(recipient, mail_addr_double_bounce()) == 0)
msg_panic("%s: attempt to bounce a double bounce", myname); 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. * 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); var_notify_classes);
VSTRING *new_id = vstring_alloc(10); 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. * 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_recipient_log(bounce, bounce_info) == 0
&& bounce_header_dsn(bounce, bounce_info) == 0 && bounce_header_dsn(bounce, bounce_info) == 0
&& bounce_recipient_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); bounce_status = post_mail_fclose(bounce);
if (bounce_status == 0) if (bounce_status == 0)
msg_info("%s: postmaster non-delivery notification: %s", 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; int count;
const char *sender; 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 * For consistency with fail/delay notifications, send notification for a
* non-bounce message as a single-bounce message, 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; char *postmaster;
int count; 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. * 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_header_dsn(bounce, bounce_info) == 0
&& bounce_diagnostic_dsn(bounce, bounce_info, && bounce_diagnostic_dsn(bounce, bounce_info,
DSN_NOTIFY_OVERRIDE) > 0) { 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); bounce_status = post_mail_fclose(bounce);
if (bounce_status == 0) if (bounce_status == 0)
msg_info("%s: postmaster delay notification: %s", 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) if (state->hop_count == 1)
argv_add(state->auto_hdrs, vstring_str(header_buf), ARGV_END); 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; char *cp = vstring_str(header_buf) + strlen(hdr_opts->name) + 1;
while (ISSPACE(*cp)) while (ISSPACE(*cp))

View File

@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20250119" #define MAIL_RELEASE_DATE "20250120"
#define MAIL_VERSION_NUMBER "3.10" #define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@@ -638,8 +638,9 @@ extern void smtp_rcpt_done(SMTP_STATE *, SMTP_RESP *, RECIPIENT *);
/* /*
* smtp_trouble.c * smtp_trouble.c
*/ */
#define SMTP_THROTTLE 1 #define SMTP_MISC_FAIL_NONE 0
#define SMTP_NOTHROTTLE 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 smtp_sess_fail(SMTP_STATE *);
extern int PRINTFLIKE(5, 6) smtp_misc_fail(SMTP_STATE *, int, const char *, extern int PRINTFLIKE(5, 6) smtp_misc_fail(SMTP_STATE *, int, const char *,
SMTP_RESP *, 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 *); extern int smtp_stream_except(SMTP_STATE *, int, const char *);
#define smtp_site_fail(state, mta, resp, ...) \ #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, ...) \ #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 * 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 * Require that the server announces REQUIRETLS when the sender required
* REQUIRETLS. TODO(wietse) try alternative MX hosts before returning the * REQUIRETLS. Return the message as undeliverable only when there are no
* message as undeliverable. * more alternative MX hosts.
*/ */
#ifdef USE_TLS #ifdef USE_TLS
if ((state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0 if ((state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0
&& (session->features & SMTP_FEATURE_REQUIRETLS) == 0 && (session->features & SMTP_FEATURE_REQUIRETLS) == 0
&& (request->sendopts & SOPT_REQUIRETLS_ESMTP) != 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"), SMTP_RESP_FAKE(&fake, "5.7.30"),
"REQUIRETLS is required, " "Sender requires authenticated TLS, but no "
"but was not offered by host %s", "mail server was found with REQUIRETLS "
session->namaddr)); "support. The last attempted server "
"was %s", session->namaddr));
#endif #endif
/* /*
@@ -1163,7 +1165,7 @@ static int smtp_start_tls(SMTP_STATE *state)
if (PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE) if (PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE)
RETRY_AS_PLAINTEXT; RETRY_AS_PLAINTEXT;
return (smtp_misc_fail(state, state->tls->level == TLS_LEV_MAY ? 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, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"), SMTP_RESP_FAKE(&fake, "4.7.5"),
"Cannot start TLS: handshake failure")); "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 * Require a server certificate match when the sender requested
* REQUIRETLS. TODO(wietse) try alternative MX hosts before * REQUIRETLS. Return the message as undeliverable only when
* returning the message as undeliverable. * there are no more alternative MX hosts.
*/ */
if ((state->request->sendopts & SOPT_REQUIRETLS_ESMTP)) { 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"), SMTP_RESP_FAKE(&fake, "5.7.10"),
"Sender requires a TLS server " "Sender requires a TLS server "
"certificate match, but the remote " "certificate match, but no matching "
"server certificate did not match")); "mail server was found. The last "
"attempted server was %s",
session->namaddr));
} }
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"), SMTP_RESP_FAKE(&fake, "4.7.5"),

View File

@@ -33,9 +33,9 @@
/* int exception; /* int exception;
/* const char *description; /* const char *description;
/* AUXILIARY FUNCTIONS /* 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; /* SMTP_STATE *state;
/* int throttle; /* int flags;
/* const char *mta_name; /* const char *mta_name;
/* SMTP_RESP *resp; /* SMTP_RESP *resp;
/* const char *format; /* const char *format;
@@ -91,8 +91,11 @@
/* /*
/* smtp_misc_fail() provides a more detailed interface than /* smtp_misc_fail() provides a more detailed interface than
/* smtp_site_fail() and smtp_mesg_fail(), which are convenience /* smtp_site_fail() and smtp_mesg_fail(), which are convenience
/* wrappers around smtp_misc_fail(). The throttle argument /* wrappers around smtp_misc_fail(). The flags argument is either
/* is either SMTP_THROTTLE or SMTP_NOTHROTTLE; it is used only /* 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 /* in the "soft error, final server" policy, and determines
/* whether a destination will be marked as problematic. /* 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 */ /* 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; DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session; SMTP_SESSION *session = state->session;
@@ -220,8 +223,21 @@ static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
int aggregate_status; int aggregate_status;
int soft_error = (STR(why->status)[0] == '4'); int soft_error = (STR(why->status)[0] == '4');
int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce);
int throttle_queue = (flags & SMTP_MISC_FAIL_THROTTLE);
int nrcpt; 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 * Don't defer the recipients just yet when this error qualifies them for
* delivery to a backup server. Just log something informative to show * 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 * because this error information is collected by a routine that
* terminates BEFORE the error is reported. * 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 */ /* 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 */ /* 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,...) SMTP_RESP *resp, const char *format,...)
{ {
va_list ap; 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. * 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 */ /* 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 * falling back to plaintext, because RETRY_AS_PLAINTEXT clears the
* FINAL_SERVER flag. * 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. */ ; /* Already processed early. */ ;
#ifdef USE_TLS #ifdef USE_TLS
} else if (var_requiretls_enable } else if (var_requiretls_enable
&& state->tls_context != 0
&& (state->ehlo_discard_mask & EHLO_MASK_REQUIRETLS) == 0 && (state->ehlo_discard_mask & EHLO_MASK_REQUIRETLS) == 0
&& strcasecmp(arg, "REQUIRETLS") == 0) { /* RFC 8689 */ && strcasecmp(arg, "REQUIRETLS") == 0) { /* RFC 8689 */
state->flags |= SMTPD_FLAG_REQUIRETLS; state->flags |= SMTPD_FLAG_REQUIRETLS;