From: Wietse Venema Date: Sat, 15 Jun 2019 05:00:00 +0000 (-0500) Subject: postfix-3.5-20190615 X-Git-Tag: v3.5.0~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2ff7c91a461a093ff7a04a2ea9d1d3c0f1376243;p=thirdparty%2Fpostfix.git postfix-3.5-20190615 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 53b78a05a..9131c1f7c 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -34,6 +34,7 @@ -TBOUNCE_TEMPLATES -TBOUNCE_TIME_DIVISOR -TBOUNCE_TIME_PARAMETER +-TBYTE_MASK -TCFG_PARSER -TCIDR_MATCH -TCLEANUP_REGION @@ -321,6 +322,7 @@ -TSMTPD_TOKEN -TSMTPD_XFORWARD_ATTR -TSMTP_ADDR +-TSMTP_CLI_ATTR -TSMTP_CMD -TSMTP_ITERATOR -TSMTP_RESP diff --git a/postfix/HISTORY b/postfix/HISTORY index 90995d74a..9cfd7fc08 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -24286,3 +24286,31 @@ Apologies for any names omitted. "check_ccert_access { inline:{a=b} { search_order=c,d } }". Fixed by adding another level of recursion. File: postconf/postconf_dbms.c. + +20190525 + + Infrastructure: reject_deliver_request() to reject an entire + delivery request and bounce or defer all its recipients. + File: global/reject_deliver_request.c. + +20190609 + + Infrastructure: byte_mask() to convert "flags=mumble" into + a byte mask. This is similar to name_mask(). Files: + util/byte_mask.[hc] and tests. + +20190615 + + Feature: SMTP/LMTP client support for 'D', 'O', 'R', 'X' + flags similar to the pipe(8) daemon, to produce Delivered-To, + X-Original-To, and Return-Path headers, and to indicate + final delivery. Files: smtp/smtp.c, smtp/smtp.h, smtp/smtp_misc.c, + smtp/smtp_proto.c, smtp/smtp_rcpt.c. + + Cleanup: don't wait for the TLS peer to respond after sending + a TLS 'close' notification. This should be safe with TLSv1.2 + and later. Specify "tls_fast_shutdown_enable = no" to enable + historical behavior where Postfix waits, and then sends a + second TLS 'close' notification before closing the TCP + connection. Files: global/mail_params.h, tls/tls_session.c, + and documentation. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 8dded9175..8a7ebaf40 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -25,6 +25,31 @@ more recent Eclipse Public License 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. +Incompatibility with snapshot 20190615 +==================================== + +The Postfix TLS library by default no longer waits after sending a +TLS 'close' notification. This should be safe with TLSv1.2 and +later. Specify "tls_fast_shutdown_enable = no" to enable historical +Postfix behavior. + +Major changes with snapshot 20190615 +==================================== + +After sending a TLS 'close' notification, the Postfix library by +default no longer waits for the TLS peer to respond. According to +RFC 2246 (TLSv1.2) section 7.2.1, "It is not required for the +initiator of the close to wait for the responding close_notify alert +before closing the read side of the connection." + +The SMTP+LMTP delivery agent can now prepend Delivered-To, X-Original-To +and Return-Path headers, just like the pipe(8) delivery agent. This +uses the same "flags=DOR" command-line flags in master.cf. See the +smtp(8) manpage for details. + +This obsoletes the "lmtp_assume_final = yes" setting, and replaces +it with "flags=...X...", for consistency with pipe(8). + Major changes with snapshot 20190517 ==================================== @@ -54,8 +79,9 @@ exact same result: search_order = cert_fingerprint, pubkey_fingerprint } } ... -The check_ccert_access search order also supports the subject and -issuer properties. Support is planned for rfc822name and smtputf8mailbox. +The check_ccert_access search order also supports the subject_cn and +issuer_cn properties. Support is planned for rfc822name and +smtputf8mailbox. Incompatibility with snapshot 20190427 ====================================== diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 35ab5c714..ce9c9bea3 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -1,5 +1,13 @@ Wish list: + transport policy protocol (clone of check_policy). + + See also postscreen even-driven client for policy delegation + below. + + Replace ad-hoc code for pipe(8) flags handling, with + infrastructure that was built for smtp(8). + Things to do before the stable release: Spell-check, double-word check, HTML validator check, diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 7af8bdeea..9247ef7be 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -249,7 +249,7 @@ unknown_local_recipient_reject_code = 550 # # By default (mynetworks_style = subnet), Postfix "trusts" SMTP # clients in the same IP subnetworks as the local machine. -# On Linux, this does works correctly only with interfaces specified +# On Linux, this works correctly only with interfaces specified # with the "ifconfig" command. # # Specify "mynetworks_style = class" when Postfix should "trust" SMTP diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index e02d89807..175b10394 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -10,7 +10,7 @@ SMTP(8) SMTP(8) smtp - Postfix SMTP+LMTP client SYNOPSIS - smtp [generic Postfix daemon options] + smtp [generic Postfix daemon options] [flags=DORX] DESCRIPTION The Postfix SMTP+LMTP client implements the SMTP and LMTP mail delivery @@ -84,10 +84,58 @@ SMTP(8) SMTP(8) will be used. An IPv6 address must be formatted as [ipv6:address]. +SINGLE-RECIPIENT DELIVERY + By default, the Postfix SMTP+LMTP client delivers mail to multiple + recipients per delivery request. This is undesirable when prepending a + Delivered-to: or X-Original-To: message header. To prevent Postfix from + sending multiple recipients per delivery request, specify + + transport_destination_recipient_limit = 1 + + in the Postfix main.cf file, where transport is the name in the first + column of the Postfix master.cf entry for the pipe-based delivery + transport. + +COMMAND ATTRIBUTE SYNTAX + flags=DORX (optional) + Optional message processing flags. + + D Prepend a "Delivered-To: recipient" message header with + the envelope recipient address. Note: for this to work, + the transport_destination_recipient_limit must be 1 (see + SINGLE-RECIPIENT DELIVERY above for details). + + The D flag also enforces loop detection: if a message + already contains a Delivered-To: header with the same + recipient address, then the message is returned as unde- + liverable. The address comparison is case insensitive. + + This feature is available as of Postfix 3.5. + + O Prepend an "X-Original-To: recipient" message header with + the recipient address as given to Postfix. Note: for this + to work, the transport_destination_recipient_limit must + be 1 (see SINGLE-RECIPIENT DELIVERY above for details). + + This feature is available as of Postfix 3.5. + + R Prepend a "Return-Path: <sender>" message header with the + envelope sender address. + + This feature is available as of Postfix 3.5. + + X Indicates that the delivery is final. This flag affects + the status reported in "success" DSN (delivery status + notification) messages, and changes it from "relayed" + into "delivered". + + This feature is available as of Postfix 3.5. + SECURITY - The SMTP+LMTP client is moderately security-sensitive. It talks to SMTP - or LMTP servers and to DNS servers on the network. The SMTP+LMTP client - can be run chrooted at fixed low privilege. + The SMTP+LMTP client is moderately security-sensitive. It + talks to SMTP or LMTP servers and to DNS servers on the + network. The SMTP+LMTP client can be run chrooted at fixed + low privilege. STANDARDS RFC 821 (SMTP protocol) @@ -120,13 +168,11 @@ SMTP(8) SMTP(8) ter is notified of bounces, protocol problems, and of other trouble. BUGS - SMTP and LMTP connection caching does not work with TLS. The necessary - support for TLS object passivation and re-activation does not exist - without closing the session, which defeats the purpose. + SMTP and LMTP connection reuse for TLS (without closing the SMTP or + LMTP connection) is not supported before Postfix 3.4. - SMTP and LMTP connection caching assumes that SASL credentials are - valid for all destinations that map onto the same IP address and TCP - port. + SMTP and LMTP connection reuse assumes that SASL credentials are valid + for all destinations that map onto the same IP address and TCP port. CONFIGURATION PARAMETERS Before Postfix version 2.3, the LMTP client is a separate program that @@ -595,6 +641,13 @@ SMTP(8) SMTP(8) Optional name to send to the remote SMTP server in the TLS Server Name Indication (SNI) extension. + Available in Postfix version 3.5 and later: + + tls_fast_shutdown_enable (yes) + After sending a TLS 'close' notification, do not wait for the + TLS peer to respond, and do not send a second TLS 'close' noti- + fication. + OBSOLETE STARTTLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 33840234a..7d5c900cc 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -14200,11 +14200,12 @@ Postfix version 2.5). This feature is available with Postfix version order (Postfix 3.5 and later). The default search order as described above corresponds with: -
check_ccert_access { type:table { search_order = cert_fingerprint, +
check_ccert_access { type:table, { search_order = cert_fingerprint, pubkey_fingerprint } }
-
Other valid search_order elements are "subject" (the certificate -subject DN) and "issuer" (the certificate issuer DN).
+
The commas are optional. Other valid search_order elements are +"subject_cn" (the certificate subject CN) and "issuer_cn" (the +certificate issuer CN).
check_client_access type:table
@@ -18542,6 +18543,21 @@ encouraged to not change this setting.

This feature is available in Postfix 2.3 and later.

+ + +
tls_fast_shutdown_enable +(default: yes)
+ +

After sending a TLS 'close' notification, do not wait for the +TLS peer to respond, and do not send a second TLS 'close' notification. +According to RFC 2246 (TLSv1.2) section 7.2.1, "It is not required +for the initiator of the close to wait for the responding close_notify +alert before closing the read side of the connection."

+ +

Specify "tls_fast_shutdown_enable = no" to enable historical +Postfix behavior.

+ +
tls_high_cipherlist diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index e02d89807..175b10394 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -10,7 +10,7 @@ SMTP(8) SMTP(8) smtp - Postfix SMTP+LMTP client SYNOPSIS - smtp [generic Postfix daemon options] + smtp [generic Postfix daemon options] [flags=DORX] DESCRIPTION The Postfix SMTP+LMTP client implements the SMTP and LMTP mail delivery @@ -84,10 +84,58 @@ SMTP(8) SMTP(8) will be used. An IPv6 address must be formatted as [ipv6:address]. +SINGLE-RECIPIENT DELIVERY + By default, the Postfix SMTP+LMTP client delivers mail to multiple + recipients per delivery request. This is undesirable when prepending a + Delivered-to: or X-Original-To: message header. To prevent Postfix from + sending multiple recipients per delivery request, specify + + transport_destination_recipient_limit = 1 + + in the Postfix main.cf file, where transport is the name in the first + column of the Postfix master.cf entry for the pipe-based delivery + transport. + +COMMAND ATTRIBUTE SYNTAX + flags=DORX (optional) + Optional message processing flags. + + D Prepend a "Delivered-To: recipient" message header with + the envelope recipient address. Note: for this to work, + the transport_destination_recipient_limit must be 1 (see + SINGLE-RECIPIENT DELIVERY above for details). + + The D flag also enforces loop detection: if a message + already contains a Delivered-To: header with the same + recipient address, then the message is returned as unde- + liverable. The address comparison is case insensitive. + + This feature is available as of Postfix 3.5. + + O Prepend an "X-Original-To: recipient" message header with + the recipient address as given to Postfix. Note: for this + to work, the transport_destination_recipient_limit must + be 1 (see SINGLE-RECIPIENT DELIVERY above for details). + + This feature is available as of Postfix 3.5. + + R Prepend a "Return-Path: <sender>" message header with the + envelope sender address. + + This feature is available as of Postfix 3.5. + + X Indicates that the delivery is final. This flag affects + the status reported in "success" DSN (delivery status + notification) messages, and changes it from "relayed" + into "delivered". + + This feature is available as of Postfix 3.5. + SECURITY - The SMTP+LMTP client is moderately security-sensitive. It talks to SMTP - or LMTP servers and to DNS servers on the network. The SMTP+LMTP client - can be run chrooted at fixed low privilege. + The SMTP+LMTP client is moderately security-sensitive. It + talks to SMTP or LMTP servers and to DNS servers on the + network. The SMTP+LMTP client can be run chrooted at fixed + low privilege. STANDARDS RFC 821 (SMTP protocol) @@ -120,13 +168,11 @@ SMTP(8) SMTP(8) ter is notified of bounces, protocol problems, and of other trouble. BUGS - SMTP and LMTP connection caching does not work with TLS. The necessary - support for TLS object passivation and re-activation does not exist - without closing the session, which defeats the purpose. + SMTP and LMTP connection reuse for TLS (without closing the SMTP or + LMTP connection) is not supported before Postfix 3.4. - SMTP and LMTP connection caching assumes that SASL credentials are - valid for all destinations that map onto the same IP address and TCP - port. + SMTP and LMTP connection reuse assumes that SASL credentials are valid + for all destinations that map onto the same IP address and TCP port. CONFIGURATION PARAMETERS Before Postfix version 2.3, the LMTP client is a separate program that @@ -595,6 +641,13 @@ SMTP(8) SMTP(8) Optional name to send to the remote SMTP server in the TLS Server Name Indication (SNI) extension. + Available in Postfix version 3.5 and later: + + tls_fast_shutdown_enable (yes) + After sending a TLS 'close' notification, do not wait for the + TLS peer to respond, and do not send a second TLS 'close' noti- + fication. + OBSOLETE STARTTLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 191fe63b6..43e98f02a 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -589,6 +589,13 @@ SMTPD(8) SMTPD(8) clients via the TLS Server Name Indication (SNI) extension to the appropriate keys and certificate chains. + Available in Postfix version 3.5 and later: + + tls_fast_shutdown_enable (yes) + After sending a TLS 'close' notification, do not wait for the + TLS peer to respond, and do not send a second TLS 'close' noti- + fication. + OBSOLETE STARTTLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff --git a/postfix/html/tlsproxy.8.html b/postfix/html/tlsproxy.8.html index 25016ce57..49fee4bd7 100644 --- a/postfix/html/tlsproxy.8.html +++ b/postfix/html/tlsproxy.8.html @@ -144,6 +144,13 @@ TLSPROXY(8) TLSPROXY(8) clients via the TLS Server Name Indication (SNI) extension to the appropriate keys and certificate chains. + Available in Postfix version 3.5 and later: + + tls_fast_shutdown_enable (yes) + After sending a TLS 'close' notification, do not wait for the + TLS peer to respond, and do not send a second TLS 'close' noti- + fication. + STARTTLS SERVER CONTROLS These settings are clones of Postfix SMTP server settings. They allow tlsproxy(8) to load the same certificate and private key information as diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 4093ad0b6..a32ec971f 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -12942,6 +12942,15 @@ the default cipherlist for the SMTP server. You are strongly encouraged to not change this setting. .PP This feature is available in Postfix 2.3 and later. +.SH tls_fast_shutdown_enable (default: yes) +After sending a TLS 'close' notification, do not wait for the +TLS peer to respond, and do not send a second TLS 'close' notification. +According to RFC 2246 (TLSv1.2) section 7.2.1, "It is not required +for the initiator of the close to wait for the responding close_notify +alert before closing the read side of the connection." +.PP +Specify "tls_fast_shutdown_enable = no" to enable historical +Postfix behavior. .SH tls_high_cipherlist (default: see "postconf \-d" output) The OpenSSL cipherlist for "high" grade ciphers. This defines the meaning of the "high" setting in smtpd_tls_ciphers, diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index ca81ebc48..2edbe5f0e 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -8,7 +8,7 @@ Postfix SMTP+LMTP client .SH "SYNOPSIS" .na .nf -\fBsmtp\fR [generic Postfix daemon options] +\fBsmtp\fR [generic Postfix daemon options] [flags=DORX] .SH DESCRIPTION .ad .fi @@ -80,12 +80,68 @@ remote host. If no port is specified, connect to the port defined as If no such service is found, the \fBlmtp_tcp_port\fR configuration parameter (default value of 24) will be used. An IPv6 address must be formatted as [\fBipv6\fR:\fIaddress\fR]. -.PP -.SH "SECURITY" +.SH "SINGLE-RECIPIENT DELIVERY" .na .nf .ad .fi +By default, the Postfix SMTP+LMTP client delivers mail to +multiple recipients per delivery request. This is undesirable +when prepending a \fBDelivered\-to:\fR or \fBX\-Original\-To:\fR +message header. To prevent Postfix from sending multiple +recipients per delivery request, specify +.sp +.nf + \fItransport\fB_destination_recipient_limit = 1\fR +.fi + +in the Postfix \fBmain.cf\fR file, where \fItransport\fR +is the name in the first column of the Postfix \fBmaster.cf\fR +entry for the pipe\-based delivery transport. +.SH "COMMAND ATTRIBUTE SYNTAX" +.na +.nf +.ad +.fi +.IP "\fBflags=DORX\fR (optional)" +Optional message processing flags. +.RS +.IP \fBD\fR +Prepend a "\fBDelivered\-To: \fIrecipient\fR" message header +with the envelope recipient address. Note: for this to work, +the \fItransport\fB_destination_recipient_limit\fR must be +1 (see SINGLE\-RECIPIENT DELIVERY above for details). +.sp +The \fBD\fR flag also enforces loop detection: if a message +already contains a \fBDelivered\-To:\fR header with the same +recipient address, then the message is returned as +undeliverable. The address comparison is case insensitive. +.sp +This feature is available as of Postfix 3.5. +.IP \fBO\fR +Prepend an "\fBX\-Original\-To: \fIrecipient\fR" message +header with the recipient address as given to Postfix. Note: +for this to work, the +\fItransport\fB_destination_recipient_limit\fR must be 1 +(see SINGLE\-RECIPIENT DELIVERY above for details). +.sp +This feature is available as of Postfix 3.5. +.IP \fBR\fR +Prepend a "\fBReturn\-Path: <\fIsender\fB>\fR" message header +with the envelope sender address. +.sp +This feature is available as of Postfix 3.5. +.IP \fBX\fR +Indicates that the delivery is final. This flag affects +the status reported in "success" DSN (delivery status +notification) messages, and changes it from "relayed" into +"delivered". +.sp +This feature is available as of Postfix 3.5. +.RE +.SH "SECURITY" +.na +.nf The SMTP+LMTP client is moderately security\-sensitive. It talks to SMTP or LMTP servers and to DNS servers on the network. The SMTP+LMTP client can be run chrooted at fixed @@ -526,6 +582,11 @@ directly followed by a corresponding certificate chain. .IP "\fBsmtp_tls_servername (empty)\fR" Optional name to send to the remote SMTP server in the TLS Server Name Indication (SNI) extension. +.PP +Available in Postfix version 3.5 and later: +.IP "\fBtls_fast_shutdown_enable (yes)\fR" +After sending a TLS 'close' notification, do not wait for the +TLS peer to respond, and do not send a second TLS 'close' notification. .SH "OBSOLETE STARTTLS CONTROLS" .na .nf diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 1ea172fdf..e03a4b056 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -527,6 +527,11 @@ directly followed by a corresponding certificate chain. Optional lookup tables that map names received from remote SMTP clients via the TLS Server Name Indication (SNI) extension to the appropriate keys and certificate chains. +.PP +Available in Postfix version 3.5 and later: +.IP "\fBtls_fast_shutdown_enable (yes)\fR" +After sending a TLS 'close' notification, do not wait for the +TLS peer to respond, and do not send a second TLS 'close' notification. .SH "OBSOLETE STARTTLS CONTROLS" .na .nf diff --git a/postfix/man/man8/tlsproxy.8 b/postfix/man/man8/tlsproxy.8 index 71a3e4e8d..fd46a1c56 100644 --- a/postfix/man/man8/tlsproxy.8 +++ b/postfix/man/man8/tlsproxy.8 @@ -145,6 +145,11 @@ Available in Postfix version 3.4 and later: Optional lookup tables that map names received from remote SMTP clients via the TLS Server Name Indication (SNI) extension to the appropriate keys and certificate chains. +.PP +Available in Postfix version 3.5 and later: +.IP "\fBtls_fast_shutdown_enable (yes)\fR" +After sending a TLS 'close' notification, do not wait for the +TLS peer to respond, and do not send a second TLS 'close' notification. .SH "STARTTLS SERVER CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index e2749fe9b..4d5817d2a 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -765,6 +765,7 @@ while (<>) { s;\btls_ssl_options\b;$&;g; s;\btls_dane_digest_agility\b;$&;g; s;\btls_dane_trust_anchor_digest_enable\b;$&;g; + s;\btls_fast_shutdown_enable\b;$&;g; s;\bfrozen_delivered_to\b;$&;g; s;\breset_owner_alias\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 24291cc4d..df44d0c79 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -5115,11 +5115,12 @@ Postfix version 2.5). This feature is available with Postfix version order (Postfix 3.5 and later). The default search order as described above corresponds with: -
check_ccert_access { type:table { search_order = cert_fingerprint, +
check_ccert_access { type:table, { search_order = cert_fingerprint, pubkey_fingerprint } }
-
Other valid search_order elements are "subject" (the certificate -subject DN) and "issuer" (the certificate issuer DN).
+
The commas are optional. Other valid search_order elements are +"subject_cn" (the certificate subject CN) and "issuer_cn" (the +certificate issuer CN).
check_client_access type:table
@@ -17621,3 +17622,14 @@ default suffix, YYYYMMDD-HHMMSS, allows logs to be rotated frequently.

This feature is available in Postfix 3.4 and later.

+ +%PARAM tls_fast_shutdown_enable yes + +

After sending a TLS 'close' notification, do not wait for the +TLS peer to respond, and do not send a second TLS 'close' notification. +According to RFC 2246 (TLSv1.2) section 7.2.1, "It is not required +for the initiator of the close to wait for the responding close_notify +alert before closing the read side of the connection."

+ +

Specify "tls_fast_shutdown_enable = no" to enable historical +Postfix behavior.

diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 8dc1f696e..6577d287f 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -35,7 +35,7 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ mkmap_fail.c haproxy_srvr.c dsn_filter.c dynamicmaps.c uxtext.c \ smtputf8.c mail_conf_over.c mail_parm_split.c midna_adomain.c \ mail_addr_form.c quote_flags.c maillog_client.c \ - normalize_mailhost_addr.c map_search.c + normalize_mailhost_addr.c map_search.c reject_deliver_request.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 \ @@ -72,7 +72,7 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ mkmap_fail.o haproxy_srvr.o dsn_filter.o dynamicmaps.o uxtext.o \ smtputf8.o attr_override.o mail_parm_split.o midna_adomain.o \ $(NON_PLUGIN_MAP_OBJ) mail_addr_form.o quote_flags.o maillog_client.o \ - normalize_mailhost_addr.o map_search.o + normalize_mailhost_addr.o map_search.o reject_deliver_request.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. @@ -2462,6 +2462,25 @@ record.o: off_cvt.h record.o: rec_type.h record.o: record.c record.o: record.h +reject_deliver_request.o: ../../include/attr.h +reject_deliver_request.o: ../../include/check_arg.h +reject_deliver_request.o: ../../include/htable.h +reject_deliver_request.o: ../../include/msg.h +reject_deliver_request.o: ../../include/mymalloc.h +reject_deliver_request.o: ../../include/nvtable.h +reject_deliver_request.o: ../../include/sys_defs.h +reject_deliver_request.o: ../../include/vbuf.h +reject_deliver_request.o: ../../include/vstream.h +reject_deliver_request.o: ../../include/vstring.h +reject_deliver_request.o: bounce.h +reject_deliver_request.o: defer.h +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: msg_stats.h +reject_deliver_request.o: recipient_list.h +reject_deliver_request.o: reject_deliver_request.c remove.o: ../../include/check_arg.h remove.o: ../../include/sys_defs.h remove.o: ../../include/vbuf.h diff --git a/postfix/src/global/deliver_request.h b/postfix/src/global/deliver_request.h index 0dd25734e..a00bcf7a2 100644 --- a/postfix/src/global/deliver_request.h +++ b/postfix/src/global/deliver_request.h @@ -11,6 +11,12 @@ /* DESCRIPTION /* .nf + /* + * System library. + */ +#include +#include + /* * Utility library. */ @@ -128,6 +134,9 @@ typedef struct VSTREAM _deliver_vstream_; extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *); extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, int); +extern int PRINTFLIKE(4, 5) reject_deliver_request(const char *, + DELIVER_REQUEST *, const char *, const char *,...); + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 638daa15d..258c5dcad 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3330,6 +3330,13 @@ extern char *var_tls_server_sni_maps; #define DEF_TLS_DANE_DIGESTS "sha512 sha256" extern char *var_tls_dane_digests; + /* + * Backwards compatibility for Postfix 3.5 and later. + */ +#define VAR_TLS_FAST_SHUTDOWN "tls_fast_shutdown" +#define DEF_TLS_FAST_SHUTDOWN 1 +extern bool var_tls_fast_shutdown; + /* * Sendmail-style mail filter support. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 713ba8ec8..adfb936e0 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 "20190518" +#define MAIL_RELEASE_DATE "20190615" #define MAIL_VERSION_NUMBER "3.5" #ifdef SNAPSHOT diff --git a/postfix/src/global/reject_deliver_request.c b/postfix/src/global/reject_deliver_request.c new file mode 100644 index 000000000..1b1b2a57a --- /dev/null +++ b/postfix/src/global/reject_deliver_request.c @@ -0,0 +1,105 @@ +/*++ +/* NAME +/* reject_deliver_request 3 +/* SUMMARY +/* reject an entire delivery request +/* SYNOPSIS +/* #include +/* +/* int reject_deliver_request( +/* const char *service, +/* DELIVER_REQUEST *request, +/* const char *code, +/* const char *format, ...); +/* DESCRIPTION +/* reject_deliver_request() rejects an entire delivery request +/* and bounces or defers all its recipients. The result value +/* is the request's delivery status. +/* +/* Arguments: +/* .IP service +/* The service name from master.cf. +/* .IP request +/* The delivery request that is being rejected. +/* .IP code +/* Enhanced status code, must be in 4.X.X or 5.X.X. form. +/* All recipients in the request are bounced or deferred +/* depending on the status code value. +/* .IP "format, ..." +/* Format string and optional arguments. +/* DIAGNOSTICS +/* Panic: interface violation. Fatal: out of memory. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include +#include +#include +#include +#include + +/* reject_deliver_request - reject an entire delivery request */ + +int reject_deliver_request(const char *service, DELIVER_REQUEST *request, + const char *code, + const char *format,...) +{ + const char myname[] = "reject_deliver_request"; + va_list ap; + RECIPIENT *rcpt; + DSN_BUF *why; + int status; + int result = 0; + int n; + + /* + * Format something that we can pass to bounce_append() or + * defer_append(). + */ + va_start(ap, format); + why = vdsb_simple(dsb_create(), code, format, ap); + va_end(ap); + (void) DSN_FROM_DSN_BUF(why); + if (strchr("45", vstring_str(why->status)[0]) == 0) + msg_panic("%s: bad enhanced status code %s", myname, code); + + /* + * Blindly bounce or defer all recipients. + */ + for (n = 0; n < request->rcpt_list.len; n++) { + rcpt = request->rcpt_list.info + n; + status = (vstring_str(why->status)[0] != '4' ? + bounce_append : defer_append) + (DEL_REQ_TRACE_FLAGS(request->flags), + request->queue_id, + &request->msg_stats, rcpt, + service, &why->dsn); + if (status == 0) + deliver_completed(request->fp, rcpt->offset); + result |= status; + } + dsb_free(why); + return (result); +} diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 7d4556496..140192f89 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -2,11 +2,11 @@ SHELL = /bin/sh SRCS = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \ smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \ smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \ - smtp_sasl_auth_cache.c smtp_key.c + smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c OBJS = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \ smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \ smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \ - smtp_sasl_auth_cache.o smtp_key.o + smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o HDRS = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) @@ -89,6 +89,7 @@ depend: $(MAKES) lmtp_params.o: lmtp_params.c smtp.o: ../../include/argv.h smtp.o: ../../include/attr.h +smtp.o: ../../include/byte_mask.h smtp.o: ../../include/check_arg.h smtp.o: ../../include/debug_peer.h smtp.o: ../../include/deliver_request.h @@ -358,6 +359,46 @@ smtp_map11.o: ../../include/vstream.h smtp_map11.o: ../../include/vstring.h smtp_map11.o: smtp.h smtp_map11.o: smtp_map11.c +smtp_misc.o: ../../include/argv.h +smtp_misc.o: ../../include/attr.h +smtp_misc.o: ../../include/check_arg.h +smtp_misc.o: ../../include/deliver_request.h +smtp_misc.o: ../../include/dict.h +smtp_misc.o: ../../include/dns.h +smtp_misc.o: ../../include/dsn.h +smtp_misc.o: ../../include/dsn_buf.h +smtp_misc.o: ../../include/ext_prop.h +smtp_misc.o: ../../include/header_body_checks.h +smtp_misc.o: ../../include/header_opts.h +smtp_misc.o: ../../include/htable.h +smtp_misc.o: ../../include/mail_params.h +smtp_misc.o: ../../include/maps.h +smtp_misc.o: ../../include/match_list.h +smtp_misc.o: ../../include/mime_state.h +smtp_misc.o: ../../include/msg_stats.h +smtp_misc.o: ../../include/myaddrinfo.h +smtp_misc.o: ../../include/myflock.h +smtp_misc.o: ../../include/mymalloc.h +smtp_misc.o: ../../include/name_code.h +smtp_misc.o: ../../include/name_mask.h +smtp_misc.o: ../../include/nvtable.h +smtp_misc.o: ../../include/quote_821_local.h +smtp_misc.o: ../../include/quote_822_local.h +smtp_misc.o: ../../include/quote_flags.h +smtp_misc.o: ../../include/recipient_list.h +smtp_misc.o: ../../include/resolve_clnt.h +smtp_misc.o: ../../include/scache.h +smtp_misc.o: ../../include/sock_addr.h +smtp_misc.o: ../../include/string_list.h +smtp_misc.o: ../../include/sys_defs.h +smtp_misc.o: ../../include/tls.h +smtp_misc.o: ../../include/tls_proxy.h +smtp_misc.o: ../../include/tok822.h +smtp_misc.o: ../../include/vbuf.h +smtp_misc.o: ../../include/vstream.h +smtp_misc.o: ../../include/vstring.h +smtp_misc.o: smtp.h +smtp_misc.o: smtp_misc.c smtp_params.o: smtp_params.c smtp_proto.o: ../../include/argv.h smtp_proto.o: ../../include/attr.h @@ -397,7 +438,6 @@ smtp_proto.o: ../../include/name_code.h smtp_proto.o: ../../include/name_mask.h smtp_proto.o: ../../include/nvtable.h smtp_proto.o: ../../include/off_cvt.h -smtp_proto.o: ../../include/quote_821_local.h smtp_proto.o: ../../include/quote_822_local.h smtp_proto.o: ../../include/quote_flags.h smtp_proto.o: ../../include/rec_type.h diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 4e50699f8..2d5757875 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -4,7 +4,7 @@ /* SUMMARY /* Postfix SMTP+LMTP client /* SYNOPSIS -/* \fBsmtp\fR [generic Postfix daemon options] +/* \fBsmtp\fR [generic Postfix daemon options] [flags=DORX] /* DESCRIPTION /* The Postfix SMTP+LMTP client implements the SMTP and LMTP mail /* delivery protocols. It processes message delivery requests from @@ -70,10 +70,62 @@ /* If no such service is found, the \fBlmtp_tcp_port\fR configuration /* parameter (default value of 24) will be used. /* An IPv6 address must be formatted as [\fBipv6\fR:\fIaddress\fR]. -/* .PP -/* SECURITY +/* SINGLE-RECIPIENT DELIVERY +/* .ad +/* .fi +/* By default, the Postfix SMTP+LMTP client delivers mail to +/* multiple recipients per delivery request. This is undesirable +/* when prepending a \fBDelivered-to:\fR or \fBX-Original-To:\fR +/* message header. To prevent Postfix from sending multiple +/* recipients per delivery request, specify +/* .sp +/* .nf +/* \fItransport\fB_destination_recipient_limit = 1\fR +/* .fi +/* +/* in the Postfix \fBmain.cf\fR file, where \fItransport\fR +/* is the name in the first column of the Postfix \fBmaster.cf\fR +/* entry for the pipe-based delivery transport. +/* COMMAND ATTRIBUTE SYNTAX /* .ad /* .fi +/* .IP "\fBflags=DORX\fR (optional)" +/* Optional message processing flags. +/* .RS +/* .IP \fBD\fR +/* Prepend a "\fBDelivered-To: \fIrecipient\fR" message header +/* with the envelope recipient address. Note: for this to work, +/* the \fItransport\fB_destination_recipient_limit\fR must be +/* 1 (see SINGLE-RECIPIENT DELIVERY above for details). +/* .sp +/* The \fBD\fR flag also enforces loop detection: if a message +/* already contains a \fBDelivered-To:\fR header with the same +/* recipient address, then the message is returned as +/* undeliverable. The address comparison is case insensitive. +/* .sp +/* This feature is available as of Postfix 3.5. +/* .IP \fBO\fR +/* Prepend an "\fBX-Original-To: \fIrecipient\fR" message +/* header with the recipient address as given to Postfix. Note: +/* for this to work, the +/* \fItransport\fB_destination_recipient_limit\fR must be 1 +/* (see SINGLE-RECIPIENT DELIVERY above for details). +/* .sp +/* This feature is available as of Postfix 3.5. +/* .IP \fBR\fR +/* Prepend a "\fBReturn-Path: <\fIsender\fB>\fR" message header +/* with the envelope sender address. +/* .sp +/* This feature is available as of Postfix 3.5. +/* .IP \fBX\fR +/* Indicates that the delivery is final. This flag affects +/* the status reported in "success" DSN (delivery status +/* notification) messages, and changes it from "relayed" into +/* "delivered". +/* .sp +/* This feature is available as of Postfix 3.5. +/* .RE +/* SECURITY /* The SMTP+LMTP client is moderately security-sensitive. It /* talks to SMTP or LMTP servers and to DNS servers on the /* network. The SMTP+LMTP client can be run chrooted at fixed @@ -109,11 +161,10 @@ /* the postmaster is notified of bounces, protocol problems, and of /* other trouble. /* BUGS -/* SMTP and LMTP connection caching does not work with TLS. The necessary -/* support for TLS object passivation and re-activation does not -/* exist without closing the session, which defeats the purpose. +/* SMTP and LMTP connection reuse for TLS (without closing the +/* SMTP or LMTP connection) is not supported before Postfix 3.4. /* -/* SMTP and LMTP connection caching assumes that SASL credentials +/* SMTP and LMTP connection reuse assumes that SASL credentials /* are valid for all destinations that map onto the same IP /* address and TCP port. /* CONFIGURATION PARAMETERS @@ -496,6 +547,11 @@ /* .IP "\fBsmtp_tls_servername (empty)\fR" /* Optional name to send to the remote SMTP server in the TLS Server /* Name Indication (SNI) extension. +/* .PP +/* Available in Postfix version 3.5 and later: +/* .IP "\fBtls_fast_shutdown_enable (yes)\fR" +/* After sending a TLS 'close' notification, do not wait for the +/* TLS peer to respond, and do not send a second TLS 'close' notification. /* OBSOLETE STARTTLS CONTROLS /* .ad /* .fi @@ -829,10 +885,12 @@ #include #include #include +#include /* Global library. */ #include +#include #include #include #include @@ -1000,6 +1058,7 @@ unsigned smtp_dns_res_opt; MAPS *smtp_pix_bug_maps; HBC_CHECKS *smtp_header_checks; /* limited header checks */ HBC_CHECKS *smtp_body_checks; /* limited body checks */ +SMTP_CLI_ATTR smtp_cli_attr; /* parsed command-line */ #ifdef USE_TLS @@ -1016,6 +1075,59 @@ int smtp_tls_insecure_mx_policy; */ static int smtp_addr_pref; +/* get_cli_attr - get command-line attributes */ + +static void get_cli_attr(SMTP_CLI_ATTR *attr, char **argv) +{ + const char myname[] = "get_cli_attr"; + const char *last_flags = "flags="; /* i.e. empty */ + static const BYTE_MASK flags_map[] = { + 'D', SMTP_CLI_FLAG_DELIVERED_TO, + 'O', SMTP_CLI_FLAG_ORIG_RCPT, + 'R', SMTP_CLI_FLAG_RETURN_PATH, + 'X', SMTP_CLI_FLAG_FINAL_DELIVERY, + 0, + }; + + /* + * Initialize. + */ + attr->flags = 0; + + /* + * Iterate over the command-line attribute list. Errors are fatal. + */ + for ( /* void */ ; *argv != 0; argv++) { + + /* + * flags=stuff. Errors are fatal. + */ + if (strncasecmp("flags=", *argv, sizeof("flags=") - 1) == 0) { + last_flags = *argv; + if (msg_verbose) + msg_info("%s: %s", myname, last_flags); + attr->flags = byte_mask(*argv, flags_map, + *argv + sizeof("flags=") - 1); + } + + /* + * Bad. + */ + else + msg_fatal("unknown attribute name: %s", *argv); + } + + /* + * Backwards compatibility, redundancy, and obsolescence. + */ + if (!smtp_mode && var_lmtp_assume_final + && (attr->flags & SMTP_CLI_FLAG_FINAL_DELIVERY) == 0) { + attr->flags |= SMTP_CLI_FLAG_FINAL_DELIVERY; + msg_warn("%s is obsolete; instead, specify \"%sX\" in %s", + VAR_LMTP_ASSUME_FINAL, last_flags, MASTER_CONF_FILE); + } +} + /* deliver_message - deliver message with extreme prejudice */ static int deliver_message(const char *service, DELIVER_REQUEST *request) @@ -1036,6 +1148,55 @@ static int deliver_message(const char *service, DELIVER_REQUEST *request) if (request->rcpt_list.len <= 0) msg_fatal("recipient count: %d", request->rcpt_list.len); + /* + * D flag checks. + */ + if (smtp_cli_attr.flags & SMTP_CLI_FLAG_DELIVERED_TO) { + + /* + * The D flag cannot be specified for multi-recipient deliveries. + */ + if (request->rcpt_list.len > 1) { + msg_warn("flag `D' requires %s_destination_recipient_limit = 1", + service); + return (reject_deliver_request(service, request, "4.3.5", + "mail system configuration error")); + } + + /* + * The recipient cannot appear in a Delivered-To: header. + */ + else { + DELIVERED_HDR_INFO *delivered_info = delivered_hdr_init( + request->fp, request->data_offset, FOLD_ADDR_ALL); + VSTRING *generic_rcpt = vstring_alloc(100); + int have_delivered_loop; + + smtp_rewrite_generic_internal(generic_rcpt, + request->rcpt_list.info->address); + have_delivered_loop = delivered_hdr_find( + delivered_info, STR(generic_rcpt)); + vstring_free(generic_rcpt); + delivered_hdr_free(delivered_info); + if (have_delivered_loop) { + return (reject_deliver_request(service, request, "5.4.6", + "mail forwarding loop for %s", + request->rcpt_list.info->address)); + } + } + } + + /* + * The O flag cannot be specified for multi-recipient deliveries. + */ + if ((smtp_cli_attr.flags & SMTP_CLI_FLAG_ORIG_RCPT) + && request->rcpt_list.len > 1) { + msg_warn("flag `O' requires %s_destination_recipient_limit = 1", + service); + return (reject_deliver_request(service, request, "4.3.5", + "mail system configuration error")); + } + /* * Initialize. Bundle all information about the delivery request, so that * we can produce understandable diagnostics when something goes wrong @@ -1066,17 +1227,12 @@ static int deliver_message(const char *service, DELIVER_REQUEST *request) /* smtp_service - perform service for client */ -static void smtp_service(VSTREAM *client_stream, char *service, char **argv) +static void smtp_service(VSTREAM *client_stream, char *service, + char **unused_argv) { DELIVER_REQUEST *request; int status; - /* - * Sanity check. This service takes no command-line arguments. - */ - if (argv[0]) - msg_fatal("unexpected command-line argument: %s", argv[0]); - /* * This routine runs whenever a client connects to the UNIX-domain socket * dedicated to remote SMTP delivery service. What we see below is a @@ -1093,7 +1249,7 @@ static void smtp_service(VSTREAM *client_stream, char *service, char **argv) /* post_init - post-jail initialization */ -static void post_init(char *unused_name, char **unused_argv) +static void post_init(char *unused_name, char **argv) { static const NAME_MASK lookup_masks[] = { SMTP_HOST_LOOKUP_DNS, SMTP_HOST_FLAG_DNS, @@ -1180,6 +1336,12 @@ static void post_init(char *unused_name, char **unused_argv) * Address verification. */ smtp_vrfy_init(); + + /* + * Look up service command-line attributes; these do not change during + * the process lifetime. + */ + get_cli_attr(&smtp_cli_attr, argv); } /* pre_init - pre-jail initialization */ @@ -1396,6 +1558,7 @@ int main(int argc, char **argv) else if (strcmp(sane_procname, "lmtp") == 0) smtp_mode = 0; else + /* TODO: logging is not initialized. */ msg_fatal("unexpected process name \"%s\" - " "specify \"smtp\" or \"lmtp\"", var_procname); diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index ed3e6a526..281cfe42f 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -700,6 +700,31 @@ extern int smtp_mode; #define VAR_LMTP_SMTP(x) (smtp_mode ? VAR_SMTP_##x : VAR_LMTP_##x) #define LMTP_SMTP_SUFFIX(x) (smtp_mode ? x##_SMTP : x##_LMTP) + /* + * Parsed command-line attributes. These do not change during the process + * lifetime. + */ +typedef struct { + int flags; /* from flags=, see below */ +} SMTP_CLI_ATTR; + +#define SMTP_CLI_FLAG_DELIVERED_TO (1<<0) /* prepend Delivered-To: */ +#define SMTP_CLI_FLAG_ORIG_RCPT (1<<1) /* prepend X-Original-To: */ +#define SMTP_CLI_FLAG_RETURN_PATH (1<<2) /* prepend Return-Path: */ +#define SMTP_CLI_FLAG_FINAL_DELIVERY (1<<3) /* final, not relay */ + +#define SMTP_CLI_MASK_ADD_HEADERS (SMTP_CLI_FLAG_DELIVERED_TO | \ + SMTP_CLI_FLAG_ORIG_RCPT | SMTP_CLI_FLAG_RETURN_PATH) + +extern SMTP_CLI_ATTR smtp_cli_attr; + + /* + * smtp_misc.c. + */ +extern void smtp_rewrite_generic_internal(VSTRING *, const char *); +extern void smtp_quote_822_address_flags(VSTRING *, const char *, int); +extern void smtp_quote_821_address(VSTRING *, const char *); + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/smtp/smtp_misc.c b/postfix/src/smtp/smtp_misc.c new file mode 100644 index 000000000..53b76fd11 --- /dev/null +++ b/postfix/src/smtp/smtp_misc.c @@ -0,0 +1,100 @@ +/*++ +/* NAME +/* smtp_misc 3 +/* SUMMARY +/* SMTP client address rewriting +/* SYNOPSIS +/* #include +/* +/* void smtp_rewrite_generic_internal( +/* VSTRING *dst, +/* const char *src); +/* +/* void smtp_quote_822_address_flags( +/* VSTRING *dst, +/* const char *src, +/* int flags); +/* +/* void smtp_quote_821_address( +/* VSTRING *dst, +/* const char *src); +/* DESCRIPTION +/* smtp_rewrite_generic_internal() rewrites a non-empty address +/* if generic mapping is enabled, otherwise copies it literally. +/* +/* smtp_quote_822_address_flags() is a wrapper around +/* quote_822_local_flags(), except for the empty address which +/* is copied literally. +/* +/* smtp_quote_821_address() is a wrapper around quote_821_local(), +/* except for the empty address or with "smtp_quote_rfc821_envelope +/* = no"; in those cases the addres is copied literally. +/* DIAGNOSTICS +/* Fatal: out of memory. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include +#include +#include +#include + + /* + * Application-specific. + */ +#include + +/* smtp_rewrite_generic_internal - generic non-empty address rewriting */ + +void smtp_rewrite_generic_internal(VSTRING *dst, const char *src) +{ + vstring_strcpy(dst, src); + if (*src && smtp_generic_maps) + smtp_map11_internal(dst, smtp_generic_maps, + smtp_ext_prop_mask & EXT_PROP_GENERIC); +} + +/* smtp_quote_822_address_flags - quote non-empty header address */ + +void smtp_quote_822_address_flags(VSTRING *dst, const char *src, int flags) +{ + if (*src) { + quote_822_local_flags(dst, src, flags); + } else if (flags & QUOTE_FLAG_APPEND) { + vstring_strcat(dst, src); + } else { + vstring_strcpy(dst, src); + } +} + +/* smtp_quote_821_address - quote non-empty envelope address */ + +void smtp_quote_821_address(VSTRING *dst, const char *src) +{ + if (*src && var_smtp_quote_821_env) { + quote_821_local(dst, src); + } else { + vstring_strcpy(dst, src); + } +} diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index a43a326af..a968ff295 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -140,7 +140,6 @@ #include #include #include -#include #include #include #include @@ -1388,6 +1387,64 @@ static void smtp_mime_fail(SMTP_STATE *state, int mime_errs) "%s", detail->text); } +/* smtp_out_raw_or_mime - output buffer, raw output or MIME-aware */ + +static int smtp_out_raw_or_mime(SMTP_STATE *state, VSTRING *buf) +{ + SMTP_SESSION *session = state->session; + int mime_errs; + + if (session->mime_state == 0) { + smtp_text_out((void *) state, REC_TYPE_NORM, vstring_str(buf), + VSTRING_LEN(buf), (off_t) 0); + } else { + mime_errs = + mime_state_update(session->mime_state, REC_TYPE_NORM, + vstring_str(buf), VSTRING_LEN(buf)); + if (mime_errs) { + smtp_mime_fail(state, mime_errs); + return (-1); + } + } + return (0); +} + +/* smtp_out_add_header - format address header, uses session->scratch* */ + +static int smtp_out_add_header(SMTP_STATE *state, const char *label, + const char *lt, const char *addr, + const char *gt) +{ + SMTP_SESSION *session = state->session; + + smtp_rewrite_generic_internal(session->scratch2, addr); + vstring_sprintf(session->scratch, "%s: %s", label, lt); + smtp_quote_822_address_flags(session->scratch, + vstring_str(session->scratch2), + QUOTE_FLAG_DEFAULT | QUOTE_FLAG_APPEND); + vstring_strcat(session->scratch, gt); + return (smtp_out_raw_or_mime(state, session->scratch)); +} + +/* smtp_out_add_headers - output additional headers, uses session->scratch* */ + +static int smtp_out_add_headers(SMTP_STATE *state) +{ + if (smtp_cli_attr.flags & SMTP_CLI_FLAG_DELIVERED_TO) + if (smtp_out_add_header(state, "Delivered-To", "", + state->request->rcpt_list.info->address, "") < 0) + return (-1); + if (smtp_cli_attr.flags & SMTP_CLI_FLAG_ORIG_RCPT) + if (smtp_out_add_header(state, "X-Original-To", "", + state->request->rcpt_list.info->orig_addr, "") < 0) + return (-1); + if (smtp_cli_attr.flags & SMTP_CLI_FLAG_RETURN_PATH) + if (smtp_out_add_header(state, "Return-Path", "<", + state->request->sender, ">") < 0) + return (-1); + return (0); +} + /* smtp_loop - exercise the SMTP protocol engine */ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, @@ -1416,24 +1473,6 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, SMTP_RESP fake; int fail_status; - /* - * Macros for readability. - */ -#define REWRITE_ADDRESS(dst, src) do { \ - vstring_strcpy(dst, src); \ - if (*(src) && smtp_generic_maps) \ - smtp_map11_internal(dst, smtp_generic_maps, \ - smtp_ext_prop_mask & EXT_PROP_GENERIC); \ - } while (0) - -#define QUOTE_ADDRESS(dst, src) do { \ - if (*(src) && var_smtp_quote_821_env) { \ - quote_821_local(dst, src); \ - } else { \ - vstring_strcpy(dst, src); \ - } \ - } while (0) - /* Caution: changes to RETURN() also affect code outside the main loop. */ #define RETURN(x) do { \ @@ -1608,8 +1647,9 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, case SMTP_STATE_MAIL: request->msg_stats.reuse_count = session->reuse_count; GETTIMEOFDAY(&request->msg_stats.conn_setup_done); - REWRITE_ADDRESS(session->scratch2, request->sender); - QUOTE_ADDRESS(session->scratch, vstring_str(session->scratch2)); + smtp_rewrite_generic_internal(session->scratch2, request->sender); + smtp_quote_821_address(session->scratch, + vstring_str(session->scratch2)); vstring_sprintf(next_command, "MAIL FROM:<%s>", vstring_str(session->scratch)); /* XXX Don't announce SIZE if we're going to MIME downgrade. */ @@ -1697,8 +1737,9 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, */ case SMTP_STATE_RCPT: rcpt = request->rcpt_list.info + send_rcpt; - REWRITE_ADDRESS(session->scratch2, rcpt->address); - QUOTE_ADDRESS(session->scratch, vstring_str(session->scratch2)); + smtp_rewrite_generic_internal(session->scratch2, rcpt->address); + smtp_quote_821_address(session->scratch, + vstring_str(session->scratch2)); vstring_sprintf(next_command, "RCPT TO:<%s>", vstring_str(session->scratch)); if (session->features & SMTP_FEATURE_DSN) { @@ -2259,24 +2300,15 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, (void *) state); state->space_left = var_smtp_line_limit; + if ((smtp_cli_attr.flags & SMTP_CLI_MASK_ADD_HEADERS) != 0 + && smtp_out_add_headers(state) < 0) + RETURN(0); + while ((rec_type = rec_get(state->src, session->scratch, 0)) > 0) { if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) break; - if (session->mime_state == 0) { - smtp_text_out((void *) state, rec_type, - vstring_str(session->scratch), - VSTRING_LEN(session->scratch), - (off_t) 0); - } else { - mime_errs = - mime_state_update(session->mime_state, rec_type, - vstring_str(session->scratch), - VSTRING_LEN(session->scratch)); - if (mime_errs) { - smtp_mime_fail(state, mime_errs); - RETURN(0); - } - } + if (smtp_out_raw_or_mime(state, session->scratch) < 0) + RETURN(0); prev_type = rec_type; } diff --git a/postfix/src/smtp/smtp_rcpt.c b/postfix/src/smtp/smtp_rcpt.c index 673f0b4ef..3d00a7cb8 100644 --- a/postfix/src/smtp/smtp_rcpt.c +++ b/postfix/src/smtp/smtp_rcpt.c @@ -160,7 +160,7 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt) */ if ((session->features & SMTP_FEATURE_DSN) == 0 && !smtp_mode - && var_lmtp_assume_final != 0) + && (smtp_cli_attr.flags & SMTP_CLI_FLAG_FINAL_DELIVERY) != 0) dsn_action = "delivered"; /* diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 3eaf905c8..2a74d21e7 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -493,6 +493,11 @@ /* Optional lookup tables that map names received from remote SMTP /* clients via the TLS Server Name Indication (SNI) extension to the /* appropriate keys and certificate chains. +/* .PP +/* Available in Postfix version 3.5 and later: +/* .IP "\fBtls_fast_shutdown_enable (yes)\fR" +/* After sending a TLS 'close' notification, do not wait for the +/* TLS peer to respond, and do not send a second TLS 'close' notification. /* OBSOLETE STARTTLS CONTROLS /* .ad /* .fi diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 401b1d8bc..90860065e 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -521,8 +521,8 @@ static ATTR_OVER_STR str_table[] = { */ #define SMTPD_ACL_SEARCH_NAME_CERT_FPRINT "cert_fingerprint" #define SMTPD_ACL_SEARCH_NAME_PKEY_FPRINT "pubkey_fingerprint" -#define SMTPD_ACL_SEARCH_NAME_CERT_ISSUER "issuer" -#define SMTPD_ACL_SEARCH_NAME_CERT_SUBJECT "subject" +#define SMTPD_ACL_SEARCH_NAME_CERT_ISSUER_CN "issuer_cn" +#define SMTPD_ACL_SEARCH_NAME_CERT_SUBJECT_CN "subject_cn" /* * Search order tokens must be distinct, and 1..126 inclusive, so that they @@ -531,8 +531,8 @@ static ATTR_OVER_STR str_table[] = { */ #define SMTPD_ACL_SEARCH_CODE_CERT_FPRINT 1 #define SMTPD_ACL_SEARCH_CODE_PKEY_FPRINT 2 -#define SMTPD_ACL_SEARCH_CODE_CERT_ISSUER 3 -#define SMTPD_ACL_SEARCH_CODE_CERT_SUBJECT 4 +#define SMTPD_ACL_SEARCH_CODE_CERT_ISSUER_CN 3 +#define SMTPD_ACL_SEARCH_CODE_CERT_SUBJECT_CN 4 /* * Mapping from search-list names and to search-list codes. @@ -540,8 +540,8 @@ static ATTR_OVER_STR str_table[] = { static const NAME_CODE search_actions[] = { SMTPD_ACL_SEARCH_NAME_CERT_FPRINT, SMTPD_ACL_SEARCH_CODE_CERT_FPRINT, SMTPD_ACL_SEARCH_NAME_PKEY_FPRINT, SMTPD_ACL_SEARCH_CODE_PKEY_FPRINT, - SMTPD_ACL_SEARCH_NAME_CERT_ISSUER, SMTPD_ACL_SEARCH_CODE_CERT_ISSUER, - SMTPD_ACL_SEARCH_NAME_CERT_SUBJECT, SMTPD_ACL_SEARCH_CODE_CERT_SUBJECT, + SMTPD_ACL_SEARCH_NAME_CERT_ISSUER_CN, SMTPD_ACL_SEARCH_CODE_CERT_ISSUER_CN, + SMTPD_ACL_SEARCH_NAME_CERT_SUBJECT_CN, SMTPD_ACL_SEARCH_CODE_CERT_SUBJECT_CN, 0, MAP_SEARCH_CODE_UNKNOWN, }; @@ -3168,8 +3168,8 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, if ((search_order = acl->search_order) == 0) search_order = default_search; if (msg_verbose) - msg_info("%s: search_order length=%d", - myname, strlen(search_order)); + msg_info("%s: search_order length=%ld", + myname, (long) strlen(search_order)); /* * When directly checking the fingerprint, it is OK if the issuing CA is @@ -3188,10 +3188,10 @@ static int check_ccert_access(SMTPD_STATE *state, const char *acl_spec, case SMTPD_ACL_SEARCH_CODE_PKEY_FPRINT: match_this = state->tls_context->peer_pkey_fprint; break; - case SMTPD_ACL_SEARCH_CODE_CERT_ISSUER: + case SMTPD_ACL_SEARCH_CODE_CERT_ISSUER_CN: match_this = state->tls_context->issuer_CN; break; - case SMTPD_ACL_SEARCH_CODE_CERT_SUBJECT: + case SMTPD_ACL_SEARCH_CODE_CERT_SUBJECT_CN: match_this = state->tls_context->peer_CN; break; default: diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index dabd6403c..fec7d4542 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -572,6 +572,7 @@ tls_server.o: tls_server.c tls_session.o: ../../include/argv.h tls_session.o: ../../include/check_arg.h tls_session.o: ../../include/dns.h +tls_session.o: ../../include/mail_params.h tls_session.o: ../../include/msg.h tls_session.o: ../../include/myaddrinfo.h tls_session.o: ../../include/mymalloc.h diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index a28623af5..a6f572403 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -289,6 +289,7 @@ char *var_tls_mgr_service; char *var_tls_tkt_cipher; char *var_openssl_path; char *var_tls_server_sni_maps; +bool var_tls_fast_shutdown; static MAPS *tls_server_sni_maps; @@ -625,6 +626,7 @@ void tls_param_init(void) VAR_TLS_BC_PKEY_FPRINT, DEF_TLS_BC_PKEY_FPRINT, &var_tls_bc_pkey_fprint, VAR_TLS_PREEMPT_CLIST, DEF_TLS_PREEMPT_CLIST, &var_tls_preempt_clist, VAR_TLS_MULTI_WILDCARD, DEF_TLS_MULTI_WILDCARD, &var_tls_multi_wildcard, + VAR_TLS_FAST_SHUTDOWN, DEF_TLS_FAST_SHUTDOWN, &var_tls_fast_shutdown, 0, }; static int init_done; diff --git a/postfix/src/tls/tls_session.c b/postfix/src/tls/tls_session.c index 112b89df0..3f6027fc4 100644 --- a/postfix/src/tls/tls_session.c +++ b/postfix/src/tls/tls_session.c @@ -71,6 +71,10 @@ #include #include +/* Global library. */ + +#include + /* TLS library. */ #define TLS_INTERNAL @@ -95,6 +99,18 @@ void tls_session_stop(TLS_APPL_STATE *unused_ctx, VSTREAM *stream, int timeou msg_panic("%s: stream has no active TLS context", myname); /* + * According to RFC 2246 (TLS 1.0), there is no requirement to wait for + * the peer's close-notify. If the application protocol provides + * sufficient session termination signaling, then there's no need to + * duplicate that at the TLS close-notify layer. + * + * https://tools.ietf.org/html/rfc2246#section-7.2.1 + * https://tools.ietf.org/html/rfc4346#section-7.2.1 + * https://tools.ietf.org/html/rfc5246#section-7.2.1 + * + * Specify 'tls_fast_shutdown = no' to enable the historical behavior + * described below. + * * Perform SSL_shutdown() twice, as the first attempt will send out the * shutdown alert but it will not wait for the peer's shutdown alert. * Therefore, when we are the first party to send the alert, we must call @@ -104,7 +120,7 @@ void tls_session_stop(TLS_APPL_STATE *unused_ctx, VSTREAM *stream, int timeou */ if (!failure) { retval = tls_bio_shutdown(vstream_fileno(stream), timeout, TLScontext); - if (retval == 0) + if (!var_tls_fast_shutdown && retval == 0) tls_bio_shutdown(vstream_fileno(stream), timeout, TLScontext); } tls_free_context(TLScontext); diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index 7339da6b9..a9aa29dd0 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -129,6 +129,11 @@ /* Optional lookup tables that map names received from remote SMTP /* clients via the TLS Server Name Indication (SNI) extension to the /* appropriate keys and certificate chains. +/* .PP +/* Available in Postfix version 3.5 and later: +/* .IP "\fBtls_fast_shutdown_enable (yes)\fR" +/* After sending a TLS 'close' notification, do not wait for the +/* TLS peer to respond, and do not send a second TLS 'close' notification. /* STARTTLS SERVER CONTROLS /* .ad /* .fi diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 381bb6e13..6c23678ab 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -41,7 +41,8 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ valid_utf8_hostname.c midna_domain.c argv_splitq.c balpar.c dict_union.c \ extpar.c dict_inline.c casefold.c dict_utf8.c strcasecmp_utf8.c \ split_qnameval.c argv_attr_print.c argv_attr_scan.c dict_file.c \ - msg_logger.c logwriter.c unix_dgram_connect.c unix_dgram_listen.c + msg_logger.c logwriter.c unix_dgram_connect.c unix_dgram_listen.c \ + byte_mask.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -84,7 +85,8 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ valid_utf8_hostname.o midna_domain.o argv_splitq.o balpar.o dict_union.o \ extpar.o dict_inline.o casefold.o dict_utf8.o strcasecmp_utf8.o \ split_qnameval.o argv_attr_print.o argv_attr_scan.o dict_file.o \ - msg_logger.o logwriter.o unix_dgram_connect.o unix_dgram_listen.o + msg_logger.o logwriter.o unix_dgram_connect.o unix_dgram_listen.o \ + byte_mask.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. @@ -114,7 +116,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h \ slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \ valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \ - check_arg.h argv_attr.h msg_logger.h logwriter.h + check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ stream_test.c dup2_pass_on_exec.c DEFS = -I. -D$(SYSTYPE) @@ -134,7 +136,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv \ valid_utf8_string ip_match base32_code msg_rate_delay netstring \ vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \ - vbuf_print split_qnameval vstream msg_logger + vbuf_print split_qnameval vstream msg_logger byte_mask PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) LIB_DIR = ../../lib @@ -351,6 +353,11 @@ name_mask: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o +byte_mask: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + rand_sleep: $(LIB) mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) @@ -552,7 +559,7 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \ miss_endif_pcre_test miss_endif_regexp_test split_qnameval_test \ vstring_test vstream_test dict_pcre_file_test dict_regexp_file_test \ dict_cidr_file_test dict_static_file_test dict_random_test \ - dict_random_file_test dict_inline_file_test + dict_random_file_test dict_inline_file_test byte_mask_tests root_tests: @@ -734,7 +741,7 @@ name_mask_tests: name_mask_test0 name_mask_test1 name_mask_test2 \ name_mask_test7 name_mask_test8 name_mask_test9 name_mask_test0: name_mask name_mask.in name_mask.ref0 - $(SHLIB_ENV) ${VALGRIND} ./name_mask IGNORE IGNORE < name_mask.in > name_mask.tmp 2>&0 + $(SHLIB_ENV) ${VALGRIND} ./name_mask IGNORE IGNORE < name_mask.in > name_mask.tmp 2>&1 diff name_mask.ref0 name_mask.tmp rm -f name_mask.tmp @@ -783,6 +790,29 @@ name_mask_test9: name_mask name_mask.in name_mask.ref9 diff name_mask.ref9 name_mask.tmp rm -f name_mask.tmp +byte_mask_tests: byte_mask_test0 byte_mask_test1 byte_mask_test2 + +byte_mask_test0: byte_mask byte_mask.in byte_mask.ref0 + while read line; do \ + echo "$$line" | $(SHLIB_ENV) ${VALGRIND} ./byte_mask IGNORE IGNORE; \ + done < byte_mask.in > byte_mask.tmp 2>&1 + diff byte_mask.ref0 byte_mask.tmp + rm -f byte_mask.tmp + +byte_mask_test1: byte_mask byte_mask.in byte_mask.ref1 + while read line; do \ + echo "$$line" | $(SHLIB_ENV) ${VALGRIND} ./byte_mask WARN WARN; \ + done < byte_mask.in > byte_mask.tmp 2>&1 + diff byte_mask.ref1 byte_mask.tmp + rm -f byte_mask.tmp + +byte_mask_test2: byte_mask byte_mask.in byte_mask.ref2 + -while read line; do \ + echo "$$line" | $(SHLIB_ENV) ${VALGRIND} ./byte_mask FATAL FATAL; \ + done < byte_mask.in > byte_mask.tmp 2>&1 + diff byte_mask.ref2 byte_mask.tmp + rm -f byte_mask.tmp + base32_code_test: base32_code $(SHLIB_ENV) ${VALGRIND} ./base32_code @@ -1143,6 +1173,15 @@ binhash.o: binhash.h binhash.o: msg.h binhash.o: mymalloc.h binhash.o: sys_defs.h +byte_mask.o: byte_mask.c +byte_mask.o: byte_mask.h +byte_mask.o: check_arg.h +byte_mask.o: msg.h +byte_mask.o: mymalloc.h +byte_mask.o: stringops.h +byte_mask.o: sys_defs.h +byte_mask.o: vbuf.h +byte_mask.o: vstring.h casefold.o: casefold.c casefold.o: check_arg.h casefold.o: msg.h @@ -1910,8 +1949,6 @@ load_file.o: vbuf.h load_file.o: vstream.h load_file.o: warn_stat.h load_lib.o: load_lib.c -load_lib.o: load_lib.h -load_lib.o: msg.h load_lib.o: sys_defs.h logwriter.o: check_arg.h logwriter.o: iostuff.h diff --git a/postfix/src/util/byte_mask.c b/postfix/src/util/byte_mask.c new file mode 100644 index 000000000..37cfd7e11 --- /dev/null +++ b/postfix/src/util/byte_mask.c @@ -0,0 +1,299 @@ +/*++ +/* NAME +/* byte_mask 3 +/* SUMMARY +/* map byte sequence to bit mask +/* SYNOPSIS +/* #include +/* +/* int byte_mask( +/* const char *context, +/* const BYTE_MASK *table, +/* const char *bytes); +/* +/* const char *str_byte_mask( +/* const char *context, +/* const BYTE_MASK *table, +/* int mask); +/* +/* int byte_mask_opt( +/* const char *context; +/* const BYTE_MASK *table, +/* const char *bytes, +/* int flags); +/* +/* const char *str_byte_mask_opt( +/* VSTRING *buf, +/* const char *context, +/* const BYTE_MASK *table, +/* int mask, +/* int flags); +/* DESCRIPTION +/* byte_mask() takes a null-terminated \fItable\fR with (byte +/* value, single-bit mask) pairs and computes the bit-wise OR +/* of the masks that correspond to the byte values pointed to +/* by the \fIbytes\fR argument. +/* +/* str_byte_mask() translates a bit mask into its equivalent +/* bytes. The result is written to a static buffer that is +/* overwritten upon each call. +/* +/* byte_mask_opt() and str_name_mask_opt() are extended versions +/* with additional fine control. +/* +/* Arguments: +/* .IP buf +/* Null pointer or pointer to buffer storage. +/* .IP context +/* What kind of byte values and bit masks are being manipulated, +/* reported in error messages. Typically, this would be the +/* name of a user-configurable parameter or command-line +/* attribute. +/* .IP table +/* Table with (byte value, single-bit mask) pairs. +/* .IP bytes +/* A null-terminated string that is to be converted into a bit +/* mask. +/* .IP mask +/* A bit mask that is to be converted into null-terminated +/* string. +/* .IP flags +/* Bit-wise OR of one or more of the following. Where features +/* would have conflicting results (e.g., FATAL versus IGNORE), +/* the feature that takes precedence is described first. +/* +/* When converting from string to mask, at least one of the +/* following must be specified: BYTE_MASK_FATAL, BYTE_MASK_RETURN, +/* BYTE_MASK_WARN or BYTE_MASK_IGNORE. +/* +/* When converting from mask to string, at least one of the +/* following must be specified: BYTE_MASK_FATAL, BYTE_MASK_RETURN, +/* BYTE_MASK_WARN or BYTE_MASK_IGNORE. +/* .RS +/* .IP BYTE_MASK_FATAL +/* Require that all values in \fIbytes\fR exist in \fItable\fR, +/* and require that all bits listed in \fImask\fR exist in +/* \fItable\fR. Terminate with a fatal run-time error if this +/* condition is not met. This feature is enabled by default +/* when calling byte_mask() or str_name_mask(). +/* .IP BYTE_MASK_RETURN +/* Require that all values in \fIbytes\fR exist in \fItable\fR, +/* and require that all bits listed in \fImask\fR exist in +/* \fItable\fR. Log a warning, and return 0 (byte_mask()) or +/* a null pointer (str_byte_mask()) if this condition is not +/* met. This feature is not enabled by default when calling +/* byte_mask() or str_name_mask(). +/* .IP BYTE_MASK_WARN +/* Require that all values in \fIbytes\fR exist in \fItable\fR, +/* and require that all bits listed in \fImask\fR exist in +/* \fItable\fR. Log a warning if this condition is not met, +/* continue processing, and return all valid bits or bytes. +/* This feature is not enabled by default when calling byte_mask() +/* or str_byte_mask(). +/* .IP BYTE_MASK_IGNORE +/* Silently ignore values in \fIbytes\fR that don't exist in +/* \fItable\fR, and silently ignore bits listed in \fImask\fR +/* that don't exist in \fItable\fR. This feature is not enabled +/* by default when calling byte_mask() or str_byte_mask(). +/* .IP BYTE_MASK_ANY_CASE +/* Enable case-insensitive matching. This feature is not +/* enabled by default when calling byte_mask(); it has no +/* effect with str_byte_mask(). +/* .RE +/* The value BYTE_MASK_NONE explicitly requests no features, +/* and BYTE_MASK_DEFAULT enables the default options. +/* DIAGNOSTICS +/* Fatal: the \fIbytes\fR argument specifies a name not found in +/* \fItable\fR, or the \fImask\fR specifies a bit not found in +/* \fItable\fR. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* HISTORY +/* This code is a clone of Postfix name_mask(3). +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +#define STR(x) vstring_str(x) + +/* byte_mask_opt - compute mask corresponding to byte string */ + +int byte_mask_opt(const char *context, const BYTE_MASK *table, + const char *bytes, int flags) +{ + const char myname[] = "byte_mask"; + const char *bp; + int result = 0; + const BYTE_MASK *np; + + if ((flags & BYTE_MASK_REQUIRED) == 0) + msg_panic("%s: missing BYTE_MASK_FATAL/RETURN/WARN/IGNORE flag", + myname); + + /* + * Iterate over bytes string, and look up each byte in the table. If the + * byte is found, merge its mask with the result. + */ + for (bp = bytes; *bp; bp++) { + int byte_val = *(const unsigned char *) bp; + + for (np = table; /* void */ ; np++) { + if (np->byte_val == 0) { + if (flags & BYTE_MASK_FATAL) { + msg_fatal("unknown %s value \"%c\" in \"%s\"", + context, byte_val, bytes); + } else if (flags & BYTE_MASK_RETURN) { + msg_warn("unknown %s value \"%c\" in \"%s\"", + context, byte_val, bytes); + return (0); + } else if (flags & BYTE_MASK_WARN) { + msg_warn("unknown %s value \"%c\" in \"%s\"", + context, byte_val, bytes); + } + break; + } + if ((flags & BYTE_MASK_ANY_CASE) ? + (TOLOWER(byte_val) == TOLOWER(np->byte_val)) : + (byte_val == np->byte_val)) { + if (msg_verbose) + msg_info("%s: %c", myname, byte_val); + result |= np->mask; + break; + } + } + } + return (result); +} + +/* str_byte_mask_opt - mask to string */ + +const char *str_byte_mask_opt(VSTRING *buf, const char *context, + const BYTE_MASK *table, + int mask, int flags) +{ + const char myname[] = "byte_mask"; + const BYTE_MASK *np; + static VSTRING *my_buf = 0; + + if ((flags & STR_BYTE_MASK_REQUIRED) == 0) + msg_panic("%s: missing BYTE_MASK_FATAL/RETURN/WARN/IGNORE flag", + myname); + + if (buf == 0) { + if (my_buf == 0) + my_buf = vstring_alloc(1); + buf = my_buf; + } + VSTRING_RESET(buf); + + for (np = table; mask != 0; np++) { + if (np->byte_val == 0) { + if (flags & BYTE_MASK_FATAL) { + msg_fatal("%s: unknown %s bit in mask: 0x%x", + myname, context, mask); + } else if (flags & BYTE_MASK_RETURN) { + msg_warn("%s: unknown %s bit in mask: 0x%x", + myname, context, mask); + return (0); + } else if (flags & BYTE_MASK_WARN) { + msg_warn("%s: unknown %s bit in mask: 0x%x", + myname, context, mask); + } + break; + } + if (mask & np->mask) { + mask &= ~np->mask; + vstring_sprintf_append(buf, "%c", np->byte_val); + } + } + VSTRING_TERMINATE(buf); + + return (STR(buf)); +} + +#ifdef TEST + + /* + * Stand-alone test program. + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + static const BYTE_MASK demo_table[] = { + '0', 1 << 0, + '1', 1 << 1, + '2', 1 << 2, + '3', 1 << 3, + 0, 0, + }; + static const NAME_MASK feature_table[] = { + "DEFAULT", BYTE_MASK_DEFAULT, + "FATAL", BYTE_MASK_FATAL, + "ANY_CASE", BYTE_MASK_ANY_CASE, + "RETURN", BYTE_MASK_RETURN, + "WARN", BYTE_MASK_WARN, + "IGNORE", BYTE_MASK_IGNORE, + 0, + }; + int in_feature_mask; + int out_feature_mask; + int demo_mask; + const char *demo_str; + VSTRING *out_buf = vstring_alloc(1); + VSTRING *in_buf = vstring_alloc(1); + + if (argc != 3) + msg_fatal("usage: %s in-feature-mask out-feature-mask", argv[0]); + in_feature_mask = name_mask(argv[1], feature_table, argv[1]); + out_feature_mask = name_mask(argv[2], feature_table, argv[2]); + while (vstring_get_nonl(in_buf, VSTREAM_IN) != VSTREAM_EOF) { + demo_mask = byte_mask_opt("name", demo_table, + STR(in_buf), in_feature_mask); + demo_str = str_byte_mask_opt(out_buf, "mask", demo_table, + demo_mask, out_feature_mask); + vstream_printf("%s -> 0x%x -> %s\n", + STR(in_buf), demo_mask, + demo_str ? demo_str : "(null)"); + demo_mask <<=1; + demo_str = str_byte_mask_opt(out_buf, "mask", demo_table, + demo_mask, out_feature_mask); + vstream_printf("0x%x -> %s\n", + demo_mask, demo_str ? demo_str : "(null)"); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(in_buf); + vstring_free(out_buf); + exit(0); +} + +#endif diff --git a/postfix/src/util/byte_mask.h b/postfix/src/util/byte_mask.h new file mode 100644 index 000000000..6cdea1d12 --- /dev/null +++ b/postfix/src/util/byte_mask.h @@ -0,0 +1,64 @@ +#ifndef _BYTE_MASK_H_INCLUDED_ +#define _BYTE_MASK_H_INCLUDED_ + +/*++ +/* NAME +/* byte_mask 3h +/* SUMMARY +/* map names to bit mask +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +typedef struct { + int byte_val; + int mask; +} BYTE_MASK; + +#define BYTE_MASK_FATAL (1<<0) +#define BYTE_MASK_ANY_CASE (1<<1) +#define BYTE_MASK_RETURN (1<<2) +#define BYTE_MASK_WARN (1<<6) +#define BYTE_MASK_IGNORE (1<<7) + +#define BYTE_MASK_REQUIRED \ + (BYTE_MASK_FATAL | BYTE_MASK_RETURN | BYTE_MASK_WARN | BYTE_MASK_IGNORE) +#define STR_BYTE_MASK_REQUIRED (BYTE_MASK_REQUIRED) + +#define BYTE_MASK_NONE 0 +#define BYTE_MASK_DEFAULT (BYTE_MASK_FATAL) + +#define byte_mask(tag, table, str) \ + byte_mask_opt((tag), (table), (str), BYTE_MASK_DEFAULT) +#define str_byte_mask(tag, table, mask) \ + str_byte_mask_opt(((VSTRING *) 0), (tag), (table), (mask), BYTE_MASK_DEFAULT) + +extern int byte_mask_opt(const char *, const BYTE_MASK *, const char *, int); +extern const char *str_byte_mask_opt(VSTRING *, const char *, const BYTE_MASK *, int, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +#endif diff --git a/postfix/src/util/byte_mask.in b/postfix/src/util/byte_mask.in new file mode 100644 index 000000000..6d89c5ab2 --- /dev/null +++ b/postfix/src/util/byte_mask.in @@ -0,0 +1,7 @@ +0123 +0 +1 +2 +3 +1234 +abcd diff --git a/postfix/src/util/byte_mask.ref0 b/postfix/src/util/byte_mask.ref0 new file mode 100644 index 000000000..a6ab11be9 --- /dev/null +++ b/postfix/src/util/byte_mask.ref0 @@ -0,0 +1,14 @@ +0123 -> 0xf -> 0123 +0x1e -> 123 +0 -> 0x1 -> 0 +0x2 -> 1 +1 -> 0x2 -> 1 +0x4 -> 2 +2 -> 0x4 -> 2 +0x8 -> 3 +3 -> 0x8 -> 3 +0x10 -> +1234 -> 0xe -> 123 +0x1c -> 23 +abcd -> 0x0 -> +0x0 -> diff --git a/postfix/src/util/byte_mask.ref1 b/postfix/src/util/byte_mask.ref1 new file mode 100644 index 000000000..92e3f4f0a --- /dev/null +++ b/postfix/src/util/byte_mask.ref1 @@ -0,0 +1,22 @@ +unknown: warning: byte_mask: unknown mask bit in mask: 0x10 +0123 -> 0xf -> 0123 +0x1e -> 123 +0 -> 0x1 -> 0 +0x2 -> 1 +1 -> 0x2 -> 1 +0x4 -> 2 +2 -> 0x4 -> 2 +0x8 -> 3 +unknown: warning: byte_mask: unknown mask bit in mask: 0x10 +3 -> 0x8 -> 3 +0x10 -> +unknown: warning: unknown name value "4" in "1234" +unknown: warning: byte_mask: unknown mask bit in mask: 0x10 +1234 -> 0xe -> 123 +0x1c -> 23 +unknown: warning: unknown name value "a" in "abcd" +unknown: warning: unknown name value "b" in "abcd" +unknown: warning: unknown name value "c" in "abcd" +unknown: warning: unknown name value "d" in "abcd" +abcd -> 0x0 -> +0x0 -> diff --git a/postfix/src/util/byte_mask.ref2 b/postfix/src/util/byte_mask.ref2 new file mode 100644 index 000000000..631ec533d --- /dev/null +++ b/postfix/src/util/byte_mask.ref2 @@ -0,0 +1,10 @@ +unknown: fatal: byte_mask: unknown mask bit in mask: 0x10 +0 -> 0x1 -> 0 +0x2 -> 1 +1 -> 0x2 -> 1 +0x4 -> 2 +2 -> 0x4 -> 2 +0x8 -> 3 +unknown: fatal: byte_mask: unknown mask bit in mask: 0x10 +unknown: fatal: unknown name value "4" in "1234" +unknown: fatal: unknown name value "a" in "abcd"