pickup/pickup.c, qmgr/qmgr.h, qmgr/qmgr_active.c,
qmgr/qmgr_deliver.c, qmgr/qmgr_message.c, qmqpd/qmqpd.c,
smtp/smtp_proto.c, smtpd/smtpd.c, verify/verify.c,
+
+20250117
+
+ Cleanup: factored out the sendopts.c unit test code, and
+ added two missing tests. File: global/sendopts_test.c.
+
+ Cleanup: increased the capacity to remember which types of
+ message headers have been seen, and encapsulated some set
+ operations. Files: cleanup/cleanup.h, cleanup/cleanup_message.c.
+
+ Feature: support for the RFC 8689 "TLS-Required: no" message
+ header. This limits the Postfix SMTP client TLS security
+ level to "smtp_tls_security = may", which does not authenticate
+ remote SMTP server TLS certificates, and which allows falling
+ back to plaintext. This is needed for the delivery of
+ messages such as TLSRPT summaries, which should be sent
+ even when the preferred TLS security policy cannot be
+ enforced. Support for the REQUIRETLS ESMTP extension remains
+ future work. Files: cleanup/cleanup_message.c,
+ global/header_opts.c, global/header_opts.h, smtp/smtp_connect.c,
+ proto/TLSRPT_README.html.
+
+ Cleanup: memory leaks in test code. Files: util/hex_code.c,
+ util/argv.c.
Options:
+ * Specify the "T\bTL\bLS\bS-\b-R\bRe\beq\bqu\bui\bir\bre\bed\bd:\b: n\bno\bo" message header, defined in RFC 8689, to
+ reduce the TLS security level to "m\bma\bay\by" (that is, do not verify remote SMTP
+ server certificates, and fall back to plaintext if TLS is unavailable).
+
+ This feature is available in Postfix 3.10 and later.
+
* Do nothing. When TLS security enforcement is required but fails, a TLSRPT
summary message will be delayed until the problem is addressed, or until
the message expires in the mail queue. Keep in mind that TLSRPT is not a
(EPL) 2.0. Recipients can choose to take the software under the
license of their choice. Those who are more comfortable with the
IPL can continue with that license.
+
+[Feature 20250117]
+
+Support for the RFC 8689 "TLS-Required: no" message header to request
+delivery of messages such as TLSRPT summaries even if the preferred
+TLS security policy cannot be enforced. This limits the Postfix
+SMTP client to "smtp_tls_security_level = may" which does not
+authenticate server certificates and which allows falling back to
+plaintext.
+
+Support for the REQUIRETLS SMTP service extension remains future work.
[Incompat 20250116]
<ul>
+<li> <p> Specify the "<b>TLS-Required: no</b>" message header,
+defined in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>, to reduce the TLS security level to "<b>may</b>"
+(that is, do not verify remote SMTP server certificates, and fall
+back to plaintext if TLS is unavailable). <br> <br> This feature
+is available in Postfix 3.10 and later. </p>
+
<li> <p> Do nothing. When TLS security enforcement is required but
fails, a TLSRPT summary message will be delayed
until the problem is addressed, or until the message expires
<ul>
+<li> <p> Specify the "<b>TLS-Required: no</b>" message header,
+defined in RFC 8689, to reduce the TLS security level to "<b>may</b>"
+(that is, do not verify remote SMTP server certificates, and fall
+back to plaintext if TLS is unavailable). <br> <br> This feature
+is available in Postfix 3.10 and later. </p>
+
<li> <p> Do nothing. When TLS security enforcement is required but
fails, a TLSRPT summary message will be delayed
until the problem is addressed, or until the message expires
pickup pickup c qmgr qmgr h qmgr qmgr_active c
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
CLOSEFROM
Roessner
bitflags
+Schulze
cleanup_envelope.o: ../../include/vstring.h
cleanup_envelope.o: cleanup.h
cleanup_envelope.o: cleanup_envelope.c
+cleanup_envelope_test.o: ../../include/argv.h
+cleanup_envelope_test.o: ../../include/attr.h
+cleanup_envelope_test.o: ../../include/been_here.h
+cleanup_envelope_test.o: ../../include/check_arg.h
+cleanup_envelope_test.o: ../../include/cleanup_user.h
+cleanup_envelope_test.o: ../../include/dict.h
+cleanup_envelope_test.o: ../../include/dsn_mask.h
+cleanup_envelope_test.o: ../../include/header_body_checks.h
+cleanup_envelope_test.o: ../../include/header_opts.h
+cleanup_envelope_test.o: ../../include/htable.h
+cleanup_envelope_test.o: ../../include/mail_conf.h
+cleanup_envelope_test.o: ../../include/mail_params.h
+cleanup_envelope_test.o: ../../include/mail_stream.h
+cleanup_envelope_test.o: ../../include/maps.h
+cleanup_envelope_test.o: ../../include/match_list.h
+cleanup_envelope_test.o: ../../include/milter.h
+cleanup_envelope_test.o: ../../include/mime_state.h
+cleanup_envelope_test.o: ../../include/msg.h
+cleanup_envelope_test.o: ../../include/msg_vstream.h
+cleanup_envelope_test.o: ../../include/myflock.h
+cleanup_envelope_test.o: ../../include/mymalloc.h
+cleanup_envelope_test.o: ../../include/nvtable.h
+cleanup_envelope_test.o: ../../include/rec_type.h
+cleanup_envelope_test.o: ../../include/record.h
+cleanup_envelope_test.o: ../../include/resolve_clnt.h
+cleanup_envelope_test.o: ../../include/sendopts.h
+cleanup_envelope_test.o: ../../include/smtputf8.h
+cleanup_envelope_test.o: ../../include/string_list.h
+cleanup_envelope_test.o: ../../include/stringops.h
+cleanup_envelope_test.o: ../../include/sys_defs.h
+cleanup_envelope_test.o: ../../include/tok822.h
+cleanup_envelope_test.o: ../../include/vbuf.h
+cleanup_envelope_test.o: ../../include/vstream.h
+cleanup_envelope_test.o: ../../include/vstring.h
+cleanup_envelope_test.o: cleanup.h
+cleanup_envelope_test.o: cleanup_envelope_test.c
cleanup_extracted.o: ../../include/argv.h
cleanup_extracted.o: ../../include/attr.h
cleanup_extracted.o: ../../include/been_here.h
cleanup_message.o: ../../include/attr.h
cleanup_message.o: ../../include/been_here.h
cleanup_message.o: ../../include/check_arg.h
+cleanup_message.o: ../../include/clean_ascii_cntrl_space.h
cleanup_message.o: ../../include/cleanup_user.h
cleanup_message.o: ../../include/conv_time.h
cleanup_message.o: ../../include/dict.h
cleanup_message.o: ../../include/record.h
cleanup_message.o: ../../include/resolve_clnt.h
cleanup_message.o: ../../include/rfc2047_code.h
+cleanup_message.o: ../../include/sendopts.h
cleanup_message.o: ../../include/split_at.h
cleanup_message.o: ../../include/string_list.h
cleanup_message.o: ../../include/stringops.h
cleanup_message.o: ../../include/sys_defs.h
cleanup_message.o: ../../include/tok822.h
-cleanup_message.o: ../../include/clean_ascii_cntrl_space.h
cleanup_message.o: ../../include/vbuf.h
cleanup_message.o: ../../include/vstream.h
cleanup_message.o: ../../include/vstring.h
* System library.
*/
#include <sys/time.h>
+#include <stdint.h> /* C99 uint64_t */
/*
* Utility library.
int qmgr_opts; /* qmgr processing options */
int errs; /* any badness experienced */
int err_mask; /* allowed badness */
- int headers_seen; /* which headers were seen */
+ uint64_t headers_seen; /* which headers were seen */
int hop_count; /* count of received: headers */
char *resent; /* any resent- header seen */
BH_TABLE *dups; /* recipient dup filter */
#define CLEANUP_FLAG_WARN_SEEN (1<<17) /* REC_TYPE_WARN record seen */
#define CLEANUP_FLAG_END_SEEN (1<<18) /* REC_TYPE_END record seen */
+ /*
+ * Bit mask for the CLEANUP_STATE.headers_seen member.
+ */
+#define HDRS_SEEN_MASK(hval) ((uint64_t) 1 << (hval))
+
/*
* Mappings.
*/
CLEANUP_STATE saved_state = *state;
/*
- * Process the test SIZE record payload and write an place-holder SIZE
- * record that will be overwritten later with final information.
+ * Process the test SIZE record payload, clear some bits from the
+ * sendopts field, and write an all-zeroes preliminary SIZE record.
*/
VSTRING *output_stream_buf = vstring_alloc(100);
input_buf = 0;
/*
- * Write an updated SIZE record to the output stream.
+ * Overwrite the SIZE record with an updated version that includes the
+ * modified sendopts field.
*/
cleanup_final(state);
if (state->errs != CLEANUP_STAT_OK) {
state->dst = 0;
/*
- * Compare the stored record content against the expected content.
+ * Read the final SIZE record content. This normally happens in the queue
+ * manager, and in the pickup daemon after a message is re-queued.
*/
VSTREAM *fp;
(void) vstream_fclose(fp);
vstring_free(output_stream_buf);
+ /*
+ * Compare the stored SIZE record content against the expected content.
+ * We expect that the fields for data_size, data_offset, rcpt_count,
+ * qmgr_opts, and cont_length, are consistent with the saved
+ * CLEANUP_STATE, and we expect to see a specific value for the sendopts
+ * field that was made by cleanup_envelope().
+ */
int got_conv;
long data_size, data_offset, cont_length;
int rcpt_count, qmgr_opts, sendopts;
#include <info_log_addr_form.h>
#include <hfrom_format.h>
#include <rfc2047_code.h>
+#include <sendopts.h>
/* Application-specific. */
* MAY remove Return-path headers before adding their own.
*/
else {
- state->headers_seen |= (1 << hdr_opts->type);
+ state->headers_seen |= HDRS_SEEN_MASK(hdr_opts->type);
if (hdr_opts->type == HDR_MESSAGE_ID) {
ssize_t len;
if (state->hop_count == 1)
argv_add(state->auto_hdrs, vstring_str(header_buf), ARGV_END);
}
+ if (hdr_opts->type == HDR_TLS_REQUIRED) {
+ char *cp = vstring_str(header_buf) + strlen(hdr_opts->name) + 1;
+
+ while (ISSPACE(*cp))
+ cp++;
+ if (strcasecmp(cp, "no") == 0)
+ state->sendopts |= SOPT_REQUIRETLS_HEADER;
+ else
+ msg_warn("ignoring malformed header: '%.100s'",
+ vstring_str(header_buf));
+ }
if (CLEANUP_OUT_OK(state)) {
if (hdr_opts->flags & HDR_OPT_RR)
state->resent = "Resent-";
* complicate future code that wants to log more name=value attributes.
*/
if ((state->hdr_rewrite_context || var_always_add_hdrs)
- && (state->headers_seen & (1 << (state->resent[0] ?
- HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) {
+ && (state->headers_seen & HDRS_SEEN_MASK(state->resent[0] ?
+ HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID)) == 0) {
if (var_long_queue_ids) {
vstring_sprintf(state->temp1, "%s@%s",
state->queue_id, var_myhostname);
msg_info("%s: %smessage-id=<%s>",
state->queue_id, *state->resent ? "resent-" : "",
vstring_str(state->temp1));
- state->headers_seen |= (1 << (state->resent[0] ?
- HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID));
+ state->headers_seen |= HDRS_SEEN_MASK(state->resent[0] ?
+ HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID);
if (state->resent[0] == 0 && state->message_id == 0)
state->message_id = concatenate("<", vstring_str(state->temp1),
">", (char *) 0);
}
- if ((state->headers_seen & (1 << HDR_MESSAGE_ID)) == 0)
+ if ((state->headers_seen & HDRS_SEEN_MASK(HDR_MESSAGE_ID)) == 0)
msg_info("%s: message-id=<>", state->queue_id);
/*
* with the GMT offset at the end.
*/
if ((state->hdr_rewrite_context || var_always_add_hdrs)
- && (state->headers_seen & (1 << (state->resent[0] ?
- HDR_RESENT_DATE : HDR_DATE))) == 0) {
+ && (state->headers_seen & HDRS_SEEN_MASK(state->resent[0] ?
+ HDR_RESENT_DATE : HDR_DATE)) == 0) {
vstring_sprintf(state->temp2, "%sDate: %s",
state->resent, mail_date(state->arrival_time.tv_sec));
cleanup_out_header(state, state->temp2);
* Add a missing (Resent-)From: header.
*/
if ((state->hdr_rewrite_context || var_always_add_hdrs)
- && (state->headers_seen & (1 << (state->resent[0] ?
- HDR_RESENT_FROM : HDR_FROM))) == 0) {
+ && (state->headers_seen & HDRS_SEEN_MASK(state->resent[0] ?
+ HDR_RESENT_FROM : HDR_FROM)) == 0) {
char *fullname;
quote_822_local(state->temp1, *state->sender ?
/*
* Add a missing destination header.
*/
-#define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \
- | (1 << HDR_CC) | (1 << HDR_RESENT_CC))
+#define VISIBLE_RCPT (HDRS_SEEN_MASK(HDR_TO) \
+ | HDRS_SEEN_MASK(HDR_RESENT_TO) \
+ | HDRS_SEEN_MASK(HDR_CC) \
+ | HDRS_SEEN_MASK(HDR_RESENT_CC))
if ((state->hdr_rewrite_context || var_always_add_hdrs)
&& (state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) {
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
haproxy_srvr map_search delivered_hdr login_sender_match \
compat_level config_known_tcp_ports hfrom_format rfc2047_code \
- ascii_header_text sendopts
+ ascii_header_text sendopts_test
LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
LIB_DIR = ../../lib
ascii_header_text: ascii_header_text.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
-sendopts: sendopts.c $(LIB) $(LIBS)
+sendopts_test: sendopts_test.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
config_known_tcp_ports: config_known_tcp_ports.c $(LIB) $(LIBS)
normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
delivered_hdr_test login_sender_match_test compat_level_test \
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
- ascii_header_text_test sendopts_test
+ ascii_header_text_test test_sendopts
mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
ascii_header_text_test: update ascii_header_text
$(SHLIB_ENV) $(VALGRIND) ./ascii_header_text
-sendopts_test: update sendopts
- -$(SHLIB_ENV) $(VALGRIND) ./sendopts
+test_sendopts: update sendopts_test
+ -$(SHLIB_ENV) $(VALGRIND) ./sendopts_test
clean:
rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAPS)
scache_single.o: ../../include/vstring.h
scache_single.o: scache.h
scache_single.o: scache_single.c
+sendopts.o: ../../include/check_arg.h
+sendopts.o: ../../include/msg.h
+sendopts.o: ../../include/name_mask.h
+sendopts.o: ../../include/sys_defs.h
+sendopts.o: ../../include/vbuf.h
+sendopts.o: ../../include/vstring.h
+sendopts.o: sendopts.c
+sendopts.o: sendopts.h
+sendopts_test.o: ../../include/check_arg.h
+sendopts_test.o: ../../include/msg.h
+sendopts_test.o: ../../include/msg_vstream.h
+sendopts_test.o: ../../include/stringops.h
+sendopts_test.o: ../../include/sys_defs.h
+sendopts_test.o: ../../include/vbuf.h
+sendopts_test.o: ../../include/vstream.h
+sendopts_test.o: ../../include/vstring.h
+sendopts_test.o: sendopts.h
+sendopts_test.o: sendopts_test.c
sent.o: ../../include/attr.h
sent.o: ../../include/check_arg.h
sent.o: ../../include/htable.h
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
"Return-Receipt-To", HDR_RETURN_RECEIPT_TO, HDR_OPT_SENDER,
"Sender", HDR_SENDER, HDR_OPT_SENDER,
"To", HDR_TO, HDR_OPT_XRECIP,
+ "TLS-Required", HDR_TLS_REQUIRED, 0,
};
#define HEADER_OPTS_SIZE (sizeof(header_opts) / sizeof(header_opts[0]))
} HEADER_OPTS;
/*
- * Header types. If we reach 31, we must group the headers we need to
- * remember at the beginning, or we should use fd_set bit sets.
+ * Header types.
*/
#define HDR_OTHER 0
#define HDR_APPARENTLY_TO 1
#define HDR_CONTENT_ID 29
#define HDR_MIME_VERSION 30
#define HDR_DISP_NOTIFICATION 31
+#define HDR_TLS_REQUIRED 32 /* RFC 8689 */
/*
* Header flags.
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20250116"
+#define MAIL_RELEASE_DATE "20250117"
#define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT
/* SYNOPSIS
/* #include <sendopts.h>
/*
-/* const char *sendopts_strflags(code)
-/* int code;
+/* const char *sendopts_strflags(
+/* int flags,
+/* int delim)
/* DESCRIPTION
/* Postfix queue files and IPC messages contain a sendopts field
/* with flags that control SMTPUTF8, REQUIRETLS, etc. support. The
/*
/* sendopts_strflags() maps a sendopts flag value to printable
/* string. The result is overwritten upon each call.
+/*
+/* Arguments:
+/* .IP flags
+/* A bitmask that is to be converted to text.
+/* .IP delim
+/* The character to separate output words with: one of ' ,|'.
+/* DIAGNOSTICS
+/* Panic: invalid delimiter. Fatal error: invalid flag.
/* LICENSE
/* .ad
/* .fi
/* System library. */
#include <sys_defs.h>
+#include <string.h>
/*
* Utility library.
#include <sendopts.h>
/*
- * Mapping from flags code to printable string.
+ * Mapping from flags to printable string.
*/
static NAME_MASK sendopts_flag_map[] = {
"smtputf8_requested", SOPT_SMTPUTF8_REQUESTED,
/* sendopts_strflags - map flags code to printable string */
-const char *sendopts_strflags(unsigned flags)
+const char *sendopts_strflags(unsigned flags, int delim)
{
+ const char myname[] = "sendopts_strflags";
+ static const char delims[] = " ,|";
+ static const int dflags[] = {0, NAME_MASK_COMMA, NAME_MASK_PIPE};
static VSTRING *result;
+ const char *cp;
if (flags == 0)
return ("none");
else
VSTRING_RESET(result);
- return (str_name_mask_opt(result, "sendopts_strflags", sendopts_flag_map,
- flags, NAME_MASK_FATAL));
-}
-
-#ifdef TEST
-#include <stdlib.h>
-#include <string.h>
-#include <stringops.h>
-#include <msg_vstream.h>
+ if ((cp = strchr(delims, delim)) == 0)
+ msg_panic("%s: bad delimiter: '%c'", myname, delim);
- /*
- * Tests and test cases.
- */
-typedef struct TEST_CASE {
- const char *label; /* identifies test case */
- int mask;
- const char *want;
-} TEST_CASE;
-
-static const TEST_CASE test_cases[] = {
- {"SOPT_SMTPUTF8_ALL",
- SOPT_SMTPUTF8_ALL,
- "smtputf8_requested smtputf8_header smtputf8_sender smtputf8_recipient"
- },
- {"SOPT_SMTPUTF8_DERIVED",
- SOPT_SMTPUTF8_DERIVED,
- "smtputf8_header smtputf8_sender smtputf8_recipient"
- },
- {"SOPT_SMTPUTF8_REQUESTED",
- SOPT_SMTPUTF8_REQUESTED,
- "smtputf8_requested"
- },
- {"SOPT_SMTPUTF8_HEADER",
- SOPT_SMTPUTF8_HEADER,
- "smtputf8_header"
- },
- {"SOPT_SMTPUTF8_SENDER",
- SOPT_SMTPUTF8_SENDER,
- "smtputf8_sender"
- },
- {"SOPT_SMTPUTF8_RECIPIENT",
- SOPT_SMTPUTF8_RECIPIENT,
- "smtputf8_recipient"
- },
- {"SOPT_REQUIRETLS_ALL",
- SOPT_REQUIRETLS_ALL,
- "requiretls_header requiretls_esmtp"
- },
- {"SOPT_REQUIRETLS_DERIVED",
- SOPT_REQUIRETLS_DERIVED,
- "requiretls_header"
- },
- {"SOPT_REQUIRETLS_HEADER",
- SOPT_REQUIRETLS_HEADER,
- "requiretls_header"
- },
- {"SOPT_REQUIRETLS_ESMTP",
- SOPT_REQUIRETLS_ESMTP,
- "requiretls_esmtp"
- },
- {0},
-};
-
-int main(int argc, char **argv)
-{
- const TEST_CASE *tp;
- int pass = 0;
- int fail = 0;
- const char *got;
-
- msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
-
- for (tp = test_cases; tp->label != 0; tp++) {
- msg_info("RUN %s", tp->label);
- got = sendopts_strflags(tp->mask);
- if (strcmp(got, tp->want) != 0) {
- msg_warn("got result '%s', want: '%s'", got, tp->want);
- fail++;
- msg_info("FAIL %s", tp->label);
- } else {
- msg_info("PASS %s", tp->label);
- pass++;
- }
- }
- msg_info("PASS=%d FAIL=%d", pass, fail);
- exit(fail != 0);
+ return (str_name_mask_opt(result, "sendopts_strflags", sendopts_flag_map,
+ flags, NAME_MASK_FATAL | dflags[cp - delims]));
}
-#endif
/*
* Debug helper.
*/
-extern const char *sendopts_strflags(unsigned flags);
+extern const char *sendopts_strflags(unsigned flags, int delim);
/* LICENSE
/* .ad
--- /dev/null
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringops.h>
+
+ /*
+ * Utility library.
+ */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <sendopts.h>
+
+ /*
+ * Tests and test cases.
+ */
+typedef struct TEST_CASE {
+ const char *label; /* identifies test case */
+ int mask;
+ const char *want;
+} TEST_CASE;
+
+static const TEST_CASE test_cases[] = {
+ {"SOPT_SMTPUTF8_ALL",
+ SOPT_SMTPUTF8_ALL,
+ "smtputf8_requested smtputf8_header smtputf8_sender smtputf8_recipient"
+ },
+ {"SOPT_SMTPUTF8_DERIVED",
+ SOPT_SMTPUTF8_DERIVED,
+ "smtputf8_header smtputf8_sender smtputf8_recipient"
+ },
+ {"SOPT_SMTPUTF8_REQUESTED",
+ SOPT_SMTPUTF8_REQUESTED,
+ "smtputf8_requested"
+ },
+ {"SOPT_SMTPUTF8_HEADER",
+ SOPT_SMTPUTF8_HEADER,
+ "smtputf8_header"
+ },
+ {"SOPT_SMTPUTF8_SENDER",
+ SOPT_SMTPUTF8_SENDER,
+ "smtputf8_sender"
+ },
+ {"SOPT_SMTPUTF8_RECIPIENT",
+ SOPT_SMTPUTF8_RECIPIENT,
+ "smtputf8_recipient"
+ },
+ {"SOPT_REQUIRETLS_ALL",
+ SOPT_REQUIRETLS_ALL,
+ "requiretls_header requiretls_esmtp"
+ },
+ {"SOPT_REQUIRETLS_DERIVED",
+ SOPT_REQUIRETLS_DERIVED,
+ "requiretls_header"
+ },
+ {"SOPT_REQUIRETLS_HEADER",
+ SOPT_REQUIRETLS_HEADER,
+ "requiretls_header"
+ },
+ {"SOPT_REQUIRETLS_ESMTP",
+ SOPT_REQUIRETLS_ESMTP,
+ "requiretls_esmtp"
+ },
+ {"SOPT_FLAG_ALL",
+ SOPT_FLAG_ALL,
+ "smtputf8_requested smtputf8_header smtputf8_sender smtputf8_recipient"
+ " requiretls_header requiretls_esmtp"
+ },
+ {"SOPT_FLAG_DERIVED",
+ SOPT_FLAG_DERIVED,
+ "smtputf8_header smtputf8_sender smtputf8_recipient"
+ " requiretls_header"
+ },
+ {0},
+};
+
+int main(int argc, char **argv)
+{
+ const TEST_CASE *tp;
+ int pass = 0;
+ int fail = 0;
+ const char *got;
+
+ msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+
+ for (tp = test_cases; tp->label != 0; tp++) {
+ msg_info("RUN %s", tp->label);
+ got = sendopts_strflags(tp->mask, ' ');
+ if (strcmp(got, tp->want) != 0) {
+ msg_warn("got result '%s', want: '%s'", got, tp->want);
+ fail++;
+ msg_info("FAIL %s", tp->label);
+ } else {
+ msg_info("PASS %s", tp->label);
+ pass++;
+ }
+ }
+ msg_info("PASS=%d FAIL=%d", pass, fail);
+ exit(fail != 0);
+}
smtp_connect.o: ../../include/resolve_clnt.h
smtp_connect.o: ../../include/sane_connect.h
smtp_connect.o: ../../include/scache.h
+smtp_connect.o: ../../include/sendopts.h
smtp_connect.o: ../../include/sock_addr.h
smtp_connect.o: ../../include/split_at.h
smtp_connect.o: ../../include/string_list.h
smtp_tlsrpt.o: ../../include/header_opts.h
smtp_tlsrpt.o: ../../include/hex_code.h
smtp_tlsrpt.o: ../../include/htable.h
-smtp_tlsrpt.o: ../../include/inet_proto.h
smtp_tlsrpt.o: ../../include/mail_params.h
smtp_tlsrpt.o: ../../include/maps.h
smtp_tlsrpt.o: ../../include/match_list.h
#include <dsn_buf.h>
#include <mail_addr.h>
#include <valid_hostname.h>
+#include <sendopts.h>
/* DNS library. */
}
}
+/* smtp_get_effective_tls_level - get the effective TLS security level */
+
+static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state)
+{
+ SMTP_ITERATOR *iter = state->iterator;
+ SMTP_TLS_POLICY *tls = state->tls;
+
+ /*
+ * Determine the TLS level for this destination.
+ */
+ if (!smtp_tls_policy_cache_query(why, tls, iter)) {
+ return (0);
+ }
+
+ /*
+ * If the sender requires verified TLS, the TLS level must enforce a
+ * server certificate match.
+ */
+#if 0
+ else if ((state->request->sendopts & SOPT_REQUIRETLS_ESMTP)) {
+ if (TLS_MUST_MATCH(tls->level) == 0) {
+ dsb_simple(why, "5.7.10", "Sender requires verified TLS, "
+ " but my configured TLS security level is '%s %s'",
+ var_mail_name, str_tls_level(tls->level));
+ return (0);
+ }
+ }
+#endif
+
+ /*
+ * Otherwise, if the TLS level is not TLS_LEV_NONE or some non-level, and
+ * the message contains a "TLS-Required: no" header, limit the level to
+ * TLS_LEV_MAY.
+ */
+ else if (tls->level > TLS_LEV_NONE
+ && (state->request->sendopts & SOPT_REQUIRETLS_HEADER)) {
+ tls->level = TLS_LEV_MAY;
+ }
+
+ /*
+ * Success.
+ */
+ return (1);
+}
+
/* smtp_connect_local - connect to local server */
static void smtp_connect_local(SMTP_STATE *state, const char *path)
* of SASL-unauthenticated connections.
*/
#ifdef USE_TLS
- if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+ if (!smtp_get_effective_tls_level(why, state)) {
msg_warn("TLS policy lookup error for %s/%s: %s",
STR(iter->host), STR(iter->addr), STR(why->reason));
return;
}
SMTP_ITER_UPDATE_HOST(iter, SMTP_HNAME(addr), hostaddr.buf, addr);
#ifdef USE_TLS
- if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+ if (!smtp_get_effective_tls_level(why, state)) {
msg_warn("TLS policy lookup error for %s/%s: %s",
STR(iter->dest), STR(iter->host), STR(why->reason));
continue;
}
SMTP_ITER_UPDATE_HOST(iter, SMTP_HNAME(addr), hostaddr.buf, addr);
#ifdef USE_TLS
- if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+ if (!smtp_get_effective_tls_level(why, state)) {
msg_warn("TLS policy lookup for %s/%s: %s",
STR(iter->dest), STR(iter->host), STR(why->reason));
continue;
argvp = argv_alloc(1);
if (setjmp(test_panic_jbuf) == 0)
- tp->populate_fn(tp, argvp);
+ argvp = tp->populate_fn(tp, argvp);
test_failed = test_argv_verify(tp, argvp);
if (test_failed) {
msg_info("%s: FAIL", tp->label);
fail++;
}
}
+ vstring_free(buf);
msg_info("PASS=%d FAIL=%d", pass, fail);
return (fail > 0);
}