. The Postfix limit was 990 with Postfix 2.8
and earlier.
+.SH smtp_log_tls_feature_status (default: yes)
+Enable logging of TLS feature information in delivery status
+logging. This summarizes how features such as TLS and REQUIRETLS
+were used.
+.IP \(bu
+The logging is inserted between the "delays=a/b/c/d" and the
+"status=" information.
+.IP \(bu
+The general format is "tls=feature/feature/...". See below for
+examples.
+.IP \(bu
+The first feature name is the TLS security level: 'none',
+\&'may',' encrypt', ..., 'dane'. Other features are omitted if not
+activated. The REQUIRETLS extension's feature name will be 'requiretls'.
+.IP \(bu
+When 'disabled:' is prepended to a feature name, the remote server
+did not support this feature.
+.IP \(bu
+When the above is enclosed in "(" and ")", the feature was used
+successfully, but policy enforcement was relaxed.
+.IP \(bu
+When "!" is prepended to the above, the policy for that feature
+was not satisfied and the feature was not used..
+.IP \(bu
+When "?" is appended to the above, the policy for that feature
+is undecided, usually due to a lost connection.
+.br
+.PP
+Examples:
+.IP "tls=none"
+A connection that does not use TLS.
+.br
+.IP "tls=may"
+Opportunistic TLS after a successful
+handshake.
+.br
+.IP "tls=(disabled:may)"
+Opportunistic TLS after fallback
+to plaintext, because the server did not announce STARTTLS support
+or the server rejected the STARTTLS command.
+.br
+.IP "tls=dane"
+DANE policy compliant, no downgrade.
+.br
+.IP "tls=(dane)"
+Relaxed DANE after allowing MX records
+without DNSSEC signature, or after falling back to the TLS security
+level 'encrypt'.
+.br
+.IP "tls=dane/requiretls"
+DANE and REQUIRETLS policies were
+fully enforced, non\-error case.
+.br
+.IP "tls=dane?/requiretls?"
+DANE and REQUIRETLS policies
+were undecided, because the connection failed before or in the TLS
+handshake.
+.br
+.IP "tls=may/(disabled:requiretls)"
+Opportunistic TLS +
+opportunistic REQUIRETLS, server did not announce REQUIRETLS support.
+.br
+.br
+.PP
+This feature is available in Postfix 3.11 and later.
.SH smtp_mail_timeout (default: 300s)
The Postfix SMTP client time limit for sending the MAIL FROM command,
and for receiving the remote SMTP server response.
diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8
index b12529b3e..950ebd7b9 100644
--- a/postfix/man/man8/smtp.8
+++ b/postfix/man/man8/smtp.8
@@ -701,6 +701,9 @@ FROM" command.
.IP "\fBsmtp_requiretls_policy (see 'postconf -d smtp_requiretls_policy' output)\fR"
How the Postfix SMTP and LMTP client will enforce REQUIRETLS
for messages received with the REQUIRETLS option.
+.IP "\fBsmtp_log_tls_feature_status (yes)\fR"
+Enable logging of TLS feature information in delivery status
+logging.
.SH "OBSOLETE TLS CONTROLS"
.na
.nf
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index a06248a7b..65d1336b4 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -19866,6 +19866,71 @@ configuration parameter. See there for details.
This feature is available in Postfix ≥ 3.11.
+%PARAM smtp_log_tls_feature_status yes
+
+ Enable logging of TLS feature information in delivery status
+logging. This summarizes how features such as TLS and REQUIRETLS
+were used.
+
+
+
+- The logging is inserted between the "delays=a/b/c/d" and the
+"status=" information.
+
+
- The general format is "tls=feature/feature/...". See below for
+examples.
+
+
- The first feature name is the TLS security level: 'none',
+'may',' encrypt', ..., 'dane'. Other features are omitted if not
+activated. The REQUIRETLS extension's feature name will be 'requiretls'.
+
+
- When 'disabled:' is prepended to a feature name, the remote server
+did not support this feature.
+
+
- When the above is enclosed in "(" and ")", the feature was used
+successfully, but policy enforcement was relaxed.
+
+
- When "!" is prepended to the above, the policy for that feature
+was not satisfied and the feature was not used..
+
+
- When "?" is appended to the above, the policy for that feature
+is undecided, usually due to a lost connection.
+
+
+
+ Examples:
+
+
+
+- tls=none
- A connection that does not use TLS.
+
+- tls=may
- Opportunistic TLS after a successful
+handshake.
+
+- tls=(disabled:may)
- Opportunistic TLS after fallback
+to plaintext, because the server did not announce STARTTLS support
+or the server rejected the STARTTLS command.
+
+- tls=dane
- DANE policy compliant, no downgrade.
+
+- tls=(dane)
- Relaxed DANE after allowing MX records
+without DNSSEC signature, or after falling back to the TLS security
+level 'encrypt'.
+
+- tls=dane/requiretls
- DANE and REQUIRETLS policies were
+fully enforced, non-error case.
+
+- tls=dane?/requiretls?
- DANE and REQUIRETLS policies
+were undecided, because the connection failed before or in the TLS
+handshake.
+
+- tls=may/(disabled:requiretls)
- Opportunistic TLS +
+opportunistic REQUIRETLS, server did not announce REQUIRETLS support.
+
+
+
+
+ This feature is available in Postfix 3.11 and later.
%PARAM smtpd_hide_client_session no
Do not include SMTP client session information in the Postfix
diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc
index 1da83181c..19f79fbcd 100644
--- a/postfix/proto/stop.double-cc
+++ b/postfix/proto/stop.double-cc
@@ -346,3 +346,4 @@ encoded encoded text can contain only alpha digit
ossl_digest_new ossl_digest_new returns NULL after error ossl_digest_data
Richard Hansen rhansen rhansen org
long long or long integer
+void void tls_stats_revert TLS_STATS tstats
diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history
index f22598ceb..374d15161 100644
--- a/postfix/proto/stop.double-history
+++ b/postfix/proto/stop.double-history
@@ -208,3 +208,8 @@ proto proto COMPATIBILITY_README html
smtp smtp h smtp smtp_params c smtp smtp_proto c
information Files sendmail sendmail c pickup pickup c
postcat postcat c showq showq c
+ discard discard c error error c global Makefile in
+ global verify c global verify h local local h
+ pipe pipe c qmgr qmgr_bounce c qmgr qmgr_defer c
+ qmgr qmgr_message c smtp smtp h smtp smtp_connect c
+ smtp smtp_trouble c virtual virtual h
diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html
index f0dd67d2c..40bea1784 100644
--- a/postfix/proto/stop.double-proto-html
+++ b/postfix/proto/stop.double-proto-html
@@ -366,3 +366,4 @@ Postfix Postfix legacy TLS Support
The recommended socket location is still to be determined A good socket location would be under the Postfix queue directory for example smtp_tlsrpt_socket_name run tlsrpt tlsrpt sock The advantage of using a relative name is that it
enhanced status code and text format 45 number number text
opportunistic opportunistic starttls
+ li The general format is tls feature feature
diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc
index 02b3d46b4..a87c1a5c7 100644
--- a/postfix/proto/stop.spell-cc
+++ b/postfix/proto/stop.spell-cc
@@ -1874,3 +1874,5 @@ finalizer
REQTLS
reqtls
Esmtp
+ENF
+tstats
diff --git a/postfix/src/cleanup/cleanup_bounce.c b/postfix/src/cleanup/cleanup_bounce.c
index 8358d1227..87de4cc62 100644
--- a/postfix/src/cleanup/cleanup_bounce.c
+++ b/postfix/src/cleanup/cleanup_bounce.c
@@ -75,7 +75,7 @@ static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
*/
if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
CLEANUP_MSG_STATS(&stats, state),
- rcpt, "none", dsn) != 0) {
+ rcpt, "none", NO_TLS_STATS, dsn) != 0) {
state->errs |= CLEANUP_STAT_WRITE;
}
}
diff --git a/postfix/src/cleanup/cleanup_out_recipient.c b/postfix/src/cleanup/cleanup_out_recipient.c
index 003983b71..e4b059116 100644
--- a/postfix/src/cleanup/cleanup_out_recipient.c
+++ b/postfix/src/cleanup/cleanup_out_recipient.c
@@ -104,7 +104,7 @@ static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
}
if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id,
CLEANUP_MSG_STATS(&stats, state),
- rcpt, "none", dsn) != 0) {
+ rcpt, "none", NO_TLS_STATS, dsn) != 0) {
msg_warn("%s: trace logfile update error", state->queue_id);
state->errs |= CLEANUP_STAT_WRITE;
}
@@ -118,7 +118,7 @@ static void cleanup_verify_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
MSG_STATS stats;
if (verify_append(state->queue_id, CLEANUP_MSG_STATS(&stats, state),
- rcpt, "none", dsn, verify_status) != 0) {
+ rcpt, "none", NO_TLS_STATS, dsn, verify_status) != 0) {
msg_warn("%s: verify service update error", state->queue_id);
state->errs |= CLEANUP_STAT_WRITE;
}
diff --git a/postfix/src/discard/discard.c b/postfix/src/discard/discard.c
index f21b95cbc..43b728a9c 100644
--- a/postfix/src/discard/discard.c
+++ b/postfix/src/discard/discard.c
@@ -186,7 +186,8 @@ static int deliver_message(DELIVER_REQUEST *request)
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
status = sent(BOUNCE_FLAGS(request), request->queue_id,
- &request->msg_stats, rcpt, "none", &dsn);
+ &request->msg_stats, rcpt, "none",
+ NO_TLS_STATS, &dsn);
if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
deliver_completed(src, rcpt->offset);
result |= status;
diff --git a/postfix/src/error/error.c b/postfix/src/error/error.c
index e1ff1cb7c..a1f70d662 100644
--- a/postfix/src/error/error.c
+++ b/postfix/src/error/error.c
@@ -148,7 +148,7 @@
static int deliver_message(DELIVER_REQUEST *request, const char *def_dsn,
int (*append) (int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *))
+ const char *, const TLS_STATS *, DSN *))
{
const char *myname = "deliver_message";
VSTREAM *src;
@@ -194,7 +194,8 @@ static int deliver_message(DELIVER_REQUEST *request, const char *def_dsn,
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
status = append(BOUNCE_FLAGS(request), request->queue_id,
- &request->msg_stats, rcpt, "none", &dsn);
+ &request->msg_stats, rcpt, "none", NO_TLS_STATS,
+ &dsn);
if (status == 0)
deliver_completed(src, rcpt->offset);
result |= status;
diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in
index cc7086cfa..7a1034cd0 100644
--- a/postfix/src/global/Makefile.in
+++ b/postfix/src/global/Makefile.in
@@ -37,7 +37,8 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
normalize_mailhost_addr.c map_search.c reject_deliver_request.c \
info_log_addr_form.c sasl_mech_filter.c login_sender_match.c \
test_main.c compat_level.c config_known_tcp_ports.c \
- hfrom_format.c rfc2047_code.c ascii_header_text.c sendopts.c
+ hfrom_format.c rfc2047_code.c ascii_header_text.c sendopts.c \
+ tls_stats.c
OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -76,7 +77,8 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
normalize_mailhost_addr.o map_search.o reject_deliver_request.o \
info_log_addr_form.o sasl_mech_filter.o login_sender_match.o \
test_main.o compat_level.o config_known_tcp_ports.o \
- hfrom_format.o rfc2047_code.o ascii_header_text.o sendopts.o
+ hfrom_format.o rfc2047_code.o ascii_header_text.o sendopts.o \
+ tls_stats.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@@ -113,7 +115,8 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
maillog_client.h normalize_mailhost_addr.h map_search.h \
info_log_addr_form.h sasl_mech_filter.h login_sender_match.h \
test_main.h compat_level.h config_known_tcp_ports.h \
- hfrom_format.h rfc2047_code.h ascii_header_text.h sendopts.h
+ hfrom_format.h rfc2047_code.h ascii_header_text.h sendopts.h \
+ tls_stats.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -836,6 +839,7 @@ abounce.o: mail_params.h
abounce.o: mail_proto.h
abounce.o: msg_stats.h
abounce.o: recipient_list.h
+abounce.o: tls_stats.h
addr_match_list.o: ../../include/argv.h
addr_match_list.o: ../../include/check_arg.h
addr_match_list.o: ../../include/match_list.h
@@ -919,6 +923,7 @@ bounce.o: mail_proto.h
bounce.o: msg_stats.h
bounce.o: rcpt_print.h
bounce.o: recipient_list.h
+bounce.o: tls_stats.h
bounce.o: trace.h
bounce.o: verify.h
bounce_log.o: ../../include/attr.h
@@ -1109,6 +1114,7 @@ defer.o: mail_queue.h
defer.o: msg_stats.h
defer.o: rcpt_print.h
defer.o: recipient_list.h
+defer.o: tls_stats.h
defer.o: trace.h
defer.o: verify.h
deliver_completed.o: ../../include/check_arg.h
@@ -1156,6 +1162,7 @@ deliver_pass.o: mail_proto.h
deliver_pass.o: msg_stats.h
deliver_pass.o: rcpt_print.h
deliver_pass.o: recipient_list.h
+deliver_pass.o: tls_stats.h
deliver_request.o: ../../include/attr.h
deliver_request.o: ../../include/check_arg.h
deliver_request.o: ../../include/htable.h
@@ -1668,6 +1675,7 @@ log_adhoc.o: log_adhoc.h
log_adhoc.o: mail_params.h
log_adhoc.o: msg_stats.h
log_adhoc.o: recipient_list.h
+log_adhoc.o: tls_stats.h
login_sender_match.o: ../../include/argv.h
login_sender_match.o: ../../include/check_arg.h
login_sender_match.o: ../../include/dict.h
@@ -2574,9 +2582,11 @@ reject_deliver_request.o: deliver_completed.h
reject_deliver_request.o: deliver_request.h
reject_deliver_request.o: dsn.h
reject_deliver_request.o: dsn_buf.h
+reject_deliver_request.o: mail_params.h
reject_deliver_request.o: msg_stats.h
reject_deliver_request.o: recipient_list.h
reject_deliver_request.o: reject_deliver_request.c
+reject_deliver_request.o: tls_stats.h
remove.o: ../../include/check_arg.h
remove.o: ../../include/sys_defs.h
remove.o: ../../include/vbuf.h
@@ -2762,6 +2772,7 @@ sent.o: msg_stats.h
sent.o: recipient_list.h
sent.o: sent.c
sent.o: sent.h
+sent.o: tls_stats.h
sent.o: trace.h
sent.o: verify.h
server_acl.o: ../../include/argv.h
@@ -2887,6 +2898,12 @@ timed_ipc.o: ../../include/vstream.h
timed_ipc.o: mail_params.h
timed_ipc.o: timed_ipc.c
timed_ipc.o: timed_ipc.h
+tls_stats.o: ../../include/msg.h
+tls_stats.o: ../../include/mymalloc.h
+tls_stats.o: ../../include/sys_defs.h
+tls_stats.o: mail_params.h
+tls_stats.o: tls_stats.c
+tls_stats.o: tls_stats.h
tok822_find.o: ../../include/check_arg.h
tok822_find.o: ../../include/sys_defs.h
tok822_find.o: ../../include/vbuf.h
@@ -2968,6 +2985,7 @@ trace.o: mail_proto.h
trace.o: msg_stats.h
trace.o: rcpt_print.h
trace.o: recipient_list.h
+trace.o: tls_stats.h
trace.o: trace.c
trace.o: trace.h
user_acl.o: ../../include/argv.h
@@ -3016,6 +3034,7 @@ verify.o: mail_params.h
verify.o: mail_proto.h
verify.o: msg_stats.h
verify.o: recipient_list.h
+verify.o: tls_stats.h
verify.o: verify.c
verify.o: verify.h
verify.o: verify_clnt.h
diff --git a/postfix/src/global/bounce.c b/postfix/src/global/bounce.c
index a11ee7da8..47cbc9718 100644
--- a/postfix/src/global/bounce.c
+++ b/postfix/src/global/bounce.c
@@ -6,12 +6,13 @@
/* SYNOPSIS
/* #include
/*
-/* int bounce_append(flags, id, stats, recipient, relay, dsn)
+/* int bounce_append(flags, id, stats, recipient, relay, tstats, dsn)
/* int flags;
/* const char *id;
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/*
/* int bounce_flush(flags, queue, id, encoding, sendopts, sender,
@@ -38,7 +39,8 @@
/* const char *verp_delims;
/*
/* int bounce_one(flags, queue, id, encoding, sendopts, sender,
-/* dsn_envid, ret, stats, recipient, relay, dsn)
+/* dsn_envid, ret, stats, recipient, relay,
+/* tstats, dsn)
/* int flags;
/* const char *queue;
/* const char *id;
@@ -50,6 +52,7 @@
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/*
/* void bounce_client_init(title, maps)
@@ -58,15 +61,17 @@
/* INTERNAL API
/* DSN_FILTER *delivery_status_filter;
/*
-/* int bounce_append_intern(flags, id, stats, recipient, relay, dsn)
+/* int bounce_append_intern(flags, id, stats, recipient, relay, tstats, dsn)
/* int flags;
/* const char *id;
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
+/* DSN *dsn;
/*
/* int bounce_one_intern(flags, queue, id, encoding, sendopts, sender,
-/* dsn_envid, ret, stats, recipient, relay, dsn)
+/* dsn_envid, ret, stats, recipient, relay, tstats, dsn)
/* int flags;
/* const char *queue;
/* const char *id;
@@ -78,6 +83,7 @@
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/* DESCRIPTION
/* This module implements the client interface to the message
@@ -155,6 +161,8 @@
/* Sender-requested SMTPUTF8 or RequireTLS support.
/* .IP sender
/* The sender envelope address.
+/* .IP tstats
+/* TLS per-feature status.
/* .IP dsn_envid
/* Optional DSN envelope ID.
/* .IP dsn_ret
@@ -229,7 +237,7 @@ DSN_FILTER *delivery_status_filter;
int bounce_append(int flags, const char *id, MSG_STATS *stats,
RECIPIENT *rcpt, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
DSN *dsn_res;
@@ -249,17 +257,19 @@ int bounce_append(int flags, const char *id, MSG_STATS *stats,
if (delivery_status_filter != 0
&& (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
if (dsn_res->status[0] == '4')
- return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
+ return (defer_append_intern(flags, id, stats, rcpt, relay, tstats,
+ dsn_res));
my_dsn = *dsn_res;
}
- return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
+ return (bounce_append_intern(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn));
}
/* bounce_append_intern - append delivery status to per-message bounce log */
int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
RECIPIENT *rcpt, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
int status;
@@ -270,7 +280,7 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
*/
if (flags & DEL_REQ_FLAG_MTA_VRFY) {
my_dsn.action = "undeliverable";
- status = verify_append(id, stats, rcpt, relay, &my_dsn,
+ status = verify_append(id, stats, rcpt, relay, tstats, &my_dsn,
DEL_RCPT_STAT_BOUNCE);
return (status);
}
@@ -281,7 +291,7 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
*/
if (flags & DEL_REQ_FLAG_USR_VRFY) {
my_dsn.action = "undeliverable";
- status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
+ status = trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn);
return (status);
}
@@ -326,9 +336,9 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
ATTR_TYPE_END) == 0
&& ((flags & DEL_REQ_FLAG_RECORD) == 0
- || trace_append(flags, id, stats, rcpt, relay,
+ || trace_append(flags, id, stats, rcpt, relay, tstats,
&my_dsn) == 0)) {
- log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status);
+ log_adhoc(id, stats, rcpt, relay, tstats, &my_dsn, log_status);
status = (var_soft_bounce ? -1 : 0);
} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
VSTRING *junk = vstring_alloc(100);
@@ -337,7 +347,8 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
vstring_sprintf(junk, "%s or %s service failure",
var_bounce_service, var_trace_service);
my_dsn.reason = vstring_str(junk);
- status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn);
+ status = defer_append_intern(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn);
vstring_free(junk);
} else {
status = -1;
@@ -424,7 +435,7 @@ int bounce_one(int flags, const char *queue, const char *id,
const char *encoding, int sendopts,
const char *sender, const char *dsn_envid,
int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt,
- const char *relay, DSN *dsn)
+ const char *relay, const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
DSN *dsn_res;
@@ -443,11 +454,13 @@ int bounce_one(int flags, const char *queue, const char *id,
if (delivery_status_filter != 0
&& (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
if (dsn_res->status[0] == '4')
- return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
+ return (defer_append_intern(flags, id, stats, rcpt, relay, tstats,
+ dsn_res));
my_dsn = *dsn_res;
}
return (bounce_one_intern(flags, queue, id, encoding, sendopts, sender,
- dsn_envid, dsn_ret, stats, rcpt, relay, &my_dsn));
+ dsn_envid, dsn_ret, stats, rcpt, relay, tstats,
+ &my_dsn));
}
/* bounce_one_intern - send notice for one recipient */
@@ -457,7 +470,7 @@ int bounce_one_intern(int flags, const char *queue, const char *id,
const char *sender, const char *dsn_envid,
int dsn_ret, MSG_STATS *stats,
RECIPIENT *rcpt, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
int status;
@@ -468,7 +481,7 @@ int bounce_one_intern(int flags, const char *queue, const char *id,
*/
if (flags & DEL_REQ_FLAG_MTA_VRFY) {
my_dsn.action = "undeliverable";
- status = verify_append(id, stats, rcpt, relay, &my_dsn,
+ status = verify_append(id, stats, rcpt, relay, tstats, &my_dsn,
DEL_RCPT_STAT_BOUNCE);
return (status);
}
@@ -479,7 +492,7 @@ int bounce_one_intern(int flags, const char *queue, const char *id,
*/
if (flags & DEL_REQ_FLAG_USR_VRFY) {
my_dsn.action = "undeliverable";
- status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
+ status = trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn);
return (status);
}
@@ -488,7 +501,8 @@ int bounce_one_intern(int flags, const char *queue, const char *id,
* based procedure.
*/
else if (var_soft_bounce) {
- return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
+ return (bounce_append_intern(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn));
}
/*
@@ -519,9 +533,9 @@ int bounce_one_intern(int flags, const char *queue, const char *id,
SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
ATTR_TYPE_END) == 0
&& ((flags & DEL_REQ_FLAG_RECORD) == 0
- || trace_append(flags, id, stats, rcpt, relay,
+ || trace_append(flags, id, stats, rcpt, relay, tstats,
&my_dsn) == 0)) {
- log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced");
+ log_adhoc(id, stats, rcpt, relay, tstats, &my_dsn, "bounced");
status = 0;
} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
VSTRING *junk = vstring_alloc(100);
@@ -530,7 +544,8 @@ int bounce_one_intern(int flags, const char *queue, const char *id,
vstring_sprintf(junk, "%s or %s service failure",
var_bounce_service, var_trace_service);
my_dsn.reason = vstring_str(junk);
- status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn);
+ status = defer_append_intern(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn);
vstring_free(junk);
} else {
status = -1;
diff --git a/postfix/src/global/bounce.h b/postfix/src/global/bounce.h
index e2d67bd3a..7e99aab75 100644
--- a/postfix/src/global/bounce.h
+++ b/postfix/src/global/bounce.h
@@ -21,12 +21,13 @@
*/
#include
#include
+#include
/*
* Client interface.
*/
extern int bounce_append(int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
extern int bounce_flush(int, const char *, const char *, const char *, int,
const char *, const char *, int);
extern int bounce_flush_verp(int, const char *, const char *, const char *, int,
@@ -34,7 +35,7 @@ extern int bounce_flush_verp(int, const char *, const char *, const char *, int,
extern int bounce_one(int, const char *, const char *, const char *, int,
const char *, const char *,
int, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
extern void bounce_client_init(const char *, const char *);
/*
@@ -77,11 +78,11 @@ extern void bounce_client_init(const char *, const char *);
extern DSN_FILTER *delivery_status_filter;
extern int bounce_append_intern(int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
extern int bounce_one_intern(int, const char *, const char *, const char *,
int, const char *, const char *,
int, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
#endif
@@ -94,6 +95,9 @@ extern int bounce_one_intern(int, const char *, const char *, const char *,
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/global/defer.c b/postfix/src/global/defer.c
index 919484d63..8c1a17b3e 100644
--- a/postfix/src/global/defer.c
+++ b/postfix/src/global/defer.c
@@ -6,12 +6,13 @@
/* SYNOPSIS
/* #include
/*
-/* int defer_append(flags, id, stats, rcpt, relay, dsn)
+/* int defer_append(flags, id, stats, rcpt, relay, tstats, dsn)
/* int flags;
/* const char *id;
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/*
/* int defer_flush(flags, queue, id, encoding, sendopts, sender,
@@ -37,7 +38,8 @@
/* int dsn_ret;
/*
/* int defer_one(flags, queue, id, encoding, sendopts, sender,
-/* dsn_envid, ret, stats, recipient, relay, dsn)
+/* dsn_envid, ret, stats, recipient, relay,
+/* tstats, dsn)
/* int flags;
/* const char *queue;
/* const char *id;
@@ -49,14 +51,17 @@
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/* INTERNAL API
-/* int defer_append_intern(flags, id, stats, rcpt, relay, dsn)
+/* int defer_append_intern(flags, id, stats, rcpt, relay, tstats, dsn)
/* int flags;
/* const char *id;
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
+/* DSN *dsn;
/* DESCRIPTION
/* This module implements a client interface to the defer service,
/* which maintains a per-message logfile with status records for
@@ -124,6 +129,8 @@
/* Recipient information. See recipient_list(3).
/* .IP relay
/* Host we could not talk to.
+/* .IP tstats
+/* TLS per-feature status.
/* .IP dsn
/* Delivery status. See dsn(3). The specified action is ignored.
/* .IP encoding
@@ -194,7 +201,7 @@
int defer_append(int flags, const char *id, MSG_STATS *stats,
RECIPIENT *rcpt, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
DSN *dsn_res;
@@ -213,17 +220,19 @@ int defer_append(int flags, const char *id, MSG_STATS *stats,
if (delivery_status_filter != 0
&& (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
if (dsn_res->status[0] == '5')
- return (bounce_append_intern(flags, id, stats, rcpt, relay, dsn_res));
+ return (bounce_append_intern(flags, id, stats, rcpt, relay, tstats,
+ dsn_res));
my_dsn = *dsn_res;
}
- return (defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
+ return (defer_append_intern(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn));
}
/* defer_append_intern - defer message delivery */
int defer_append_intern(int flags, const char *id, MSG_STATS *stats,
RECIPIENT *rcpt, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
const char *rcpt_domain;
DSN my_dsn = *dsn;
@@ -235,7 +244,7 @@ int defer_append_intern(int flags, const char *id, MSG_STATS *stats,
*/
if (flags & DEL_REQ_FLAG_MTA_VRFY) {
my_dsn.action = "undeliverable";
- status = verify_append(id, stats, rcpt, relay, &my_dsn,
+ status = verify_append(id, stats, rcpt, relay, tstats, &my_dsn,
DEL_RCPT_STAT_DEFER);
return (status);
}
@@ -246,7 +255,7 @@ int defer_append_intern(int flags, const char *id, MSG_STATS *stats,
*/
if (flags & DEL_REQ_FLAG_USR_VRFY) {
my_dsn.action = "undeliverable";
- status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
+ status = trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn);
return (status);
}
@@ -273,13 +282,14 @@ int defer_append_intern(int flags, const char *id, MSG_STATS *stats,
SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
ATTR_TYPE_END) != 0)
msg_warn("%s: %s service failure", id, var_defer_service);
- log_adhoc(id, stats, rcpt, relay, &my_dsn, "deferred");
+ log_adhoc(id, stats, rcpt, relay, tstats, &my_dsn, "deferred");
/*
* Traced delivery.
*/
if (flags & DEL_REQ_FLAG_RECORD)
- if (trace_append(flags, id, stats, rcpt, relay, &my_dsn) != 0)
+ if (trace_append(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn) != 0)
msg_warn("%s: %s service failure", id, var_trace_service);
/*
@@ -358,7 +368,7 @@ int defer_one(int flags, const char *queue, const char *id,
const char *encoding, int sendopts,
const char *sender, const char *dsn_envid,
int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt,
- const char *relay, DSN *dsn)
+ const char *relay, const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
DSN *dsn_res;
@@ -379,8 +389,9 @@ int defer_one(int flags, const char *queue, const char *id,
if (dsn_res->status[0] == '5')
return (bounce_one_intern(flags, queue, id, encoding, sendopts,
sender, dsn_envid, dsn_ret, stats,
- rcpt, relay, dsn_res));
+ rcpt, relay, tstats, dsn_res));
my_dsn = *dsn_res;
}
- return (defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
+ return (defer_append_intern(flags, id, stats, rcpt, relay, tstats,
+ &my_dsn));
}
diff --git a/postfix/src/global/defer.h b/postfix/src/global/defer.h
index a015052f5..c0e93b7f6 100644
--- a/postfix/src/global/defer.h
+++ b/postfix/src/global/defer.h
@@ -20,7 +20,7 @@
* External interface.
*/
extern int defer_append(int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
extern int defer_flush(int, const char *, const char *, const char *, int,
const char *, const char *, int);
extern int defer_warn(int, const char *, const char *, const char *, int,
@@ -28,7 +28,7 @@ extern int defer_warn(int, const char *, const char *, const char *, int,
extern int defer_one(int, const char *, const char *, const char *, int,
const char *, const char *,
int, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
/*
* Start of private API.
@@ -36,7 +36,7 @@ extern int defer_one(int, const char *, const char *, const char *, int,
#ifdef DSN_INTERN
extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
#endif
@@ -49,6 +49,9 @@ extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *,
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/global/deliver_pass.c b/postfix/src/global/deliver_pass.c
index 545282945..111e51865 100644
--- a/postfix/src/global/deliver_pass.c
+++ b/postfix/src/global/deliver_pass.c
@@ -210,13 +210,13 @@ int deliver_pass(const char *class, const char *service,
(void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable");
status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
- rcpt, "none", &dsn);
+ rcpt, "none", NO_TLS_STATS, &dsn);
} else if ((status = deliver_pass_final_reply(stream, dsb))
== DELIVER_PASS_UNKNOWN) {
(void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error");
status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
- rcpt, "none", &dsn);
+ rcpt, "none", NO_TLS_STATS, &dsn);
}
/*
diff --git a/postfix/src/global/log_adhoc.c b/postfix/src/global/log_adhoc.c
index e00e93054..58d281ddd 100644
--- a/postfix/src/global/log_adhoc.c
+++ b/postfix/src/global/log_adhoc.c
@@ -6,11 +6,12 @@
/* SYNOPSIS
/* #include
/*
-/* void log_adhoc(id, stats, recipient, relay, dsn, status)
+/* void log_adhoc(id, stats, recipient, relay, tstats, dsn, status)
/* const char *id;
/* MSG_STATS *stats;
/* RECIPIENT *recipient;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/* const char *status;
/* DESCRIPTION
@@ -33,6 +34,8 @@
/* Host we could (not) talk to.
/* .IP status
/* bounced, deferred, sent, and so on.
+/* .IP tstats
+/* TLS per-feature status.
/* .IP dsn
/* Delivery status information. See dsn(3).
/* BUGS
@@ -52,6 +55,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -71,6 +77,7 @@
#include
#include
#include
+#include
/*
* Don't use "struct timeval" for time differences; use explicit signed
@@ -85,8 +92,8 @@ typedef struct {
/* log_adhoc - ad-hoc logging */
void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient,
- const char *relay, DSN *dsn,
- const char *status)
+ const char *relay, const TLS_STATS *tstats,
+ DSN *dsn, const char *status)
{
static VSTRING *buf;
DELTA_TIME delay; /* end-to-end delay */
@@ -140,6 +147,8 @@ void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient,
*
* XXX Apparently, Solaris gettimeofday() can return out-of-range
* microsecond values.
+ *
+ * TODO(wietse) move this to msg_stats.c
*/
#define DELTA(x, y, z) \
do { \
@@ -208,6 +217,41 @@ void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient,
PRETTY_FORMAT(buf, "/", sdelay);
PRETTY_FORMAT(buf, "/", xdelay);
+ /*
+ * Optional: TLS per-feature status summary. TODO(wietse) move this to
+ * tls_stats.c.
+ */
+#ifdef USE_TLS
+ if (tstats && tls_stats_used(tstats)) {
+ int idx;
+ const TLS_STAT *tstat;
+ int field_count = 0;
+ const char *if_disabled[2] = {"", "disabled:"};
+
+#define RELAXED(tstat) ((tstat)->enforce == TLS_STAT_ENF_RELAXED)
+#define DISABLED(tstat) ((tstat)->status == TLS_STAT_DISABLED)
+
+ vstring_sprintf_append(buf, ", tls=");
+ for (idx = 0; idx < TLS_STATS_SIZE; idx++) {
+ tstat = tls_stat_access(tstats, idx);
+ if (tstat->status == TLS_STAT_INACTIVE)
+ continue;
+ if (field_count > 0)
+ vstring_strcat(buf, "/");
+ if (tstat->status == TLS_STAT_VIOLATION)
+ vstring_strcat(buf, "!");
+ vstring_sprintf_append(buf, "%s%s%s%s",
+ "(" + !RELAXED(tstat),
+ if_disabled[DISABLED(tstat)],
+ tstat->name,
+ ")" + !RELAXED(tstat));
+ if (tstat->status == TLS_STAT_UNDECIDED)
+ vstring_strcat(buf, "?");
+ field_count += 1;
+ }
+ }
+#endif
+
/*
* Finally, the delivery status.
*/
diff --git a/postfix/src/global/log_adhoc.h b/postfix/src/global/log_adhoc.h
index 583cb6122..cb691a120 100644
--- a/postfix/src/global/log_adhoc.h
+++ b/postfix/src/global/log_adhoc.h
@@ -22,12 +22,13 @@
#include
#include
#include
+#include
/*
* Client interface.
*/
extern void log_adhoc(const char *, MSG_STATS *, RECIPIENT *, const char *,
- DSN *, const char *);
+ const TLS_STATS *, DSN *, const char *);
/* LICENSE
/* .ad
@@ -38,6 +39,9 @@ extern void log_adhoc(const char *, MSG_STATS *, RECIPIENT *, const char *,
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 6ca723257..5b9cf7c01 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -4424,6 +4424,15 @@ extern char *var_smtp_reqtls_policy;
#define DEF_REQTLS_REDACT_DSN "yes"
extern int var_reqtls_redact_dsn;
+ /*
+ * TS per-feature policy status.
+ */
+#define VAR_SMTP_LOG_TLS_FEATURE_STATUS "smtp_log_tls_feature_status"
+#define DEF_SMTP_LOG_TLS_FEATURE_STATUS "yes"
+#define VAR_LMTP_LOG_TLS_FEATURE_STATUS "lmtp_log_tls_feature_status"
+#define DEF_LMTP_LOG_TLS_FEATURE_STATUS "yes"
+extern bool var_log_tls_feature_status;
+
/*
* Workaround for future incompatibility. Our implementation of RFC 2308
* negative reply caching relies on the promise that res_query() and
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 564e97222..afae49718 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -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 "20250906"
+#define MAIL_RELEASE_DATE "20250910"
#define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT
@@ -104,6 +104,9 @@ extern void check_mail_version(const char *);
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/global/reject_deliver_request.c b/postfix/src/global/reject_deliver_request.c
index 1b1b2a57a..8a7bcb4fc 100644
--- a/postfix/src/global/reject_deliver_request.c
+++ b/postfix/src/global/reject_deliver_request.c
@@ -38,6 +38,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/*
@@ -95,7 +98,7 @@ int reject_deliver_request(const char *service, DELIVER_REQUEST *request,
(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id,
&request->msg_stats, rcpt,
- service, &why->dsn);
+ service, NO_TLS_STATS, &why->dsn);
if (status == 0)
deliver_completed(request->fp, rcpt->offset);
result |= status;
diff --git a/postfix/src/global/sent.c b/postfix/src/global/sent.c
index c81ccebdc..66f62d7fa 100644
--- a/postfix/src/global/sent.c
+++ b/postfix/src/global/sent.c
@@ -6,12 +6,13 @@
/* SYNOPSIS
/* #include
/*
-/* int sent(flags, queue_id, stats, recipient, relay, dsn)
+/* int sent(flags, queue_id, stats, recipient, relay, tstats, dsn)
/* int flags;
/* const char *queue_id;
/* MSG_STATS *stats;
/* RECIPIENT *recipient;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/* DESCRIPTION
/* sent() logs that a message was successfully delivered,
@@ -44,6 +45,8 @@
/* Recipient information. See recipient_list(3).
/* .IP relay
/* Name of the host we're talking to.
+/* .IP tstats
+/* TLS per-feature status.
/* .IP dsn
/* Delivery status. See dsn(3). The action is ignored in case
/* of a probe message. Otherwise, "delivered" is assumed when
@@ -69,6 +72,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -99,7 +105,7 @@
int sent(int flags, const char *id, MSG_STATS *stats,
RECIPIENT *recipient, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
DSN my_dsn = *dsn;
DSN *dsn_res;
@@ -126,7 +132,7 @@ int sent(int flags, const char *id, MSG_STATS *stats,
*/
if (flags & DEL_REQ_FLAG_MTA_VRFY) {
my_dsn.action = "deliverable";
- status = verify_append(id, stats, recipient, relay, &my_dsn,
+ status = verify_append(id, stats, recipient, relay, tstats, &my_dsn,
DEL_RCPT_STAT_OK);
return (status);
}
@@ -137,7 +143,8 @@ int sent(int flags, const char *id, MSG_STATS *stats,
*/
if (flags & DEL_REQ_FLAG_USR_VRFY) {
my_dsn.action = "deliverable";
- status = trace_append(flags, id, stats, recipient, relay, &my_dsn);
+ status = trace_append(flags, id, stats, recipient, relay, tstats,
+ &my_dsn);
return (status);
}
@@ -156,10 +163,10 @@ int sent(int flags, const char *id, MSG_STATS *stats,
my_dsn.action = "delivered";
if (((REC_ALL_SENT(flags) == 0 && REC_DLY_SENT(flags, recipient) == 0)
- || trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)
+ || trace_append(flags, id, stats, recipient, relay, tstats, &my_dsn) == 0)
&& ((recipient->dsn_notify & DSN_NOTIFY_SUCCESS) == 0
- || trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)) {
- log_adhoc(id, stats, recipient, relay, &my_dsn, "sent");
+ || trace_append(flags, id, stats, recipient, relay, tstats, &my_dsn) == 0)) {
+ log_adhoc(id, stats, recipient, relay, tstats, &my_dsn, "sent");
status = 0;
} else {
VSTRING *junk = vstring_alloc(100);
@@ -168,7 +175,8 @@ int sent(int flags, const char *id, MSG_STATS *stats,
id, var_trace_service);
my_dsn.reason = vstring_str(junk);
my_dsn.status = "4.3.0";
- status = defer_append(flags, id, stats, recipient, relay, &my_dsn);
+ status = defer_append(flags, id, stats, recipient, relay, tstats,
+ &my_dsn);
vstring_free(junk);
}
return (status);
diff --git a/postfix/src/global/sent.h b/postfix/src/global/sent.h
index 2ed3856a5..8846be5dd 100644
--- a/postfix/src/global/sent.h
+++ b/postfix/src/global/sent.h
@@ -29,7 +29,7 @@
#define SENT_FLAG_NONE (0)
extern int sent(int, const char *, MSG_STATS *, RECIPIENT *, const char *,
- DSN *);
+ const TLS_STATS *, DSN *);
/* LICENSE
/* .ad
@@ -40,6 +40,9 @@ extern int sent(int, const char *, MSG_STATS *, RECIPIENT *, const char *,
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/global/tls_stats.c b/postfix/src/global/tls_stats.c
new file mode 100644
index 000000000..e64205ec7
--- /dev/null
+++ b/postfix/src/global/tls_stats.c
@@ -0,0 +1,188 @@
+/*++
+/* NAME
+/* tls_stats 3
+/* SUMMARY
+/* manage TLS per-feature policy compliance status
+/* SYNOPSIS
+/* #include
+/*
+/* TLS_STATS *tls_stats_create(void)
+/*
+/* void tls_stats_revert(TLS_STATS *tstats)
+/*
+/* void tls_stats_free(TLS_STATS *tstats)
+/*
+/* void tls_stat_activate(
+/* TLS_STATS *tstats,
+/* int idx,
+/* const char *name,
+/* bool enforce)
+/*
+/* void tls_stat_decide(
+/* TLS_STATS *tstats,
+/* int idx,
+/* const char *name,
+/* int status,
+/* bool enforce)
+/*
+/* const TLS_STAT *tls_stats_access(
+/* TLS_STATS *tstats,
+/* int idx)
+/*
+/* int tls_stats_used(TLS_STATS *tstats)
+/* DESCRIPTION
+/* This module maintains for each active TLS feature whether
+/* the current outbound SMTP connection satisfies the policy
+/* requirements for that feature. For example, whether a server
+/* certificate matches DANE or STS requirements.
+/*
+/* tls_stats_create() creates one TLS_STATS instance with all status
+/* information set to TLS_STAT_INACTIVE.
+/*
+/* tls_stats_revert() reverts changes after tls_stats_create().
+/*
+/* tls_stats_free() recycles storage for a TLS_STATS instance.
+/*
+/* Specific status information is accessed with an index. Valid
+/* indices are 0 or 1 (the caller decides their purpose).
+/*
+/* The enforcement level of a feature is either TLS_STAT_ENF_FULL
+/* (full enforcement) or TLS_STAT_ENF_RELAXED (some requirements
+/* are relaxed).
+/*
+/* tls_stat_activate() changes the status in tstats at index idx
+/* from TLS_STAT_INACTIVE to TLS_STAT_UNDECIDED, and updates the
+/* name (pointer copy) and 'enforce' level. Calls with an invalid
+/* index result in a panic(), and calls with an already active
+/* index result in a warning.
+/*
+/* tls_stat_decide() updates the status in tstats at index idx from
+/* TLS_STAT_UNDECIDED to TLS_STAT_COMPLIANT, TLS_STAT_VIOLATION,
+/* or TLS_STAT_DISABLED, and updates its name and enforcement
+/* level. Calls with an invalid index or an unexpected decision
+/* status result in a panic(), and calls with an inactive or
+/* already decided index status result in a warning.
+/*
+/* tls_stats_access() returns a const pointer to the status
+/* information in tstats at index idx.
+/*
+/* tls_stats_used() returns the number of activated categories for
+/* its argument.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+#ifdef USE_TLS
+
+ /*
+ * System library.
+ */
+#include
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+
+ /*
+ * Global library.
+ */
+#include
+
+/* tls_stats_create - create an all-inactive TLS_STATS instance */
+
+TLS_STATS *tls_stats_create(void)
+{
+ TLS_STATS *tstats;
+ TLS_STAT *tp;
+
+#define TLS_STAT_INIT(tp) do { \
+ (tp)->name = 0; \
+ (tp)->status = TLS_STAT_INACTIVE; \
+ (tp)->enforce = 0; \
+ } while (0);
+
+ tstats = (TLS_STATS *) mymalloc(sizeof(*tstats));
+ tstats->used = 0;
+ for (tp = tstats->st; tp < tstats->st + TLS_STATS_SIZE; tp++)
+ TLS_STAT_INIT(tp);
+ return tstats;
+}
+
+/* tls_stats_revert - revert changes after tls_stats_create() */
+
+void tls_stats_revert(TLS_STATS *tstats)
+{
+ TLS_STAT *tp;
+
+ tstats->used = 0;
+ for (tp = tstats->st; tp < tstats->st + TLS_STATS_SIZE; tp++)
+ if (tp->status != TLS_STAT_INACTIVE)
+ TLS_STAT_INIT(tp);
+}
+
+/* tls_stats_free - TLS_STATS destructor */
+
+void tls_stats_free(TLS_STATS *tstats)
+{
+ myfree(tstats);
+}
+
+/* tls_stat_activate - activate status at index */
+
+void tls_stat_activate(TLS_STATS *tstats, int idx, const char *name,
+ bool enforce)
+{
+ TLS_STAT *tls_stat;
+
+ if (idx < 0 || idx >= TLS_STATS_SIZE)
+ msg_panic("%s: bad index: %d", __func__, idx);
+ tls_stat = tstats->st + idx;
+ if (tls_stat->status != TLS_STAT_INACTIVE)
+ msg_warn("%s: already active TLS_STAT at index %d", __func__, idx);
+ tls_stat->name = name;
+ tls_stat->status = TLS_STAT_UNDECIDED;
+ tls_stat->enforce = enforce;
+ tstats->used += 1;
+}
+
+/* tls_stat_decide - update undecided status at index */
+
+extern void tls_stat_decide(TLS_STATS *tstats, int idx, const char *name,
+ int status, bool enforce)
+{
+ TLS_STAT *tls_stat;
+
+ if (status != TLS_STAT_VIOLATION && status != TLS_STAT_COMPLIANT
+ && status != TLS_STAT_DISABLED)
+ msg_panic("%s: bad new status: %d", __func__, status);
+ if (idx < 0 || idx >= TLS_STATS_SIZE)
+ msg_panic("%s: bad index: %d", __func__, idx);
+ tls_stat = tstats->st + idx;
+ if (tls_stat->status != TLS_STAT_UNDECIDED)
+ msg_warn("%s: unexpected status %d at index %d",
+ __func__, tls_stat->status, idx);
+ tls_stat->name = name;
+ tls_stat->status = status;
+ tls_stat->enforce = enforce;
+}
+
+/* tls_stat_access - peek at specific TLS_STAT instance. */
+
+const TLS_STAT *tls_stat_access(const TLS_STATS *tstats, int idx)
+{
+ const TLS_STAT *tls_stat;
+
+ if (idx < 0 || idx >= TLS_STATS_SIZE)
+ msg_panic("%s: bad index: %d", __func__, idx);
+ tls_stat = tstats->st + idx;
+ return (tls_stat);
+}
+
+#endif /* USE_TLS */
diff --git a/postfix/src/global/tls_stats.h b/postfix/src/global/tls_stats.h
new file mode 100644
index 000000000..93e334c51
--- /dev/null
+++ b/postfix/src/global/tls_stats.h
@@ -0,0 +1,69 @@
+#ifndef _TLS_STATS_H_INCLUDED_
+#define _TLS_STATS_H_INCLUDED_
+
+/*++
+/* NAME
+/* tls_stats 3h
+/* SUMMARY
+/* manage TLS per-feature policy compliance status
+/* SYNOPSIS
+/* #include
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * TODO(wietse) adopt C99 bool.
+ */
+#include
+
+ /*
+ * External interface.
+ */
+typedef struct TLS_STAT {
+ const char *name; /* Human-readable feature name */
+ int status; /* See below */
+ bool enforce; /* See below */
+} TLS_STAT;
+
+#define TLS_STAT_INACTIVE 0 /* No data */
+#define TLS_STAT_UNDECIDED 1 /* Pending decision */
+#define TLS_STAT_VIOLATION 2 /* Definitely did not meet policy */
+#define TLS_STAT_COMPLIANT 3 /* Definitely did meet policy */
+#define TLS_STAT_DISABLED 4 /* Definitely disabled */
+#define TLS_STAT_ENF_FULL 1 /* Full enforcement */
+#define TLS_STAT_ENF_RELAXED 0 /* Relaxed enforcement */
+
+ /*
+ * Wrap it in a structure for sanity-checked access.
+ */
+#define TLS_STATS_SIZE 2 /* TLS level and REQUIRETLS */
+
+typedef struct TLS_STATS {
+ int used;
+ TLS_STAT st[TLS_STATS_SIZE];
+} TLS_STATS;
+
+#ifdef USE_TLS
+
+extern TLS_STATS *tls_stats_create(void);
+extern void tls_stats_revert(TLS_STATS *);
+extern void tls_stats_free(TLS_STATS *);
+
+#define tls_stats_used(t) ((t)->used)
+extern void tls_stat_activate(TLS_STATS *, int, const char *, bool);
+extern void tls_stat_decide(TLS_STATS *, int, const char *, int, bool);
+extern const TLS_STAT *tls_stat_access(const TLS_STATS *, int);
+
+#define NO_TLS_STATS ((TLS_STATS *) 0)
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+#endif /* USE_TLS */
+#endif /* _TLS_STATS_H_INCLUDED_ */
diff --git a/postfix/src/global/trace.c b/postfix/src/global/trace.c
index d826a6494..019bd25dd 100644
--- a/postfix/src/global/trace.c
+++ b/postfix/src/global/trace.c
@@ -6,12 +6,13 @@
/* SYNOPSIS
/* #include
/*
-/* int trace_append(flags, id, stats, rcpt, relay, dsn)
+/* int trace_append(flags, id, stats, rcpt, relay, tstats, dsn)
/* int flags;
/* const char *id;
/* MSG_STATS *stats;
/* RECIPIENT *rcpt;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/*
/* int trace_flush(flags, queue, id, encoding, sender,
@@ -61,6 +62,8 @@
/* Recipient information. See recipient_list(3).
/* .IP relay
/* The host we sent the mail to.
+/* .IP tstats
+/* TLS per-feature status.
/* .IP dsn
/* Delivery status information. See dsn(3).
/* DIAGNOSTICS
@@ -84,6 +87,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -110,7 +116,7 @@
int trace_append(int flags, const char *id, MSG_STATS *stats,
RECIPIENT *rcpt, const char *relay,
- DSN *dsn)
+ const TLS_STATS *tstats, DSN *dsn)
{
VSTRING *why = vstring_alloc(100);
DSN my_dsn = *dsn;
@@ -137,7 +143,7 @@ int trace_append(int flags, const char *id, MSG_STATS *stats,
req_stat = -1;
} else {
if (flags & DEL_REQ_FLAG_USR_VRFY)
- log_adhoc(id, stats, rcpt, relay, dsn, my_dsn.action);
+ log_adhoc(id, stats, rcpt, relay, tstats, dsn, my_dsn.action);
req_stat = 0;
}
vstring_free(why);
diff --git a/postfix/src/global/trace.h b/postfix/src/global/trace.h
index 201360198..d76811edf 100644
--- a/postfix/src/global/trace.h
+++ b/postfix/src/global/trace.h
@@ -20,7 +20,7 @@
* External interface.
*/
extern int trace_append(int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const TLS_STATS *, DSN *);
extern int trace_flush(int, const char *, const char *, const char *,
const char *, const char *, int);
@@ -33,6 +33,9 @@ extern int trace_flush(int, const char *, const char *, const char *,
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/global/verify.c b/postfix/src/global/verify.c
index 2ce091a5d..53f17f16b 100644
--- a/postfix/src/global/verify.c
+++ b/postfix/src/global/verify.c
@@ -6,12 +6,13 @@
/* SYNOPSIS
/* #include
/*
-/* int verify_append(queue_id, stats, recipient, relay, dsn,
+/* int verify_append(queue_id, stats, recipient, relay, tstats, dsn,
/* verify_status)
/* const char *queue_id;
/* MSG_STATS *stats;
/* RECIPIENT *recipient;
/* const char *relay;
+/* const TLS_STATS *tstats;
/* DSN *dsn;
/* int verify_status;
/* DESCRIPTION
@@ -32,6 +33,8 @@
/* Recipient information. See recipient_list(3).
/* .IP relay
/* Name of the host we're talking to.
+/* .IP tstats
+/* TLS per-feature status.
/* .IP dsn
/* Delivery status information. See dsn(3).
/* The action is one of "deliverable" or "undeliverable".
@@ -66,6 +69,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -91,7 +97,8 @@
int verify_append(const char *queue_id, MSG_STATS *stats,
RECIPIENT *recipient, const char *relay,
- DSN *dsn, int vrfy_stat)
+ const TLS_STATS *tstats, DSN *dsn,
+ int vrfy_stat)
{
int req_stat;
DSN my_dsn = *dsn;
@@ -120,7 +127,8 @@ int verify_append(const char *queue_id, MSG_STATS *stats,
req_stat = VRFY_STAT_OK;
}
if (req_stat == VRFY_STAT_OK) {
- log_adhoc(queue_id, stats, recipient, relay, dsn, my_dsn.action);
+ log_adhoc(queue_id, stats, recipient, relay, tstats, dsn,
+ my_dsn.action);
req_stat = 0;
} else {
msg_warn("%s: %s service failure", queue_id, var_verify_service);
diff --git a/postfix/src/global/verify.h b/postfix/src/global/verify.h
index 250eb6d65..5e09d3622 100644
--- a/postfix/src/global/verify.h
+++ b/postfix/src/global/verify.h
@@ -20,12 +20,14 @@
* Global library.
*/
#include
+#include
/*
* External interface.
*/
extern int verify_append(const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *, int);
+ const char *, const TLS_STATS *,
+ DSN *, int);
/* LICENSE
/* .ad
@@ -36,6 +38,9 @@ extern int verify_append(const char *, MSG_STATS *, RECIPIENT *,
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/local/local.h b/postfix/src/local/local.h
index 13d0dc79e..0f024e45f 100644
--- a/postfix/src/local/local.h
+++ b/postfix/src/local/local.h
@@ -133,15 +133,15 @@ typedef struct LOCAL_STATE {
#define BOUNCE_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(attr.why)
+ NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why)
#define BOUNCE_ONE_ATTR(attr) \
attr.queue_name, attr.queue_id, attr.encoding, attr.sendopts, \
attr.sender, attr.dsn_envid, attr.dsn_ret, \
&attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(attr.why)
+ NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why)
#define SENT_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(attr.why)
+ NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why)
#define OPENED_ATTR(attr) \
attr.queue_id, attr.sender
#define COPY_ATTR(attr) \
diff --git a/postfix/src/oqmgr/qmgr_bounce.c b/postfix/src/oqmgr/qmgr_bounce.c
index 00ba885bf..ee9f56863 100644
--- a/postfix/src/oqmgr/qmgr_bounce.c
+++ b/postfix/src/oqmgr/qmgr_bounce.c
@@ -62,7 +62,7 @@ void qmgr_bounce_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
status = bounce_append(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", dsn);
+ "none", NO_TLS_STATS, dsn);
if (status == 0)
deliver_completed(message->fp, recipient->offset);
diff --git a/postfix/src/oqmgr/qmgr_defer.c b/postfix/src/oqmgr/qmgr_defer.c
index dc0319e77..e87fe0b6e 100644
--- a/postfix/src/oqmgr/qmgr_defer.c
+++ b/postfix/src/oqmgr/qmgr_defer.c
@@ -154,5 +154,5 @@ void qmgr_defer_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
*/
message->flags |= defer_append(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", dsn);
+ "none", NO_TLS_STATS, dsn);
}
diff --git a/postfix/src/oqmgr/qmgr_message.c b/postfix/src/oqmgr/qmgr_message.c
index a88c8e5e7..85b0d7141 100644
--- a/postfix/src/oqmgr/qmgr_message.c
+++ b/postfix/src/oqmgr/qmgr_message.c
@@ -1133,7 +1133,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
&& !var_double_bounce_sender[len]) {
status = sent(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", DSN_SIMPLE(&dsn, "2.0.0",
+ "none", NO_TLS_STATS, DSN_SIMPLE(&dsn, "2.0.0",
"undeliverable postmaster notification discarded"));
if (status == 0) {
deliver_completed(message->fp, recipient->offset);
diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c
index feb35f252..0788f640a 100644
--- a/postfix/src/pipe/pipe.c
+++ b/postfix/src/pipe/pipe.c
@@ -1095,7 +1095,7 @@ static int eval_command_status(int command_status, char *service,
rcpt = request->rcpt_list.info + n;
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats, rcpt,
- service, &why->dsn);
+ service, NO_TLS_STATS, &why->dsn);
if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
deliver_completed(request->fp, rcpt->offset);
result |= status;
@@ -1112,7 +1112,7 @@ static int eval_command_status(int command_status, char *service,
(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id,
&request->msg_stats, rcpt,
- service, &why->dsn);
+ service, NO_TLS_STATS, &why->dsn);
if (status == 0)
deliver_completed(request->fp, rcpt->offset);
result |= status;
@@ -1224,7 +1224,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
rcpt = request->rcpt_list.info + n;
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
- rcpt, service, &why->dsn);
+ rcpt, service, NO_TLS_STATS, &why->dsn);
if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
deliver_completed(request->fp, rcpt->offset);
deliver_status |= status;
diff --git a/postfix/src/qmgr/qmgr_bounce.c b/postfix/src/qmgr/qmgr_bounce.c
index 00ba885bf..ee9f56863 100644
--- a/postfix/src/qmgr/qmgr_bounce.c
+++ b/postfix/src/qmgr/qmgr_bounce.c
@@ -62,7 +62,7 @@ void qmgr_bounce_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
status = bounce_append(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", dsn);
+ "none", NO_TLS_STATS, dsn);
if (status == 0)
deliver_completed(message->fp, recipient->offset);
diff --git a/postfix/src/qmgr/qmgr_defer.c b/postfix/src/qmgr/qmgr_defer.c
index 79615cc0f..1cce65ca5 100644
--- a/postfix/src/qmgr/qmgr_defer.c
+++ b/postfix/src/qmgr/qmgr_defer.c
@@ -159,5 +159,5 @@ void qmgr_defer_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
*/
message->flags |= defer_append(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", dsn);
+ "none", NO_TLS_STATS, dsn);
}
diff --git a/postfix/src/qmgr/qmgr_message.c b/postfix/src/qmgr/qmgr_message.c
index 2e44c1d22..56533b2a2 100644
--- a/postfix/src/qmgr/qmgr_message.c
+++ b/postfix/src/qmgr/qmgr_message.c
@@ -1192,7 +1192,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
&& !var_double_bounce_sender[len]) {
status = sent(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", DSN_SIMPLE(&dsn, "2.0.0",
+ "none", NO_TLS_STATS, DSN_SIMPLE(&dsn, "2.0.0",
"undeliverable postmaster notification discarded"));
if (status == 0) {
deliver_completed(message->fp, recipient->offset);
diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c
index 924037091..f50541d59 100644
--- a/postfix/src/smtp/lmtp_params.c
+++ b/postfix/src/smtp/lmtp_params.c
@@ -142,5 +142,6 @@
VAR_LMTP_TLSRPT_ENABLE, DEF_LMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable,
VAR_LMTP_TLSRPT_SKIP_REUSED_HS, DEF_LMTP_TLSRPT_SKIP_REUSED_HS, &var_smtp_tlsrpt_skip_reused_hs,
VAR_LMTP_TLS_ENF_STS_MX_PAT, DEF_LMTP_TLS_ENF_STS_MX_PAT, &var_smtp_tls_enf_sts_mx_pat,
+ VAR_LMTP_LOG_TLS_FEATURE_STATUS, DEF_LMTP_LOG_TLS_FEATURE_STATUS, &var_log_tls_feature_status,
0,
};
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index 1b8d5bc39..4ee1250c9 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -667,6 +667,9 @@
/* .IP "\fBsmtp_requiretls_policy (see 'postconf -d smtp_requiretls_policy' output)\fR"
/* How the Postfix SMTP and LMTP client will enforce REQUIRETLS
/* for messages received with the REQUIRETLS option.
+/* .IP "\fBsmtp_log_tls_feature_status (yes)\fR"
+/* Enable logging of TLS feature information in delivery status
+/* logging.
/* OBSOLETE TLS CONTROLS
/* .ad
/* .fi
@@ -1180,6 +1183,7 @@ bool var_smtp_tlsrpt_enable;
char *var_smtp_tlsrpt_sockname;
bool var_smtp_tlsrpt_skip_reused_hs;
char *var_smtp_reqtls_policy;
+bool var_log_tls_feature_status;
/* Special handling of 535 AUTH errors. */
char *var_smtp_sasl_auth_cache_name;
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index 8433828fc..dd5018de7 100644
--- a/postfix/src/smtp/smtp.h
+++ b/postfix/src/smtp/smtp.h
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
/*
* Postfix TLS library.
@@ -200,7 +201,7 @@ typedef struct SMTP_STATE {
SMTP_ITERATOR iterator[1]; /* Usage: state->iterator->member */
/*
- * Global iterator.
+ * TLS policy related.
*/
#ifdef USE_TLS
SMTP_TLS_POLICY tls[1]; /* Usage: state->tls->member */
@@ -208,6 +209,7 @@ typedef struct SMTP_STATE {
struct TLSRPT_WRAPPER *tlsrpt;
#endif
int reqtls_level; /* from smtp_reqtls_policy */
+ TLS_STATS *tls_stats; /* policy compliance status */
#endif
/*
@@ -246,6 +248,28 @@ typedef struct SMTP_STATE {
unsigned logged_line_length_limit:1;
} SMTP_STATE;
+#define SMTP_TLS_STAT_IDX_SEC_LEVEL 0
+#define SMTP_TLS_STAT_IDX_REQTLS 1
+
+/* Use the TLS policy name for the TLS security level status feature. */
+#define SMTP_TLS_STAT_NAME_REQTLS "requiretls"
+
+#define smtp_tls_stat_activate_sec_level(tstats, level, enforce) \
+ tls_stat_activate((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \
+ str_tls_level(level), (enforce))
+
+#define smtp_tls_stat_decide_sec_level(tstats, level, status, enforce) \
+ tls_stat_decide((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \
+ str_tls_level(level), (status), (enforce))
+
+#define smtp_tls_stat_activate_reqtls(tstats, enforce) \
+ tls_stat_activate((tstats), SMTP_TLS_STAT_IDX_REQTLS, \
+ SMTP_TLS_STAT_NAME_REQTLS, (enforce))
+
+#define smtp_tls_stat_decide_reqtls(tstats, status, enforce) \
+ tls_stat_decide((tstats), SMTP_TLS_STAT_IDX_REQTLS, \
+ SMTP_TLS_STAT_NAME_REQTLS, (status), (enforce))
+
#ifdef USE_TLS
#define STATE_TLS_NOT_REQUIRED(state) \
(var_tls_required_enable \
diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
index ded52ab4a..6ed033e15 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -526,6 +526,10 @@ static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state)
switch (state->reqtls_level) {
case SMTP_REQTLS_POLICY_ACT_ENFORCE:
if (TLS_MUST_MATCH(tls->level) == 0) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_FULL);
dsb_simple(why, "5.7.10", "REQUIRETLS Failure: sender "
"requested REQUIRETLS, but my configured TLS "
"security level '%s' disables certificate "
@@ -536,6 +540,10 @@ static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state)
break;
case SMTP_REQTLS_POLICY_ACT_OPP_TLS:
if (tls->level == TLS_LEV_NONE) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_RELAXED);
dsb_simple(why, "5.7.10", "REQUIRETLS Failure: sender "
"requested REQUIRETLS, but my configured TLS "
"security level '%s' disables encryption. The "
@@ -1187,6 +1195,15 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
continue;
/* XXX Assume there is no code at the end of this loop. */
}
+ if (state->tls_stats) {
+ tls_stats_revert(state->tls_stats);
+ smtp_tls_stat_activate_sec_level(state->tls_stats,
+ state->tls->level,
+ state->tls->level != TLS_LEV_MAY);
+ if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE)
+ smtp_tls_stat_activate_reqtls(state->tls_stats,
+ state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE);
+ }
/* Disable TLS when retrying after a handshake failure */
if (retry_plain) {
state->tls->level = TLS_LEV_NONE;
diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c
index 898689e41..5fe33c078 100644
--- a/postfix/src/smtp/smtp_params.c
+++ b/postfix/src/smtp/smtp_params.c
@@ -146,5 +146,6 @@
VAR_SMTP_TLSRPT_ENABLE, DEF_SMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable,
VAR_SMTP_TLSRPT_SKIP_REUSED_HS, DEF_SMTP_TLSRPT_SKIP_REUSED_HS, &var_smtp_tlsrpt_skip_reused_hs,
VAR_SMTP_TLS_ENF_STS_MX_PAT, DEF_SMTP_TLS_ENF_STS_MX_PAT, &var_smtp_tls_enf_sts_mx_pat,
+ VAR_SMTP_LOG_TLS_FEATURE_STATUS, DEF_SMTP_LOG_TLS_FEATURE_STATUS, &var_log_tls_feature_status,
0,
};
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index 992763eeb..59142cea2 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -693,25 +693,48 @@ int smtp_helo(SMTP_STATE *state)
* that the server does not offer REQUIRETLS.
*/
#ifdef USE_TLS
- if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)
- && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0
- && (session->features & SMTP_FEATURE_REQTLS) == 0) {
- switch (state->reqtls_level) {
- case SMTP_REQTLS_POLICY_ACT_ENFORCE:
- return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
- DSN_BY_LOCAL_MTA,
- SMTP_RESP_FAKE(&fake, "5.7.30"),
- "REQUIRETLS Failure: sender "
- "requested REQUIRETLS, but no "
- "server was found that supports "
- "REQUIRETLS. The last attempted "
- "server was %s", session->namaddr));
- default:
+ if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE
+ && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0) {
+ if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) {
+ if ((session->features & SMTP_FEATURE_REQTLS) != 0) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_COMPLIANT,
+ TLS_STAT_ENF_FULL);
+ } else {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_FULL);
+ return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
+ DSN_BY_LOCAL_MTA,
+ SMTP_RESP_FAKE(&fake, "5.7.30"),
+ "REQUIRETLS Failure: sender "
+ "requested REQUIRETLS, but no "
+ "server was found that supports "
+ "REQUIRETLS. The last attempted "
+ "server was %s", session->namaddr));
+ }
+ } else if ((session->features & SMTP_FEATURE_REQTLS) != 0) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_COMPLIANT,
+ TLS_STAT_ENF_RELAXED);
+ } else {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_DISABLED,
+ TLS_STAT_ENF_RELAXED);
msg_info("%s: REQUIRETLS Debug: sender requested REQUIRETLS, "
"but REQUIRETLS support was not offered by host "
"%s", request->queue_id, session->namaddr);
}
}
+
+ /*
+ * TODO(wietse) Maybe log servers that announce REQUIRETLS and whether
+ * the connection is authenticated?
+ */
#endif
/*
@@ -842,6 +865,13 @@ int smtp_helo(SMTP_STATE *state)
state->misc_flags &= ~SMTP_MISC_FLAG_IN_STARTTLS;
return (tls_helo_status);
}
+#ifdef USE_TLSRPT
+ if (state->tlsrpt)
+ trw_report_failure(state->tlsrpt,
+ TLSRPT_STARTTLS_NOT_SUPPORTED,
+ /* additional_info= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+#endif
/*
* Give up if we must use TLS but the server rejects STARTTLS
@@ -854,14 +884,23 @@ int smtp_helo(SMTP_STATE *state)
session->features &= ~SMTP_FEATURE_STARTTLS;
if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level)
|| TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) {
-#ifdef USE_TLSRPT
- if (state->tlsrpt)
- trw_report_failure(state->tlsrpt,
- TLSRPT_STARTTLS_NOT_SUPPORTED,
- /* additional_info= */ (char *) 0,
- /* failure_reason= */ (char *) 0);
-#endif
- if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level))
+ /* Before returning, decide all relevant policy status info. */
+ if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION,
+ state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE ?
+ TLS_STAT_ENF_FULL :
+ TLS_STAT_ENF_RELAXED);
+ }
+ if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level))
+ if (state->tls_stats)
+ smtp_tls_stat_decide_sec_level(state->tls_stats,
+ state->tls->level,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_FULL);
+ /* Then, REQUIRETLS failure must take precedence over other. */
+ if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) {
return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "5.7.10"),
@@ -870,6 +909,7 @@ int smtp_helo(SMTP_STATE *state)
"but host %s refused to "
"start TLS: %s", session->namaddr,
translit(resp->str, "\n", " ")));
+ }
/* TLS_REQUIRED_BY_SECURITY_LEVEL */
return (smtp_site_fail(state, STR(iter->host), resp,
"TLS is required, but host %s refused to start TLS: %s",
@@ -900,6 +940,22 @@ int smtp_helo(SMTP_STATE *state)
/* additional_info= */ (char *) 0,
/* failure_reason= */ (char *) 0);
#endif
+ /* Before returning, decide all relevant policy status info. */
+ if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION,
+ state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE ?
+ TLS_STAT_ENF_FULL :
+ TLS_STAT_ENF_RELAXED);
+ }
+ if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level))
+ if (state->tls_stats)
+ smtp_tls_stat_decide_sec_level(state->tls_stats,
+ state->tls->level,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_FULL);
+ /* Then, REQUIRETLS failure must take precedence over other. */
if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level))
return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
DSN_BY_LOCAL_MTA,
@@ -926,6 +982,17 @@ int smtp_helo(SMTP_STATE *state)
"TLS is required, but unavailable"));
}
}
+ /* Continue in plain-text mode. */
+ if (state->tls_stats) {
+ if (state->tls->level == TLS_LEV_NONE) {
+ /* TODO(wietse) May be fall-back after TLS handshake failed. */
+ smtp_tls_stat_decide_sec_level(state->tls_stats, state->tls->level,
+ TLS_STAT_COMPLIANT, TLS_STAT_ENF_FULL);
+ } else {
+ smtp_tls_stat_decide_sec_level(state->tls_stats, state->tls->level,
+ TLS_STAT_DISABLED, TLS_STAT_ENF_RELAXED);
+ }
+ }
}
#endif
#ifdef USE_SASL_AUTH
@@ -944,7 +1011,6 @@ static int smtp_start_tls(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
- DELIVER_REQUEST *request = state->request;
TLS_CLIENT_START_PROPS start_props;
VSTRING *serverid;
SMTP_RESP fake;
@@ -1211,6 +1277,7 @@ static int smtp_start_tls(SMTP_STATE *state)
*/
if (PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE)
RETRY_AS_PLAINTEXT;
+ /* Leave all TLS feature policy status info as 'undecided'. */
return (smtp_misc_fail(state, state->tls->level == TLS_LEV_MAY ?
SMTP_MISC_FAIL_NONE : SMTP_MISC_FAIL_THROTTLE,
DSN_BY_LOCAL_MTA,
@@ -1253,13 +1320,22 @@ static int smtp_start_tls(SMTP_STATE *state)
/* failure_reason= */ (char *) 0);
}
#endif
+ /* Finalize TLS feature policy status info before giving up. */
+ if (state->tls_stats)
+ smtp_tls_stat_decide_sec_level(state->tls_stats,
+ session->tls_context->level,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_FULL);
/*
* When the sender requested REQUIRETLS, and REQUIRETLS is
* enforced, return the message as undeliverable only when there
* are no more alternative MX hosts.
*/
- if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level))
+ if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION, TLS_STAT_ENF_FULL);
return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL,
DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "5.7.10"),
@@ -1270,11 +1346,15 @@ static int smtp_start_tls(SMTP_STATE *state)
"server was %s", trusted ?
"matching" : "trusted",
session->namaddr));
+ } else if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE) {
+ if (state->tls_stats)
+ smtp_tls_stat_decide_reqtls(state->tls_stats,
+ TLS_STAT_VIOLATION,
+ TLS_STAT_ENF_RELAXED);
+ }
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),
"Server certificate not verified"));
- } else {
- /* TODO(wietse) record that this attempt satisfies REQUIRETLS. */
}
}
@@ -1290,6 +1370,16 @@ static int smtp_start_tls(SMTP_STATE *state)
(void) trw_report_success(state->tlsrpt);
#endif
+ /*
+ * Report relaxed enforcement for the initial TLS level if it was
+ * degraded.
+ */
+ if (state->tls_stats)
+ smtp_tls_stat_decide_sec_level(state->tls_stats,
+ state->tls->level, TLS_STAT_COMPLIANT,
+ session->tls_context->level < state->tls->level ?
+ TLS_STAT_ENF_RELAXED : TLS_STAT_ENF_FULL);
+
/*
* At this point we have to re-negotiate the "EHLO" to reget the
* feature-list.
@@ -1877,7 +1967,6 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE) {
if ((session->features & SMTP_FEATURE_REQTLS) != 0) {
vstring_strcat(next_command, " REQUIRETLS");
- /* TODO(wietse) record that REQUIRETLS is active. */
} else if (state->reqtls_level
== SMTP_REQTLS_POLICY_ACT_ENFORCE) {
msg_panic("Can't happen: must enforce REQUIRETLS, but "
diff --git a/postfix/src/smtp/smtp_rcpt.c b/postfix/src/smtp/smtp_rcpt.c
index 6608ea880..66b9a41ac 100644
--- a/postfix/src/smtp/smtp_rcpt.c
+++ b/postfix/src/smtp/smtp_rcpt.c
@@ -179,7 +179,8 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats, rcpt,
- session->namaddrport, DSN_FROM_DSN_BUF(why));
+ session->namaddrport, state->tls_stats,
+ DSN_FROM_DSN_BUF(why));
if (status == 0)
if (request->flags & DEL_REQ_FLAG_SUCCESS)
deliver_completed(state->src, rcpt->offset);
diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c
index 30c3bbbf6..8d61564e5 100644
--- a/postfix/src/smtp/smtp_state.c
+++ b/postfix/src/smtp/smtp_state.c
@@ -85,6 +85,10 @@ SMTP_STATE *smtp_state_alloc(void)
#endif
#ifdef USE_TLS
state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE;
+ if (var_log_tls_feature_status)
+ state->tls_stats = tls_stats_create();
+ else
+ state->tls_stats = 0;
#endif
if (var_smtp_cache_conn) {
state->dest_label = vstring_alloc(10);
@@ -112,6 +116,8 @@ void smtp_state_free(SMTP_STATE *state)
#ifdef USE_TLS
/* The TLS policy cache lifetime is one delivery. */
smtp_tls_policy_cache_flush();
+ if (state->tls_stats)
+ tls_stats_free(state->tls_stats);
#endif
vstring_free(state->iterator->request_nexthop);
vstring_free(state->iterator->dest);
diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c
index dd87f2979..9c182790e 100644
--- a/postfix/src/smtp/smtp_trouble.c
+++ b/postfix/src/smtp/smtp_trouble.c
@@ -285,7 +285,8 @@ static int smtp_bulk_fail(SMTP_STATE *state, int flags)
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
&request->msg_stats, rcpt,
- session ? session->namaddrport : "none", &why->dsn);
+ session ? session->namaddrport : "none", state->tls_stats,
+ &why->dsn);
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
@@ -433,7 +434,8 @@ void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name,
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
&request->msg_stats, rcpt,
- session ? session->namaddrport : "none", &why->dsn);
+ session ? session->namaddrport : "none", state->tls_stats,
+ &why->dsn);
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
diff --git a/postfix/src/virtual/virtual.h b/postfix/src/virtual/virtual.h
index 75dd6cd2e..cde8035d5 100644
--- a/postfix/src/virtual/virtual.h
+++ b/postfix/src/virtual/virtual.h
@@ -102,10 +102,10 @@ typedef struct LOCAL_STATE {
#define BOUNCE_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(attr.why)
+ NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why)
#define SENT_ATTR(attr) \
attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \
- DSN_FROM_DSN_BUF(attr.why)
+ NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why)
#define COPY_ATTR(attr) \
attr.sender, attr.rcpt.orig_addr, attr.delivered, attr.fp