]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.10-20241024
authorWietse Z Venema <wietse@porcupine.org>
Thu, 24 Oct 2024 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Mon, 28 Oct 2024 03:35:54 +0000 (14:35 +1100)
30 files changed:
postfix/HISTORY
postfix/README_FILES/TLSRPT_README
postfix/WISHLIST
postfix/conf/master.cf
postfix/html/TLSRPT_README.html
postfix/html/cidr_table.5.html
postfix/html/pcre_table.5.html
postfix/html/pgsql_table.5.html
postfix/html/regexp_table.5.html
postfix/man/man5/cidr_table.5
postfix/man/man5/pcre_table.5
postfix/man/man5/pgsql_table.5
postfix/man/man5/regexp_table.5
postfix/mantools/postlink
postfix/proto/TLSRPT_README.html
postfix/proto/cidr_table
postfix/proto/pcre_table
postfix/proto/pgsql_table
postfix/proto/regexp_table
postfix/proto/stop.spell-cc
postfix/proto/stop.spell-history
postfix/proto/stop.spell-proto-html
postfix/src/global/mail_version.h
postfix/src/smtp/smtp_tls_policy.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/util/Makefile.in
postfix/src/util/extpar.c
postfix/src/util/normalize_ws.c [new file with mode: 0644]
postfix/src/util/stringops.h

index a92edce619bdd3d08ee4b6721ddd8d02b4d3bb70..133488489fefdc2d42c874078120afeef803e317 100644 (file)
@@ -28394,3 +28394,48 @@ Apologies for any names omitted.
        util/stream_recv_fd.c, util/stream_test.c,
        util/unix_dgram_connect.c, util/unix_dgram_listen.c,
        util/vbuf.c.
+
+20241015
+
+       Documentation: updated the TLSRPT_README text and example
+       for section "Delivering TLSRPT summaries via email". File:
+       proto/TLSRPT_README.html.
+
+20241021
+
+       Bugfix (defect introduced: postfix 3.0): the default master.cf
+       syslog_name setting for the relay service did not preserve
+       multi-instance information. File: conf/master.cf.
+
+20241022
+
+       Documentation: updated the TLSRPT_README examples for
+       MTA-STS. File: proto/TLSRPT_README.html.
+
+       Documentation: add explicit guidance to use "postconf -x"
+       when checking an inline pcre or regexp table for unescaped
+       '$' characters. Files: mantools/postlink, proto/pcre_table,
+       proto/regexp_table.
+
+       Documentation: be explicit about when Postfix expands $name
+       in inline pcre, regexp, and cidr lookup tables. Files:
+       proto/cidr_table, proto/pcre_table, proto/regexp_table.
+
+       Safety: replace ASCII control characters that match isspace()
+       with space characters. This prevents line breaks etc. in
+       smtp_tls_policy attribute values that use the long form "{
+       name = value }". This form was introduced with Postfix 3.10
+       TLSRPT support. Files: smtp/smtp_tls_policy.c, util/extpar.c,
+       util/Makefile.in, util/normalize_ws.c, util/stringops.h.
+
+20241023
+
+       Logging: Postfix SMTP server 'reject' logging now shows the
+       sasl_method, sasl_username, and sasl_sender if available.
+       Viktor Dukhovni. Files: smtpd/smtpd_check.c.
+
+20241024
+
+       Documentation: in a pgsql: client configuration, the setting
+       "dbname" is required, but ignored when the setting "hosts"
+       contains an URI with a database name. File: proto/pgsql_table.
index 9b797d2a72532d71ab04c1434634605383752ad8..c843098974cfcd0a08e124855b40d767b634ac68 100644 (file)
@@ -175,37 +175,39 @@ Notes:
 
 D\bDe\bel\bli\biv\bve\ber\bri\bin\bng\bg T\bTL\bLS\bSR\bRP\bPT\bT s\bsu\bum\bmm\bma\bar\bri\bie\bes\bs v\bvi\bia\ba e\bem\bma\bai\bil\bl
 
-RFC 8460 suggests not to enforce strict TLS security when sending daily
-success/failure summaries via email, to avoid delivery delays caused by a
-failure to enforce TLS security. Postfix currently does not have a mechanism to
-disable TLS security enforcement when submitting an email message; this section
-provides a workaround.
-
-By design, TLSRPT is not a real-time notification system; it takes on average
-12 hours before a failure is reported in a daily success/failure summary. If a
-TLS-related delay of a day or more is undesirable, one could set up a transport
-map to make TLS security optional for specific TLSRPT email notification email
-addresses.
+RFC 8460 Section 3 specifies that an MTA must not enforce TLS security when
+sending failure reports via email. However, Postfix currently has no way to
+request that TLS enforcement will be disabled when submitting an email message.
 
-    /etc/postfix/main.cf:
-        transport_maps = hash:/etc/postfix/transport
+Options:
 
-    /etc/postfix/transport:
-        smtp-tls-report@example.com     allow-plaintext:
-        ...
+  * Do nothing. When TLS security enforcement is required, a persistent
+    enforcement failure will delay the delivery of a TLSRPT summary until the
+    problem is addressed, or until the message expires in the mail queue. Keep
+    in mind that TLSRPT is not a real-time monitoring service; it takes on
+    average 12 hours before a failure is reported through TLSRPT.
+
+  * Exclude the sender of TLSRPT summaries from TLS enforcement. Implement the
+    configuration below on outbound MTA instances (replace noreply-smtp-tls-
+    reporting@example.com with your actual report generator's sender address):
+
+    /etc/postfix/main.cf:
+        # Limitation: this setting is overruled with transport_maps.
+        sender_dependent_default_transport_maps = inline:{
+            { noreply-smtp-tls-reporting@example.com = allow-plaintext } }
 
     /etc/postfix/master.cf:
         # service name    type    private unpriv  chroot  wakeup  maxproc
     command
         allow-plaintext   unix    -       -       n       -       -       smtp
-            -o smtp_tls_security_level=may
-            -o smtp_tls_policy_maps=static:may
+            -o { smtp_tls_security_level = may }
+            -o { smtp_tls_policy_maps = static:may }
 
 M\bMT\bTA\bA-\b-S\bST\bTS\bS S\bSu\bup\bpp\bpo\bor\brt\bt v\bvi\bia\ba s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_p\bpo\bol\bli\bic\bcy\by_\b_m\bma\bap\bps\bs
 
-Postfix supports MTA-STS though an smtp_tls_policy_maps policy plugin. Postfix
-3.10 and later expect a policy response with the usual security level and
-matching requirements, plus any applicable name=value attributes described
+Postfix supports MTA-STS though an smtp_tls_policy_maps policy plugin, which
+replies with a TLS security level and optional matching requirements. Postfix
+3.10 and later optionally also accept the name=value attributes described
 below. Specify { name = value } when a value may contain whitespace.
 
     Note 1: Postfix 3.10 and later will accept these attributes in an MTA-STS
@@ -215,8 +217,17 @@ below. Specify { name = value } when a value may contain whitespace.
 
     Note 2: It is an error to specify these attributes for a non-STS policy.
 
-The examples in the table apply to the MTA-STS policy example given in https://
-datatracker.ietf.org/doc/html/rfc8460#section-4.5.
+The examples in the table apply to the MTA-STS policy example given in RFC 8461
+Section 3.2:
+
+    version: STSv1
+    mode: enforce
+    mx: mail.example.com
+    mx: *.example.net
+    mx: backupmx.example.com
+    max_age: 604800
+
+A policy response may contain line breaks.
 
   * policy_type=type
 
@@ -238,7 +249,7 @@ datatracker.ietf.org/doc/html/rfc8460#section-4.5.
 
     Example:
 
-        { policy_string = version: STSv1 } { policy_string = mode: testing }
+        { policy_string = version: STSv1 } { policy_string = mode: enforce }
         ...
 
     This form ignores whitespace after the opening "{", around the "=", and
@@ -251,7 +262,7 @@ datatracker.ietf.org/doc/html/rfc8460#section-4.5.
 
     Example:
 
-        mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
+        mx_host_pattern=mail.example.com mx_host_pattern=*.example.net ...
 
   * policy_failure=type
 
index 506cb7c3ed60eaf2334ec6c355b6c18e13aff088..c84db73bb149187f16834bfa9e680a24915da7b7 100644 (file)
@@ -6,6 +6,19 @@ Wish list:
 
        Disable -DSNAPSHOT and -DNONPROD in makedefs.
 
+       Add a mail_version chek to each pluggable database client.
+
+       Add an option for a built-in JSON generator. This would
+       simplify TLSRPT adoption by eliminating a build-time and
+       run-time dependency on the libtlsrpt client library. Prior
+       art: this approach was previously used to implement Postfix
+       Milter support.
+
+       Make TLSRPT support pluggable (postfix-tlsrpt.so, like
+       postfix-ldap.so, postfix-mysql.so and so on). This avods a
+       hard install-time dependency on sys4 libtlsrpt. The sys4
+       code would still be a build-time dependency.
+
        Add smtp_tlsrpt_allow_list feature (default: static:all) to limit
        the domains for which Postfix generates TLSRPT daily summaries.
 
@@ -13,6 +26,8 @@ Wish list:
 
        Add unit tests for smtp_tlsrpt.c, tlstrpd_wrapper.c, ...
 
+       Add unit test for extpar.c
+
        Add tests for Message-ID extraction in the cleanup daemon.
 
        When debug logging is enabled, dict_db_open() logs a newline
index e5e14e449f74678d21daba1162030c750a582601..17b7551af492888eccac3697d8fe543b8552f574 100644 (file)
@@ -69,7 +69,7 @@ proxymap  unix  -       -       n       -       -       proxymap
 proxywrite unix -       -       n       -       1       proxymap
 smtp      unix  -       -       n       -       -       smtp
 relay     unix  -       -       n       -       -       smtp
-        -o syslog_name=postfix/$service_name
+        -o syslog_name=${multi_instance_name?{$multi_instance_name}:{postfix}}/$service_name
 #       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
 showq     unix  n       -       n       -       -       showq
 error     unix  -       -       n       -       -       error
index 6edf95d500f3953b5ce5077f64bd210e0c18bcd2..a1fc10f249c7c644008cb28bffc417831ee3883a 100644 (file)
@@ -266,44 +266,51 @@ have the details for why TLS authentication failed. </p>
 
 <h2> <a name="delivering"> Delivering TLSRPT summaries via email</a> </h2>
 
-<p> <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> suggests not to enforce strict TLS security when sending
-daily success/failure summaries via email, to avoid delivery delays
-caused by a failure to enforce TLS security. Postfix currently does
-not have a mechanism to disable TLS security enforcement when
-submitting an email message; this section provides a workaround. </p>
-
-<p> By design, TLSRPT is not a real-time notification system; it
-takes on average 12 hours before a failure is reported in a daily
-success/failure summary. If a TLS-related delay of a day or more
-is undesirable, one could set up a transport map to make TLS security
-optional for specific TLSRPT email notification email addresses.
-</p>
+<p> <a href="https://datatracker.ietf.org/doc/html/rfc8460#section-3">RFC
+8460 Section 3</a> specifies that an MTA must not enforce TLS
+security when sending failure reports via email. However, Postfix
+currently has no way to request that TLS enforcement will be disabled
+when submitting an email message. </p>
+
+<p> Options:
+
+<ul>
+
+<li> <p> Do nothing. When TLS security enforcement is required, a
+persistent enforcement failure will delay the delivery of a TLSRPT
+summary until the problem is addressed, or until the message expires
+in the mail queue. Keep in mind that TLSRPT is not a real-time
+monitoring service; it takes on average 12 hours before a failure
+is reported through TLSRPT. </p>
+
+<li> <p> Exclude the sender of TLSRPT summaries from TLS enforcement.
+Implement the configuration below on outbound MTA instances (replace
+noreply-smtp-tls-reporting@example.com with your actual report
+generator's sender address): </p>
 
-<blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#transport_maps">transport_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/transport
-&nbsp
-/etc/postfix/transport:
-    smtp-tls-report@example.com     allow-plaintext:
-    ...
+    # Limitation: this setting is overruled with <a href="postconf.5.html#transport_maps">transport_maps</a>.
+    <a href="postconf.5.html#sender_dependent_default_transport_maps">sender_dependent_default_transport_maps</a> = <a href="DATABASE_README.html#types">inline</a>:{
+        { noreply-smtp-tls-reporting@example.com = allow-plaintext } }
 &nbsp
 /etc/postfix/<a href="master.5.html">master.cf</a>:
     # service name    type    private unpriv  chroot  wakeup  maxproc command
     allow-plaintext   unix    -       -       n       -       -       smtp
-        -o <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>=may
-        -o <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>=<a href="DATABASE_README.html#types">static</a>:may
+        -o { <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may }
+        -o { <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = <a href="DATABASE_README.html#types">static</a>:may }
 </pre>
-</blockquote>
+
+</ul>
 
 <h2> <a name="mta-sts"> MTA-STS Support via smtp_tls_policy_maps
 </a></h2>
 
-<p> Postfix supports MTA-STS though an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>
-policy plugin. Postfix 3.10 and later expect a policy response with
-the usual security level and matching requirements, plus any
-applicable name=value attributes described below. Specify <tt>{
-name = value }</tt> when a value may contain whitespace. </p>
+<p> Postfix supports MTA-STS though an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> policy
+plugin, which replies with a TLS security level and optional matching
+requirements. Postfix 3.10 and later optionally also accept the
+name=value attributes described below. Specify <tt>{ name = value
+}</tt> when a value may contain whitespace. </p>
 
 <blockquote>
 
@@ -319,8 +326,22 @@ policy. </p>
 </blockquote>
 
 <p> The examples in the table apply to the MTA-STS policy example
-given in <a href="https://datatracker.ietf.org/doc/html/rfc8460#section-4.5">https://datatracker.ietf.org/doc/html/rfc8460#section-4.5</a>.
-<p>
+given in <a
+href="https://datatracker.ietf.org/doc/html/rfc8461#section-3.2">
+RFC 8461 Section 3.2</a>: </p>
+
+<blockquote>
+<pre>
+version: STSv1
+mode: enforce
+mx: mail.example.com
+mx: *.example.net
+mx: backupmx.example.com
+max_age: 604800
+</pre>
+</blockquote>
+
+<p> A policy response may contain line breaks. </p>
 
 <ul>
 
@@ -347,7 +368,7 @@ in attribute values. </p>
 
 <blockquote>
 <pre>
-{ policy_string = version: STSv1 } { policy_string = mode: testing } ...
+{ policy_string = version: STSv1 } { policy_string = mode: enforce } ...
 </pre> 
 </blockquote> 
 
@@ -363,7 +384,7 @@ in the MTA-STS policy. </p>
 
 <blockquote>
 <pre>
-mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
+mx_host_pattern=mail.example.com mx_host_pattern=*.example.net ...
 </pre>
 </blockquote>
 </li>
index 94982018c4b88dc22679d9ecb0d88344d48d80c1..b85167e1e268c04f0cf460a808bf46991aaf0b71 100644 (file)
@@ -109,8 +109,9 @@ CIDR_TABLE(5)                                                    CIDR_TABLE(5)
        <a href="master.5.html">master.cf</a>:
            <b>.. -o {</b> <i>parameter</i> <b>= .. <a href="cidr_table.5.html">cidr</a>:{ {</b> <i>rule-1</i> <b>}, {</b> <i>rule-2</i> <b>} .. } .. } ..</b>
 
-       Postfix  ignores  whitespace  after '{' and before '}', and writes each
-       <i>rule</i> as one text line to an in-memory file:
+       Postfix  recursively  expands any <i>$parametername</i> instances in the above
+       parameter value, ignores whitespace  after  '{'  and  before  '}',  and
+       writes each <i>rule</i> as one text line to an in-memory file:
 
        in-memory file:
            rule-1
@@ -119,7 +120,7 @@ CIDR_TABLE(5)                                                    CIDR_TABLE(5)
 
        Postfix parses the result as if it is a file in /etc/postfix.
 
-       Note: if a rule contains <b>$</b>, specify <b>$$</b> to keep Postfix from  trying  to
+       Note:  if  a rule contains <b>$</b>, specify <b>$$</b> to keep Postfix from trying to
        do <i>$name</i> expansion as it evaluates a parameter value.
 
 <b><a name="example_smtpd_access_map">EXAMPLE SMTPD ACCESS MAP</a></b>
index d2da2b2cd7b313b7d40033a660b2f634cffebbfb..304b9b2c924291ec6c1e572e00c96b12e1aa1048 100644 (file)
@@ -181,8 +181,9 @@ PCRE_TABLE(5)                                                    PCRE_TABLE(5)
        <a href="master.5.html">master.cf</a>:
            <b>.. -o {</b> <i>parameter</i> <b>= .. <a href="pcre_table.5.html">pcre</a>:{ {</b> <i>rule-1</i> <b>}, {</b> <i>rule-2</i> <b>} .. } .. } ..</b>
 
-       Postfix  ignores  whitespace  after '{' and before '}', and writes each
-       <i>rule</i> as one text line to an in-memory file:
+       Postfix  recursively  expands any <i>$parametername</i> instances in the above
+       parameter value, ignores whitespace  after  '{'  and  before  '}',  and
+       writes each <i>rule</i> as one text line to an in-memory file:
 
        in-memory file:
            rule-1
@@ -191,12 +192,29 @@ PCRE_TABLE(5)                                                    PCRE_TABLE(5)
 
        Postfix parses the result as if it is a file in /etc/postfix.
 
-       Note: if an inlined rule contains <b>$</b>, specify <b>$$</b> to  keep  Postfix  from
-       trying to do <i>$name</i> expansion as it evaluates a parameter value.
-
-       Note: when using <i>$name</i> inside an inlined pattern, use \Q<i>$name</i>\E to dis-
-       able metacharacters such as '.' in the <i>$name</i> expansion. Otherwise,  the
-       pattern may have unexpected matches.
+<b><a name="inline_specification_caveats">INLINE SPECIFICATION CAVEATS</a></b>
+       <b>o</b>      When   using  <i>$parametername</i>  inside  an  inlined  pattern,  use
+              \Q<i>$parametername</i>\E to disable metacharacters such as '.' in  the
+              <i>$parametername</i>  expansion. Otherwise, the pattern may have unex-
+              pected matches.
+
+       <b>o</b>      When an inlined rule must contain <b>$</b>, specify <b>$$</b> to keep  Postfix
+              from  trying  to  do <i>$name</i> expansion as it evaluates a parameter
+              value. To check an inline configuration, use the  "<b>postconf  -x</b>"
+              option as shown below:
+
+              <b>o</b>      When  a <a href="postconf.5.html">main.cf</a> "<i>parametername =  value</i>" setting contains
+                     an inline <a href="pcre_table.5.html">pcre</a>:  table,  use  the  command  "<b>postconf  -x</b>
+                     <i>parametername</i>".  Verify  that  there  are  no  "undefined
+                     parameter" warnings, and that the output has  the  syntax
+                     that one would use in a non-inlined Postfix <a href="pcre_table.5.html">pcre</a>: file.
+
+              <b>o</b>      When  a <a href="master.5.html">master.cf</a> "<b>-o {</b> <i>parametername = value</i> <b>}</b>" override
+                     contains an inline <a href="pcre_table.5.html">pcre</a>: table, use the command "<b>postconf</b>
+                     <b>-Px  '*/*/</b><i>parametername</i><b>'</b>  ".  Verify  that  there  are no
+                     "undefined parameter" warnings, and that the  output  has
+                     the  syntax  that  one would use in a non-inlined Postfix
+                     <a href="pcre_table.5.html">pcre</a>: file.
 
 <b><a name="example_smtpd_access_map">EXAMPLE SMTPD ACCESS MAP</a></b>
        # Protect your outgoing majordomo exploders
index 14db55e30d36e9867e53fec16c2a430bfc22cb0d..e300bcb8d8cc271c882d33d15bbe13f0b254ee70 100644 (file)
@@ -48,7 +48,7 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
               <b>inet:</b><i>host:port</i>  for  TCP  connections, where the <b>unix:</b> and <b>inet:</b>
               prefixes are accepted and ignored for  backwards  compatibility.
               Examples:
-                  hosts = postgresql://username@example.com/tablename?sslmode=require
+                  hosts = postgresql://username@example.com/<i>databasename</i>?sslmode=require
                   hosts = inet:host1.some.domain inet:host2.some.domain:port
                   hosts = host1.some.domain host2.some.domain:port
                   hosts = unix:/file/name
@@ -71,21 +71,25 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
                   user = someone
                   password = some_password
 
-       <b>dbname</b> The database name on the servers. Example:
+       <b>dbname</b> (required)
+              The database name on the servers. Example:
                   dbname = customer_database
 
+              This setting is required, but ignored when a  postgresql://  URI
+              specifies a database name.
+
        <b>encoding</b>
-              The encoding used by the database client.  The  default  setting
+              The  encoding  used  by the database client. The default setting
               is:
                   encoding = UTF8
 
-              Historically,  the  database client was hard coded to use LATIN1
+              Historically, the database client was hard coded to  use  LATIN1
               in an attempt to disable multibyte character support.
 
               This feature is available in Postfix 3.8 and later.
 
        <b>idle_interval (default: 60)</b>
-              The number of seconds after which an  idle  database  connection
+              The  number  of  seconds after which an idle database connection
               will be closed.
 
               This feature is available in Postfix 3.9 and later.
@@ -96,8 +100,8 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
 
               This feature is available in Postfix 3.9 and later.
 
-       <b>query</b>  The SQL query template used to search the database, where <b>%s</b>  is
-              a  substitute for the address Postfix is trying to resolve, e.g.
+       <b>query</b>  The  SQL query template used to search the database, where <b>%s</b> is
+              a substitute for the address Postfix is trying to resolve,  e.g.
                   query = SELECT replacement FROM aliases WHERE mailbox = '%s'
 
               This parameter supports the following '%' expansions:
@@ -105,48 +109,48 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
               <b>%%</b>     This is replaced by a literal '%' character. (Postfix 2.2
                      and later)
 
-              <b>%s</b>     This  is  replaced by the input key.  SQL quoting is used
-                     to make sure that the input key does not  add  unexpected
+              <b>%s</b>     This is replaced by the input key.  SQL quoting  is  used
+                     to  make  sure that the input key does not add unexpected
                      metacharacters.
 
               <b>%u</b>     When the input key is an address of the form user@domain,
-                     <b>%u</b> is replaced by  the  SQL  quoted  local  part  of  the
-                     address.   Otherwise, <b>%u</b> is replaced by the entire search
-                     string.  If the localpart is empty,  the  query  is  sup-
+                     <b>%u</b>  is  replaced  by  the  SQL  quoted  local part of the
+                     address.  Otherwise, <b>%u</b> is replaced by the entire  search
+                     string.   If  the  localpart  is empty, the query is sup-
                      pressed and returns no results.
 
               <b>%d</b>     When the input key is an address of the form user@domain,
-                     <b>%d</b> is replaced by the  SQL  quoted  domain  part  of  the
-                     address.   Otherwise, the query is suppressed and returns
+                     <b>%d</b>  is  replaced  by  the  SQL  quoted domain part of the
+                     address.  Otherwise, the query is suppressed and  returns
                      no results.
 
               <b>%[SUD]</b> The upper-case equivalents of the above expansions behave
-                     in  the  <b>query</b>  parameter identically to their lower-case
-                     counter-parts.  With  the  <b>result_format</b>  parameter  (see
-                     below),  they expand the input key rather than the result
+                     in the <b>query</b> parameter identically  to  their  lower-case
+                     counter-parts.   With  the  <b>result_format</b>  parameter (see
+                     below), they expand the input key rather than the  result
                      value.
 
-                     The above %S, %U and %D  expansions  are  available  with
+                     The  above  %S,  %U  and %D expansions are available with
                      Postfix 2.2 and later
 
-              <b>%[1-9]</b> The  patterns  %1,  %2, ... %9 are replaced by the corre-
-                     sponding most significant component of  the  input  key's
-                     domain.  If  the input key is <i>user@mail.example.com</i>, then
+              <b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced  by  the  corre-
+                     sponding  most  significant  component of the input key's
+                     domain. If the input key is  <i>user@mail.example.com</i>,  then
                      %1 is <b>com</b>, %2 is <b>example</b> and %3 is <b>mail</b>. If the input key
-                     is  unqualified or does not have enough domain components
-                     to satisfy all the specified patterns, the query is  sup-
+                     is unqualified or does not have enough domain  components
+                     to  satisfy all the specified patterns, the query is sup-
                      pressed and returns no results.
 
-                     The  above %1, ... %9 expansions are available with Post-
+                     The above %1, ... %9 expansions are available with  Post-
                      fix 2.2 and later
 
-              The <b>domain</b> parameter described below limits the  input  keys  to
-              addresses  in  matching  domains.  When  the <b>domain</b> parameter is
+              The  <b>domain</b>  parameter  described below limits the input keys to
+              addresses in matching domains.  When  the  <b>domain</b>  parameter  is
               non-empty, SQL queries for unqualified addresses or addresses in
               non-matching domains are suppressed and return no results.
 
-              The  precedence  of this parameter has changed with Postfix 2.2,
-              in prior releases the precedence was, from  highest  to  lowest,
+              The precedence of this parameter has changed with  Postfix  2.2,
+              in  prior  releases  the precedence was, from highest to lowest,
               <b>select_function</b>, <b>query</b>, <b>select_field</b>, ...
 
               With Postfix 2.2 the <b>query</b> parameter has highest precedence, see
@@ -156,42 +160,42 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
 
        <b>result_format (default: %s</b>)
               Format template applied to result attributes. Most commonly used
-              to  append  (or prepend) text to the result. This parameter sup-
+              to append (or prepend) text to the result. This  parameter  sup-
               ports the following '%' expansions:
 
               <b>%%</b>     This is replaced by a literal '%' character.
 
-              <b>%s</b>     This is replaced by the value of  the  result  attribute.
+              <b>%s</b>     This  is  replaced  by the value of the result attribute.
                      When result is empty it is skipped.
 
               <b>%u</b>     When the result attribute value is an address of the form
-                     user@domain, <b>%u</b> is replaced by  the  local  part  of  the
-                     address.  When  the  result  has an empty localpart it is
+                     user@domain,  <b>%u</b>  is  replaced  by  the local part of the
+                     address. When the result has an  empty  localpart  it  is
                      skipped.
 
-              <b>%d</b>     When a result attribute value is an address of  the  form
-                     user@domain,  <b>%d</b>  is  replaced  by the domain part of the
-                     attribute value. When the result  is  unqualified  it  is
+              <b>%d</b>     When  a  result attribute value is an address of the form
+                     user@domain, <b>%d</b> is replaced by the  domain  part  of  the
+                     attribute  value.  When  the  result is unqualified it is
                      skipped.
 
               <b>%[SUD1-9]</b>
-                     The  upper-case  and decimal digit expansions interpolate
-                     the parts of the input key rather than the result.  Their
-                     behavior  is  identical to that described with <b>query</b>, and
-                     in fact because  the  input  key  is  known  in  advance,
-                     queries  whose  key  does not contain all the information
-                     specified in  the  result  template  are  suppressed  and
+                     The upper-case and decimal digit  expansions  interpolate
+                     the  parts of the input key rather than the result. Their
+                     behavior is identical to that described with  <b>query</b>,  and
+                     in  fact  because  the  input  key  is  known in advance,
+                     queries whose key does not contain  all  the  information
+                     specified  in  the  result  template  are  suppressed and
                      return no results.
 
               For example, using "result_format = <a href="smtp.8.html">smtp</a>:[%s]" allows one to use
               a mailHost attribute as the basis of a <a href="transport.5.html">transport(5)</a> table. After
-              applying  the result format, multiple values are concatenated as
+              applying the result format, multiple values are concatenated  as
               comma  separated  strings.  The  expansion_limit  and  parameter
-              explained  below  allows one to restrict the number of values in
+              explained below allows one to restrict the number of  values  in
               the result, which is especially useful for maps that must return
               at most one value.
 
-              The  default value <b>%s</b> specifies that each result value should be
+              The default value <b>%s</b> specifies that each result value should  be
               used as is.
 
               This parameter is available with Postfix 2.2 and later.
@@ -199,15 +203,15 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
               NOTE: DO NOT put quotes around the result format!
 
        <b>domain (default: no domain list)</b>
-              This is a list of domain names, paths to files, or  "<a href="DATABASE_README.html">type:table</a>"
+              This  is a list of domain names, paths to files, or "<a href="DATABASE_README.html">type:table</a>"
               databases. When specified, only fully qualified search keys with
-              a *non-empty* localpart and a matching domain are  eligible  for
+              a  *non-empty*  localpart and a matching domain are eligible for
               lookup:  'user'  lookups,  bare  domain  lookups  and  "@domain"
-              lookups are not performed. This  can  significantly  reduce  the
+              lookups  are  not  performed.  This can significantly reduce the
               query load on the PostgreSQL server.
                   domain = postfix.org, <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/searchdomains
 
-              It  is best not to use SQL to store the domains eligible for SQL
+              It is best not to use SQL to store the domains eligible for  SQL
               lookups.
 
               This parameter is available with Postfix 2.2 and later.
@@ -216,28 +220,28 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
               the input keys are always unqualified.
 
        <b>expansion_limit (default: 0)</b>
-              A  limit  on  the total number of result elements returned (as a
+              A limit on the total number of result elements  returned  (as  a
               comma separated list) by a lookup against the map.  A setting of
-              zero  disables the limit. Lookups fail with a temporary error if
-              the limit is exceeded.  Setting the  limit  to  1  ensures  that
+              zero disables the limit. Lookups fail with a temporary error  if
+              the  limit  is  exceeded.   Setting  the limit to 1 ensures that
               lookups do not return multiple values.
 
 <b>OBSOLETE MAIN.CF PARAMETERS</b>
-       For  compatibility with other Postfix lookup tables, PostgreSQL parame-
-       ters can also be defined in <a href="postconf.5.html">main.cf</a>.  In order to do that,  specify  as
+       For compatibility with other Postfix lookup tables, PostgreSQL  parame-
+       ters  can  also be defined in <a href="postconf.5.html">main.cf</a>.  In order to do that, specify as
        PostgreSQL source a name that doesn't begin with a slash or a dot.  The
-       PostgreSQL parameters will then be accessible as the name you've  given
+       PostgreSQL  parameters will then be accessible as the name you've given
        the source in its definition, an underscore, and the name of the param-
-       eter.  For example, if the map is specified as  "<a href="pgsql_table.5.html">pgsql</a>:<i>pgsqlname</i>",  the
+       eter.   For  example, if the map is specified as "<a href="pgsql_table.5.html">pgsql</a>:<i>pgsqlname</i>", the
        parameter "hosts" would be defined in <a href="postconf.5.html">main.cf</a> as "<i>pgsqlname</i>_hosts".
 
-       Note:  with  this  form,  the  passwords for the PostgreSQL sources are
+       Note: with this form, the passwords  for  the  PostgreSQL  sources  are
        written in <a href="postconf.5.html">main.cf</a>, which is normally world-readable.  Support for this
        form will be removed in a future Postfix version.
 
 <b><a name="obsolete_query_interfaces">OBSOLETE QUERY INTERFACES</a></b>
        This section describes query interfaces that are deprecated as of Post-
-       fix 2.2.  Please migrate to the new <b>query</b> interface as the  old  inter-
+       fix  2.2.   Please migrate to the new <b>query</b> interface as the old inter-
        faces are slated to be phased out.
 
        <b>select_function</b>
@@ -247,14 +251,14 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
               This is equivalent to:
                   query = SELECT my_lookup_user_alias('%s')
 
-              This   parameter   overrides  the  legacy  table-related  fields
-              (described below). With Postfix versions prior to 2.2,  it  also
-              overrides  the  <b>query</b>  parameter. Starting with Postfix 2.2, the
-              <b>query</b> parameter has highest precedence, and the  <b>select_function</b>
+              This  parameter  overrides  the  legacy   table-related   fields
+              (described  below).  With Postfix versions prior to 2.2, it also
+              overrides the <b>query</b> parameter. Starting with  Postfix  2.2,  the
+              <b>query</b>  parameter has highest precedence, and the <b>select_function</b>
               parameter is deprecated.
 
-       The  following  parameters (with lower precedence than the <b>select_func-</b>
-       <b>tion</b> interface described above) can be used to  build  the  SQL  select
+       The following parameters (with lower precedence than  the  <b>select_func-</b>
+       <b>tion</b>  interface  described  above)  can be used to build the SQL select
        statement as follows:
 
            SELECT [<b>select_field</b>]
@@ -262,13 +266,13 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
            WHERE [<b>where_field</b>] = '%s'
                  [<b>additional_conditions</b>]
 
-       The  specifier %s is replaced with each lookup by the lookup key and is
-       escaped so if it contains single quotes or  other  odd  characters,  it
+       The specifier %s is replaced with each lookup by the lookup key and  is
+       escaped  so  if  it  contains single quotes or other odd characters, it
        will not cause a parse error, or worse, a security problem.
 
        Starting with Postfix 2.2, this interface is obsoleted by the more gen-
        eral <b>query</b> interface described above. If higher precedence the <b>query</b> or
-       <b>select_function</b>  parameters described above are defined, the parameters
+       <b>select_function</b> parameters described above are defined, the  parameters
        described here are ignored.
 
        <b>select_field</b>
index ec617280add7969687b2de2032d540c92a00580a..4f65ee66f0ef30b88234f3978be411de794dd319 100644 (file)
@@ -138,8 +138,9 @@ REGEXP_TABLE(5)                                                REGEXP_TABLE(5)
        <a href="master.5.html">master.cf</a>:
            <b>.. -o {</b> <i>parameter</i> <b>= .. <a href="regexp_table.5.html">regexp</a>:{ {</b> <i>rule-1</i> <b>}, {</b> <i>rule-2</i> <b>} .. } .. } ..</b>
 
-       Postfix  ignores  whitespace  after '{' and before '}', and writes each
-       <i>rule</i> as one text line to an in-memory file:
+       Postfix  recursively  expands any $parametername instances in the above
+       parameter value, ignores whitespace  after  '{'  and  before  '}',  and
+       writes each <i>rule</i> as one text line to an in-memory file:
 
        in-memory file:
            rule-1
@@ -148,12 +149,30 @@ REGEXP_TABLE(5)                                                REGEXP_TABLE(5)
 
        Postfix parses the result as if it is a file in /etc/postfix.
 
-       Note: if an inlined rule contains <b>$</b>, specify <b>$$</b> to  keep  Postfix  from
-       trying to do <i>$name</i> expansion as it evaluates a parameter value.
-
-       Note: when using <i>$name</i> inside an inlined pattern, this will not disable
-       metacharacters such as '.' in the <i>$name</i>  expansion.  To  prevent  unex-
-       pected matches, use a <a href="pcre_table.5.html">pcre</a>: table, and specify \Q<i>$name</i>\E.
+<b><a name="inline_specification_caveats">INLINE SPECIFICATION CAVEATS</a></b>
+       <b>o</b>      Avoid  using  <i>$parametername</i>  inside an inlined <a href="regexp_table.5.html">regexp</a>: pattern.
+              The  pattern  would  have  unexpected  matches  when  there  are
+              metacharacters  such  as '.' in the <i>$parametername</i> expansion. To
+              prevent unexpected matches,  use  a  <a href="pcre_table.5.html">pcre</a>:  table,  and  specify
+              \Q<i>$parametername</i>\E.
+
+       <b>o</b>      When  an inlined rule must contain <b>$</b>, specify <b>$$</b> to keep Postfix
+              from trying to do <i>$name</i> expansion as it  evaluates  a  parameter
+              value.  To  check an inline configuration, use the "<b>postconf -x</b>"
+              option as shown below:
+
+              <b>o</b>      When a <a href="postconf.5.html">main.cf</a> "<i>parametername =  value</i>" setting  contains
+                     an  inline  <a href="regexp_table.5.html">regexp</a>:  table,  use the command "<b>postconf -x</b>
+                     <i>parametername</i>".  Verify  that  there  are  no  "undefined
+                     parameter"  warnings,  and that the output has the syntax
+                     that one would use in a non-inlined Postfix <a href="regexp_table.5.html">regexp</a>: file.
+
+              <b>o</b>      When  a <a href="master.5.html">master.cf</a> "<b>-o {</b> <i>parametername = value</i> <b>}</b>" override
+                     contains an inline <a href="regexp_table.5.html">regexp</a>: table, use the command  "<b>post-</b>
+                     <b>conf  -Px '*/*/</b><i>parametername</i><b>'</b> ". Verify that there are no
+                     "undefined parameter" warnings, and that the  output  has
+                     the  syntax  that  one would use in a non-inlined Postfix
+                     <a href="regexp_table.5.html">regexp</a>: file.
 
 <b><a name="example_smtpd_access_map">EXAMPLE SMTPD ACCESS MAP</a></b>
        # Disallow sender-specified routing. This is a must if you relay mail
index 6722123d7388a175787347261a3e33e8ff6a7379..66a7a59c1a6f141e63cb68a9a0d488945536fd87 100644 (file)
@@ -127,9 +127,10 @@ master.cf:
     \fB.. \-o { \fIparameter\fR \fB= .. cidr:{ { \fIrule\-1\fB }, { \fIrule\-2\fB } .. } .. } ..\fR
 .fi
 
-Postfix ignores whitespace after '{' and before '}', and
-writes each \fIrule\fR as one text line to an in\-memory
-file:
+Postfix recursively expands any \fI$parametername\fR instances
+in the above parameter value, ignores whitespace after '{'
+and before '}', and writes each \fIrule\fR as one text line to
+an in\-memory file:
 
 .nf
 in\-memory file:
index b5adb94f3119a1366ee548b5d038e84eab524428..a112e7f68aa6617b0f46ac954a763908e47a1455 100644 (file)
@@ -193,9 +193,10 @@ master.cf:
     \fB.. \-o { \fIparameter\fR \fB= .. pcre:{ { \fIrule\-1\fB }, { \fIrule\-2\fB } .. } .. } ..\fR
 .fi
 
-Postfix ignores whitespace after '{' and before '}', and
-writes each \fIrule\fR as one text line to an in\-memory
-file:
+Postfix recursively expands any \fI$parametername\fR instances
+in the above parameter value, ignores whitespace after '{'
+and before '}', and writes each \fIrule\fR as one text line to
+an in\-memory file:
 
 .nf
 in\-memory file:
@@ -205,15 +206,35 @@ in\-memory file:
 .fi
 
 Postfix parses the result as if it is a file in /etc/postfix.
-
-Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+.SH "INLINE SPECIFICATION CAVEATS"
+.na
+.nf
+.ad
+.fi
+.IP \(bu
+When using \fI$parametername\fR inside an inlined pattern,
+use \eQ\fI$parametername\fR\eE to disable metacharacters such as
+\&'.' in the \fI$parametername\fR expansion. Otherwise, the pattern
+may have unexpected matches.
+.IP \(bu
+When an inlined rule must contain \fB$\fR, specify \fB$$\fR
 to keep Postfix from trying to do \fI$name\fR expansion as
-it evaluates a parameter value.
-
-Note: when using \fI$name\fR inside an inlined pattern, use
-\eQ\fI$name\fR\eE to disable metacharacters such as '.' in
-the \fI$name\fR expansion. Otherwise, the pattern may have
-unexpected matches.
+it evaluates a parameter value. To check an inline configuration,
+use the "\fBpostconf \-x\fR" option as shown below:
+.RS
+.IP \(bu
+When a main.cf "\fIparametername = \fI value\fR" setting contains
+an inline pcre: table, use the command "\fBpostconf \-x
+\fIparametername\fR". Verify that there are no "undefined
+parameter" warnings, and that the output has the syntax that
+one would use in a non\-inlined Postfix pcre: file.
+.IP \(bu
+When a master.cf "\fB\-o { \fIparametername = value\fB }\fR"
+override contains an inline pcre: table, use the command
+"\fBpostconf \-Px '*/*/\fIparametername\fB' \fR". Verify that there
+are no "undefined parameter" warnings, and that the output has
+the syntax that one would use in a non\-inlined Postfix pcre: file.
+.RE
 .SH "EXAMPLE SMTPD ACCESS MAP"
 .na
 .nf
index 4b4ecb12e4955218292d45e363f7ed2e3bab287f..6d9df55fd03f3be3554e85094db9b40e89ad319b 100644 (file)
@@ -62,7 +62,7 @@ connections, where the \fBunix:\fR and \fBinet:\fR prefixes
 are accepted and ignored for backwards compatibility.
 Examples:
 .nf
-    hosts = postgresql://username@example.com/tablename?sslmode=require
+    hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require
     hosts = inet:host1.some.domain inet:host2.some.domain:port
     hosts = host1.some.domain host2.some.domain:port
     hosts = unix:/file/name
@@ -85,11 +85,14 @@ Example:
     user = someone
     password = some_password
 .fi
-.IP "\fBdbname\fR"
+.IP "\fBdbname\fR (required)"
 The database name on the servers. Example:
 .nf
     dbname = customer_database
 .fi
+.sp
+This setting is required, but ignored when a postgresql://
+URI specifies a database name.
 .IP "\fBencoding\fR"
 The encoding used by the database client. The default setting
 is:
index e9698210bc2f46d7b6580d49b85701e429c44901..91c6f23a3f330b4a689dd8d5feab578f6ffe1ea7 100644 (file)
@@ -150,9 +150,10 @@ master.cf:
     \fB.. \-o { \fIparameter\fR \fB= .. regexp:{ { \fIrule\-1\fB }, { \fIrule\-2\fB } .. } .. } ..\fR
 .fi
 
-Postfix ignores whitespace after '{' and before '}', and
-writes each \fIrule\fR as one text line to an in\-memory
-file:
+Postfix recursively expands any \fi$parametername\fR instances
+in the above parameter value, ignores whitespace after '{'
+and before '}', and writes each \fIrule\fR as one text line to
+an in\-memory file:
 
 .nf
 in\-memory file:
@@ -162,15 +163,37 @@ in\-memory file:
 .fi
 
 Postfix parses the result as if it is a file in /etc/postfix.
-
-Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+.SH "INLINE SPECIFICATION CAVEATS"
+.na
+.nf
+.ad
+.fi
+.IP \(bu
+Avoid using \fI$parametername\fR inside an inlined regexp:
+pattern.  The pattern would have unexpected matches when there
+are metacharacters such as '.' in the \fI$parametername\fR
+expansion. To prevent unexpected matches, use a pcre: table,
+and specify \eQ\fI$parametername\fR\eE.
+.IP \(bu
+When an inlined rule must contain \fB$\fR, specify \fB$$\fR
 to keep Postfix from trying to do \fI$name\fR expansion as
-it evaluates a parameter value.
-
-Note: when using \fI$name\fR inside an inlined pattern,
-this will not disable metacharacters such as '.' in the
-\fI$name\fR expansion. To prevent unexpected matches, use
-a pcre: table, and specify \eQ\fI$name\fR\eE.
+it evaluates a parameter value. To check an inline configuration,
+use the "\fBpostconf \-x\fR" option as shown below:
+.RS
+.IP \(bu
+When a main.cf "\fIparametername = \fI value\fR" setting
+contains an inline regexp: table, use the command "\fBpostconf
+\-x \fIparametername\fR". Verify that there are no "undefined
+parameter" warnings, and that the output has the syntax that
+one would use in a non\-inlined Postfix regexp: file.
+.IP \(bu
+When a master.cf "\fB\-o { \fIparametername = value\fB }\fR"
+override contains an inline regexp: table, use the command
+"\fBpostconf \-Px '*/*/\fIparametername\fB' \fR". Verify that there
+are no "undefined parameter" warnings, and that the output has
+the syntax that one would use in a non\-inlined Postfix regexp:
+file.
+.RE
 .SH "EXAMPLE SMTPD ACCESS MAP"
 .na
 .nf
index 7ee74fba2978894152dc31baeb168662f15eaecf..f5ec3f4f850778c6689feae36848558b22c4b0cc 100755 (executable)
@@ -1267,7 +1267,7 @@ while (<>) {
     s;\b(pipe[-</bB>]*\n*[ <bB>]*map):;<a href="DATABASE_README.html#types">$1<\/a>:;g;
     s/\b(proxy):/<a href="proxymap.8.html">$1<\/a>:/g;
     s/\b(randmap):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
-    s/\b(regexp):/<a href="regexp_table.5.html">$1<\/a>:/g;
+    s;\b(reg[-</bB>]*\n*[ <bB>]*exp):;<a href="regexp_table.5.html">$1<\/a>:;g;
     s/\b(sdbm):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
     s/\b(socketmap):/<a href="socketmap_table.html">$1<\/a>:/g;
     s/\b(sqlite):/<a href="sqlite_table.5.html">$1<\/a>:/g;
index 3154c33e5e6e99151ef189064d4468d34f4b762f..503fd9dd3e6e0ee3d240d535fac49f561503b498 100644 (file)
@@ -266,44 +266,51 @@ have the details for why TLS authentication failed. </p>
 
 <h2> <a name="delivering"> Delivering TLSRPT summaries via email</a> </h2>
 
-<p> RFC 8460 suggests not to enforce strict TLS security when sending
-daily success/failure summaries via email, to avoid delivery delays
-caused by a failure to enforce TLS security. Postfix currently does
-not have a mechanism to disable TLS security enforcement when
-submitting an email message; this section provides a workaround. </p>
-
-<p> By design, TLSRPT is not a real-time notification system; it
-takes on average 12 hours before a failure is reported in a daily
-success/failure summary. If a TLS-related delay of a day or more
-is undesirable, one could set up a transport map to make TLS security
-optional for specific TLSRPT email notification email addresses.
-</p>
+<p> <a href="https://datatracker.ietf.org/doc/html/rfc8460#section-3">RFC
+8460 Section 3</a> specifies that an MTA must not enforce TLS
+security when sending failure reports via email. However, Postfix
+currently has no way to request that TLS enforcement will be disabled
+when submitting an email message. </p>
+
+<p> Options:
+
+<ul>
+
+<li> <p> Do nothing. When TLS security enforcement is required, a
+persistent enforcement failure will delay the delivery of a TLSRPT
+summary until the problem is addressed, or until the message expires
+in the mail queue. Keep in mind that TLSRPT is not a real-time
+monitoring service; it takes on average 12 hours before a failure
+is reported through TLSRPT. </p>
+
+<li> <p> Exclude the sender of TLSRPT summaries from TLS enforcement.
+Implement the configuration below on outbound MTA instances (replace
+noreply-smtp-tls-reporting@example.com with your actual report
+generator's sender address): </p>
 
-<blockquote>
 <pre>
 /etc/postfix/main.cf:
-    transport_maps = hash:/etc/postfix/transport
-&nbsp
-/etc/postfix/transport:
-    smtp-tls-report@example.com     allow-plaintext:
-    ...
+    # Limitation: this setting is overruled with transport_maps.
+    sender_dependent_default_transport_maps = inline:{
+        { noreply-smtp-tls-reporting@example.com = allow-plaintext } }
 &nbsp
 /etc/postfix/master.cf:
     # service name    type    private unpriv  chroot  wakeup  maxproc command
     allow-plaintext   unix    -       -       n       -       -       smtp
-        -o smtp_tls_security_level=may
-        -o smtp_tls_policy_maps=static:may
+        -o { smtp_tls_security_level = may }
+        -o { smtp_tls_policy_maps = static:may }
 </pre>
-</blockquote>
+
+</ul>
 
 <h2> <a name="mta-sts"> MTA-STS Support via smtp_tls_policy_maps
 </a></h2>
 
-<p> Postfix supports MTA-STS though an smtp_tls_policy_maps
-policy plugin. Postfix 3.10 and later expect a policy response with
-the usual security level and matching requirements, plus any
-applicable name=value attributes described below. Specify <tt>{
-name = value }</tt> when a value may contain whitespace. </p>
+<p> Postfix supports MTA-STS though an smtp_tls_policy_maps policy
+plugin, which replies with a TLS security level and optional matching
+requirements. Postfix 3.10 and later optionally also accept the
+name=value attributes described below. Specify <tt>{ name = value
+}</tt> when a value may contain whitespace. </p>
 
 <blockquote>
 
@@ -319,8 +326,22 @@ policy. </p>
 </blockquote>
 
 <p> The examples in the table apply to the MTA-STS policy example
-given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5.
-<p>
+given in <a
+href="https://datatracker.ietf.org/doc/html/rfc8461#section-3.2">
+RFC 8461 Section 3.2</a>: </p>
+
+<blockquote>
+<pre>
+version: STSv1
+mode: enforce
+mx: mail.example.com
+mx: *.example.net
+mx: backupmx.example.com
+max_age: 604800
+</pre>
+</blockquote>
+
+<p> A policy response may contain line breaks. </p>
 
 <ul>
 
@@ -347,7 +368,7 @@ in attribute values. </p>
 
 <blockquote>
 <pre>
-{ policy_string = version: STSv1 } { policy_string = mode: testing } ...
+{ policy_string = version: STSv1 } { policy_string = mode: enforce } ...
 </pre> 
 </blockquote> 
 
@@ -363,7 +384,7 @@ in the MTA-STS policy. </p>
 
 <blockquote>
 <pre>
-mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
+mx_host_pattern=mail.example.com mx_host_pattern=*.example.net ...
 </pre>
 </blockquote>
 </li>
index 9eed9ce63d485ebd6884d508d649bab1384c121a..5dad4dc455a8118c9bfd04b001f7e37224547e4c 100644 (file)
 #          \fB.. -o { \fIparameter\fR \fB= .. cidr:{ { \fIrule-1\fB }, { \fIrule-2\fB } .. } .. } ..\fR
 # .fi
 #
-#      Postfix ignores whitespace after '{' and before '}', and
-#      writes each \fIrule\fR as one text line to an in-memory
-#      file:
+#      Postfix recursively expands any \fI$parametername\fR instances
+#      in the above parameter value, ignores whitespace after '{'
+#      and before '}', and writes each \fIrule\fR as one text line to
+#      an in-memory file:
 #
 # .nf
 #      in-memory file:
index e4c6607f5fb95b11720d6455464e17db047d4c98..8b59cc2dc5dc518aef709a8ebb7c5dac7662ac1c 100644 (file)
 #          \fB.. -o { \fIparameter\fR \fB= .. pcre:{ { \fIrule-1\fB }, { \fIrule-2\fB } .. } .. } ..\fR
 # .fi
 #
-#      Postfix ignores whitespace after '{' and before '}', and
-#      writes each \fIrule\fR as one text line to an in-memory
-#      file:
+#      Postfix recursively expands any \fI$parametername\fR instances
+#      in the above parameter value, ignores whitespace after '{'
+#      and before '}', and writes each \fIrule\fR as one text line to
+#      an in-memory file:
 #
 # .nf
 #      in-memory file:
 # .fi
 #
 #      Postfix parses the result as if it is a file in /etc/postfix.
-#
-#      Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+# INLINE SPECIFICATION CAVEATS
+# .ad
+# .fi
+# .IP \(bu
+#      When using \fI$parametername\fR inside an inlined pattern,
+#      use \eQ\fI$parametername\fR\eE to disable metacharacters such as
+#      '.' in the \fI$parametername\fR expansion. Otherwise, the pattern
+#      may have unexpected matches.
+# .IP \(bu
+#      When an inlined rule must contain \fB$\fR, specify \fB$$\fR
 #      to keep Postfix from trying to do \fI$name\fR expansion as
-#      it evaluates a parameter value.
-#
-#      Note: when using \fI$name\fR inside an inlined pattern, use
-#      \eQ\fI$name\fR\eE to disable metacharacters such as '.' in
-#      the \fI$name\fR expansion. Otherwise, the pattern may have
-#      unexpected matches.
+#      it evaluates a parameter value. To check an inline configuration,
+#      use the "\fBpostconf -x\fR" option as shown below:
+# .RS
+# .IP \(bu
+#      When a main.cf "\fIparametername = \fI value\fR" setting contains
+#      an inline pcre: table, use the command "\fBpostconf -x
+#      \fIparametername\fR". Verify that there are no "undefined
+#      parameter" warnings, and that the output has the syntax that
+#      one would use in a non-inlined Postfix pcre: file.
+# .IP \(bu
+#      When a master.cf "\fB-o { \fIparametername = value\fB }\fR"
+#      override contains an inline pcre: table, use the command
+#      "\fBpostconf -Px '*/*/\fIparametername\fB' \fR". Verify that there
+#      are no "undefined parameter" warnings, and that the output has
+#      the syntax that one would use in a non-inlined Postfix pcre: file.
+# .RE
 # EXAMPLE SMTPD ACCESS MAP
 #      # Protect your outgoing majordomo exploders
 #      /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
index 31f6661e1b2b8282d2885784f47b241aebf645bd..6bc797b5b0aa05fa0c2870582eb375f2dbe027d4 100644 (file)
@@ -52,7 +52,7 @@
 #      are accepted and ignored for backwards compatibility.
 #      Examples:
 # .nf
-#          hosts = postgresql://username@example.com/tablename?sslmode=require
+#          hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require
 #          hosts = inet:host1.some.domain inet:host2.some.domain:port
 #          hosts = host1.some.domain host2.some.domain:port
 #          hosts = unix:/file/name
 #          user = someone
 #          password = some_password
 # .fi
-# .IP "\fBdbname\fR"
+# .IP "\fBdbname\fR (required)"
 #      The database name on the servers. Example:
 # .nf
 #          dbname = customer_database
 # .fi
+# .sp
+#      This setting is required, but ignored when a postgresql://
+#      URI specifies a database name.
 # .IP "\fBencoding\fR"
 #      The encoding used by the database client. The default setting
 #      is:
index 5e8c15f855f94ca6d6c5ad17006da67f0a17c890..c8e738f2ac6b709bc782b13f07a85893251b92b5 100644 (file)
 #          \fB.. -o { \fIparameter\fR \fB= .. regexp:{ { \fIrule-1\fB }, { \fIrule-2\fB } .. } .. } ..\fR
 # .fi
 #
-#      Postfix ignores whitespace after '{' and before '}', and
-#      writes each \fIrule\fR as one text line to an in-memory
-#      file:
+#      Postfix recursively expands any \fi$parametername\fR instances
+#      in the above parameter value, ignores whitespace after '{'
+#      and before '}', and writes each \fIrule\fR as one text line to
+#      an in-memory file:
 #
 # .nf
 #      in-memory file:
 # .fi
 #
 #      Postfix parses the result as if it is a file in /etc/postfix.
-#
-#      Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+# INLINE SPECIFICATION CAVEATS
+# .ad
+# .fi
+# .IP \(bu
+#      Avoid using \fI$parametername\fR inside an inlined regexp:
+#      pattern.  The pattern would have unexpected matches when there
+#      are metacharacters such as '.' in the \fI$parametername\fR
+#      expansion. To prevent unexpected matches, use a pcre: table,
+#      and specify \eQ\fI$parametername\fR\eE.
+# .IP \(bu
+#      When an inlined rule must contain \fB$\fR, specify \fB$$\fR
 #      to keep Postfix from trying to do \fI$name\fR expansion as
-#      it evaluates a parameter value.
-#
-#      Note: when using \fI$name\fR inside an inlined pattern,
-#      this will not disable metacharacters such as '.' in the
-#      \fI$name\fR expansion. To prevent unexpected matches, use
-#      a pcre: table, and specify \eQ\fI$name\fR\eE.
+#      it evaluates a parameter value. To check an inline configuration,
+#      use the "\fBpostconf -x\fR" option as shown below:
+# .RS
+# .IP \(bu
+#      When a main.cf "\fIparametername = \fI value\fR" setting
+#      contains an inline regexp: table, use the command "\fBpostconf
+#      -x \fIparametername\fR". Verify that there are no "undefined
+#      parameter" warnings, and that the output has the syntax that
+#      one would use in a non-inlined Postfix regexp: file.
+# .IP \(bu
+#      When a master.cf "\fB-o { \fIparametername = value\fB }\fR"
+#      override contains an inline regexp: table, use the command
+#      "\fBpostconf -Px '*/*/\fIparametername\fB' \fR". Verify that there
+#      are no "undefined parameter" warnings, and that the output has
+#      the syntax that one would use in a non-inlined Postfix regexp:
+#      file.
+# .RE
 # EXAMPLE SMTPD ACCESS MAP
 #      # Disallow sender-specified routing. This is a must if you relay mail
 #      # for other domains.
index e2d5861e3639b22dde93a2c11867972a736f1da9..d10fe23549d0443ea0bd46135a4744fc19a18cfc 100644 (file)
@@ -1847,3 +1847,6 @@ sts
 tlsrprt
 bdefhnoqv
 deduplicated
+WS
+isspace
+ws
index 45d00e6c2484f7d6a59ae50134e8b9a33538f39d..383098cfd699484a0440d3b8e74414e50ba9a222 100644 (file)
@@ -85,3 +85,4 @@ pkgconf
 testfiles
 Antonin
 Verrier
+unescaped
index 0bb4f3963e343d49cea36f074042bb65d7e2fe14..50436368030f488e6bef3c33ec60374e0b97ee12 100644 (file)
@@ -387,3 +387,5 @@ Sys
 Qsmtp
 Qsts
 gmail
+backupmx
+noreply
index 8cbe85dff633b6078336fe1a9897aeac2ceb7c3a..02a818a0f5fb1f428256246a5ece50b180d660c3 100644 (file)
@@ -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      "20241010"
+#define MAIL_RELEASE_DATE      "20241024"
 #define MAIL_VERSION_NUMBER    "3.10"
 
 #ifdef SNAPSHOT
index 5015094965abe8ca34f5279d0a78a9f7f8146a69..6122c2d7823a28b8e1faadbf563c5d16545a8b93 100644 (file)
@@ -285,12 +285,17 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
     /*
      * Errors in attributes may have security consequences, don't ignore
      * errors that can degrade security.
+     * 
+     * Caution: normalize whitespace, to neutralize line break etc. characters
+     * inside the value portion of { name = value }.
      */
     while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
        const char *err;
 
+#define EXTPAR_OPT     (EXTPAR_FLAG_STRIP | EXTPAR_FLAG_NORMAL_WS)
+
        if ((tok[0] == CHARS_BRACE[0]
-            && (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0)
+            && (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_OPT)) != 0)
            || (err = split_nameval(tok, &name, &val)) != 0) {
            msg_warn("%s: malformed attribute/value pair \"%s\": %s",
                     WHERE, tok, err);
index e7164da782851c9a5ac4439d520d1802532cbf90..0c2c64019e632089fb9a2987cdb7780c50e4caed 100644 (file)
@@ -2389,6 +2389,7 @@ static int mail_open_stream(SMTPD_STATE *state)
                            ", sasl_method=", state->sasl_method),
             PRINT2_OR_NULL(state->sasl_username,
                            ", sasl_username=", state->sasl_username),
+    /* This is safe because state->sasl_sender is xtext-encoded. */
             PRINT2_OR_NULL(state->sasl_sender,
                            ", sasl_sender=", state->sasl_sender),
 #else
index 41b91a1d9dd77a992971fc99c11d505d5ac796b5..ae122e336710792c198c4244605439f81dee76ce 100644 (file)
@@ -1016,6 +1016,15 @@ void    log_whatsup(SMTPD_STATE *state, const char *whatsup,
        vstring_sprintf_append(buf, " proto=%s", state->protocol);
     if (state->helo_name)
        vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
+#ifdef USE_SASL_AUTH
+    if (state->sasl_method)
+       vstring_sprintf_append(buf, " sasl_method=%s", state->sasl_method);
+    if (state->sasl_username)
+       vstring_sprintf_append(buf, " sasl_username=%s", state->sasl_username);
+    /* This is safe because state->sasl_sender is xtext-encoded. */
+    if (state->sasl_sender)
+       vstring_sprintf_append(buf, " sasl_sender=%s", state->sasl_sender);
+#endif
     msg_info("%s", STR(buf));
     vstring_free(buf);
 }
index 67bdc3ec7cc905d5d22aa96b7caa469010f475bc..531b786bfc9276171fb0186094b49d141b3ce4ec 100644 (file)
@@ -46,7 +46,7 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
        mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
        inet_addr_sizes.c quote_for_json.c mystrerror.c \
-       sane_sockaddr_to_hostaddr.c
+       sane_sockaddr_to_hostaddr.c normalize_ws.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 \
@@ -93,7 +93,8 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \
        sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
        mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
-       quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o
+       quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
+       normalize_ws.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.
@@ -147,7 +148,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \
        vbuf_print split_qnameval vstream msg_logger byte_mask \
        known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
-       clean_env inet_prefix_top printable readlline quote_for_json
+       clean_env inet_prefix_top printable readlline quote_for_json \
+       normalize_ws
 PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
        $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
 HTABLE_FIX = NORANDOMIZE=1
@@ -611,6 +613,11 @@ quote_for_json: $(LIB)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+normalize_ws: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
        hex_quote_test ctable_test inet_addr_list_test base64_code_test \
        attr_scan64_test attr_scan0_test host_port_test dict_tests \
@@ -621,7 +628,8 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
        miss_endif_regexp_test split_qnameval_test vstring_test \
        vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
        binhash_test argv_test inet_prefix_top_test printable_test \
-       valid_utf8_string_test readlline_test quote_for_json_test
+       valid_utf8_string_test readlline_test quote_for_json_test \
+       normalize_ws_test
  
 dict_tests: all dict_test \
        dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
@@ -1098,6 +1106,9 @@ inet_prefix_top_test: inet_prefix_top
 quote_for_json_test: quote_for_json
        $(SHLIB_ENV) ${VALGRIND} ./quote_for_json
 
+normalize_ws_test: normalize_ws
+       $(SHLIB_ENV) ${VALGRIND} ./normalize_ws
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -2495,6 +2506,16 @@ non_blocking.o: iostuff.h
 non_blocking.o: msg.h
 non_blocking.o: non_blocking.c
 non_blocking.o: sys_defs.h
+normalize_ws.o: check_arg.h
+normalize_ws.o: msg.h
+normalize_ws.o: msg_vstream.h
+normalize_ws.o: mymalloc.h
+normalize_ws.o: normalize_ws.c
+normalize_ws.o: stringops.h
+normalize_ws.o: sys_defs.h
+normalize_ws.o: vbuf.h
+normalize_ws.o: vstream.h
+normalize_ws.o: vstring.h
 nvtable.o: htable.h
 nvtable.o: mymalloc.h
 nvtable.o: nvtable.c
index 0b106bac825b8fc8a796148e568a171c83ce74d9..2ee317f65d8ba1bdfff30fee57d9ed9e3c469dc4 100644 (file)
 /* .IP EXTPAR_FLAG_STRIP
 /*     Skip whitespace after the opening parenthesis, and trim
 /*     whitespace before the closing parenthesis.
+/* .IP EXTPAR_FLAG_NORMAL_WS
+/*     Substitute SPACE for control characters (newline etc.) that
+/*     match isspace(). This neutralizes line break etc. characters in
+/*     the value portion of { name = value }.
 /* .RE
 /* DIAGNOSTICS
 /*     In case of error the result value is a dynamically-allocated
@@ -104,6 +108,8 @@ char   *extpar(char **bp, const char *parens, int flags)
        while (ISSPACE(*cp))
            cp++;
     }
+    if (flags & EXTPAR_FLAG_NORMAL_WS)
+       normalize_ws(cp);
     *bp = cp;
     return (err);
 }
diff --git a/postfix/src/util/normalize_ws.c b/postfix/src/util/normalize_ws.c
new file mode 100644 (file)
index 0000000..f41de4e
--- /dev/null
@@ -0,0 +1,144 @@
+/*++
+/* NAME
+/*     normalize_ws 3
+/* SUMMARY
+/*     neutralize isspace()-matching control characters
+/* SYNOPSIS
+/*     #include <stringops.h>
+/*
+/*     char    *normalize_ws(char *str)
+/* DESCRIPTION
+/*     normalize_ws() takes a null-terminated string and substitutes
+/*     ASCII SPACE for any ASCII control characters that match
+/*     isspace(). This neutralizes line break etc. characters in the
+/*     value portion of { name = value }. The function substitutes
+/*     bytes in place, and returns its str argument.
+/*
+/*     This function requires that input is encoded in ASCII or UTF-8.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+/* normalize_ws - replace isspace() members with space characters */
+
+char   *normalize_ws(char *str)
+{
+    char *cp;
+
+    for (cp = str; *(cp += strcspn(cp, "\t\n\v\f\r")); *cp = ' ')
+        /* void */ ;
+    return (str);
+}
+
+#ifdef TEST
+
+typedef struct TEST_CASE {
+    const char *label;
+    int     (*action) (const struct TEST_CASE *);
+    const char *input;
+    const char *want;
+} TEST_CASE;
+
+#define PASS   (0)
+#define FAIL   (1)
+
+#define BUFLEN 2
+
+/* if this test fails, isspace() may have changed */
+
+static int normalizes_all_isspace_members(const TEST_CASE *unused)
+{
+    int     ch;
+    char    input[BUFLEN];
+    char    want[BUFLEN];
+    char   *got;
+
+    for (ch = 0; ISASCII(ch); ch++) {
+       input[0] = ch;
+       input[1] = 0;
+       want[0] = ISSPACE(ch) ? ' ' : ch;
+       want[1] = 0;
+       got = normalize_ws(input);
+       if (got != input) {
+           msg_warn("got %p, want %p", got, input);
+           return (FAIL);
+       }
+       if (memcmp(got, want, BUFLEN) != 0) {
+           msg_warn("got '{0x%02x 0x%02x}', want '{0x%02x 0x%02x}",
+                    got[0], got[1], want[0], want[1]);
+           return (FAIL);
+       }
+    }
+    return (PASS);
+}
+
+static int test_normalize_ws(const TEST_CASE *tp)
+{
+    char   *input = mystrdup(tp->input);
+    char   *got;
+
+    got = normalize_ws(input);
+    if (strcmp(got, tp->want) != 0) {
+       msg_warn("got '%s', want '%s'", got, tp->want);
+       return (FAIL);
+    }
+    myfree(input);
+    return (PASS);
+}
+
+static const TEST_CASE test_cases[] = {
+    {"normalizes all isspace members", normalizes_all_isspace_members,},
+    {"normalizes_first", test_normalize_ws, "\tfoo", " foo",},
+    {"normalizes_middle", test_normalize_ws, "fo\to", "fo o",},
+    {"normalizes_last", test_normalize_ws, "foo\t", "foo ",},
+    {"normalizes_multiple", test_normalize_ws, "\tfo\to\t", " fo o ",},
+    {0,}
+};
+
+int     main(int argc, char **argv)
+{
+    const TEST_CASE *tp;
+    int     pass = 0;
+    int     fail = 0;
+
+    msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+
+    for (tp = test_cases; tp->label != 0; tp++) {
+       int     test_failed;
+
+       msg_info("RUN  %s", tp->label);
+       test_failed = tp->action(tp);
+       if (test_failed) {
+           msg_info("FAIL %s", tp->label);
+           fail++;
+       } else {
+           msg_info("PASS %s", tp->label);
+           pass++;
+       }
+    }
+    msg_info("PASS=%d FAIL=%d", pass, fail);
+    exit(fail != 0);
+}
+
+#endif
index 4c357c81e12f3260b2174b1f876ab2d0355e75a4..cc498479a23b0e848bf88bdfe50f8ee1d54ebfe3 100644 (file)
@@ -68,10 +68,12 @@ extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t);
 extern char *quote_for_json(VSTRING *, const char *, ssize_t);
 extern char *quote_for_json_append(VSTRING *, const char *, ssize_t);
 extern const char *mystrerror(int);
+extern char *normalize_ws(char *);
 
 #define EXTPAR_FLAG_NONE       (0)
 #define EXTPAR_FLAG_STRIP      (1<<0)  /* "{ text }" -> "text" */
 #define EXTPAR_FLAG_EXTRACT    (1<<1)  /* hint from caller's caller */
+#define EXTPAR_FLAG_NORMAL_WS  (1<<2)  /* normalize 'isspace' members */
 
 #define CASEF_FLAG_UTF8                (1<<0)
 #define CASEF_FLAG_APPEND      (1<<1)