]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.11-20131126-nonprod 20131126-nonprod
authorWietse Venema <wietse@porcupine.org>
Tue, 26 Nov 2013 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Thu, 28 Nov 2013 01:17:53 +0000 (20:17 -0500)
57 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/TLS_README
postfix/RELEASE_NOTES
postfix/html/TLS_README.html
postfix/html/lmtp.8.html
postfix/html/postconf.1.html
postfix/html/postconf.5.html
postfix/html/smtp.8.html
postfix/makedefs
postfix/man/man1/postconf.1
postfix/man/man5/postconf.5
postfix/man/man8/smtp.8
postfix/proto/TLS_README.html
postfix/proto/postconf.proto
postfix/src/dns/dns.h
postfix/src/global/mail_params.h
postfix/src/global/match_service.c
postfix/src/master/master_ent.c
postfix/src/milter/test-milter.c
postfix/src/postconf/Makefile.in
postfix/src/postconf/postconf.c
postfix/src/postconf/postconf.h
postfix/src/postconf/postconf_edit.c
postfix/src/postconf/postconf_main.c
postfix/src/postconf/postconf_master.c
postfix/src/postconf/postconf_match.c [new file with mode: 0644]
postfix/src/postconf/postconf_misc.c
postfix/src/postconf/postconf_print.c [new file with mode: 0644]
postfix/src/postconf/test10.ref
postfix/src/postconf/test37.ref
postfix/src/postconf/test40.ref
postfix/src/postconf/test41.ref [new file with mode: 0644]
postfix/src/postconf/test42.ref [new file with mode: 0644]
postfix/src/postconf/test43.ref [new file with mode: 0644]
postfix/src/postconf/test44.ref [new file with mode: 0644]
postfix/src/postconf/test45.ref [new file with mode: 0644]
postfix/src/postconf/test46.ref [new file with mode: 0644]
postfix/src/postconf/test47.ref [new file with mode: 0644]
postfix/src/postconf/test48.ref [new file with mode: 0644]
postfix/src/postconf/test49.ref [new file with mode: 0644]
postfix/src/postconf/test50.ref [new file with mode: 0644]
postfix/src/postconf/test51.ref [new file with mode: 0644]
postfix/src/postconf/test52.ref [new file with mode: 0644]
postfix/src/postconf/test53.ref [new file with mode: 0644]
postfix/src/postconf/test54.ref [new file with mode: 0644]
postfix/src/postconf/test55.ref [new file with mode: 0644]
postfix/src/postconf/test56.ref [new file with mode: 0644]
postfix/src/smtp/smtp.c
postfix/src/tls/tls_client.c
postfix/src/tls/tls_dane.c
postfix/src/tls/tls_misc.c
postfix/src/util/argv.c
postfix/src/util/argv.h
postfix/src/util/argv_split.c
postfix/src/util/host_port.c
postfix/src/util/slmdb.c

index 60bdd8d01ddfa109c92a63f61a860d53882906cf..441e160c2b17cff24a26c10f61cc9135d6c26e70 100644 (file)
 -TMKMAP_DB
 -TMKMAP_DBM
 -TMKMAP_OPEN_INFO
+-TMKMAP_SDBM
 -TMSG_STATS
 -TMULTI_SERVER
 -TMVECT
 -TOPTIONS
 -TPC_DBMS_INFO
 -TPC_EVAL_CTX
+-TPC_MASTER_EDIT_REQ
 -TPC_MASTER_ENT
+-TPC_MASTER_FIELD_REQ
 -TPC_PARAM_CTX
 -TPC_PARAM_NODE
+-TPC_PARAM_TABLE
 -TPC_SERVICE_DEF
+-TPC_SERVICE_PATTERN
 -TPC_STRING_NV
 -TPEER_NAME
 -TPGSQL_NAME
 -TXSASL_CLIENT_CREATE_ARGS
 -TXSASL_CLIENT_IMPL
 -TXSASL_CLIENT_IMPL_INFO
+-TXSASL_CYRUS_CB
 -TXSASL_CYRUS_CLIENT
 -TXSASL_CYRUS_ERROR_INFO
 -TXSASL_CYRUS_SERVER
 -TXSASL_SERVER_CREATE_ARGS
 -TXSASL_SERVER_IMPL
 -TXSASL_SERVER_IMPL_INFO
+-Tbind_props
 -Tcipher_probe_t
+-Tdane_digest
+-Tfilter_ctx
 -Tgeneral_name_stack_t
+-Tiana_digest
 -Toff_t
 -Tregex_t
 -Tregmatch_t
 -Tsasl_conn_t
 -Tsasl_secret_t
 -Tsfsistat
+-Tsigset_t
 -Tsize_t
 -Tssize_t
 -Tssl_cipher_stack_t
 -Tssl_comp_stack_t
 -Ttime_t
+-Ttlsa_filter
 -Tx509_extension_stack_t
 -Tx509_stack_t
index 3e1e222496ae65cb21fba1522443444a6a96ebc3..4883ede548a873fac46719f611a40a3f8e9082e2 100644 (file)
@@ -18218,6 +18218,13 @@ Apologies for any names omitted.
        Factor out the master.cf line parser so that it can be
        reused for "postconf -Me". File: postconf/postconf_master.c.
 
+20130113
+
+       Feature: master.cf attribute namespace. "postconf -F" shows
+       individual master.cf fields as "service/type/attribute =
+       value", where attribute is "service", "type", "private",
+       "unprivileged", "wakeup", "process_limit", or "command".
+
 20130121
 
        Bugfix (introduced 20120307): the postconf -X option erased
@@ -19232,9 +19239,67 @@ Apologies for any names omitted.
        cleanup/cleanup_milter.c, cleanup/cleanup_state.c,
        global/xtext.c, global/xtext.h, milter/test-milter.c.
 
+20131122
+
+       Feature: "postcon -Fe service/type/attribute = value" edits
+       master.cf attribute values. The -e is optional. Example:
+       use "postconf -F "*/*/chroot = n" to turn off chroot on all
+       master.cf services. Files: postconf/postconf.h,
+       postconf/postconf.c, postconf/postcof_master.c,
+       postconf/postconf_edit.c.
+
 20131124
 
        Cleanup: remove extra blank line from ccformat output,
        making it compatible with the script that Wietse actually
        uses (this line was part of a test to detect file truncation,
        but it is now obsolete).  File: mantools/ccformat.
+
+       Feature: master.cf parameter namespace. "postconf -P" shows
+       master.cf parameter settings as "service/type/parameter =
+       value". This is applicable only to parameter settings in
+       master.cf. Files: postconf/postconf.h, postconf/postconf.c,
+       postconf/postcof_master.c, postconf/postconf_print.c.
+
+       Incompatibility: the master_service_disable syntax has
+       changed: use "service/type" instead of "service.type".  The
+       new form is consistent with master.cf parameter namespaces.
+       The old form is still supported to avoid breaking existing
+       configurations.  Files: global/master_service.c,
+       master/master_ent.c.
+
+20131125
+
+       Feature: change, add or delete "-o parameter=value" setting
+       in master.cf. Examples: "postconf -P smtp/inet/parameter=value"
+       (add or modify "-o name=value" setting) and "postconf -P
+       smtp/inet/parameter" (delete "-o parameter=value" setting).
+       Files: util/argv.[hc], postconf/postconf.h,
+       postconf/postconf_edit.c, postconf_master.c.
+
+20131126
+
+       Cleanup: Leave SSLv3 enabled with DANE. Viktor Dukhovni.
+       Files: proto/TLS_README.html proto/postconf.proto
+       tls/tls_client.c.
+
+       Cleanup: DANE support: Drop support for usage 0. It SHOULD
+       NOT be supported in DANE with SMTP, and we already don't
+       support digest TLSA RRs in this case, while full content
+       TLSA RRs are not recommended for DNS bloat reasons.  Viktor
+       Dukhovni. Files: proto/postconf.proto src/global/mail_params.h
+       src/smtp/smtp.c src/tls/tls_dane.c src/tls/tls_misc.c.
+
+       Feature: TLS support: Support future digest algorithms
+       without re-compilation. Viktor Dukhovni. Files: .indent.pro
+       proto/postconf.proto src/tls/tls_dane.c.
+
+       Feature: DNS support: New configurable digest agility.
+       Viktor Dukhovni. Files: .indent.pro proto/TLS_README.html
+       proto/postconf.proto src/global/mail_params.h src/tls/tls_dane.c
+       src/tls/tls_misc.c.
+
+20131127
+
+       Bugfix (introduced: 20090106): the postconf '-#' option
+       erased prior options. File: postconf/postconf.c.
index 4728a624d2b914426649a1808ea748946d6265dd..2d07f9a999e2d1b9b19f5f92a797cec47a83c985 100644 (file)
@@ -135,9 +135,10 @@ assume that the certificate for "server.example.com" was issued by
 
         % c\bca\bat\bt s\bse\ber\brv\bve\ber\br_\b_c\bce\ber\brt\bt.\b.p\bpe\bem\bm i\bin\bnt\bte\ber\brm\bme\bed\bdi\bia\bat\bte\be_\b_C\bCA\bA.\b.p\bpe\bem\bm >\b> s\bse\ber\brv\bve\ber\br.\b.p\bpe\bem\bm
 
-  * If you use RFC 6698 TLSA "2 0 1" or "2 1 1" records to specify root CA
+  * If you publish RFC 6698 TLSA "2 0 1" or "2 1 1" records to specify root CA
     certificate digests, you must include the corresponding root CA
-    certificates in the "server.pem" certificate file.
+    certificates in the "server.pem" certificate file. See the documentation of
+    the tls_dane_trust_anchor_digest_enable main.cf parameter.
 
         % c\bca\bat\bt s\bse\ber\brv\bve\ber\br_\b_c\bce\ber\brt\bt.\b.p\bpe\bem\bm i\bin\bnt\bte\ber\brm\bme\bed\bdi\bia\bat\bte\be_\b_C\bCA\bA.\b.p\bpe\bem\bm r\bro\boo\bot\bt.\b.p\bpe\bem\bm >\b> s\bse\ber\brv\bve\ber\br.\b.p\bpe\bem\bm
 
@@ -147,20 +148,13 @@ assume that the certificate for "server.example.com" was issued by
     published TLSA records will typically cause the SMTP client to defer mail
     delivery. The foregoing also applies to "2 0 2" and "2 1 2" TLSA records or
     any other digest of a CA certificate, but it is expected that SHA256 will
-    be by far the most common digest for TLSA. You are strongly urged to
-    likewise include the root CA certificate in your server certificate file
-    even if you use "0 0 1" or "0 1 1" TLSA records. Some DANE implementations
-    in SMTP clients (Postfix among them) may treat RFC 6698 certificate usages
-    "0" and "2" interchangeably.
-
-    By default, support for TLSA records that specify certificate usage "0" via
-    a digest is disabled in the Postfix SMTP client. As a best practice,
-    publish either "3 0 1" or "3 1 1" TLSA associations that specify the SHA256
-    digest of the server certificate public key with the alias-expanded
-    hostname of each STARTTLS capable SMTP server. These continue to work when
-    a certificate is renewed with the same public/private key pair. See the
-    documentation of the tls_dane_trust_anchor_digest_enable main.cf parameter
-    for details.
+    be by far the most common digest for TLSA.
+
+    As a best practice, publish either "3 0 1" or "3 1 1" TLSA associations
+    that specify the SHA256 digest of the server certificate public key with
+    the alias-expanded hostname of each STARTTLS capable SMTP server. These
+    continue to work when a certificate is renewed with the same public/private
+    key pair.
 
 For instructions on how to compute the digest of a certificate or its public
 key for use in TLSA records, see the documentation of the
@@ -614,7 +608,8 @@ configurations with no server certificates that use o\bon\bnl\bly\by the anonymous c
 This is enabled by explicitly setting "smtpd_tls_cert_file = none" and not
 specifying an smtpd_tls_dcert_file or smtpd_tls_eccert_file.
 
-Example, MSA that requires TLSv1, not SSLv2 or SSLv3, with high grade ciphers:
+Example, MSA that requires TLSv1 or higher, not SSLv2 or SSLv3, with high grade
+ciphers:
 
     /etc/postfix/main.cf:
         smtpd_tls_cert_file = /etc/postfix/cert.pem
@@ -622,9 +617,9 @@ Example, MSA that requires TLSv1, not SSLv2 or SSLv3, with high grade ciphers:
         smtpd_tls_mandatory_ciphers = high
         smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5
         smtpd_tls_security_level = encrypt
-        # Preferred form with Postfix >= 2.5:
+        # Postfix >= 2.5:
         smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
-        # Alternative form.
+        # Legacy form with Postfix prior to 2.5:
         smtpd_tls_mandatory_protocols = TLSv1
 
 If you want to take advantage of ciphers with ephemeral Diffie-Hellman (EDH)
@@ -656,21 +651,25 @@ Examples:
         smtpd_tls_eecdh_grade = strong
 
 Postfix 2.8 and later, in combination with OpenSSL 0.9.7 and later allows TLS
-servers to preempt the TLS client's cipher preference list. This is possible
-only with SSLv3 and later, as in SSLv2 the client chooses the cipher from a
-list supplied by the server.
-
-By default, the OpenSSL server selects the client's most preferred cipher that
-the server supports. With SSLv3 and later, the server may choose its own most
-preferred cipher that is supported (offered) by the client. Setting
-"tls_preempt_cipherlist = yes" enables server cipher preferences. The default
-OpenSSL behavior applies with "tls_preempt_cipherlist = no".
-
-While server cipher selection may in some cases lead to a more secure or
-performant cipher choice, there is some risk of interoperability issues. In the
-past, some SSL clients have listed lower priority ciphers that they did not
-implement correctly. If the server chooses a cipher that the client prefers
-less, it may select a cipher whose client implementation is flawed.
+servers to preempt the TLS client's cipher-suite preference list. This is
+possible only with SSLv3 and later, as in SSLv2 the client chooses the cipher-
+suite from a list supplied by the server.
+
+By default, the OpenSSL server selects the client's most preferred cipher-suite
+that the server supports. With SSLv3 and later, the server may choose its own
+most preferred cipher-suite that is supported (offered) by the client. Setting
+"tls_preempt_cipherlist = yes" enables server cipher-suite preferences. The
+default OpenSSL behavior applies with "tls_preempt_cipherlist = no".
+
+While server cipher-suite selection may in some cases lead to a more secure or
+performant cipher-suite choice, there is some risk of interoperability issues.
+In the past, some SSL clients have listed lower priority ciphers that they did
+not implement correctly. If the server chooses a cipher that the client prefers
+less, it may select a cipher whose client implementation is flawed. Most
+notably Windows 2003 Microsoft Exchange servers have flawed implementations of
+DES-CBC3-SHA, which OpenSSL considers stronger than RC4-SHA. Enabling server
+cipher-suite selection may create interoperability issues with Windows 2003
+Microsoft Exchange clients.
 
 M\bMi\bis\bsc\bce\bel\bll\bla\ban\bne\beo\bou\bus\bs s\bse\ber\brv\bve\ber\br c\bco\bon\bnt\btr\bro\bol\bls\bs
 
@@ -916,78 +915,74 @@ level.
 The "dane" level is a stronger form of opportunistic TLS that is resistant to
 man in the middle and downgrade attacks when the destination domain uses DNSSEC
 to publish DANE TLSA records for its MX hosts. If a remote SMTP server has
-usable DANE TLSA records, these will be used to authenticate the server. If
-TLSA records are published for a given remote SMTP server (implying TLS
-support), but are not usable due to unsupported parameters, the Postfix SMTP
-client will use mandatory unauthenticated TLS. Otherwise, the Postfix SMTP
-client behavior is the same as with may.
+"usable" (see RFC 6698) DANE TLSA records, the server connection will be
+authenticated. When DANE authentication fails, there is no fallback to
+unauthenticated or plaintext delivery.
+
+If TLSA records are published for a given remote SMTP server (implying TLS
+support), but are all "unusable" due to unsupported parameters or malformed
+data, the Postfix SMTP client will use mandatory unauthenticated TLS.
+Otherwise, when no TLSA records are published, the Postfix SMTP client behavior
+is the same as with may.
+
+TLSA records must be published in DNSSEC validated DNS zones. Any TLSA records
+in DNS zones not protected via DNSSEC are ignored. The Postfix SMTP client will
+not look for TLSA records associated with MX hosts whose "A" or "AAAA" records
+lie in an "insecure" DNS zone. Such lookups have been observed to cause
+interoperability issues with poorly implemented DNS servers, and are in any
+case not expected to ever yield "secure" results, since that would require a
+very unlikey DLV DNS trust anchor configured between the host record and the
+associated "_25._tcp" child TLSA record.
 
 The "dane-only" level is a form of secure-channel TLS based on the DANE PKI. If
-usable TLSA records are present these are used to authenticate the remote SMTP
-server. Otherwise, or when server certificate verification fails, delivery via
-the server in question tempfails.
+"usable" TLSA records are present these are used to authenticate the remote
+SMTP server. Otherwise, or when server certificate verification fails, delivery
+via the server in question tempfails.
 
 At both security levels, the TLS policy for the destination is obtained via
 TLSA records validated with DNSSEC. For TLSA policy to be in effect, the
 destination domain's containing DNS zone must be signed and the Postfix SMTP
 client's operating system must be configured to send its DNS queries to a
 recursive DNS nameserver that is able to validate the signed records. Each MX
-host's DNS zone should also be signed, and should publish DANE TLSA (RFC 6698)
-records that specify how that MX host's TLS certificate is to be verified. TLSA
-records do not preempt the normal SMTP MX host selection algorithm, if some MX
-hosts support TLSA and others do not, TLS security will vary from delivery to
-delivery. It is up to the domain owner to configure their MX hosts and their
-DNS sensibly. To configure the Postfix SMTP client for DNSSEC lookups see the
-documentation for the smtp_dns_support_level main.cf parameter. The
-tls_dane_trust_anchor_digest_enable main.cf parameter controls optional support
-for trust-anchor digest TLSA records.
-
-The Postfix SMTP client deviates from RFC 6698 in cases where strict adherence
-is likely to create opportunities for delayed (or eventually bounced) email
-with no substantive security gain. Most notably, it is not expected that
-smtp_tls_CAfile and smtp_tls_CApath can reasonably include every public CA that
-a remote SMTP server's administrator may believe to be well-known. Therefore,
-certificate usages "0" and "2" from RFC 6698 which are intended to "constrain"
-existing PKI trust, are instead treated as "trust assertions" and mapped to "1"
-and "3" respectively. That is, with certificate usage "0" and "2", Postfix will
-not require the remote SMTP server's certificate to be trusted with respect to
-any locally defined public CAs, it is the domain owner's responsibility to
-ensure that the certificate associations in their TLSA records are appropriate
-to authenticate their SMTP servers.
-
-In addition, the Postfix SMTP client does not assume that the remote SMTP
-server will necessarily be configured to present the certificate of any usage
-"0" root CA in its TLS protocol certificate_list. Therefore, support for usage
-"0" certificate and public-key digests is disabled by default, see
-tls_dane_trust_anchor_digest_enable for details. While undigested trust-anchor
-certificates in TLSA records are supported, publishing complete certificates in
-DNS is unwise given the resulting excessively large DNS record sizes.
-Therefore, in its default configuration the Postfix SMTP client essentially
-supports only certificate usages "1", "2" and "3" (with "1" treated as though
-it were "3").
-
-Finally, the interaction of DANE with MX hostnames that are CNAMEs differs from
-the draft specification in the names used to construct the associated TLSA
-queries. When the MX hostname is a CNAME, the draft proposal to use the
-unexpanded MX hostname in TLSA lookups is fragile and unintuitive. For this to
-work, the domain owner has to either duplicate the TLSA records of the target
-(host, service) pair in his own DNS or furnish the target host with an
-additional certificate and private key that would be negotiated via SNI.
-Neither of these are robust or easy to manage. It is far better to expand the
-CNAME recursively to the underlying target hostname (keeping track of DNSSEC
-validation along the way) and to use the expanded name to construct the TLSA
-query and, if appropriate, in server certificate name checks. This is the
-approach taken by the Postfix SMTP client, and if sanity prevails will also be
-the approach taken in the final standard.
+host's DNS zone needs to also be signed, and needs to publish DANE TLSA (RFC
+6698) records that specify how that MX host's TLS certificate is to be
+verified.
+
+TLSA records do not preempt the normal SMTP MX host selection algorithm, if
+some MX hosts support TLSA and others do not, TLS security will vary from
+delivery to delivery. It is up to the domain owner to configure their MX hosts
+and their DNS sensibly. To configure the Postfix SMTP client for DNSSEC lookups
+see the documentation for the smtp_dns_support_level main.cf parameter. The
+tls_dane_trust_anchor_digest_enable main.cf parameter controls support for
+trust-anchor digest TLSA records. The tls_dane_digests and
+tls_dane_digest_agility parameters control the list of supported digests and
+digest downgrade attack resistance.
+
+DANE for SMTP MTAs deviates in some details from the baseline DANE protocol in
+RFC 6698. Most notably, it is not expected that SMTP MTAs can reasonably
+include every public CA that a remote SMTP server's administrator may believe
+to be well-known. Nor is there an interactive user to "click OK" when
+authentication fails.
+
+Therefore, certificate usages "0" and "1" from RFC 6698 which are intended to
+"constrain" existing PKI trust, are not supported. TLSA records with usage "0"
+are treated as "unusable". TLSA records with usage "1" are instead treated as
+"trust assertions" and mapped to usage "3". Specifically, with certificate
+usage "1", Postfix will not require the remote SMTP server's certificate to be
+trusted with respect to any locally defined public CAs, it is the domain
+owner's responsibility to ensure that the certificate associations in their
+TLSA records are appropriate to authenticate their SMTP servers.
+
+The Postfix SMTP client supports only certificate usages "2" and "3" (with "1"
+treated as though it were "3"). See tls_dane_trust_anchor_digest_enable for
+usage "2" usability considerations. Support for certificate usage "1" is an
+experiment, it may be withdrawn in the future. Server operators SHOULD NOT
+publish TLSA records with usage "1".
 
 When usable TLSA records are obtained for the remote SMTP server the Postfix
-SMTP client is obligated to include the SNI TLS extension in its SSL client
-hello message. This may help the remote SMTP server live up to its promise to
-provide a certificate that matches its TLSA records. Since TLS extensions
-require TLS 1.0 or later, the Postfix SMTP client must disable SSLv2 and SSLv3
-when SNI is required. If you use "dane" or "dane-only", do not disable TLSv1,
-except perhaps via the policy table for destinations which you are sure will
-support TLSv1.1 or TLSv1.2.
+SMTP client sends the SNI TLS extension in its SSL client hello message. This
+may help the remote SMTP server live up to its promise to provide a certificate
+that matches its TLSA records.
 
 For purposes of protocol and cipher selection, the "dane" security level is
 treated like a "mandatory" TLS security level, and weak ciphers and protocols
@@ -1010,8 +1005,8 @@ administrators should publish such EE records in preference to all other types.
 
 The pre-requisites for DANE support in the Postfix SMTP client are:
 
-  * A compile-time OpenSSL library that supports the TLS SNI extension and the
-    "sha256" and "sha512" message digests.
+  * A compile-time OpenSSL library that supports the TLS SNI extension and
+    "SHA-2" message digests.
   * A compile-time DNS resolver library that supports DNSSEC. Postfix binaries
     built on an older system will not support DNSSEC even if deployed on a
     system with an updated resolver library.
@@ -1566,9 +1561,9 @@ security policy.
 On the SMTP client, there are further complications. When delivering mail to a
 given domain, in contrast to HTTPS, one rarely uses the domain name directly as
 the target host of the SMTP session. More typically, one uses MX lookups -
-these are usually unauthenticated - to obtain the domain's SMTP server hostname
-(s). When, as is current practice, the client verifies the insecurely obtained
-MX hostname, it is subject to a DNS man-in-the-middle attack.
+- these are usually unauthenticated -- to obtain the domain's SMTP server
+hostname(s). When, as is current practice, the client verifies the insecurely
+obtained MX hostname, it is subject to a DNS man-in-the-middle attack.
 
 Adoption of DNSSEC and RFC6698 (DANE) may gradually (as domains implement
 DNSSEC and publish TLSA records for their MX hosts) address the DNS man-in-the-
@@ -1660,21 +1655,18 @@ d\bda\ban\bne\be
     TLSA records in DNSSEC. If no TLSA records are found, the effective
     security level used is may. If TLSA records are found, but none are usable,
     the effective security level is encrypt. When usable TLSA records are
-    obtained for the remote SMTP server, SSLv2 and SSLv3 are automatically
-    disabled (see smtp_tls_mandatory_protocols), and the server certificate
-    must match the TLSA records. The tls_dane_trust_anchor_digest_enable
-    parameter controls optional support for trust-anchor digest TLSA records.
-    RFC 6698 (DANE) TLS authentication and DNSSEC support is available with
-    Postfix 2.11 and later.
+    obtained for the remote SMTP server, SSLv2 is automatically disabled (see
+    smtp_tls_mandatory_protocols), and the server certificate must match the
+    TLSA records. RFC 6698 (DANE) TLS authentication and DNSSEC support is
+    available with Postfix 2.11 and later.
 d\bda\ban\bne\be-\b-o\bon\bnl\bly\by
     Mandatory DANE TLS. The TLS policy for the destination is obtained via TLSA
     records in DNSSEC. If no TLSA records are found, or none are usable, no
     connection is made to the server. When usable TLSA records are obtained for
-    the remote SMTP server, SSLv2 and SSLv3 are automatically disabled (see
+    the remote SMTP server, SSLv2 is automatically disabled (see
     smtp_tls_mandatory_protocols), and the server certificate must match the
-    TLSA records. The tls_dane_trust_anchor_digest_enable parameter controls
-    optional support for trust-anchor digest TLSA records. RFC 6698 (DANE) TLS
-    authentication and DNSSEC support is available with Postfix 2.11 and later.
+    TLSA records. RFC 6698 (DANE) TLS authentication and DNSSEC support is
+    available with Postfix 2.11 and later.
 f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt
     Certificate fingerprint verification. Available with Postfix 2.5 and later.
     At this security level, there are no trusted certificate authorities. The
@@ -1745,9 +1737,8 @@ Example:
     /etc/postfix/tls_policy:
         example.edu             none
         example.mil             may
-        example.gov             encrypt protocols=SSLv3:TLSv1 ciphers=high
-        example.com             verify
-                match=hostname:dot-nexthop protocols=SSLv3:TLSv1 ciphers=high
+        example.gov             encrypt ciphers=high
+        example.com             verify match=hostname:dot-nexthop ciphers=high
         example.net             secure
         .example.net            secure match=.example.net:example.net
         [mail.example.org]:587  secure match=nexthop
@@ -1838,7 +1829,7 @@ Example:
         smtp_tls_exclude_ciphers = aNULL
         # Preferred form with Postfix >= 2.5:
         smtp_tls_mandatory_protocols = !SSLv2
-        # Alternative form.
+        # Legacy form for Postifx < 2.5:
         smtp_tls_mandatory_protocols = SSLv3, TLSv1
         # Also available with Postfix >= 2.6:
         smtp_tls_ciphers = export
index 0a3ded0ecdaffb7030c5087caaa92b12f363a782..2c8619a89573d1a90459dd08a77e4c89997b58ff 100644 (file)
@@ -14,6 +14,131 @@ specifies the release date of a stable release or snapshot release.
 If you upgrade from Postfix 2.9 or earlier, read RELEASE_NOTES-2.10
 before proceeding.
 
+Incompatible changes with snapshot 20131126-nonprod
+===================================================
+
+The master_service_disable syntax has changed: use "service/type"
+instead of "service.type".  The new form is consistent with master.cf
+parameter namespaces.  The old form is still supported to avoid
+breaking existing configurations.
+
+Major changes with with snapshot 20131126-nonprod
+=================================================
+
+Support for advanced master.cf query and update operations.  This
+was implemented primarily to support automated system management
+tools.
+
+The idea is to make all Postfix master.cf details accessible as a
+list of "name=value" pairs, where the names are organized into
+structured name spaces.  This allows other programs to query
+information or request updates, without having to worry about the
+exact layout of master.cf files.
+
+First, an example that shows the smtp/inet service in the traditional
+form:
+
+    $ postconf -M smtp/inet
+    smtp       inet  n       -       n       -       -       smtpd
+
+Different variants of this command show different amounts of output.
+For example, "postconf -M smtp" enumerates all services that have
+a name "smtp" and any service type ("inet", "unix", etc.), and
+"postconf -M" enumerates all master.cf services.  
+
+General rule: each name component that is not present becomes a "*"
+wildcard.
+
+Coming back to the above example, the postconf -F option can now
+enumerate the smtp/inet service fields as follows:
+
+    $ postconf -F smtp/inet
+    smtp/inet/service = smtp
+    smtp/inet/type = inet
+    smtp/inet/private = n
+    smtp/inet/unprivileged = -
+    smtp/inet/chroot = n
+    smtp/inet/wakeup = -
+    smtp/inet/process_limit = -
+    smtp/inet/command = smtpd
+
+This form makes it very easy to change one field in master.cf.
+For example to turn on chroot on the smtp/inet service you use:
+
+    $ postconf -F smtp/inet/chroot=y
+
+Moreover, with "-F" you can specify "*" for service name or service
+type to get a wild-card match.  For example, to turn off chroot on
+all Postfix daemons, use this:
+
+    $ postconf -F '*/*/chroot=n'
+
+For a second example, let's look at the submission service.  This
+service typically has multple "-o parameter=value" overrides. First
+the traditional view:
+
+    $ postconf -Mf submission
+    submission inet  n       -       n       -       -       smtpd
+        -o smtpd_tls_security_level=encrypt
+        -o smtpd_sasl_auth_enable=yes
+        ...
+
+The postconf -P option can now enumerate these parameters as follows:
+
+    $ postconf -P submission
+    submission/inet/smtpd_sasl_auth_enable = yes
+    submission/inet/smtpd_tls_security_level = encrypt
+    ...
+
+Again, this form makes it very easy to modify one parameter
+setting, for example to change the smtpd_tls_security_level setting for
+the submission/inet service:
+
+    $ postconf -P 'submission/inet/smtpd_tls_security_level=may'
+
+You can create or remove a parametername=parametervalue setting:
+
+Create:
+    $ postconf -P 'submission/inet/parametername=parametervalue'
+
+Remove:
+    $ postconf -PX submission/inet/parametername
+
+Finally, adding master.cf entries is possible, but currently this
+does not yet have "advanced" support. It can only be done at the
+level of the traditional master.cf file format.
+
+For example, to clone the smtp/unix service settings and create a
+new delay/unix service you would enumerate the smtp/unix service
+like this:
+
+    $ postconf -M smtp/unix
+    smtp      unix  -       -       n       -       -       smtp
+
+Then you would copy those fields (except the first field) to 
+update or create the delay/unix service:
+
+    $ postconf -M delay/unix="delay   unix   -   -   n   -   -   smtp"
+
+To combine the above in one command:
+
+    $ postconf -M delay/unix="`postconf -M smtp/unix|awk '{$1 = "delay"}'`"
+
+This is perhaps not super-convenient for manual cloning, but it
+should be sufficient for programmatic configuration management.
+
+The -X (delete entry) and -# (comment out entry) options already
+exist for main.cf, and they now also work work for entire master.cf
+entries:
+
+Remove main.cf or master.cf entry:
+    $ postconf -X parametername
+    $ postconf -MX delay/unix
+
+Comment out main.cf or master.cf entry:
+    $ postconf -# parametername
+    $ postconf -M# delay/unix
+
 Major changes with snapshot 20131031
 ====================================
 
index 34939a3b4cfbb5cff40c7613b8bfd25af54f8fa7..fd14e88b60c2bade644484e2855b6ef5dafc2924 100644 (file)
@@ -226,9 +226,11 @@ size of the server TLS handshake.  </p>
 </pre>
 </blockquote>
 
-<li> <p> If you use <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> TLSA "2 0 1" or "2 1 1" records to
+<li> <p> If you publish <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> TLSA "2 0 1" or "2 1 1" records to
 specify root CA certificate digests, you must include the corresponding
-root CA certificates in the "server.pem" certificate file. </p>
+root CA certificates in the "server.pem" certificate file.  See the
+documentation of the <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a>
+parameter. </p>
 
 <blockquote>
 <pre>
@@ -236,29 +238,20 @@ root CA certificates in the "server.pem" certificate file. </p>
 </pre>
 </blockquote>
 
-<p> Remote SMTP clients will
-be able to use the TLSA record you publish (which only contains the
-certificate digest) only if they have access to the corresponding
-certificate.  Failure to verify certificates per the server's
-published TLSA records will typically cause the SMTP client to defer
-mail delivery.  The foregoing also applies to "2 0 2" and "2 1 2"
-TLSA records or any other digest of a CA certificate, but it is
-expected that SHA256 will be by far the most common digest for TLSA.
-You are <i>strongly</i> urged to likewise include the root CA
-certificate in your server certificate file even if you use "0 0
-1" or "0 1 1" TLSA records.  Some DANE implementations in SMTP
-clients (Postfix among them) may treat <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> certificate usages
-"0" and "2" interchangeably.  </p>
-
-<p>By default, support for TLSA records that specify certificate
-usage "0" via a digest is disabled in the Postfix SMTP client.  As
-a best practice, publish either "3 0 1" or "3 1 1" TLSA associations
-that specify the SHA256 digest of the server certificate public key
-with the alias-expanded hostname of each STARTTLS capable SMTP
-server.  These continue to work when a certificate is renewed with
-the same public/private key pair.  See the documentation of the
-<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a> parameter for details.
-</p>
+<p> Remote SMTP clients will be able to use the TLSA record you
+publish (which only contains the certificate digest) only if they
+have access to the corresponding certificate.  Failure to verify
+certificates per the server's published TLSA records will typically
+cause the SMTP client to defer mail delivery.  The foregoing also
+applies to "2 0 2" and "2 1 2" TLSA records or any other digest of
+a CA certificate, but it is expected that SHA256 will be by far the
+most common digest for TLSA.  </p>
+
+<p> As a best practice, publish either "3 0 1" or "3 1 1" TLSA
+associations that specify the SHA256 digest of the server certificate
+public key with the alias-expanded hostname of each STARTTLS capable
+SMTP server.  These continue to work when a certificate is renewed
+with the same public/private key pair.  </p>
 
 </ul>
 
@@ -861,8 +854,8 @@ certificates</a> that use <b>only</b> the anonymous ciphers. This is
 enabled by explicitly setting "<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> = none"
 and not specifying an <a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a> or <a href="postconf.5.html#smtpd_tls_eccert_file">smtpd_tls_eccert_file</a>. </p>
 
-<p> Example, MSA that requires TLSv1, not SSLv2 or SSLv3, with high grade
-ciphers: </p>
+<p> Example, MSA that requires TLSv1 or higher, not SSLv2 or SSLv3,
+with high grade ciphers: </p>
 
 <blockquote>
 <pre>
@@ -872,9 +865,9 @@ ciphers: </p>
     <a href="postconf.5.html#smtpd_tls_mandatory_ciphers">smtpd_tls_mandatory_ciphers</a> = high
     <a href="postconf.5.html#smtpd_tls_mandatory_exclude_ciphers">smtpd_tls_mandatory_exclude_ciphers</a> = aNULL, MD5
     <a href="postconf.5.html#smtpd_tls_security_level">smtpd_tls_security_level</a> = encrypt
-    # Preferred form with Postfix &ge; 2.5:
+    # Postfix &ge; 2.5:
     <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
-    # Alternative form.
+    # Legacy form with Postfix prior to 2.5:
     <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = TLSv1
 </pre>
 </blockquote>
@@ -918,23 +911,27 @@ secure for most situations. </p>
 </blockquote>
 
 <p> Postfix 2.8 and later, in combination with OpenSSL 0.9.7 and later
-allows TLS servers to preempt the TLS client's cipher preference list.
+allows TLS servers to preempt the TLS client's cipher-suite preference list.
 This is possible only with SSLv3 and later, as in SSLv2 the client
-chooses the cipher from a list supplied by the server. </p>
+chooses the cipher-suite from a list supplied by the server. </p>
 
 <p> By default, the OpenSSL server selects the client's most preferred
-cipher that the server supports. With SSLv3 and later, the server
-may choose its own most preferred cipher that is supported (offered)
+cipher-suite that the server supports.  With SSLv3 and later, the server
+may choose its own most preferred cipher-suite that is supported (offered)
 by the client. Setting "<a href="postconf.5.html#tls_preempt_cipherlist">tls_preempt_cipherlist</a> = yes" enables server
-cipher preferences. The default OpenSSL behavior applies with
+cipher-suite preferences. The default OpenSSL behavior applies with
 "<a href="postconf.5.html#tls_preempt_cipherlist">tls_preempt_cipherlist</a> = no". </p>
 
-<p> While server cipher selection may in some cases lead to a more secure
-or performant cipher choice, there is some risk of interoperability
-issues. In the past, some SSL clients have listed lower priority ciphers
-that they did not implement correctly. If the server chooses a cipher
-that the client prefers less, it may select a cipher whose client
-implementation is flawed. </p>
+<p> While server cipher-suite selection may in some cases lead to
+a more secure or performant cipher-suite choice, there is some risk
+of interoperability issues.  In the past, some SSL clients have
+listed lower priority ciphers that they did not implement correctly.
+If the server chooses a cipher that the client prefers less, it may
+select a cipher whose client implementation is flawed.  Most notably
+Windows 2003 Microsoft Exchange servers have flawed implementations
+of DES-CBC3-SHA, which OpenSSL considers stronger than RC4-SHA.
+Enabling server cipher-suite selection may create interoperability
+issues with Windows 2003 Microsoft Exchange clients.  </p>
 
 <h3><a name="server_misc"> Miscellaneous server controls</a> </h3>
 
@@ -1251,17 +1248,30 @@ the mandatory "dane-only" level. </p>
 href="#client_tls_may">opportunistic</a> TLS that is resistant to
 man in the middle and downgrade attacks when the destination domain
 uses DNSSEC to publish DANE TLSA records for its MX hosts.  If a
-remote SMTP server has usable DANE TLSA records, these will be used
-to authenticate the server.  If TLSA records are published for a
-given remote SMTP server (implying TLS support), but are not usable
-due to unsupported parameters, the Postfix SMTP client will use <a
+remote SMTP server has "usable" (see <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>) DANE TLSA records,
+the server connection will be authenticated.  When DANE authentication
+fails, there is no fallback to unauthenticated or plaintext delivery. </p>
+
+<p> If TLSA records are published for a given remote SMTP server
+(implying TLS support), but are all "unusable" due to unsupported
+parameters or malformed data, the Postfix SMTP client will use <a
 href="#client_tls_encrypt">mandatory</a> unauthenticated TLS.
-Otherwise, the Postfix SMTP client behavior is the same as with <a
-href="#client_tls_may">may</a>.  </p>
+Otherwise, when no TLSA records are published, the Postfix SMTP
+client behavior is the same as with <a href="#client_tls_may">may</a>. </p>
+
+<p> TLSA records must be published in DNSSEC validated DNS zones.
+Any TLSA records in DNS zones not protected via DNSSEC are ignored.
+The Postfix SMTP client will not look for TLSA records associated
+with MX hosts whose "A" or "AAAA" records lie in an "insecure" DNS
+zone.  Such lookups have been observed to cause interoperability
+issues with poorly implemented DNS servers, and are in any case not
+expected to ever yield "secure" results, since that would require
+a very unlikey DLV DNS trust anchor configured between the host
+record and the associated "_25._tcp" child TLSA record. </p>
 
 <p> The "dane-only" level is a form of <a
 href="#client_tls_secure">secure-channel</a> TLS based on the DANE PKI.
-If usable TLSA records are present these are used to authenticate the
+If "usable" TLSA records are present these are used to authenticate the
 remote SMTP server.  Otherwise, or when server certificate verification
 fails, delivery via the server in question tempfails.  </p>
 
@@ -1271,73 +1281,51 @@ to be in effect, the destination domain's containing DNS zone must
 be signed and the Postfix SMTP client's operating system must be
 configured to send its DNS queries to a recursive DNS nameserver
 that is able to validate the signed records.  Each MX host's DNS
-zone should also be signed, and should publish DANE TLSA (<a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>)
+zone needs to also be signed, and needs to publish DANE TLSA (<a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>)
 records that specify how that MX host's TLS certificate is to be
-verified.  TLSA records do not preempt the normal SMTP MX host
+verified.  </p>
+
+<p> TLSA records do not preempt the normal SMTP MX host
 selection algorithm, if some MX hosts support TLSA and others do
 not, TLS security will vary from delivery to delivery.  It is up
 to the domain owner to configure their MX hosts and their DNS
 sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
 see the documentation for the <a href="postconf.5.html#smtp_dns_support_level">smtp_dns_support_level</a> <a href="postconf.5.html">main.cf</a>
 parameter.  The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a> parameter
-controls optional support for trust-anchor digest TLSA records.
+controls support for trust-anchor digest TLSA records.  The
+<a href="postconf.5.html#tls_dane_digests">tls_dane_digests</a> and tls_dane_digest_agility parameters control
+the list of supported digests and digest downgrade attack resistance.
 </p>
 
-<p> The Postfix SMTP client deviates from <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> in cases where
-strict adherence is likely to create opportunities for delayed (or
-eventually bounced) email with no substantive security gain.  Most
-notably, it is not expected that <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> and <a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a>
-can reasonably include every public CA that a remote SMTP server's
-administrator may believe to be well-known.  Therefore, certificate
-usages "0" and "2" from <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> which are intended to "constrain"
-existing PKI trust, are instead treated as "trust assertions" and
-mapped to "1" and "3" respectively.  That is, with certificate usage
-"0" and "2", Postfix will not require the remote SMTP server's
-certificate to be trusted with respect to any locally defined public
-CAs, it is the domain owner's responsibility to ensure that the
-certificate associations in their TLSA records are appropriate
-to authenticate their SMTP servers.  </p>
-
-<p> In addition, the Postfix SMTP client does not assume that the
-remote SMTP server will necessarily be configured to present the
-certificate of any usage "0" root CA in its TLS protocol <a
-href="https://tools.ietf.org/html/rfc2246#section-7.4.2">certificate_list</a>.
-Therefore, support for usage "0" certificate and public-key digests
-is disabled by default, see <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> for
-details.  While undigested trust-anchor certificates in TLSA records
-are supported, publishing complete certificates in DNS is unwise
-given the resulting excessively large DNS record sizes.  Therefore,
-in its default configuration the Postfix SMTP client essentially
-supports only certificate usages "1", "2" and "3" (with "1" treated as
-though it were "3").  </p>
-
-<p> Finally, the interaction of DANE with MX hostnames that are
-CNAMEs differs from the draft specification in the names used to
-construct the associated <a
-href="https://tools.ietf.org/html/draft-ietf-dane-srv-02#section-3.2">TLSA
-queries</a>.  When the MX hostname is a CNAME, the draft proposal
-to use the unexpanded MX hostname in TLSA lookups is fragile and
-unintuitive.  For this to work, the domain owner has to either
-duplicate the TLSA records of the target (host, service) pair in
-his own DNS or furnish the target host with an additional
-certificate and private key that would be negotiated via SNI.
-Neither of these are robust or easy to manage.  It is far better
-to expand the CNAME recursively to the underlying target hostname
-(keeping track of DNSSEC validation along the way) and to use the
-expanded name to construct the TLSA query and, if appropriate, in
-server certificate name checks.  This is the approach taken by the
-Postfix SMTP client, and if sanity prevails will also be the approach
-taken in the final standard.  </p>
+<p> DANE for SMTP MTAs deviates in some details from the baseline
+DANE protocol in <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>.  Most notably, it is not expected that
+SMTP MTAs can reasonably include every public CA that a remote SMTP
+server's administrator may believe to be well-known.  Nor is there
+an interactive user to "click OK" when authentication fails. </p>
+
+<p> Therefore, certificate usages "0" and "1" from <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> which
+are intended to "constrain" existing PKI trust, are not supported.
+TLSA records with usage "0" are treated as "unusable".  TLSA records
+with usage "1" are instead treated as "trust assertions" and mapped
+to usage "3".  Specifically, with certificate usage "1", Postfix
+will not require the remote SMTP server's certificate to be trusted
+with respect to any locally defined public CAs, it is the domain
+owner's responsibility to ensure that the certificate associations
+in their TLSA records are appropriate to authenticate their SMTP
+servers.  </p>
+
+<p> The Postfix SMTP client supports only certificate usages "2"
+and "3" (with "1" treated as though it were "3").  See
+<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> for usage "2" usability
+considerations.  Support for certificate usage "1" is an experiment,
+it may be withdrawn in the future.  Server operators SHOULD NOT
+publish TLSA records with usage "1".  </p>
 
 <p> When usable TLSA records are obtained for the remote SMTP server
-the Postfix SMTP client is obligated to include the SNI TLS extension
-in its SSL client hello message.  This may help the remote SMTP
-server live up to its promise to provide a certificate that matches
-its TLSA records.  Since TLS extensions require TLS 1.0 or later,
-the Postfix SMTP client must disable SSLv2 and SSLv3 when SNI is
-required.  If you use "dane" or "dane-only", do not disable TLSv1,
-except perhaps via the policy table for destinations which you are
-sure will support TLSv1.1 or TLSv1.2.  </p>
+the Postfix SMTP client sends the SNI TLS extension in its SSL
+client hello message.  This may help the remote SMTP server live
+up to its promise to provide a certificate that matches its TLSA
+records.  </p>
 
 <p> For purposes of protocol and cipher selection, the "dane"
 security level is treated like a "mandatory" TLS security level,
@@ -1357,14 +1345,14 @@ certificate must contain at least one of these names.  </p>
 <p> When a DANE TLSA record specifies an end-entity (EE) certificate,
 (that is the actual server certificate), as with the fingerprint
 security level below, no name checks or certificate expiration checks
-are applied. The server certificate (or its public key) either matches
+are applied.  The server certificate (or its public key) either matches
 the DANE record or not.  Server administrators should publish such
 EE records in preference to all other types. </p>
 
 <p> The pre-requisites for DANE support in the Postfix SMTP client are: </p>
 <ul>
 <li> A <i>compile-time</i> OpenSSL library that supports the TLS SNI
-extension and the "sha256" and "sha512" message digests.
+extension and "SHA-2" message digests.
 <li> A <i>compile-time</i> DNS resolver library that supports DNSSEC.
 Postfix binaries built on an older system will not support DNSSEC even
 if deployed on a system with an updated resolver library.
@@ -2068,12 +2056,13 @@ server has a greater opportunity to mandate client security policy when
 it is a dedicated MSA that only handles outbound mail from trusted clients,
 below we focus on the client security policy. </p>
 
-<p> On the SMTP client, there are further complications. When delivering
-mail to a given domain, in contrast to HTTPS, one rarely uses the domain
-name directly as the target host of the SMTP session. More typically,
-one uses MX lookups - these are usually unauthenticated - to obtain the domain's SMTP server
-hostname(s). When, as is current practice, the client verifies the
-insecurely obtained MX hostname, it is subject to a DNS man-in-the-middle
+<p> On the SMTP client, there are further complications. When
+delivering mail to a given domain, in contrast to HTTPS, one rarely
+uses the domain name directly as the target host of the SMTP session.
+More typically, one uses MX lookups &mdash; these are usually
+unauthenticated &mdash; to obtain the domain's SMTP server hostname(s).
+When, as is current practice, the client verifies the insecurely
+obtained MX hostname, it is subject to a DNS man-in-the-middle
 attack. </p>
 
 <p> Adoption of DNSSEC and <a href="http://tools.ietf.org/html/rfc6698">RFC6698</a> (DANE) may gradually (as domains
@@ -2184,24 +2173,19 @@ DNSSEC.  If no TLSA records are found, the effective security level
 used is <a href="#client_tls_may">may</a>.  If TLSA records are
 found, but none are usable, the effective security level is <a
 href="#client_tls_encrypt">encrypt</a>.  When usable TLSA records
-are obtained for the remote SMTP server, SSLv2 and SSLv3 are
-automatically disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>), and the
-server certificate must match the TLSA records.  The
-<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls optional
-support for trust-anchor digest TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
-authentication and DNSSEC support is available with Postfix 2.11
-and later.  </dd>
+are obtained for the remote SMTP server, SSLv2 is automatically
+disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>), and the server certificate
+must match the TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication
+and DNSSEC support is available with Postfix 2.11 and later.  </dd>
 
 <dt><b>dane-only</b></dt> <dd><a href="#client_tls_dane">Mandatory DANE TLS</a>.
 The TLS policy for the destination is obtained via TLSA records in
 DNSSEC.  If no TLSA records are found, or none are usable, no
 connection is made to the server.  When usable TLSA records are
-obtained for the remote SMTP server, SSLv2 and SSLv3 are automatically
-disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>), and the server certificate
-must match the TLSA records.  The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>
-parameter controls optional support for trust-anchor digest TLSA
-records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication and DNSSEC support is
-available with Postfix 2.11 and later.  </dd>
+obtained for the remote SMTP server, SSLv2 is automatically disabled
+(see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>), and the server certificate must
+match the TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication and
+DNSSEC support is available with Postfix 2.11 and later.  </dd>
 
 <dt><b>fingerprint</b></dt> <dd><a href="#client_tls_fprint">Certificate
 fingerprint verification.</a> Available with Postfix 2.5 and
@@ -2291,9 +2275,8 @@ Example:
 /etc/postfix/tls_policy:
     example.edu             none
     example.mil             may
-    example.gov             encrypt protocols=SSLv3:TLSv1 ciphers=high
-    example.com             verify     
-            match=hostname:dot-nexthop protocols=SSLv3:TLSv1 ciphers=high
+    example.gov             encrypt ciphers=high
+    example.com             verify match=hostname:dot-nexthop ciphers=high
     example.net             secure
     .example.net            secure match=.example.net:example.net
     [mail.example.org]:587  secure match=nexthop
@@ -2407,7 +2390,7 @@ the SSL/TLS protocols used with opportunistic TLS. </p>
     <a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = aNULL
     # Preferred form with Postfix &ge; 2.5:
     <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2
-    # Alternative form.
+    # Legacy form for Postifx &lt; 2.5:
     <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = SSLv3, TLSv1
     # Also available with Postfix &ge; 2.6:
     <a href="postconf.5.html#smtp_tls_ciphers">smtp_tls_ciphers</a> = export
index 4ef7b501f96287ccb7d3303357643d0ba58961c8..fce80c8e5669ef83c366a7ac5a34feab833cd8d9 100644 (file)
@@ -598,14 +598,13 @@ SMTP(8)                                                                SMTP(8)
               hostname  is  not  an alias and its address records
               lie in an unsigned zone.
 
-       <b><a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>   (trust-anchor-asser-</b>
-       <b>tion)</b>
+       <b><a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> (yes)</b>
               <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> trust-anchor digest support in the Postfix
               TLS library.
 
        <b><a href="postconf.5.html#tlsmgr_service_name">tlsmgr_service_name</a> (tlsmgr)</b>
-              The  name  of  the  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in mas-
-              ter.cf.
+              The  name  of  the  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in <a href="master.5.html">mas-
+              ter.cf</a>.
 
 <b>OBSOLETE STARTTLS CONTROLS</b>
        The following configuration parameters exist for  compati-
index 44fc4a22a87b23a8583a44a4c3ad6992b8a86733..db98a74728c6ed5f3e255be655c8ef210cac0749 100644 (file)
@@ -15,13 +15,35 @@ POSTCONF(1)                                                        POSTCONF(1)
        <b>postconf</b> [<b>-dfhnovx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-C</b> <i>class,...</i>] [<i>param-</i>
        <i>eter ...</i>]
 
-       <b>postconf</b> [<b>-ev</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>parameter=value ...</i>]
+       <b>postconf</b> [<b>-ev</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>parameter=value ...</i>
 
-       <b>postconf</b> [<b>-#vX</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>parameter ...</i>]
+       <b>postconf</b> [<b>-#vX</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>parameter ...</i>
 
-       <b>Managing <a href="master.5.html">master.cf</a>:</b>
+       <b>Managing <a href="master.5.html">master.cf</a> service entries:</b>
 
-       <b>postconf</b> [<b>-fMovx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service ...</i>]
+       <b>postconf</b> [<b>-fMovx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>] <i>...</i>]
+
+       <b>postconf</b> [<b>-eMv</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type=value ...</i>
+
+       <b>postconf</b> [<b>-#MvX</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type ...</i>
+
+       <b>Managing <a href="master.5.html">master.cf</a> service fields:</b>
+
+       <b>postconf</b> [<b>-fFovx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>[<b>/</b><i>field</i>]]
+       <i>...</i>]
+
+       <b>postconf</b> [<b>-eFv</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type</i><b>/</b><i>field=value</i>
+       <i>...</i>
+
+       <b>Managing <a href="master.5.html">master.cf</a> service parameters:</b>
+
+       <b>postconf</b> [<b>-fPovx</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<i>service</i>[<b>/</b><i>type</i>[<b>/</b><i>parame-</i>
+       <i>ter</i>]] <i>...</i>]
+
+       <b>postconf</b> [<b>-ePv</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type</i><b>/</b><i>parame-</i>
+       <i>ter=value ...</i>
+
+       <b>postconf</b> [<b>-PXv</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <i>service</i><b>/</b><i>type</i><b>/</b><i>parameter ...</i>
 
        <b>Managing bounce message templates:</b>
 
@@ -35,73 +57,73 @@ POSTCONF(1)                                                        POSTCONF(1)
        By default, the <a href="postconf.1.html"><b>postconf</b>(1)</a> command displays the values of
        <a href="postconf.5.html"><b>main.cf</b></a> configuration parameters, and warns about possible
        mis-typed parameter names (Postfix 2.9 and later).  It can
-       also  change  <a href="postconf.5.html"><b>main.cf</b></a>  configuration  parameter values, or
-       display other configuration information about the  Postfix
+       also change <a href="postconf.5.html"><b>main.cf</b></a>  configuration  parameter  values,  or
+       display  other configuration information about the Postfix
        mail system.
 
        Options:
 
-       <b>-a</b>     List  the available SASL server plug-in types.  The
-              SASL   plug-in   type   is   selected   with    the
+       <b>-a</b>     List the available SASL server plug-in types.   The
+              SASL    plug-in   type   is   selected   with   the
               <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b> configuration parameter by specify-
               ing one of the names listed below.
 
-              <b>cyrus</b>  This server plug-in is available when  Post-
+              <b>cyrus</b>  This  server plug-in is available when Post-
                      fix is built with Cyrus SASL support.
 
               <b>dovecot</b>
                      This server plug-in uses the Dovecot authen-
                      tication server, and is available when Post-
-                     fix  is built with any form of SASL support.
+                     fix is built with any form of SASL  support.
 
-              This feature is  available  with  Postfix  2.3  and
+              This  feature  is  available  with  Postfix 2.3 and
               later.
 
-       <b>-A</b>     List  the available SASL client plug-in types.  The
-              SASL   plug-in   type   is   selected   with    the
-              <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b>   or   <b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b>  configuration
-              parameters by specifying one of  the  names  listed
+       <b>-A</b>     List the available SASL client plug-in types.   The
+              SASL    plug-in   type   is   selected   with   the
+              <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b>  or   <b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b>   configuration
+              parameters  by  specifying  one of the names listed
               below.
 
-              <b>cyrus</b>  This  client plug-in is available when Post-
+              <b>cyrus</b>  This client plug-in is available when  Post-
                      fix is built with Cyrus SASL support.
 
-              This feature is  available  with  Postfix  2.3  and
+              This  feature  is  available  with  Postfix 2.3 and
               later.
 
        <b>-b</b> [<i>template</i><b>_</b><i>file</i>]
               Display the message text that appears at the begin-
-              ning of delivery  status  notification  (DSN)  mes-
+              ning  of  delivery  status  notification (DSN) mes-
               sages, replacing $<b>name</b> expressions with actual val-
               ues as described in <a href="bounce.5.html"><b>bounce</b>(5)</a>.
 
-              To override the built-in templates, specify a  tem-
-              plate  file name at the end of the <a href="postconf.1.html"><b>postconf</b>(1)</a> com-
-              mand line, or specify a file name in  <a href="postconf.5.html"><b>main.cf</b></a>  with
+              To  override the built-in templates, specify a tem-
+              plate file name at the end of the <a href="postconf.1.html"><b>postconf</b>(1)</a>  com-
+              mand  line,  or specify a file name in <a href="postconf.5.html"><b>main.cf</b></a> with
               the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter.
 
               To force selection of the built-in templates, spec-
-              ify an empty template file name on the  <a href="postconf.1.html"><b>postconf</b>(1)</a>
+              ify  an empty template file name on the <a href="postconf.1.html"><b>postconf</b>(1)</a>
               command line (in shell language: "").
 
-              This  feature  is  available  with  Postfix 2.3 and
+              This feature is  available  with  Postfix  2.3  and
               later.
 
        <b>-c</b> <i>config</i><b>_</b><i>dir</i>
-              The <a href="postconf.5.html"><b>main.cf</b></a> configuration  file  is  in  the  named
+              The  <a href="postconf.5.html"><b>main.cf</b></a>  configuration  file  is  in the named
               directory  instead  of  the  default  configuration
               directory.
 
        <b>-C</b> <i>class,...</i>
-              When displaying  <a href="postconf.5.html"><b>main.cf</b></a>  parameters,  select  only
+              When  displaying  <a href="postconf.5.html"><b>main.cf</b></a>  parameters,  select only
               parameters from the specified class(es):
 
               <b>builtin</b>
                      Parameters with built-in names.
 
               <b>service</b>
-                     Parameters  with  service-defined names (the
-                     first field of  a  <a href="master.5.html"><b>master.cf</b></a>  entry  plus  a
+                     Parameters with service-defined  names  (the
+                     first  field  of  a  <a href="master.5.html"><b>master.cf</b></a>  entry plus a
                      Postfix-defined suffix).
 
               <b>user</b>   Parameters with user-defined names.
@@ -111,18 +133,41 @@ POSTCONF(1)                                                        POSTCONF(1)
               The default is as if "<b>-C all</b>" is specified.
 
        <b>-d</b>     Print <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of
-              actual settings.  Specify <b>-df</b> to  fold  long  lines
+              actual  settings.   Specify  <b>-df</b> to fold long lines
               for human readability (Postfix 2.9 and later).
 
-       <b>-e</b>     Edit  the  <a href="postconf.5.html"><b>main.cf</b></a>  configuration  file, and update
-              parameter settings with the "<i>name=value</i>"  pairs  on
-              the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. The file is copied to
-              a temporary file then renamed into place.   Specify
-              quotes to protect special characters and whitespace
-              on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
-
-              The <b>-e</b> is no longer needed with Postfix version 2.8
-              and later.
+       <b>-e</b>     Edit the <a href="postconf.5.html"><b>main.cf</b></a>  configuration  file,  and  update
+              parameter  settings  with the "<i>name=value</i>" pairs on
+              the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
+
+              With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and
+              replace one or more service entries with new values
+              as specified with "<i>service/type=value</i>" on the <a href="postconf.1.html"><b>post-</b></a>
+              <a href="postconf.1.html"><b>conf</b>(1)</a> command line.
+
+              With <b>-F</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and
+              replace one or more service fields with new  values
+              as  specied  with "<i>service/type/field=value</i>" on the
+              <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Currently, the  "command"
+              field  contains  the command name and command argu-
+              ments.  this may change in the near future, so that
+              the "command" field contains only the command name,
+              and a new "arguments" pseudofield contains the com-
+              mand arguments.
+
+              With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and
+              add or update one or more  service  parameter  set-
+              tings (-o parameter=value settings) with new values
+              as specied with  "<i>service/type/parameter=value</i>"  on
+              the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
+
+              In all cases the file is copied to a temporary file
+              then renamed into place.  Specify quotes to protect
+              special  characters  and  whitespace  on  the <a href="postconf.1.html"><b>post-</b></a>
+              <a href="postconf.1.html"><b>conf</b>(1)</a> command line.
+
+              The <b>-e</b> option is no longer needed with Postfix ver-
+              sion 2.8 and later.
 
        <b>-f</b>     Fold  long lines when printing <a href="postconf.5.html"><b>main.cf</b></a> or <a href="master.5.html"><b>master.cf</b></a>
               configuration file entries, for human  readability.
@@ -130,8 +175,22 @@ POSTCONF(1)                                                        POSTCONF(1)
               This  feature  is  available  with  Postfix 2.9 and
               later.
 
-       <b>-h</b>     Show <a href="postconf.5.html"><b>main.cf</b></a> parameter values without the "<i>name</i> = "
-              label that normally precedes the value.
+       <b>-F</b>     Show <a href="master.5.html"><b>master.cf</b></a> per-entry field settings (by default
+              all  services  and  all  fields),  formatted as one
+              "<i>service/type/field=value</i>" per line. Specify <b>-Ff</b> to
+              fold long lines.
+
+              Specify  one or more "<i>service/type/field</i>" instances
+              on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line to limit the output
+              to  fields of interest.  Trailing parameter name or
+              service type fields that are omitted will  be  han-
+              dled as "*" wildcard fields.
+
+              This  feature  is  available  with Postfix 2.11 and
+              later.
+
+       <b>-h</b>     Show parameter  or  attribute  values  without  the
+              "<i>name</i> = " label that normally precedes the value.
 
        <b>-l</b>     List  the  names  of  all supported mailbox locking
               methods.  Postfix supports the following methods:
@@ -195,15 +254,15 @@ POSTCONF(1)                                                        POSTCONF(1)
                      A non-shared, in-memory hash table. Its con-
                      tent are lost when a process terminates.
 
-              <b>ldap</b> (read-only)
-                     LDAP database client. This is  described  in
-                     <a href="ldap_table.5.html"><b>ldap_table</b>(5)</a>.
-
               <b>lmdb</b>   OpenLDAP  LMDB  database  (a  memory-mapped,
                      persistent file).  Available on systems with
-                     support   for   LMDB   databases.   This  is
+                     support  for  LMDB   databases.    This   is
                      described in <a href="lmdb_table.5.html"><b>lmdb_table</b>(5)</a>.
 
+              <b>ldap</b> (read-only)
+                     LDAP  database  client. This is described in
+                     <a href="ldap_table.5.html"><b>ldap_table</b>(5)</a>.
+
               <b>memcache</b>
                      Memcache database client. This is  described
                      in <a href="memcache_table.5.html"><b>memcache_table</b>(5)</a>.
@@ -213,12 +272,6 @@ POSTCONF(1)                                                        POSTCONF(1)
                      with support for MySQL databases.   This  is
                      described in <a href="mysql_table.5.html"><b>mysql_table</b>(5)</a>.
 
-              <b>nis</b> (read-only)
-                     NIS client.
-
-              <b>nisplus</b> (read-only)
-                     NIS+ client.
-
               <b>pcre</b> (read-only)
                      A lookup table based on Perl Compatible Reg-
                      ular  Expressions.   The  file   format   is
@@ -293,28 +346,52 @@ POSTCONF(1)                                                        POSTCONF(1)
               file  contents.  Specify <b>-Mf</b> to fold long lines for
               human readability.
 
-              If <i>service ...</i> is specified, only the matching ser-
-              vices  will  be  output. For example, "<b>postconf -Mf</b>
-              <b>inet</b>" will output all services that listen  on  the
-              network.
-
-              Specify  zero  or  more arguments, each with a <i>ser-</i>
-              <i>vice-type</i> name (<b>inet</b>, <b>unix</b>, <b>fifo</b>, or <b>pass</b>) or  with
-              a  <i>service-name.service-type</i>  pair,  where <i>service-</i>
-              <i>name</i> is the first field of a <a href="master.5.html">master.cf</a> entry.
+              Specify zero or more arguments, each  with  a  <i>ser-</i>
+              <i>vice-name</i>  or <i>service-name/service-type</i> pair, where
+              <i>service-name</i> is the  first  field  of  a  <a href="master.5.html">master.cf</a>
+              entry and <i>service-type</i> is one of (<b>inet</b>, <b>unix</b>, <b>fifo</b>,
+              or <b>pass</b>).
+
+              If  <i>service-name</i>  or  <i>service-name/service-type</i>  is
+              specified, only the matching <a href="master.5.html">master.cf</a> entries will
+              be output. For example, "<b>postconf  -Mf  smtp</b>"  will
+              output all services named "smtp", and "<b>postconf -Mf</b>
+              <b>smtp/inet</b>" will output only the smtp  service  that
+              listens  on  the  network.   Trailing  service type
+              fields that are omitted  will  be  handled  as  "*"
+              wildcard fields.
 
-              This feature is  available  with  Postfix  2.9  and
-              later.
+              This  feature  is  available  with  Postfix 2.9 and
+              later. The syntax was changed from  "<i>name.type</i>"  to
+              "<i>name/type</i>",  and  "*"  wildcard  support was added
+              with Postfix 2.11.
 
-       <b>-n</b>     Show   only   configuration  parameters  that  have
-              explicit <i>name=value</i> settings in  <a href="postconf.5.html"><b>main.cf</b></a>.   Specify
+       <b>-n</b>     Show  only  configuration  parameters   that   have
+              explicit  <i>name=value</i>  settings in <a href="postconf.5.html"><b>main.cf</b></a>.  Specify
               <b>-nf</b> to fold long lines for human readability (Post-
               fix 2.9 and later).
 
        <b>-o</b> <i>name=value</i>
               Override <a href="postconf.5.html"><b>main.cf</b></a> parameter settings.
 
-              This feature is available  with  Postfix  2.10  and
+              This  feature  is  available  with Postfix 2.10 and
+              later.
+
+       <b>-p</b>     Show  <a href="postconf.5.html"><b>main.cf</b></a>  parameter  settings.  This  is   the
+              default.
+
+       <b>-P</b>     Show   <a href="master.5.html"><b>master.cf</b></a>  service  parameter  settings  (by
+              default all services and all parameters).   format-
+              ted as one "<i>service/type/parameter=value</i>" per line.
+              Specify <b>-Pf</b> to fold long lines.
+
+              Specify  one   or   more   "<i>service/type/parameter</i>"
+              instances  on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line to limit
+              the output to  parameters  of  interest.   Trailing
+              parameter  name  or  service  type  fields that are
+              omitted will be handled as "*" wildcard fields.
+
+              This feature is available  with  Postfix  2.11  and
               later.
 
        <b>-t</b> [<i>template</i><b>_</b><i>file</i>]
@@ -346,25 +423,51 @@ POSTCONF(1)                                                        POSTCONF(1)
 
        <b>-X</b>     Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and remove the
               parameters named on the <a href="postconf.1.html"><b>postconf</b>(1)</a>  command  line.
-              The file is copied to a temporary file then renamed
-              into place.  Specify a list of parameter names, not
-              "<i>name=value</i>"  pairs.   There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> com-
-              mand to perform the reverse operation.
+              Specify a list of parameter names, not "<i>name=value</i>"
+              pairs.
 
-              This feature is available  with  Postfix  2.10  and
-              later.
+              With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and
+              remove  one  or  more  service entries as specified
+              with  "<i>service/type</i>"  on  the  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command
+              line.
+
+              With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and
+              remove one or more service parameter  settings  (-o
+              parameter=value  settings)  as  specied  with "<i>ser-</i>
+              <i>vice/type/parameter</i>"  on  the  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command
+              line.
+
+              In all cases the file is copied to a temporary file
+              then renamed into place.  Specify quotes to protect
+              special characters on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
+
+              There is no  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command  to  perform  the
+              reverse operation.
+
+              This  feature  is  available  with Postfix 2.10 and
+              later.  Support for -M and -P was added with  Post-
+              fix 2.11.
 
        <b>-#</b>     Edit  the  <a href="postconf.5.html"><b>main.cf</b></a>  configuration file, and comment
               out the parameters named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command
               line,  so  that  those  parameters  revert to their
-              default values.  The file is copied to a  temporary
-              file  then  renamed  into place.  Specify a list of
-              parameter names, not "<i>name=value</i>" pairs.  There  is
-              no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse oper-
-              ation.
+              default values.  Specify a list of parameter names,
+              not "<i>name=value</i>" pairs.
+
+              With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and
+              comment out one or more service entries  as  speci-
+              fied with "<i>service/type</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command
+              line.
+
+              In all cases the file is copied to a temporary file
+              then renamed into place.  Specify quotes to protect
+              special characters on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
+
+              There  is  no  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command  to perform the
+              reverse operation.
 
               This feature is  available  with  Postfix  2.6  and
-              later.
+              later.  Support for -M was added with Postfix 2.11.
 
 <b>DIAGNOSTICS</b>
        Problems are reported to the standard error stream.
@@ -374,41 +477,39 @@ POSTCONF(1)                                                        POSTCONF(1)
               Directory with Postfix configuration files.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The  following  <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
+       The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are  especially  relevant
        to this program.
 
-       The text below provides  only  a  parameter  summary.  See
+       The  text  below  provides  only  a parameter summary. See
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location  of  the Postfix <a href="postconf.5.html">main.cf</a> and
+              The default location of  the  Postfix  <a href="postconf.5.html">main.cf</a>  and
               <a href="master.5.html">master.cf</a> configuration files.
 
        <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a> (empty)</b>
-              Pathname of a configuration file with  bounce  mes-
+              Pathname  of  a configuration file with bounce mes-
               sage templates.
 
 <b>FILES</b>
        /etc/postfix/<a href="postconf.5.html">main.cf</a>, Postfix configuration parameters
-       /etc/postfix/<a href="master.5.html">master.cf</a>, Postfix master daemon configuraton
+       /etc/postfix/<a href="master.5.html">master.cf</a>, Postfix master daemon configuration
 
 <b>SEE ALSO</b>
-       <a href="bounce.5.html">bounce(5)</a>, bounce template file format
-       <a href="master.5.html">master(5)</a>, <a href="master.5.html">master.cf</a> configuration file syntax
-       <a href="postconf.5.html">postconf(5)</a>, <a href="postconf.5.html">main.cf</a> configuration file syntax
+       <a href="bounce.5.html">bounce(5)</a>, bounce template file format <a href="master.5.html">master(5)</a>, <a href="master.5.html">master.cf</a>
+       configuration file syntax <a href="postconf.5.html">postconf(5)</a>, <a href="postconf.5.html">main.cf</a> configuration
+       file syntax
 
 <b>README FILES</b>
        <a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
 
 <b>LICENSE</b>
-       The Secure Mailer license must be  distributed  with  this
+       The  Secure  Mailer  license must be distributed with this
        software.
 
 <b>AUTHOR(S)</b>
-       Wietse Venema
-       IBM T.J. Watson Research
-       P.O. Box 704
-       Yorktown Heights, NY 10598, USA
+       Wietse Venema IBM T.J. Watson Research P.O. Box 704 Yorktown
+       Heights, NY 10598, USA
 
                                                                    POSTCONF(1)
 </pre> </body> </html>
index 1c672089f491342e37479b65530b1e395e65029e..2d5bc198454d15d2b6c78069acc29e07b7502651 100644 (file)
@@ -11479,18 +11479,6 @@ whitespace, commas or colons. In the policy table "protocols" attribute
 empty value means allow all protocols. The valid protocol names, (see
 <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and "TLSv1". </p>
 
-<p> At the <a href="TLS_README.html#client_tls_dane">dane</a> and
-<a href="TLS_README.html#client_tls_dane">dane-only</a> security
-levels, when usable TLSA records are obtained for the remote SMTP
-server, the Postfix SMTP client is obligated to include the SNI TLS
-extension in its SSL client hello message.  This may help the remote
-SMTP server live up to its promise to provide a certificate that
-matches its TLSA records.  Since TLS extensions require TLS 1.0 or
-later, the Postfix SMTP client must disable SSLv2 and SSLv3 when
-SNI is required.  If you use "dane" or "dane-only" do not disable
-TLSv1, except perhaps via the policy table for destinations which
-you are sure will support TLSv1.1 or TLSv1.2.  </p>
-
 <p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
 1.0.1 or later, these, or any other new protocol versions, are
@@ -11518,7 +11506,7 @@ and higher. </p>
 <pre>
 # Preferred form with Postfix &ge; 2.5:
 <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
-# Alternative form.
+# Legacy form with Postfix &lt; 2.5:
 <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = TLSv1
 </pre>
 
@@ -11690,23 +11678,17 @@ the effective security level used is <a
 href="TLS_README.html#client_tls_may">may</a>.  If TLSA records are
 found, but none are usable, the effective security level is <a
 href="TLS_README.html#client_tls_encrypt">encrypt</a>.  When usable
-TLSA records are obtained for the remote SMTP server, SSLv2 and
-SSLv3 are automatically disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>),
-and the server certificate must match the TLSA records.  The
-<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls optional
-support for trust-anchor digest TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
-authentication and DNSSEC support is available with Postfix 2.11
-and later.  </dd>
+TLSA records are obtained for the remote SMTP server, the
+server certificate must match the TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE)
+TLS authentication and DNSSEC support is available with Postfix
+2.11 and later.  </dd>
 
 <dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
 <dd>Mandatory DANE TLS.  The TLS policy for the destination is
 obtained via TLSA records in DNSSEC.  If no TLSA records are found,
 or none are usable, no connection is made to the server.  When
-usable TLSA records are obtained for the remote SMTP server, SSLv2
-and SSLv3 are automatically disabled (see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>),
-and the server certificate must match the TLSA records.  The
-<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls optional
-support for trust-anchor digest TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
+usable TLSA records are obtained for the remote SMTP server, the
+server certificate must match the TLSA records.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS
 authentication and DNSSEC support is available with Postfix 2.11
 and later.  </dd>
 
@@ -11822,7 +11804,7 @@ when the OpenSSL library is newer than Postfix.  </p>
 
 <p> Example: </p>
 <pre>
-# TLSv1 only!
+# TLSv1 or better:
 <a href="postconf.5.html#smtp_tls_protocols">smtp_tls_protocols</a> = !SSLv2, !SSLv3
 </pre>
 
@@ -11955,20 +11937,17 @@ parameter.  When TLSA records are not found or are all unusable the
 effective security level is "may" or "encrypt" respectively.  For
 purposes of protocol and cipher selection, the "dane" security level
 is treated like a "mandatory" TLS security level, and weak ciphers
-and protocols are disabled.  SSLv2 and SSLv3 are automatically
-disabled when usable TLSA records are obtained for the remote SMTP
-server, see <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> for details.  Since DANE
-authenticates server certificates the "aNULL" cipher-suites are
-transparently excluded at this level, no need to configure this
-manually.  The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <a href="postconf.5.html">main.cf</a> parameter
-controls optional support for trust-anchor digest TLSA records.
-<a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication is available with Postfix 2.11
-and later.  </dd>
+and protocols are disabled.  Since DANE authenticates server
+certificates the "aNULL" cipher-suites are transparently excluded
+at this level, no need to configure this manually.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE)
+TLS authentication is available with Postfix 2.11 and later.  </dd>
 
 <dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
-<dd>Mandatory DANE TLS.  This is just like "dane" above, only DANE
-TLSA authentication is mandatory.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE) TLS authentication
-is available with Postfix 2.11 and later.  </dd>
+<dd>Mandatory DANE TLS.  This is just like "dane" above, but DANE
+TLSA authentication is required.  There is no fallback to "may" or
+"encrypt" when TLSA records are missing or unusable.  <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>
+(DANE) TLS authentication is available with Postfix 2.11 and later.
+</dd>
 
 <dt><b><a href="TLS_README.html#client_tls_fingerprint">fingerprint</a></b></dt>
 <dd>Certificate fingerprint verification.
@@ -15615,11 +15594,11 @@ works in addition to the exclusions listed with <a href="postconf.5.html#smtpd_t
 (default: !SSLv2)</b></DT><DD>
 
 <p> The SSL/TLS protocols accepted by the Postfix SMTP server with
-mandatory TLS encryption. If the list is empty, the server supports all
-available SSL/TLS protocol versions.  A non-empty value is a list
-of protocol
-names separated by whitespace, commas or colons. The supported protocol
-names are "SSLv2", "SSLv3" and "TLSv1", and are not case sensitive. </p>
+mandatory TLS encryption. If the list is empty, the server supports
+all available SSL/TLS protocol versions.  A non-empty value is a
+list of protocol names separated by whitespace, commas or colons.
+The supported protocol names are "SSLv2", "SSLv3" and "TLSv1", and
+are not case sensitive. </p>
 
 <p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
@@ -15643,9 +15622,9 @@ level. </p>
 <p> Example: </p>
 
 <pre>
-<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = TLSv1
-# Alternative form with Postfix &ge; 2.5:
 <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
+# Legacy form with Postfix &lt; 2.5:
+<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = TLSv1
 </pre>
 
 <p> This feature is available in Postfix 2.3 and later. </p>
@@ -16212,6 +16191,70 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 <p> This feature is available in Postfix 2.2 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="tls_dane_digest_agility">tls_dane_digest_agility</a>
+(default: on)</b></DT><DD>
+
+<p> Configure DANE TLSA digest algorithm agility.  When digest
+algorithm agility is enabled, and the server and client support a
+common strong digest algorithm, TLSA records with weaker digest
+algorithms are ignored.  </p>
+
+<p> Specify one of the following: </p>
+
+<dl>
+
+<dt><b>off</b></dt>
+<dd> DANE verification examines each well-formed record in the TLSA
+RRset whose matching type is either "0" (no hash used) or is one of
+the digest algorithms listed in $<a href="postconf.5.html#tls_dane_digests">tls_dane_digests</a>.  This setting
+is not recommended.  </dd>
+
+<dt><b>on</b></dt>
+<dd> From each group of well-formed TLSA RRs a non-zero digest
+matching type with the same certificate usage and selector, DANE
+verification examines only those records whose matching type has
+the highest precedence (appear earliest in $<a href="postconf.5.html#tls_dane_digests">tls_dane_digests</a>) are
+considered.  </dd>
+
+<dt><b>maybe</b></dt>
+<dd> For compatibility with digest algorithm agility, each certificate
+or public key whose digest is included in a DANE TLSA RRset, SHOULD
+be published with the same set of digest matching type values as
+any other with the same usage and selector.  Therefore, compatible
+TLSA RRsets will contain an identical count of well-formed RRs with
+each non-zero digest matching type for any fixed combination of
+usage and selector.  When this constraint is violated, or any of
+the digest records are malformed, digest algorithm agility will
+disabled.  Otherwise, digest algorithm agility is enabled.   </dd>
+
+</dl>
+
+<p> Digest algorithm agility ensures that the strongest digest
+supported by both the Postfix SMTP client and the remote server is
+used, and weaker digests are ignored.  This supports non-disruptive
+deprecation of outdated digest algorithms. </p>
+
+<p> To ensure compatibility with digest algorithm agility during
+key rotation, when a certificate or public key is being replaced
+with another, and both are published during the transition, both
+the old and the new certificate MUST be specified with the same set
+of digests.  One can change the list of digest algorithms later,
+once old keys are retired.  At any given time, change either the
+list of digests without changing the list of certificates or public
+keys or the list of certificates or public keys without changing
+the list of digests.  Full value matching type "0" records are not
+subject to this constraint, but are discouraged due to the size of
+the resulting DNS records.  </p>
+
+<p> It is expected that this algorithm agility mechanism will be
+published in a standards track RFC for SMTP with DANE, and also in
+an eventual update to <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>. </p>
+
+<p> This feature is available in Postfix 2.11 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="tls_dane_digests">tls_dane_digests</a>
@@ -16220,85 +16263,58 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 <p> <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> TLSA resource-record "matching type" digest algorithms
 in descending preference order.  All the specified algorithms must
 be supported by the underlying OpenSSL library, otherwise the Postfix
-SMTP client will not support DANE TLSA security. </p>
+SMTP client will not support DANE TLSA security.  </p>
+
+<p> Specify a list of digest names separated by commas and/or
+whitespace.  Each digest name may be followed by an optional
+"=&lt;number&gt;" suffix.  For example, "sha512" may instead be specified
+as "sha512=2" and "sha256" may instead be specified as "sha256=1".
+The optional number must match the <a
+href="https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types"
+>IANA</a> assigned TLSA matching type number the algorithm in question.
+Postfix will check this constraint for the algorithms it knows about.
+Additional matching type algorithms registered with IANA can be added
+with explicit numbers provided they are supported by OpenSSL. </p>
+
+<p> Invalid list elements are logged with a warning and disable DANE
+support.  TLSA RRs that specify digests not included in the list are
+ignored with a warning. </p>
+
+<p> Note: It is unwise to omit sha256 from the digest list.  This
+digest algorithm is the only mandatory to implement digest algorithm
+in <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>, and many servers are expected publish TLSA records
+with just sha256 digests.  Unless one of the standard digests is
+seriously compromised and servers have had ample time to update their
+TLSA records you should not omit any standard digests, just arrange
+them in order from strongest to weakest.  </p>
 
 <p> When for a particular combination of "certificate usage" and
-"selector" the TLSA RRset contains a well-formed record with a
-matching type of "0", i.e. a full value of the associated certificate
-or public key, the Postfix SMTP client will ignore all other matching
-types for the same certificate usage and selector.  In this case
-the first algorithm listed in <a href="postconf.5.html#tls_dane_digests">tls_dane_digests</a> will be used to
-compute a digest of the full value, which will then be used to match
-certificates or public keys in the server's certificate chain. </p>
-
-<p> Otherwise, when for a particular combination of "certificate
-usage" and "selector" the TLSA RRset contains a records with more
-than one non-zero matching type, i.e. multiple digest algorithms,
-only records with the highest preference digest are used after
-discarding any records with an incorrect digest length as unusable. </p>
-
-<p> This strategy ensures that the strongest digest supported by
-both the Postfix SMTP client and the remote server is used, and
-weaker digests are ignored.  This supports non-disruptive deprecation
-of outdated digest algorithms. </p>
-
-<p> The strategy requires that when a TLSA RRset provides association
-data for multiple certificates or public keys, all RRs with the same
-"certificate usage" and "selector" be published with the same set
-of digests.  In particular, during key rotation, when a certificate
-or public key is being replaced with another (and both are published
-during the transition) both the old and the new certificate MUST be
-specified with the same set of digests.  One can change the list of
-digest algorithms later, once old keys are retired.  At any given
-time change either the list of digests without changing the list of
-certificates or public keys or the list of certificates or public
-keys without changing the list of digests. </p>
+"selector" the TLSA RRset contains records with more than one digest
+matching type, the tls_dane_digest_agility parameter determines
+whether all the RRs are used, or only those with the most preferred
+digest matching type.  </p>
 
-<p> It is expected that this algorithm agility mechanism will be
-published in a standards track RFC for SMTP with DANE, and perhaps
-in an eventual update to <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>. </p>
+<p> The <a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> parameter controls
+whether any digest TLSA records are acceptable in usage "2" (trust
+anchor assertion) TLSA records. </p>
 
-<p> This feature is available in Postfix 2.11. </p>
+<p> This feature is available in Postfix 2.11 and later. </p>
 
 
 </DD>
 
 <DT><b><a name="tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>
-(default: trust-anchor-assertion)</b></DT><DD>
+(default: yes)</b></DT><DD>
 
 <p> <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> trust-anchor digest support in the Postfix TLS library.
-Specify zero or more of the following options, separated by comma or
-whitespace.  Option names are case-insensitive.  </p>
-
-<dl>
-
-<dt><b>ca-constraint</b></dt>
-
-<dd> Enable support for <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA) DNS records that
-contain digests of trust-anchors with certificate usage "0".
-These are often public root CAs, and server operators may
-expect that clients will have the corresponding root certificate
-in their CAfile or CApath.  Most SSL/TLS servers do not send public
-root CA certificate in their certificate chain, so if Postfix does
-not have the CA certificate locally, there is no way to associate
-the digest with the trust chain from the server.  These TLSA records
-are fragile, and only work when you have a complete (whatever that
-means) set of public CAs in your CAfile or CApath.  Enable this
-with caution if at all.  </dd>
-
-<dt><b>trust-anchor-assertion</b></dt>
-
-<dd> Enable support for <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA) DNS records that contain
+Enable support for <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA) DNS records that contain
 digests of trust-anchors with certificate usage "2".  In this case
 the certificate usage logically requires the server administrator
 to configure the server to include the trust-anchor certificate in
-the server's SSL certificate chain.  These TLSA records are less
-fragile than "ca-constraint" TLSA records, and are enabled by
-default.  Having a "complete" CAfile or CApath does not help in
-this case, you are at the mercy of the remote server administrator's
-competence.  </dd>
-
-</dl>
+the server's SSL certificate chain.  If enough domains mess this
+up, you can disable support for these TLSA records, but you'll no
+longer have secure connections that get it right and only publish
+trust anchor records.  </p>
 
 <p> At the <a href="TLS_README.html#client_tls_dane">dane</a>
 security level, when a TLSA RRset includes only unusable associations,
@@ -16309,6 +16325,11 @@ href="TLS_README.html#client_tls_dane">dane-only</a> security level,
 the server in question is skipped and delivery is deferred if no
 secure servers are found.  </p>
 
+<p> The <a href="postconf.5.html#tls_dane_digests">tls_dane_digests</a> parameter controls the list of digest
+algorithms that are supported in TLSA records.  The tls_dane_digest_agility
+parameter controls digest algorithm downgrade attack resistance.
+</p>
+
 <p> This feature is available in Postfix 2.11 and later.  </p>
 
 
@@ -16572,7 +16593,11 @@ or performant cipher choice, there is some risk of interoperability
 issues. In the past, some SSL clients have listed lower priority ciphers
 that they did not implement correctly. If the server chooses a cipher
 that the client prefers less, it may select a cipher whose client
-implementation is flawed. </p>
+implementation is flawed. Most notably Windows 2003 Microsoft
+Exchange servers have flawed implementations of DES-CBC3-SHA, which
+OpenSSL considers stronger than RC4-SHA.  Enabling server cipher-suite
+selection may create interoperability issues with Windows 2003
+Microsoft Exchange clients.  </p>
 
 <p> This feature is available in Postfix 2.8 and later, in combination
 with OpenSSL 0.9.7 and later. </p>
index 4ef7b501f96287ccb7d3303357643d0ba58961c8..fce80c8e5669ef83c366a7ac5a34feab833cd8d9 100644 (file)
@@ -598,14 +598,13 @@ SMTP(8)                                                                SMTP(8)
               hostname  is  not  an alias and its address records
               lie in an unsigned zone.
 
-       <b><a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>   (trust-anchor-asser-</b>
-       <b>tion)</b>
+       <b><a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> (yes)</b>
               <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> trust-anchor digest support in the Postfix
               TLS library.
 
        <b><a href="postconf.5.html#tlsmgr_service_name">tlsmgr_service_name</a> (tlsmgr)</b>
-              The  name  of  the  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in mas-
-              ter.cf.
+              The  name  of  the  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in <a href="master.5.html">mas-
+              ter.cf</a>.
 
 <b>OBSOLETE STARTTLS CONTROLS</b>
        The following configuration parameters exist for  compati-
index b75c572dd6114d7c5b323a7e0edb374439e0f715..ad82b1193d5ea4ad66a49f71e79365e2cb20aa1b 100644 (file)
@@ -636,7 +636,7 @@ CCARGS="$CCARGS -DSNAPSHOT"
 
 # Non-production: needs thorough testing, or major changes are still
 # needed before the code stabilizes.
-#CCARGS="$CCARGS -DNONPROD"
+CCARGS="$CCARGS -DNONPROD"
 
 sed 's/  / /g' <<EOF
 SYSTYPE        = $SYSTYPE
index 0ef5441171cf07860a4e64e1909a0c85cfe2c307..d51da1950d3f9fc874be80f796f732caf0fb1db1 100644 (file)
@@ -15,15 +15,40 @@ Postfix configuration utility
 [\fB-C \fIclass,...\fR] [\fIparameter ...\fR]
 
 \fBpostconf\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
-[\fIparameter=value ...\fR]
+\fIparameter=value ...\fR
 
 \fBpostconf\fR [\fB-#vX\fR] [\fB-c \fIconfig_dir\fR]
-[\fIparameter ...\fR]
+\fIparameter ...\fR
 
-\fBManaging master.cf:\fR
+\fBManaging master.cf service entries:\fR
 
 \fBpostconf\fR [\fB-fMovx\fR] [\fB-c \fIconfig_dir\fR]
-[\fIservice ...\fR]
+[\fIservice\fR[\fB/\fItype\fR]\fI ...\fR]
+
+\fBpostconf\fR [\fB-eMv\fR] [\fB-c \fIconfig_dir\fR]
+\fIservice\fB/\fItype=value ...\fR
+
+\fBpostconf\fR [\fB-#MvX\fR] [\fB-c \fIconfig_dir\fR]
+\fIservice\fB/\fItype ...\fR
+
+\fBManaging master.cf service fields:\fR
+
+\fBpostconf\fR [\fB-fFovx\fR] [\fB-c \fIconfig_dir\fR]
+[\fIservice\fR[\fB/\fItype\fR[\fB/\fIfield\fR]]\fI ...\fR]
+
+\fBpostconf\fR [\fB-eFv\fR] [\fB-c \fIconfig_dir\fR]
+\fIservice\fB/\fItype\fB/\fIfield=value ...\fR
+
+\fBManaging master.cf service parameters:\fR
+
+\fBpostconf\fR [\fB-fPovx\fR] [\fB-c \fIconfig_dir\fR]
+[\fIservice\fR[\fB/\fItype\fR[\fB/\fIparameter\fR]]\fI ...\fR]
+
+\fBpostconf\fR [\fB-ePv\fR] [\fB-c \fIconfig_dir\fR]
+\fIservice\fB/\fItype\fB/\fIparameter=value ...\fR
+
+\fBpostconf\fR [\fB-PXv\fR] [\fB-c \fIconfig_dir\fR]
+\fIservice\fB/\fItype\fB/\fIparameter ...\fR
 
 \fBManaging bounce message templates:\fR
 
@@ -114,21 +139,56 @@ Specify \fB-df\fR to fold long lines for human readability
 (Postfix 2.9 and later).
 .IP \fB-e\fR
 Edit the \fBmain.cf\fR configuration file, and update
-parameter settings with the "\fIname=value\fR" pairs
-on the \fBpostconf\fR(1) command line. The file is copied
-to a temporary file then renamed into place.
-Specify quotes to protect special characters and whitespace
-on the \fBpostconf\fR(1) command line.
-
-The \fB-e\fR is no longer needed with Postfix version 2.8
-and later.
+parameter settings with the "\fIname=value\fR" pairs on the
+\fBpostconf\fR(1) command line.
+
+With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+and replace one or more service entries with new values as
+specified with "\fIservice/type=value\fR" on the \fBpostconf\fR(1)
+command line.
+
+With \fB-F\fR, edit the \fBmaster.cf\fR configuration file,
+and replace one or more service fields with new values as
+specied with "\fIservice/type/field=value\fR" on the
+\fBpostconf\fR(1) command line. Currently, the "command"
+field contains the command name and command arguments.  this
+may change in the near future, so that the "command" field
+contains only the command name, and a new "arguments"
+pseudofield contains the command arguments.
+
+With \fB-P\fR, edit the \fBmaster.cf\fR configuration file,
+and add or update one or more service parameter settings
+(-o parameter=value settings) with new values as specied
+with "\fIservice/type/parameter=value\fR" on the \fBpostconf\fR(1)
+command line.
+
+In all cases the file is copied to a temporary file then
+renamed into place.  Specify quotes to protect special
+characters and whitespace on the \fBpostconf\fR(1) command
+line.
+
+The \fB-e\fR option is no longer needed with Postfix version
+2.8 and later.
 .IP \fB-f\fR
 Fold long lines when printing \fBmain.cf\fR or \fBmaster.cf\fR
 configuration file entries, for human readability.
 
 This feature is available with Postfix 2.9 and later.
+.IP \fB-F\fR
+Show \fBmaster.cf\fR per-entry field settings (by default
+all services and all fields), formatted as one
+"\fIservice/type/field=value\fR" per line. Specify \fB-Ff\fR
+to fold long lines.
+
+Specify one or more "\fIservice/type/field\fR" instances
+on the \fBpostconf\fR(1) command line to limit the output
+to fields of interest.  Trailing parameter name or service
+type fields that are omitted will be handled as "*" wildcard
+fields.
+
+This feature is available with Postfix 2.11 and later.
 .IP \fB-h\fR
-Show \fBmain.cf\fR parameter values without the "\fIname\fR
+Show parameter or attribute values without the "\fIname\fR
 = " label that normally precedes the value.
 .IP \fB-l\fR
 List the names of all supported mailbox locking methods.
@@ -139,21 +199,22 @@ A kernel-based advisory locking method for local files only.
 This locking method is available on systems with a BSD
 compatible library.
 .IP \fBfcntl\fR
-A kernel-based advisory locking method for local and remote files.
+A kernel-based advisory locking method for local and remote
+files.
 .IP \fBdotlock\fR
-An application-level locking method. An application locks a file
-named \fIfilename\fR by creating a file named \fIfilename\fB.lock\fR.
-The application is expected to remove its own lock file, as well as
-stale lock files that were left behind after abnormal program
-termination.
+An application-level locking method. An application locks
+a file named \fIfilename\fR by creating a file named
+\fIfilename\fB.lock\fR.  The application is expected to
+remove its own lock file, as well as stale lock files that
+were left behind after abnormal program termination.
 .RE
 .IP \fB-m\fR
-List the names of all supported lookup table types. In Postfix
-configuration files,
-lookup tables are specified as \fItype\fB:\fIname\fR, where
-\fItype\fR is one of the types listed below. The table \fIname\fR
-syntax depends on the lookup table type as described in the
-DATABASE_README document.
+List the names of all supported lookup table types. In
+Postfix configuration files, lookup tables are specified
+as \fItype\fB:\fIname\fR, where \fItype\fR is one of the
+types listed below. The table \fIname\fR syntax depends on
+the lookup table type as described in the DATABASE_README
+document.
 .RS
 .IP \fBbtree\fR
 A sorted, balanced tree structure.  Available on systems
@@ -181,22 +242,18 @@ with support for Berkeley DB databases.
 .IP \fBinternal\fR
 A non-shared, in-memory hash table. Its content are lost
 when a process terminates.
-.IP "\fBldap\fR (read-only)"
-LDAP database client. This is described in \fBldap_table\fR(5).
 .IP "\fBlmdb\fR"
 OpenLDAP LMDB database (a memory-mapped, persistent file).
 Available on systems with support for LMDB databases.  This
 is described in \fBlmdb_table\fR(5).
+.IP "\fBldap\fR (read-only)"
+LDAP database client. This is described in \fBldap_table\fR(5).
 .IP "\fBmemcache\fR"
 Memcache database client. This is described in
 \fBmemcache_table\fR(5).
 .IP "\fBmysql\fR (read-only)"
 MySQL database client.  Available on systems with support
 for MySQL databases.  This is described in \fBmysql_table\fR(5).
-.IP "\fBnis\fR (read-only)"
-NIS client.
-.IP "\fBnisplus\fR (read-only)"
-NIS+ client.
 .IP "\fBpcre\fR (read-only)"
 A lookup table based on Perl Compatible Regular Expressions.
 The file format is described in \fBpcre_table\fR(5).
@@ -220,53 +277,78 @@ UNIX-domain server. This is described in \fBsocketmap_table\fR(5).
 .IP "\fBsqlite\fR (read-only)"
 SQLite database. This is described in \fBsqlite_table\fR(5).
 .IP "\fBstatic\fR (read-only)"
-A table that always returns its name as lookup result. For example,
-\fBstatic:foobar\fR always returns the string \fBfoobar\fR as lookup
-result.
+A table that always returns its name as lookup result. For
+example, \fBstatic:foobar\fR always returns the string
+\fBfoobar\fR as lookup result.
 .IP "\fBtcp\fR (read-only)"
 TCP/IP client. The protocol is described in \fBtcp_table\fR(5).
 .IP "\fBtexthash\fR (read-only)"
-Produces similar results as hash: files, except that you don't
-need to run the \fBpostmap\fR(1) command before you can use the file,
-and that it does not detect changes after the file is read.
+Produces similar results as hash: files, except that you
+don't need to run the \fBpostmap\fR(1) command before you
+can use the file, and that it does not detect changes after
+the file is read.
 .IP "\fBunix\fR (read-only)"
 A limited view of the UNIX authentication database. The
 following tables are implemented:
 .RS
 . IP \fBunix:passwd.byname\fR
-The table is the UNIX password database. The key is a login name.
-The result is a password file entry in \fBpasswd\fR(5) format.
+The table is the UNIX password database. The key is a login
+name.  The result is a password file entry in \fBpasswd\fR(5)
+format.
 .IP \fBunix:group.byname\fR
-The table is the UNIX group database. The key is a group name.
-The result is a group file entry in \fBgroup\fR(5) format.
+The table is the UNIX group database. The key is a group
+name.  The result is a group file entry in \fBgroup\fR(5)
+format.
 .RE
 .RE
 .IP
-Other table types may exist depending on how Postfix was built.
+Other table types may exist depending on how Postfix was
+built.
 .IP \fB-M\fR
 Show \fBmaster.cf\fR file contents instead of \fBmain.cf\fR
-file contents.
-Specify \fB-Mf\fR to fold long lines for human readability.
+file contents.  Specify \fB-Mf\fR to fold long lines for
+human readability.
 
-If \fIservice ...\fR is specified, only the matching services
-will be output. For example, "\fBpostconf -Mf inet\fR"
-will output all services that listen on the network.
+Specify zero or more arguments, each with a \fIservice-name\fR
+or \fIservice-name/service-type\fR pair, where \fIservice-name\fR
+is the first field of a master.cf entry and \fIservice-type\fR
+is one of (\fBinet\fR, \fBunix\fR, \fBfifo\fR, or \fBpass\fR).
 
-Specify zero or more arguments, each with a \fIservice-type\fR
-name (\fBinet\fR, \fBunix\fR, \fBfifo\fR, or \fBpass\fR)
-or with a \fIservice-name.service-type\fR pair, where
-\fIservice-name\fR is the first field of a master.cf entry.
+If \fIservice-name\fR or \fIservice-name/service-type\fR
+is specified, only the matching master.cf entries will be
+output. For example, "\fBpostconf -Mf smtp\fR" will output
+all services named "smtp", and "\fBpostconf -Mf smtp/inet\fR"
+will output only the smtp service that listens on the
+network.  Trailing service type fields that are omitted
+will be handled as "*" wildcard fields.
 
-This feature is available with Postfix 2.9 and later.
+This feature is available with Postfix 2.9 and later. The
+syntax was changed from "\fIname.type\fR" to "\fIname/type\fR",
+and "*" wildcard support was added with Postfix 2.11.
 .IP \fB-n\fR
 Show only configuration parameters that have explicit
-\fIname=value\fR settings in \fBmain.cf\fR.
-Specify \fB-nf\fR to fold long lines for human readability
-(Postfix 2.9 and later).
+\fIname=value\fR settings in \fBmain.cf\fR.  Specify \fB-nf\fR
+to fold long lines for human readability (Postfix 2.9 and
+later).
 .IP "\fB-o \fIname=value\fR"
 Override \fBmain.cf\fR parameter settings.
 
 This feature is available with Postfix 2.10 and later.
+.IP \fB-p\fR
+Show \fBmain.cf\fR parameter settings. This is the default.
+.IP \fB-P\fR
+Show \fBmaster.cf\fR service parameter settings (by default
+all services and all parameters).  formatted as one
+"\fIservice/type/parameter=value\fR" per line.  Specify
+\fB-Pf\fR to fold long lines.
+
+Specify one or more "\fIservice/type/parameter\fR" instances
+on the \fBpostconf\fR(1) command line to limit the output
+to parameters of interest.  Trailing parameter name or
+service type fields that are omitted will be handled as "*"
+wildcard fields.
+
+This feature is available with Postfix 2.11 and later.
 .IP "\fB-t\fR [\fItemplate_file\fR]"
 Display the templates for text that appears at the beginning
 of delivery status notification (DSN) messages, without
@@ -283,34 +365,59 @@ line (in shell language: "").
 
 This feature is available with Postfix 2.3 and later.
 .IP \fB-v\fR
-Enable verbose logging for debugging purposes. Multiple \fB-v\fR
-options make the software increasingly verbose.
+Enable verbose logging for debugging purposes. Multiple
+\fB-v\fR options make the software increasingly verbose.
 .IP \fB-x\fR
 Expand \fI$name\fR in \fBmain.cf\fR or \fBmaster.cf\fR
 parameter values. The expansion is recursive.
 
 This feature is available with Postfix 2.10 and later.
 .IP \fB-X\fR
-Edit the \fBmain.cf\fR configuration file, and remove
-the parameters named on the \fBpostconf\fR(1) command line.
-The file is copied to a temporary file then renamed into
-place.
+Edit the \fBmain.cf\fR configuration file, and remove the
+parameters named on the \fBpostconf\fR(1) command line.
 Specify a list of parameter names, not "\fIname=value\fR"
-pairs.  There is no \fBpostconf\fR(1) command to perform
-the reverse operation.
+pairs.
+
+With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+and remove one or more service entries as specified with
+"\fIservice/type\fR" on the \fBpostconf\fR(1) command line.
+
+With \fB-P\fR, edit the \fBmaster.cf\fR configuration file,
+and remove one or more service parameter settings (-o
+parameter=value settings) as specied with
+"\fIservice/type/parameter\fR" on the \fBpostconf\fR(1)
+command line.
+
+In all cases the file is copied to a temporary file then
+renamed into place.  Specify quotes to protect special
+characters on the \fBpostconf\fR(1) command line.
+
+There is no \fBpostconf\fR(1) command to perform the reverse
+operation.
 
 This feature is available with Postfix 2.10 and later.
+Support for -M and -P was added with Postfix 2.11.
 .IP \fB-#\fR
 Edit the \fBmain.cf\fR configuration file, and comment out
 the parameters named on the \fBpostconf\fR(1) command line,
 so that those parameters revert to their default values.
-The file is copied to a temporary file then renamed into
-place.
 Specify a list of parameter names, not "\fIname=value\fR"
-pairs.  There is no \fBpostconf\fR(1) command to perform
-the reverse operation.
+pairs.
+
+With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+and comment out one or more service entries as specified
+with "\fIservice/type\fR" on the \fBpostconf\fR(1) command
+line.
+
+In all cases the file is copied to a temporary file then
+renamed into place.  Specify quotes to protect special
+characters on the \fBpostconf\fR(1) command line.
+
+There is no \fBpostconf\fR(1) command to perform the reverse
+operation.
 
-This feature is available with Postfix 2.6 and later.
+This feature is available with Postfix 2.6 and later. Support
+for -M was added with Postfix 2.11.
 .SH DIAGNOSTICS
 .ad
 .fi
@@ -327,8 +434,8 @@ Directory with Postfix configuration files.
 .nf
 .ad
 .fi
-The following \fBmain.cf\fR parameters are especially relevant to
-this program.
+The following \fBmain.cf\fR parameters are especially
+relevant to this program.
 
 The text below provides only a parameter summary. See
 \fBpostconf\fR(5) for more details including examples.
@@ -341,20 +448,20 @@ Pathname of a configuration file with bounce message templates.
 .na
 .nf
 /etc/postfix/main.cf, Postfix configuration parameters
-/etc/postfix/master.cf, Postfix master daemon configuraton
+/etc/postfix/master.cf, Postfix master daemon configuration
 .SH "SEE ALSO"
 .na
 .nf
-bounce(5), bounce template file format
-master(5), master.cf configuration file syntax
-postconf(5), main.cf configuration file syntax
+bounce(5), bounce template file format master(5), master.cf
+configuration file syntax postconf(5), main.cf configuration
+file syntax
 .SH "README FILES"
 .na
 .nf
 .ad
 .fi
-Use "\fBpostconf readme_directory\fR" or
-"\fBpostconf html_directory\fR" to locate this information.
+Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+html_directory\fR" to locate this information.
 .na
 .nf
 DATABASE_README, Postfix lookup table overview
@@ -363,11 +470,10 @@ DATABASE_README, Postfix lookup table overview
 .nf
 .ad
 .fi
-The Secure Mailer license must be distributed with this software.
+The Secure Mailer license must be distributed with this
+software.
 .SH "AUTHOR(S)"
 .na
 .nf
-Wietse Venema
-IBM T.J. Watson Research
-P.O. Box 704
-Yorktown Heights, NY 10598, USA
+Wietse Venema IBM T.J. Watson Research P.O. Box 704 Yorktown
+Heights, NY 10598, USA
index 4da8a488bbb4c5c34127bbca6be4edeaf2ef59c3..a44eaf15c7bf5058278cb78c81c1527134f2ccb3 100644 (file)
@@ -7220,18 +7220,6 @@ whitespace, commas or colons. In the policy table "protocols" attribute
 empty value means allow all protocols. The valid protocol names, (see
 \\fBfBSSL_get_version\fR(3)\fR), are "SSLv2", "SSLv3" and "TLSv1".
 .PP
-At the dane and
-dane-only security
-levels, when usable TLSA records are obtained for the remote SMTP
-server, the Postfix SMTP client is obligated to include the SNI TLS
-extension in its SSL client hello message.  This may help the remote
-SMTP server live up to its promise to provide a certificate that
-matches its TLSA records.  Since TLS extensions require TLS 1.0 or
-later, the Postfix SMTP client must disable SSLv2 and SSLv3 when
-SNI is required.  If you use "dane" or "dane-only" do not disable
-TLSv1, except perhaps via the policy table for destinations which
-you are sure will support TLSv1.1 or TLSv1.2.
-.PP
 Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
 1.0.1 or later, these, or any other new protocol versions, are
@@ -7260,7 +7248,7 @@ Example:
 .ft C
 # Preferred form with Postfix >= 2.5:
 smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
-# Alternative form.
+# Legacy form with Postfix < 2.5:
 smtp_tls_mandatory_protocols = TLSv1
 .fi
 .ad
@@ -7415,23 +7403,17 @@ Opportunistic DANE TLS.  The TLS policy for the destination is
 obtained via TLSA records in DNSSEC.  If no TLSA records are found,
 the effective security level used is may.  If TLSA records are
 found, but none are usable, the effective security level is encrypt.  When usable
-TLSA records are obtained for the remote SMTP server, SSLv2 and
-SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
-and the server certificate must match the TLSA records.  The
-tls_dane_trust_anchor_digest_enable parameter controls optional
-support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
-authentication and DNSSEC support is available with Postfix 2.11
-and later.
+TLSA records are obtained for the remote SMTP server, the
+server certificate must match the TLSA records.  RFC 6698 (DANE)
+TLS authentication and DNSSEC support is available with Postfix
+2.11 and later.
 .br
 .IP "\fBdane-only\fR"
 Mandatory DANE TLS.  The TLS policy for the destination is
 obtained via TLSA records in DNSSEC.  If no TLSA records are found,
 or none are usable, no connection is made to the server.  When
-usable TLSA records are obtained for the remote SMTP server, SSLv2
-and SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
-and the server certificate must match the TLSA records.  The
-tls_dane_trust_anchor_digest_enable parameter controls optional
-support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+usable TLSA records are obtained for the remote SMTP server, the
+server certificate must match the TLSA records.  RFC 6698 (DANE) TLS
 authentication and DNSSEC support is available with Postfix 2.11
 and later.
 .br
@@ -7549,7 +7531,7 @@ Example:
 .nf
 .na
 .ft C
-# TLSv1 only!
+# TLSv1 or better:
 smtp_tls_protocols = !SSLv2, !SSLv3
 .fi
 .ad
@@ -7666,20 +7648,16 @@ parameter.  When TLSA records are not found or are all unusable the
 effective security level is "may" or "encrypt" respectively.  For
 purposes of protocol and cipher selection, the "dane" security level
 is treated like a "mandatory" TLS security level, and weak ciphers
-and protocols are disabled.  SSLv2 and SSLv3 are automatically
-disabled when usable TLSA records are obtained for the remote SMTP
-server, see smtp_tls_mandatory_protocols for details.  Since DANE
-authenticates server certificates the "aNULL" cipher-suites are
-transparently excluded at this level, no need to configure this
-manually.  The tls_dane_trust_anchor_digest_enable main.cf parameter
-controls optional support for trust-anchor digest TLSA records.
-RFC 6698 (DANE) TLS authentication is available with Postfix 2.11
-and later.
+and protocols are disabled.  Since DANE authenticates server
+certificates the "aNULL" cipher-suites are transparently excluded
+at this level, no need to configure this manually.  RFC 6698 (DANE)
+TLS authentication is available with Postfix 2.11 and later.
 .br
 .IP "\fBdane-only\fR"
-Mandatory DANE TLS.  This is just like "dane" above, only DANE
-TLSA authentication is mandatory.  RFC 6698 (DANE) TLS authentication
-is available with Postfix 2.11 and later.
+Mandatory DANE TLS.  This is just like "dane" above, but DANE
+TLSA authentication is required.  There is no fallback to "may" or
+"encrypt" when TLSA records are missing or unusable.  RFC 6698
+(DANE) TLS authentication is available with Postfix 2.11 and later.
 .br
 .IP "\fBfingerprint\fR"
 Certificate fingerprint verification.
@@ -10599,11 +10577,11 @@ works in addition to the exclusions listed with smtpd_tls_exclude_ciphers
 This feature is available in Postfix 2.3 and later.
 .SH smtpd_tls_mandatory_protocols (default: !SSLv2)
 The SSL/TLS protocols accepted by the Postfix SMTP server with
-mandatory TLS encryption. If the list is empty, the server supports all
-available SSL/TLS protocol versions.  A non-empty value is a list
-of protocol
-names separated by whitespace, commas or colons. The supported protocol
-names are "SSLv2", "SSLv3" and "TLSv1", and are not case sensitive.
+mandatory TLS encryption. If the list is empty, the server supports
+all available SSL/TLS protocol versions.  A non-empty value is a
+list of protocol names separated by whitespace, commas or colons.
+The supported protocol names are "SSLv2", "SSLv3" and "TLSv1", and
+are not case sensitive.
 .PP
 Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
@@ -10628,9 +10606,9 @@ Example:
 .nf
 .na
 .ft C
-smtpd_tls_mandatory_protocols = TLSv1
-# Alternative form with Postfix >= 2.5:
 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+# Legacy form with Postfix < 2.5:
+smtpd_tls_mandatory_protocols = TLSv1
 .fi
 .ad
 .ft R
@@ -11007,78 +10985,111 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 (or 168bit) session key.
 .PP
 This feature is available in Postfix 2.2 and later.
+.SH tls_dane_digest_agility (default: on)
+Configure DANE TLSA digest algorithm agility.  When digest
+algorithm agility is enabled, and the server and client support a
+common strong digest algorithm, TLSA records with weaker digest
+algorithms are ignored.
+.PP
+Specify one of the following:
+.IP "\fBoff\fR"
+DANE verification examines each well-formed record in the TLSA
+RRset whose matching type is either "0" (no hash used) or is one of
+the digest algorithms listed in $tls_dane_digests.  This setting
+is not recommended.
+.br
+.IP "\fBon\fR"
+From each group of well-formed TLSA RRs a non-zero digest
+matching type with the same certificate usage and selector, DANE
+verification examines only those records whose matching type has
+the highest precedence (appear earliest in $tls_dane_digests) are
+considered.
+.br
+.IP "\fBmaybe\fR"
+For compatibility with digest algorithm agility, each certificate
+or public key whose digest is included in a DANE TLSA RRset, SHOULD
+be published with the same set of digest matching type values as
+any other with the same usage and selector.  Therefore, compatible
+TLSA RRsets will contain an identical count of well-formed RRs with
+each non-zero digest matching type for any fixed combination of
+usage and selector.  When this constraint is violated, or any of
+the digest records are malformed, digest algorithm agility will
+disabled.  Otherwise, digest algorithm agility is enabled.
+.br
+.br
+.PP
+Digest algorithm agility ensures that the strongest digest
+supported by both the Postfix SMTP client and the remote server is
+used, and weaker digests are ignored.  This supports non-disruptive
+deprecation of outdated digest algorithms.
+.PP
+To ensure compatibility with digest algorithm agility during
+key rotation, when a certificate or public key is being replaced
+with another, and both are published during the transition, both
+the old and the new certificate MUST be specified with the same set
+of digests.  One can change the list of digest algorithms later,
+once old keys are retired.  At any given time, change either the
+list of digests without changing the list of certificates or public
+keys or the list of certificates or public keys without changing
+the list of digests.  Full value matching type "0" records are not
+subject to this constraint, but are discouraged due to the size of
+the resulting DNS records.
+.PP
+It is expected that this algorithm agility mechanism will be
+published in a standards track RFC for SMTP with DANE, and also in
+an eventual update to RFC 6698.
+.PP
+This feature is available in Postfix 2.11 and later.
 .SH tls_dane_digests (default: sha512 sha256)
 RFC 6698 TLSA resource-record "matching type" digest algorithms
 in descending preference order.  All the specified algorithms must
 be supported by the underlying OpenSSL library, otherwise the Postfix
 SMTP client will not support DANE TLSA security.
 .PP
+Specify a list of digest names separated by commas and/or
+whitespace.  Each digest name may be followed by an optional
+"=<number>" suffix.  For example, "sha512" may instead be specified
+as "sha512=2" and "sha256" may instead be specified as "sha256=1".
+The optional number must match the <a
+href="https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types"
+>IANA assigned TLSA matching type number the algorithm in question.
+Postfix will check this constraint for the algorithms it knows about.
+Additional matching type algorithms registered with IANA can be added
+with explicit numbers provided they are supported by OpenSSL.
+.PP
+Invalid list elements are logged with a warning and disable DANE
+support.  TLSA RRs that specify digests not included in the list are
+ignored with a warning.
+.PP
+Note: It is unwise to omit sha256 from the digest list.  This
+digest algorithm is the only mandatory to implement digest algorithm
+in RFC 6698, and many servers are expected publish TLSA records
+with just sha256 digests.  Unless one of the standard digests is
+seriously compromised and servers have had ample time to update their
+TLSA records you should not omit any standard digests, just arrange
+them in order from strongest to weakest.
+.PP
 When for a particular combination of "certificate usage" and
-"selector" the TLSA RRset contains a well-formed record with a
-matching type of "0", i.e. a full value of the associated certificate
-or public key, the Postfix SMTP client will ignore all other matching
-types for the same certificate usage and selector.  In this case
-the first algorithm listed in tls_dane_digests will be used to
-compute a digest of the full value, which will then be used to match
-certificates or public keys in the server's certificate chain.
-.PP
-Otherwise, when for a particular combination of "certificate
-usage" and "selector" the TLSA RRset contains a records with more
-than one non-zero matching type, i.e. multiple digest algorithms,
-only records with the highest preference digest are used after
-discarding any records with an incorrect digest length as unusable.
-.PP
-This strategy ensures that the strongest digest supported by
-both the Postfix SMTP client and the remote server is used, and
-weaker digests are ignored.  This supports non-disruptive deprecation
-of outdated digest algorithms.
-.PP
-The strategy requires that when a TLSA RRset provides association
-data for multiple certificates or public keys, all RRs with the same
-"certificate usage" and "selector" be published with the same set
-of digests.  In particular, during key rotation, when a certificate
-or public key is being replaced with another (and both are published
-during the transition) both the old and the new certificate MUST be
-specified with the same set of digests.  One can change the list of
-digest algorithms later, once old keys are retired.  At any given
-time change either the list of digests without changing the list of
-certificates or public keys or the list of certificates or public
-keys without changing the list of digests.
+"selector" the TLSA RRset contains records with more than one digest
+matching type, the tls_dane_digest_agility parameter determines
+whether all the RRs are used, or only those with the most preferred
+digest matching type.
 .PP
-It is expected that this algorithm agility mechanism will be
-published in a standards track RFC for SMTP with DANE, and perhaps
-in an eventual update to RFC 6698.
+The tls_dane_trust_anchor_digest_enable parameter controls
+whether any digest TLSA records are acceptable in usage "2" (trust
+anchor assertion) TLSA records.
 .PP
-This feature is available in Postfix 2.11.
-.SH tls_dane_trust_anchor_digest_enable (default: trust-anchor-assertion)
+This feature is available in Postfix 2.11 and later.
+.SH tls_dane_trust_anchor_digest_enable (default: yes)
 RFC 6698 trust-anchor digest support in the Postfix TLS library.
-Specify zero or more of the following options, separated by comma or
-whitespace.  Option names are case-insensitive.
-.IP "\fBca-constraint\fR"
-Enable support for RFC 6698 (DANE TLSA) DNS records that
-contain digests of trust-anchors with certificate usage "0".
-These are often public root CAs, and server operators may
-expect that clients will have the corresponding root certificate
-in their CAfile or CApath.  Most SSL/TLS servers do not send public
-root CA certificate in their certificate chain, so if Postfix does
-not have the CA certificate locally, there is no way to associate
-the digest with the trust chain from the server.  These TLSA records
-are fragile, and only work when you have a complete (whatever that
-means) set of public CAs in your CAfile or CApath.  Enable this
-with caution if at all.
-.br
-.IP "\fBtrust-anchor-assertion\fR"
 Enable support for RFC 6698 (DANE TLSA) DNS records that contain
 digests of trust-anchors with certificate usage "2".  In this case
 the certificate usage logically requires the server administrator
 to configure the server to include the trust-anchor certificate in
-the server's SSL certificate chain.  These TLSA records are less
-fragile than "ca-constraint" TLSA records, and are enabled by
-default.  Having a "complete" CAfile or CApath does not help in
-this case, you are at the mercy of the remote server administrator's
-competence.
-.br
-.br
+the server's SSL certificate chain.  If enough domains mess this
+up, you can disable support for these TLSA records, but you'll no
+longer have secure connections that get it right and only publish
+trust anchor records.
 .PP
 At the dane
 security level, when a TLSA RRset includes only unusable associations,
@@ -11088,6 +11099,10 @@ security level.  At the dane-only security level,
 the server in question is skipped and delivery is deferred if no
 secure servers are found.
 .PP
+The tls_dane_digests parameter controls the list of digest
+algorithms that are supported in TLSA records.  The tls_dane_digest_agility
+parameter controls digest algorithm downgrade attack resistance.
+.PP
 This feature is available in Postfix 2.11 and later.
 .SH tls_disable_workarounds (default: see "postconf -d" output)
 List or bit-mask of OpenSSL bug work-arounds to disable.
@@ -11301,7 +11316,11 @@ or performant cipher choice, there is some risk of interoperability
 issues. In the past, some SSL clients have listed lower priority ciphers
 that they did not implement correctly. If the server chooses a cipher
 that the client prefers less, it may select a cipher whose client
-implementation is flawed.
+implementation is flawed. Most notably Windows 2003 Microsoft
+Exchange servers have flawed implementations of DES-CBC3-SHA, which
+OpenSSL considers stronger than RC4-SHA.  Enabling server cipher-suite
+selection may create interoperability issues with Windows 2003
+Microsoft Exchange clients.
 .PP
 This feature is available in Postfix 2.8 and later, in combination
 with OpenSSL 0.9.7 and later.
index fcc914029f4ebbbd5205d404b09881d4e0b56bdf..e25ac4a17d73a19b8d4e6fe13546bf22311cd626 100644 (file)
@@ -479,7 +479,7 @@ and/or public keys.
 .IP "\fBsmtp_tls_force_insecure_host_tlsa_lookup (no)\fR"
 Lookup the associated DANE TLSA RRset even when a hostname is
 not an alias and its address records lie in an unsigned zone.
-.IP "\fBtls_dane_trust_anchor_digest_enable (trust-anchor-assertion)\fR"
+.IP "\fBtls_dane_trust_anchor_digest_enable (yes)\fR"
 RFC 6698 trust-anchor digest support in the Postfix TLS library.
 .IP "\fBtlsmgr_service_name (tlsmgr)\fR"
 The name of the \fBtlsmgr\fR(8) service entry in master.cf.
index ad5177224ff70c6c891c4132d18109721045de09..10527fc93620c48e91078c1e3912d332dc5d2cab 100644 (file)
@@ -226,9 +226,11 @@ size of the server TLS handshake.  </p>
 </pre>
 </blockquote>
 
-<li> <p> If you use RFC 6698 TLSA "2 0 1" or "2 1 1" records to
+<li> <p> If you publish RFC 6698 TLSA "2 0 1" or "2 1 1" records to
 specify root CA certificate digests, you must include the corresponding
-root CA certificates in the "server.pem" certificate file. </p>
+root CA certificates in the "server.pem" certificate file.  See the
+documentation of the tls_dane_trust_anchor_digest_enable main.cf
+parameter. </p>
 
 <blockquote>
 <pre>
@@ -236,29 +238,20 @@ root CA certificates in the "server.pem" certificate file. </p>
 </pre>
 </blockquote>
 
-<p> Remote SMTP clients will
-be able to use the TLSA record you publish (which only contains the
-certificate digest) only if they have access to the corresponding
-certificate.  Failure to verify certificates per the server's
-published TLSA records will typically cause the SMTP client to defer
-mail delivery.  The foregoing also applies to "2 0 2" and "2 1 2"
-TLSA records or any other digest of a CA certificate, but it is
-expected that SHA256 will be by far the most common digest for TLSA.
-You are <i>strongly</i> urged to likewise include the root CA
-certificate in your server certificate file even if you use "0 0
-1" or "0 1 1" TLSA records.  Some DANE implementations in SMTP
-clients (Postfix among them) may treat RFC 6698 certificate usages
-"0" and "2" interchangeably.  </p>
-
-<p>By default, support for TLSA records that specify certificate
-usage "0" via a digest is disabled in the Postfix SMTP client.  As
-a best practice, publish either "3 0 1" or "3 1 1" TLSA associations
-that specify the SHA256 digest of the server certificate public key
-with the alias-expanded hostname of each STARTTLS capable SMTP
-server.  These continue to work when a certificate is renewed with
-the same public/private key pair.  See the documentation of the
-tls_dane_trust_anchor_digest_enable main.cf parameter for details.
-</p>
+<p> Remote SMTP clients will be able to use the TLSA record you
+publish (which only contains the certificate digest) only if they
+have access to the corresponding certificate.  Failure to verify
+certificates per the server's published TLSA records will typically
+cause the SMTP client to defer mail delivery.  The foregoing also
+applies to "2 0 2" and "2 1 2" TLSA records or any other digest of
+a CA certificate, but it is expected that SHA256 will be by far the
+most common digest for TLSA.  </p>
+
+<p> As a best practice, publish either "3 0 1" or "3 1 1" TLSA
+associations that specify the SHA256 digest of the server certificate
+public key with the alias-expanded hostname of each STARTTLS capable
+SMTP server.  These continue to work when a certificate is renewed
+with the same public/private key pair.  </p>
 
 </ul>
 
@@ -861,8 +854,8 @@ certificates</a> that use <b>only</b> the anonymous ciphers. This is
 enabled by explicitly setting "smtpd_tls_cert_file = none"
 and not specifying an smtpd_tls_dcert_file or smtpd_tls_eccert_file. </p>
 
-<p> Example, MSA that requires TLSv1, not SSLv2 or SSLv3, with high grade
-ciphers: </p>
+<p> Example, MSA that requires TLSv1 or higher, not SSLv2 or SSLv3,
+with high grade ciphers: </p>
 
 <blockquote>
 <pre>
@@ -872,9 +865,9 @@ ciphers: </p>
     smtpd_tls_mandatory_ciphers = high
     smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5
     smtpd_tls_security_level = encrypt
-    # Preferred form with Postfix &ge; 2.5:
+    # Postfix &ge; 2.5:
     smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
-    # Alternative form.
+    # Legacy form with Postfix prior to 2.5:
     smtpd_tls_mandatory_protocols = TLSv1
 </pre>
 </blockquote>
@@ -918,23 +911,27 @@ secure for most situations. </p>
 </blockquote>
 
 <p> Postfix 2.8 and later, in combination with OpenSSL 0.9.7 and later
-allows TLS servers to preempt the TLS client's cipher preference list.
+allows TLS servers to preempt the TLS client's cipher-suite preference list.
 This is possible only with SSLv3 and later, as in SSLv2 the client
-chooses the cipher from a list supplied by the server. </p>
+chooses the cipher-suite from a list supplied by the server. </p>
 
 <p> By default, the OpenSSL server selects the client's most preferred
-cipher that the server supports. With SSLv3 and later, the server
-may choose its own most preferred cipher that is supported (offered)
+cipher-suite that the server supports.  With SSLv3 and later, the server
+may choose its own most preferred cipher-suite that is supported (offered)
 by the client. Setting "tls_preempt_cipherlist = yes" enables server
-cipher preferences. The default OpenSSL behavior applies with
+cipher-suite preferences. The default OpenSSL behavior applies with
 "tls_preempt_cipherlist = no". </p>
 
-<p> While server cipher selection may in some cases lead to a more secure
-or performant cipher choice, there is some risk of interoperability
-issues. In the past, some SSL clients have listed lower priority ciphers
-that they did not implement correctly. If the server chooses a cipher
-that the client prefers less, it may select a cipher whose client
-implementation is flawed. </p>
+<p> While server cipher-suite selection may in some cases lead to
+a more secure or performant cipher-suite choice, there is some risk
+of interoperability issues.  In the past, some SSL clients have
+listed lower priority ciphers that they did not implement correctly.
+If the server chooses a cipher that the client prefers less, it may
+select a cipher whose client implementation is flawed.  Most notably
+Windows 2003 Microsoft Exchange servers have flawed implementations
+of DES-CBC3-SHA, which OpenSSL considers stronger than RC4-SHA.
+Enabling server cipher-suite selection may create interoperability
+issues with Windows 2003 Microsoft Exchange clients.  </p>
 
 <h3><a name="server_misc"> Miscellaneous server controls</a> </h3>
 
@@ -1251,17 +1248,30 @@ the mandatory "dane-only" level. </p>
 href="#client_tls_may">opportunistic</a> TLS that is resistant to
 man in the middle and downgrade attacks when the destination domain
 uses DNSSEC to publish DANE TLSA records for its MX hosts.  If a
-remote SMTP server has usable DANE TLSA records, these will be used
-to authenticate the server.  If TLSA records are published for a
-given remote SMTP server (implying TLS support), but are not usable
-due to unsupported parameters, the Postfix SMTP client will use <a
+remote SMTP server has "usable" (see RFC 6698) DANE TLSA records,
+the server connection will be authenticated.  When DANE authentication
+fails, there is no fallback to unauthenticated or plaintext delivery. </p>
+
+<p> If TLSA records are published for a given remote SMTP server
+(implying TLS support), but are all "unusable" due to unsupported
+parameters or malformed data, the Postfix SMTP client will use <a
 href="#client_tls_encrypt">mandatory</a> unauthenticated TLS.
-Otherwise, the Postfix SMTP client behavior is the same as with <a
-href="#client_tls_may">may</a>.  </p>
+Otherwise, when no TLSA records are published, the Postfix SMTP
+client behavior is the same as with <a href="#client_tls_may">may</a>. </p>
+
+<p> TLSA records must be published in DNSSEC validated DNS zones.
+Any TLSA records in DNS zones not protected via DNSSEC are ignored.
+The Postfix SMTP client will not look for TLSA records associated
+with MX hosts whose "A" or "AAAA" records lie in an "insecure" DNS
+zone.  Such lookups have been observed to cause interoperability
+issues with poorly implemented DNS servers, and are in any case not
+expected to ever yield "secure" results, since that would require
+a very unlikey DLV DNS trust anchor configured between the host
+record and the associated "_25._tcp" child TLSA record. </p>
 
 <p> The "dane-only" level is a form of <a
 href="#client_tls_secure">secure-channel</a> TLS based on the DANE PKI.
-If usable TLSA records are present these are used to authenticate the
+If "usable" TLSA records are present these are used to authenticate the
 remote SMTP server.  Otherwise, or when server certificate verification
 fails, delivery via the server in question tempfails.  </p>
 
@@ -1271,73 +1281,51 @@ to be in effect, the destination domain's containing DNS zone must
 be signed and the Postfix SMTP client's operating system must be
 configured to send its DNS queries to a recursive DNS nameserver
 that is able to validate the signed records.  Each MX host's DNS
-zone should also be signed, and should publish DANE TLSA (RFC 6698)
+zone needs to also be signed, and needs to publish DANE TLSA (RFC 6698)
 records that specify how that MX host's TLS certificate is to be
-verified.  TLSA records do not preempt the normal SMTP MX host
+verified.  </p>
+
+<p> TLSA records do not preempt the normal SMTP MX host
 selection algorithm, if some MX hosts support TLSA and others do
 not, TLS security will vary from delivery to delivery.  It is up
 to the domain owner to configure their MX hosts and their DNS
 sensibly.  To configure the Postfix SMTP client for DNSSEC lookups
 see the documentation for the smtp_dns_support_level main.cf
 parameter.  The tls_dane_trust_anchor_digest_enable main.cf parameter
-controls optional support for trust-anchor digest TLSA records.
+controls support for trust-anchor digest TLSA records.  The
+tls_dane_digests and tls_dane_digest_agility parameters control
+the list of supported digests and digest downgrade attack resistance.
 </p>
 
-<p> The Postfix SMTP client deviates from RFC 6698 in cases where
-strict adherence is likely to create opportunities for delayed (or
-eventually bounced) email with no substantive security gain.  Most
-notably, it is not expected that smtp_tls_CAfile and smtp_tls_CApath
-can reasonably include every public CA that a remote SMTP server's
-administrator may believe to be well-known.  Therefore, certificate
-usages "0" and "2" from RFC 6698 which are intended to "constrain"
-existing PKI trust, are instead treated as "trust assertions" and
-mapped to "1" and "3" respectively.  That is, with certificate usage
-"0" and "2", Postfix will not require the remote SMTP server's
-certificate to be trusted with respect to any locally defined public
-CAs, it is the domain owner's responsibility to ensure that the
-certificate associations in their TLSA records are appropriate
-to authenticate their SMTP servers.  </p>
-
-<p> In addition, the Postfix SMTP client does not assume that the
-remote SMTP server will necessarily be configured to present the
-certificate of any usage "0" root CA in its TLS protocol <a
-href="https://tools.ietf.org/html/rfc2246#section-7.4.2">certificate_list</a>.
-Therefore, support for usage "0" certificate and public-key digests
-is disabled by default, see tls_dane_trust_anchor_digest_enable for
-details.  While undigested trust-anchor certificates in TLSA records
-are supported, publishing complete certificates in DNS is unwise
-given the resulting excessively large DNS record sizes.  Therefore,
-in its default configuration the Postfix SMTP client essentially
-supports only certificate usages "1", "2" and "3" (with "1" treated as
-though it were "3").  </p>
-
-<p> Finally, the interaction of DANE with MX hostnames that are
-CNAMEs differs from the draft specification in the names used to
-construct the associated <a
-href="https://tools.ietf.org/html/draft-ietf-dane-srv-02#section-3.2">TLSA
-queries</a>.  When the MX hostname is a CNAME, the draft proposal
-to use the unexpanded MX hostname in TLSA lookups is fragile and
-unintuitive.  For this to work, the domain owner has to either
-duplicate the TLSA records of the target (host, service) pair in
-his own DNS or furnish the target host with an additional
-certificate and private key that would be negotiated via SNI.
-Neither of these are robust or easy to manage.  It is far better
-to expand the CNAME recursively to the underlying target hostname
-(keeping track of DNSSEC validation along the way) and to use the
-expanded name to construct the TLSA query and, if appropriate, in
-server certificate name checks.  This is the approach taken by the
-Postfix SMTP client, and if sanity prevails will also be the approach
-taken in the final standard.  </p>
+<p> DANE for SMTP MTAs deviates in some details from the baseline
+DANE protocol in RFC 6698.  Most notably, it is not expected that
+SMTP MTAs can reasonably include every public CA that a remote SMTP
+server's administrator may believe to be well-known.  Nor is there
+an interactive user to "click OK" when authentication fails. </p>
+
+<p> Therefore, certificate usages "0" and "1" from RFC 6698 which
+are intended to "constrain" existing PKI trust, are not supported.
+TLSA records with usage "0" are treated as "unusable".  TLSA records
+with usage "1" are instead treated as "trust assertions" and mapped
+to usage "3".  Specifically, with certificate usage "1", Postfix
+will not require the remote SMTP server's certificate to be trusted
+with respect to any locally defined public CAs, it is the domain
+owner's responsibility to ensure that the certificate associations
+in their TLSA records are appropriate to authenticate their SMTP
+servers.  </p>
+
+<p> The Postfix SMTP client supports only certificate usages "2"
+and "3" (with "1" treated as though it were "3").  See
+tls_dane_trust_anchor_digest_enable for usage "2" usability
+considerations.  Support for certificate usage "1" is an experiment,
+it may be withdrawn in the future.  Server operators SHOULD NOT
+publish TLSA records with usage "1".  </p>
 
 <p> When usable TLSA records are obtained for the remote SMTP server
-the Postfix SMTP client is obligated to include the SNI TLS extension
-in its SSL client hello message.  This may help the remote SMTP
-server live up to its promise to provide a certificate that matches
-its TLSA records.  Since TLS extensions require TLS 1.0 or later,
-the Postfix SMTP client must disable SSLv2 and SSLv3 when SNI is
-required.  If you use "dane" or "dane-only", do not disable TLSv1,
-except perhaps via the policy table for destinations which you are
-sure will support TLSv1.1 or TLSv1.2.  </p>
+the Postfix SMTP client sends the SNI TLS extension in its SSL
+client hello message.  This may help the remote SMTP server live
+up to its promise to provide a certificate that matches its TLSA
+records.  </p>
 
 <p> For purposes of protocol and cipher selection, the "dane"
 security level is treated like a "mandatory" TLS security level,
@@ -1357,14 +1345,14 @@ certificate must contain at least one of these names.  </p>
 <p> When a DANE TLSA record specifies an end-entity (EE) certificate,
 (that is the actual server certificate), as with the fingerprint
 security level below, no name checks or certificate expiration checks
-are applied. The server certificate (or its public key) either matches
+are applied.  The server certificate (or its public key) either matches
 the DANE record or not.  Server administrators should publish such
 EE records in preference to all other types. </p>
 
 <p> The pre-requisites for DANE support in the Postfix SMTP client are: </p>
 <ul>
 <li> A <i>compile-time</i> OpenSSL library that supports the TLS SNI
-extension and the "sha256" and "sha512" message digests.
+extension and "SHA-2" message digests.
 <li> A <i>compile-time</i> DNS resolver library that supports DNSSEC.
 Postfix binaries built on an older system will not support DNSSEC even
 if deployed on a system with an updated resolver library.
@@ -2068,12 +2056,13 @@ server has a greater opportunity to mandate client security policy when
 it is a dedicated MSA that only handles outbound mail from trusted clients,
 below we focus on the client security policy. </p>
 
-<p> On the SMTP client, there are further complications. When delivering
-mail to a given domain, in contrast to HTTPS, one rarely uses the domain
-name directly as the target host of the SMTP session. More typically,
-one uses MX lookups - these are usually unauthenticated - to obtain the domain's SMTP server
-hostname(s). When, as is current practice, the client verifies the
-insecurely obtained MX hostname, it is subject to a DNS man-in-the-middle
+<p> On the SMTP client, there are further complications. When
+delivering mail to a given domain, in contrast to HTTPS, one rarely
+uses the domain name directly as the target host of the SMTP session.
+More typically, one uses MX lookups &mdash; these are usually
+unauthenticated &mdash; to obtain the domain's SMTP server hostname(s).
+When, as is current practice, the client verifies the insecurely
+obtained MX hostname, it is subject to a DNS man-in-the-middle
 attack. </p>
 
 <p> Adoption of DNSSEC and RFC6698 (DANE) may gradually (as domains
@@ -2184,24 +2173,19 @@ DNSSEC.  If no TLSA records are found, the effective security level
 used is <a href="#client_tls_may">may</a>.  If TLSA records are
 found, but none are usable, the effective security level is <a
 href="#client_tls_encrypt">encrypt</a>.  When usable TLSA records
-are obtained for the remote SMTP server, SSLv2 and SSLv3 are
-automatically disabled (see smtp_tls_mandatory_protocols), and the
-server certificate must match the TLSA records.  The
-tls_dane_trust_anchor_digest_enable parameter controls optional
-support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
-authentication and DNSSEC support is available with Postfix 2.11
-and later.  </dd>
+are obtained for the remote SMTP server, SSLv2 is automatically
+disabled (see smtp_tls_mandatory_protocols), and the server certificate
+must match the TLSA records.  RFC 6698 (DANE) TLS authentication
+and DNSSEC support is available with Postfix 2.11 and later.  </dd>
 
 <dt><b>dane-only</b></dt> <dd><a href="#client_tls_dane">Mandatory DANE TLS</a>.
 The TLS policy for the destination is obtained via TLSA records in
 DNSSEC.  If no TLSA records are found, or none are usable, no
 connection is made to the server.  When usable TLSA records are
-obtained for the remote SMTP server, SSLv2 and SSLv3 are automatically
-disabled (see smtp_tls_mandatory_protocols), and the server certificate
-must match the TLSA records.  The tls_dane_trust_anchor_digest_enable
-parameter controls optional support for trust-anchor digest TLSA
-records.  RFC 6698 (DANE) TLS authentication and DNSSEC support is
-available with Postfix 2.11 and later.  </dd>
+obtained for the remote SMTP server, SSLv2 is automatically disabled
+(see smtp_tls_mandatory_protocols), and the server certificate must
+match the TLSA records.  RFC 6698 (DANE) TLS authentication and
+DNSSEC support is available with Postfix 2.11 and later.  </dd>
 
 <dt><b>fingerprint</b></dt> <dd><a href="#client_tls_fprint">Certificate
 fingerprint verification.</a> Available with Postfix 2.5 and
@@ -2291,9 +2275,8 @@ Example:
 /etc/postfix/tls_policy:
     example.edu             none
     example.mil             may
-    example.gov             encrypt protocols=SSLv3:TLSv1 ciphers=high
-    example.com             verify     
-            match=hostname:dot-nexthop protocols=SSLv3:TLSv1 ciphers=high
+    example.gov             encrypt ciphers=high
+    example.com             verify match=hostname:dot-nexthop ciphers=high
     example.net             secure
     .example.net            secure match=.example.net:example.net
     [mail.example.org]:587  secure match=nexthop
@@ -2407,7 +2390,7 @@ the SSL/TLS protocols used with opportunistic TLS. </p>
     smtp_tls_exclude_ciphers = aNULL
     # Preferred form with Postfix &ge; 2.5:
     smtp_tls_mandatory_protocols = !SSLv2
-    # Alternative form.
+    # Legacy form for Postifx &lt; 2.5:
     smtp_tls_mandatory_protocols = SSLv3, TLSv1
     # Also available with Postfix &ge; 2.6:
     smtp_tls_ciphers = export
index 32925f00acad7994fcaaf954238e7b67b54c83f3..4b8d976d87fefd133e62fb72d48c2b1faa8e5f0c 100644 (file)
@@ -10668,23 +10668,17 @@ the effective security level used is <a
 href="TLS_README.html#client_tls_may">may</a>.  If TLSA records are
 found, but none are usable, the effective security level is <a
 href="TLS_README.html#client_tls_encrypt">encrypt</a>.  When usable
-TLSA records are obtained for the remote SMTP server, SSLv2 and
-SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
-and the server certificate must match the TLSA records.  The
-tls_dane_trust_anchor_digest_enable parameter controls optional
-support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
-authentication and DNSSEC support is available with Postfix 2.11
-and later.  </dd>
+TLSA records are obtained for the remote SMTP server, the
+server certificate must match the TLSA records.  RFC 6698 (DANE)
+TLS authentication and DNSSEC support is available with Postfix
+2.11 and later.  </dd>
 
 <dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
 <dd>Mandatory DANE TLS.  The TLS policy for the destination is
 obtained via TLSA records in DNSSEC.  If no TLSA records are found,
 or none are usable, no connection is made to the server.  When
-usable TLSA records are obtained for the remote SMTP server, SSLv2
-and SSLv3 are automatically disabled (see smtp_tls_mandatory_protocols),
-and the server certificate must match the TLSA records.  The
-tls_dane_trust_anchor_digest_enable parameter controls optional
-support for trust-anchor digest TLSA records.  RFC 6698 (DANE) TLS
+usable TLSA records are obtained for the remote SMTP server, the
+server certificate must match the TLSA records.  RFC 6698 (DANE) TLS
 authentication and DNSSEC support is available with Postfix 2.11
 and later.  </dd>
 
@@ -10777,18 +10771,6 @@ whitespace, commas or colons. In the policy table "protocols" attribute
 empty value means allow all protocols. The valid protocol names, (see
 <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and "TLSv1". </p>
 
-<p> At the <a href="TLS_README.html#client_tls_dane">dane</a> and
-<a href="TLS_README.html#client_tls_dane">dane-only</a> security
-levels, when usable TLSA records are obtained for the remote SMTP
-server, the Postfix SMTP client is obligated to include the SNI TLS
-extension in its SSL client hello message.  This may help the remote
-SMTP server live up to its promise to provide a certificate that
-matches its TLSA records.  Since TLS extensions require TLS 1.0 or
-later, the Postfix SMTP client must disable SSLv2 and SSLv3 when
-SNI is required.  If you use "dane" or "dane-only" do not disable
-TLSv1, except perhaps via the policy table for destinations which
-you are sure will support TLSv1.1 or TLSv1.2.  </p>
-
 <p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
 1.0.1 or later, these, or any other new protocol versions, are
@@ -10816,7 +10798,7 @@ TLS_README for more information about security levels. </p>
 <pre>
 # Preferred form with Postfix &ge; 2.5:
 smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
-# Alternative form.
+# Legacy form with Postfix &lt; 2.5:
 smtp_tls_mandatory_protocols = TLSv1
 </pre>
 
@@ -10970,11 +10952,11 @@ configuration parameter. See there for details. </p>
 %PARAM smtpd_tls_mandatory_protocols !SSLv2
 
 <p> The SSL/TLS protocols accepted by the Postfix SMTP server with
-mandatory TLS encryption. If the list is empty, the server supports all
-available SSL/TLS protocol versions.  A non-empty value is a list
-of protocol
-names separated by whitespace, commas or colons. The supported protocol
-names are "SSLv2", "SSLv3" and "TLSv1", and are not case sensitive. </p>
+mandatory TLS encryption. If the list is empty, the server supports
+all available SSL/TLS protocol versions.  A non-empty value is a
+list of protocol names separated by whitespace, commas or colons.
+The supported protocol names are "SSLv2", "SSLv3" and "TLSv1", and
+are not case sensitive. </p>
 
 <p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
 and "TLSv1.2". If an older Postfix version is linked against OpenSSL
@@ -10998,9 +10980,9 @@ level. </p>
 <p> Example: </p>
 
 <pre>
-smtpd_tls_mandatory_protocols = TLSv1
-# Alternative form with Postfix &ge; 2.5:
 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+# Legacy form with Postfix &lt; 2.5:
+smtpd_tls_mandatory_protocols = TLSv1
 </pre>
 
 <p> This feature is available in Postfix 2.3 and later. </p>
@@ -11063,21 +11045,18 @@ parameter.  When TLSA records are not found or are all unusable the
 effective security level is "may" or "encrypt" respectively.  For
 purposes of protocol and cipher selection, the "dane" security level
 is treated like a "mandatory" TLS security level, and weak ciphers
-and protocols are disabled.  SSLv2 and SSLv3 are automatically
-disabled when usable TLSA records are obtained for the remote SMTP
-server, see smtp_tls_mandatory_protocols for details.  Since DANE
-authenticates server certificates the "aNULL" cipher-suites are
-transparently excluded at this level, no need to configure this
-manually.  The tls_dane_trust_anchor_digest_enable main.cf parameter
-controls optional support for trust-anchor digest TLSA records.
-RFC 6698 (DANE) TLS authentication is available with Postfix 2.11
-and later.  </dd>
+and protocols are disabled.  Since DANE authenticates server
+certificates the "aNULL" cipher-suites are transparently excluded
+at this level, no need to configure this manually.  RFC 6698 (DANE)
+TLS authentication is available with Postfix 2.11 and later.  </dd>
 
 
 <dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
-<dd>Mandatory DANE TLS.  This is just like "dane" above, only DANE
-TLSA authentication is mandatory.  RFC 6698 (DANE) TLS authentication
-is available with Postfix 2.11 and later.  </dd>
+<dd>Mandatory DANE TLS.  This is just like "dane" above, but DANE
+TLSA authentication is required.  There is no fallback to "may" or
+"encrypt" when TLSA records are missing or unusable.  RFC 6698
+(DANE) TLS authentication is available with Postfix 2.11 and later.
+</dd>
 
 <dt><b><a href="TLS_README.html#client_tls_fingerprint">fingerprint</a></b></dt>
 <dd>Certificate fingerprint verification.
@@ -12142,7 +12121,7 @@ when the OpenSSL library is newer than Postfix.  </p>
 
 <p> Example: </p>
 <pre>
-# TLSv1 only!
+# TLSv1 or better:
 smtp_tls_protocols = !SSLv2, !SSLv3
 </pre>
 
@@ -14265,7 +14244,11 @@ or performant cipher choice, there is some risk of interoperability
 issues. In the past, some SSL clients have listed lower priority ciphers
 that they did not implement correctly. If the server chooses a cipher
 that the client prefers less, it may select a cipher whose client
-implementation is flawed. </p>
+implementation is flawed. Most notably Windows 2003 Microsoft
+Exchange servers have flawed implementations of DES-CBC3-SHA, which
+OpenSSL considers stronger than RC4-SHA.  Enabling server cipher-suite
+selection may create interoperability issues with Windows 2003
+Microsoft Exchange clients.  </p>
 
 <p> This feature is available in Postfix 2.8 and later, in combination
 with OpenSSL 0.9.7 and later. </p>
@@ -15256,41 +15239,17 @@ configuration parameter.  See there for details.  </p>
 
 <p> This feature is available in Postfix 2.11 and later.  </p>
 
-%PARAM tls_dane_trust_anchor_digest_enable trust-anchor-assertion
+%PARAM tls_dane_trust_anchor_digest_enable yes
 
 <p> RFC 6698 trust-anchor digest support in the Postfix TLS library.
-Specify zero or more of the following options, separated by comma or
-whitespace.  Option names are case-insensitive.  </p>
-
-<dl>
-
-<dt><b>ca-constraint</b></dt>
-
-<dd> Enable support for RFC 6698 (DANE TLSA) DNS records that
-contain digests of trust-anchors with certificate usage "0".
-These are often public root CAs, and server operators may
-expect that clients will have the corresponding root certificate
-in their CAfile or CApath.  Most SSL/TLS servers do not send public
-root CA certificate in their certificate chain, so if Postfix does
-not have the CA certificate locally, there is no way to associate
-the digest with the trust chain from the server.  These TLSA records
-are fragile, and only work when you have a complete (whatever that
-means) set of public CAs in your CAfile or CApath.  Enable this
-with caution if at all.  </dd>
-
-<dt><b>trust-anchor-assertion</b></dt>
-
-<dd> Enable support for RFC 6698 (DANE TLSA) DNS records that contain
+Enable support for RFC 6698 (DANE TLSA) DNS records that contain
 digests of trust-anchors with certificate usage "2".  In this case
 the certificate usage logically requires the server administrator
 to configure the server to include the trust-anchor certificate in
-the server's SSL certificate chain.  These TLSA records are less
-fragile than "ca-constraint" TLSA records, and are enabled by
-default.  Having a "complete" CAfile or CApath does not help in
-this case, you are at the mercy of the remote server administrator's
-competence.  </dd>
-
-</dl>
+the server's SSL certificate chain.  If enough domains mess this
+up, you can disable support for these TLSA records, but you'll no
+longer have secure connections that get it right and only publish
+trust anchor records.  </p>
 
 <p> At the <a href="TLS_README.html#client_tls_dane">dane</a>
 security level, when a TLSA RRset includes only unusable associations,
@@ -15301,6 +15260,11 @@ href="TLS_README.html#client_tls_dane">dane-only</a> security level,
 the server in question is skipped and delivery is deferred if no
 secure servers are found.  </p>
 
+<p> The tls_dane_digests parameter controls the list of digest
+algorithms that are supported in TLSA records.  The tls_dane_digest_agility
+parameter controls digest algorithm downgrade attack resistance.
+</p>
+
 <p> This feature is available in Postfix 2.11 and later.  </p>
 
 %PARAM tls_wildcard_matches_multiple_labels yes
@@ -15448,47 +15412,104 @@ are not prepared to handle the new TLSA RRset.  </p>
 
 <p> This feature is available in Postfix 2.11. </p>
 
+%PARAM tls_dane_digest_agility on
+
+<p> Configure DANE TLSA digest algorithm agility.  When digest
+algorithm agility is enabled, and the server and client support a
+common strong digest algorithm, TLSA records with weaker digest
+algorithms are ignored.  </p>
+
+<p> Specify one of the following: </p>
+
+<dl>
+
+<dt><b>off</b></dt>
+<dd> DANE verification examines each well-formed record in the TLSA
+RRset whose matching type is either "0" (no hash used) or is one of
+the digest algorithms listed in $tls_dane_digests.  This setting
+is not recommended.  </dd>
+
+<dt><b>on</b></dt>
+<dd> From each group of well-formed TLSA RRs a non-zero digest
+matching type with the same certificate usage and selector, DANE
+verification examines only those records whose matching type has
+the highest precedence (appear earliest in $tls_dane_digests) are
+considered.  </dd>
+
+<dt><b>maybe</b></dt>
+<dd> For compatibility with digest algorithm agility, each certificate
+or public key whose digest is included in a DANE TLSA RRset, SHOULD
+be published with the same set of digest matching type values as
+any other with the same usage and selector.  Therefore, compatible
+TLSA RRsets will contain an identical count of well-formed RRs with
+each non-zero digest matching type for any fixed combination of
+usage and selector.  When this constraint is violated, or any of
+the digest records are malformed, digest algorithm agility will
+disabled.  Otherwise, digest algorithm agility is enabled.   </dd>
+
+</dl>
+
+<p> Digest algorithm agility ensures that the strongest digest
+supported by both the Postfix SMTP client and the remote server is
+used, and weaker digests are ignored.  This supports non-disruptive
+deprecation of outdated digest algorithms. </p>
+
+<p> To ensure compatibility with digest algorithm agility during
+key rotation, when a certificate or public key is being replaced
+with another, and both are published during the transition, both
+the old and the new certificate MUST be specified with the same set
+of digests.  One can change the list of digest algorithms later,
+once old keys are retired.  At any given time, change either the
+list of digests without changing the list of certificates or public
+keys or the list of certificates or public keys without changing
+the list of digests.  Full value matching type "0" records are not
+subject to this constraint, but are discouraged due to the size of
+the resulting DNS records.  </p>
+
+<p> It is expected that this algorithm agility mechanism will be
+published in a standards track RFC for SMTP with DANE, and also in
+an eventual update to RFC 6698. </p>
+
+<p> This feature is available in Postfix 2.11 and later. </p>
+
 %PARAM tls_dane_digests sha512 sha256
 
 <p> RFC 6698 TLSA resource-record "matching type" digest algorithms
 in descending preference order.  All the specified algorithms must
 be supported by the underlying OpenSSL library, otherwise the Postfix
-SMTP client will not support DANE TLSA security. </p>
+SMTP client will not support DANE TLSA security.  </p>
+
+<p> Specify a list of digest names separated by commas and/or
+whitespace.  Each digest name may be followed by an optional
+"=&lt;number&gt;" suffix.  For example, "sha512" may instead be specified
+as "sha512=2" and "sha256" may instead be specified as "sha256=1".
+The optional number must match the <a
+href="https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types"
+>IANA</a> assigned TLSA matching type number the algorithm in question.
+Postfix will check this constraint for the algorithms it knows about.
+Additional matching type algorithms registered with IANA can be added
+with explicit numbers provided they are supported by OpenSSL. </p>
+
+<p> Invalid list elements are logged with a warning and disable DANE
+support.  TLSA RRs that specify digests not included in the list are
+ignored with a warning. </p>
+
+<p> Note: It is unwise to omit sha256 from the digest list.  This
+digest algorithm is the only mandatory to implement digest algorithm
+in RFC 6698, and many servers are expected publish TLSA records
+with just sha256 digests.  Unless one of the standard digests is
+seriously compromised and servers have had ample time to update their
+TLSA records you should not omit any standard digests, just arrange
+them in order from strongest to weakest.  </p>
 
 <p> When for a particular combination of "certificate usage" and
-"selector" the TLSA RRset contains a well-formed record with a
-matching type of "0", i.e. a full value of the associated certificate
-or public key, the Postfix SMTP client will ignore all other matching
-types for the same certificate usage and selector.  In this case
-the first algorithm listed in tls_dane_digests will be used to
-compute a digest of the full value, which will then be used to match
-certificates or public keys in the server's certificate chain. </p>
-
-<p> Otherwise, when for a particular combination of "certificate
-usage" and "selector" the TLSA RRset contains a records with more
-than one non-zero matching type, i.e. multiple digest algorithms,
-only records with the highest preference digest are used after
-discarding any records with an incorrect digest length as unusable. </p>
-
-<p> This strategy ensures that the strongest digest supported by
-both the Postfix SMTP client and the remote server is used, and
-weaker digests are ignored.  This supports non-disruptive deprecation
-of outdated digest algorithms. </p>
-
-<p> The strategy requires that when a TLSA RRset provides association
-data for multiple certificates or public keys, all RRs with the same
-"certificate usage" and "selector" be published with the same set
-of digests.  In particular, during key rotation, when a certificate
-or public key is being replaced with another (and both are published
-during the transition) both the old and the new certificate MUST be
-specified with the same set of digests.  One can change the list of
-digest algorithms later, once old keys are retired.  At any given
-time change either the list of digests without changing the list of
-certificates or public keys or the list of certificates or public
-keys without changing the list of digests. </p>
+"selector" the TLSA RRset contains records with more than one digest
+matching type, the tls_dane_digest_agility parameter determines
+whether all the RRs are used, or only those with the most preferred
+digest matching type.  </p>
 
-<p> It is expected that this algorithm agility mechanism will be
-published in a standards track RFC for SMTP with DANE, and perhaps
-in an eventual update to RFC 6698. </p>
+<p> The tls_dane_trust_anchor_digest_enable parameter controls
+whether any digest TLSA records are acceptable in usage "2" (trust
+anchor assertion) TLSA records. </p>
 
-<p> This feature is available in Postfix 2.11. </p>
+<p> This feature is available in Postfix 2.11 and later. </p>
index 999f71528e05899baaa2bca201ac33484b2a3070..0d11e35ed114785f8bb01c993d181f3450a7c81c 100644 (file)
@@ -208,11 +208,12 @@ extern int dns_rr_eq_sa(DNS_RR *, struct sockaddr *);
   * dns_lookup.c
   */
 extern int dns_lookup_r(const char *, unsigned, unsigned, DNS_RR **,
-                             VSTRING *, VSTRING *, int *);
+                               VSTRING *, VSTRING *, int *);
 extern int dns_lookup_rl(const char *, unsigned, DNS_RR **, VSTRING *,
-                               VSTRING *, int *, int,...);
+                                VSTRING *, int *, int,...);
 extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *,
-                               VSTRING *, int *, int, unsigned *);
+                                VSTRING *, int *, int, unsigned *);
+
 #define dns_lookup(name, type, rflags, list, fqdn, why) \
     dns_lookup_r((name), (type), (rflags), (list), (fqdn), (why), (int *) 0)
 #define dns_lookup_l(name, rflags, list, fqdn, why, lflags, ...) \
index d181947a0422633060375445ce728d344cebcd78..45168f97a26d7d1ca5fb7aed83047677e59021f0 100644 (file)
@@ -3103,6 +3103,16 @@ extern char *var_tls_ssl_options;
 #define DEF_TLS_BC_PKEY_FPRINT 0
 extern bool var_tls_bc_pkey_fprint;
 
+ /*
+  * Ordered list of DANE digest algorithms.
+  */
+#define TLS_DANE_AGILITY_OFF   "off"
+#define TLS_DANE_AGILITY_ON    "on"
+#define TLS_DANE_AGILITY_MAYBE "maybe"
+#define VAR_TLS_DANE_AGILITY   "tls_dane_digest_agility"
+#define DEF_TLS_DANE_AGILITY   TLS_DANE_AGILITY_ON
+extern char *var_tls_dane_agility;
+
  /*
   * Ordered list of DANE digest algorithms.
   */
@@ -3113,13 +3123,11 @@ extern char *var_tls_dane_digests;
  /*
   * External interface for enabling trust-anchor digests, which are risky
   * when the corresponding certificate is missing from the peer chain (this
-  * can't happend with the leaf certificate).
+  * can't happen with the leaf certificate).
   */
-#define TLS_DANE_CC            "ca-constraint"
-#define TLS_DANE_TAA           "trust-anchor-assertion"
-#define VAR_TLS_DANE_TA_DGST   "tls_dane_trust_anchor_digest_enable"
-#define DEF_TLS_DANE_TA_DGST   TLS_DANE_TAA
-extern char *var_tls_dane_ta_dgst;
+#define VAR_TLS_DANE_TAA_DGST  "tls_dane_trust_anchor_digest_enable"
+#define DEF_TLS_DANE_TAA_DGST  1
+extern bool var_tls_dane_taa_dgst;
 
  /*
   * Sendmail-style mail filter support.
index 353c4c504e091843cb4726a9153df5ad56322ddc..ad029892c58da4a4e54edccedce39f6c7119d911 100644 (file)
@@ -26,7 +26,7 @@
 /*     that it does not drag in all the LDAP, SQL and other map
 /*     lookup client code into programs that don't need it.
 /*
-/*     Each pattern is of the form "name.type" or "type", where
+/*     Each pattern is of the form "name/type" or "type", where
 /*     "name" and "type" are the first two fields of a master.cf
 /*     entry. Patterns are separated by whitespace and/or commas.
 /*     Matches are case insensitive. Patterns are matched in the
@@ -34,6 +34,9 @@
 /*     match.  In order to reverse the result of a pattern match,
 /*     precede a pattern with an exclamation point (!).
 /*
+/*     For backwards compatibility, the form name.type is still
+/*     supported.
+/*
 /*     match_service_init() parses the pattern list. The result
 /*     must be passed to match_service_match() or match_service_free().
 /*
 #include <stringops.h>
 #include <match_service.h>
 
+/* match_service_compat - backwards compatibility */
+
+static void match_service_compat(ARGV *argv)
+{
+    char  **cpp;
+    char   *cp;
+
+    for (cpp = argv->argv; *cpp; cpp++) {
+       if (strrchr(*cpp, '/') == 0 && (cp = strrchr(*cpp, '.')) != 0)
+           *cp = '/';
+    }
+}
+
 /* match_service_init - initialize pattern list */
 
 ARGV   *match_service_init(const char *patterns)
@@ -92,6 +108,7 @@ ARGV   *match_service_init(const char *patterns)
        argv_add(list, item, (char *) 0);
     argv_terminate(list);
     myfree(saved_patterns);
+    match_service_compat(list);
     return (list);
 }
 
@@ -105,6 +122,7 @@ ARGV   *match_service_init_argv(char **patterns)
     for (cpp = patterns; *cpp; cpp++)
        argv_add(list, *cpp, (char *) 0);
     argv_terminate(list);
+    match_service_compat(list);
     return (list);
 }
 
@@ -127,8 +145,8 @@ int     match_service_match(ARGV *list, const char *name_type)
     /*
      * Sanity check.
      */
-    if ((type = strrchr(name_type, '.')) == 0 || *++type == 0)
-       msg_panic("%s: malformed service: \"%s\"; need \"name.type\" format",
+    if ((type = strrchr(name_type, '/')) == 0 || *++type == 0)
+       msg_panic("%s: malformed service: \"%s\"; need \"name/type\" format",
                  myname, name_type);
 
     /*
@@ -139,7 +157,7 @@ int     match_service_match(ARGV *list, const char *name_type)
            msg_info("%s: %s ~? %s", myname, name_type, pattern);
        for (match = 1; *pattern == '!'; pattern++)
            match = !match;
-       if (strcasecmp(strchr(pattern, '.') ? name_type : type, pattern) == 0) {
+       if (strcasecmp(strchr(pattern, '/') ? name_type : type, pattern) == 0) {
            if (msg_verbose)
                msg_info("%s: %s: found match", myname, name_type);
            return (match);
index 9441255fa1aaf9b1b2ac3421ab6a4ece28dbdaeb..b1044f8a611744bcb4a96ac291e08bfcf1f7ab20 100644 (file)
@@ -294,7 +294,7 @@ MASTER_SERV *get_master_ent()
            continue;
        name = cp;
        transport = get_str_ent(&bufp, "transport type", (char *) 0);
-       vstring_sprintf(junk, "%s.%s", name, transport);
+       vstring_sprintf(junk, "%s/%s", name, transport);
        if (match_service_match(master_disable, vstring_str(junk)) == 0)
            break;
     }
index a89ae34fffb4128e160e3ba66307ece48beabce0..cbd0e4eb833ec3bf30d7c9c67d1f7ef6afe32a9c 100644 (file)
@@ -358,7 +358,7 @@ static sfsistat test_eom(SMFICTX *ctx)
 #endif
     {
        int     count;
-       char *args;
+       char   *args;
 
        for (count = 0; count < add_rcpt_count; count++) {
            if ((args = strchr(add_rcpt[count], ' ')) != 0) {
index 3b5d43b04c70a965133a72590c88572535470e80..4db4defefdeca6aa5168c9733a229db8fc6ae4f1 100644 (file)
@@ -2,11 +2,11 @@ SHELL = /bin/sh
 SRCS   = postconf.c postconf_builtin.c postconf_edit.c postconf_main.c \
        postconf_master.c postconf_misc.c postconf_node.c postconf_other.c \
        postconf_service.c postconf_unused.c postconf_user.c postconf_dbms.c \
-       postconf_lookup.c
+       postconf_lookup.c postconf_match.c postconf_print.c
 OBJS   = postconf.o postconf_builtin.o postconf_edit.o postconf_main.o \
        postconf_master.o postconf_misc.o postconf_node.o postconf_other.o \
        postconf_service.o postconf_unused.o postconf_user.o postconf_dbms.o \
-       postconf_lookup.o
+       postconf_lookup.o postconf_match.o postconf_print.o
 HDRS   = postconf.h
 TESTSRC        =
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -45,7 +45,9 @@ test: $(TESTPROG)
 tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \
        test12 test13 test14 test15 test16 test17 test18 test19 test20 test21 \
        test22 test23 test24 test25 test26 test27 test28 test29 test30 test4b \
-       test31 test32 test33 test34 test35 test36 test37 test39 test40
+       test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \
+       test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
+       test52 test53 test54 test55 test56
 
 root_tests:
 
@@ -169,7 +171,7 @@ test9:      $(PROG) test9.ref
        touch main.cf master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo bar unix - n n - 0 spawn >> master.cf
-       ./$(PROG) -c . -M inet >test9.tmp 2>&1
+       ./$(PROG) -c . -M '*'/inet >test9.tmp 2>&1
        diff test9.ref test9.tmp
        rm -f main.cf master.cf test9.tmp
 
@@ -178,7 +180,7 @@ test10:     $(PROG) test10.ref
        touch main.cf master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo bar unix - n n - 0 spawn >> master.cf
-       ./$(PROG) -c . -M bar.inet foo.unix >test10.tmp 2>&1
+       ./$(PROG) -c . -M bar/inet foo/unix >test10.tmp 2>&1
        diff test10.ref test10.tmp
        rm -f main.cf master.cf test10.tmp
 
@@ -513,7 +515,7 @@ test39: $(PROG) test39.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
-       ./$(PROG) -Mfc . unix >test39.tmp 2>&1
+       ./$(PROG) -Mfc . '*'/unix >test39.tmp 2>&1
        diff test39.ref test39.tmp
        rm -f main.cf master.cf test39.tmp
 
@@ -524,10 +526,183 @@ test40: $(PROG) test40.ref
        echo ' -voaaa=bbb' >> master.cf
        echo ' -vo ccc=$$aaa' >> master.cf
        echo ' -v -oddd=$$ccc' >> master.cf
-       ./$(PROG) -Mfxc . unix >test40.tmp 2>&1
+       ./$(PROG) -Mfxc . '*'/unix >test40.tmp 2>&1
        diff test40.ref test40.tmp
        rm -f main.cf master.cf test40.tmp
 
+test41: $(PROG) test41.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar unix - n n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test41.tmp 2>&1
+       ./$(PROG) -Mfc. >>test41.tmp 2>&1
+       ./$(PROG) -Pc . bar/unix/xxx=YYY bar/unix/aaa=BBB >>test41.tmp 2>&1
+       ./$(PROG) -Mfc. >>test41.tmp 2>&1
+       ./$(PROG) -Pc . >>test41.tmp 2>&1
+       diff test41.ref test41.tmp
+       rm -f main.cf master.cf test41.tmp
+
+test42: $(PROG) test42.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar unix - n n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test42.tmp 2>&1
+       ./$(PROG) -Mfc. >>test42.tmp 2>&1
+       ./$(PROG) -Pc . >>test42.tmp 2>&1
+       ./$(PROG) -PXc. bar/unix/xxx bar/unix/aaa >>test42.tmp 2>&1
+       ./$(PROG) -Mfc. >>test42.tmp 2>&1
+       diff test42.ref test42.tmp
+       rm -f main.cf master.cf test42.tmp
+
+test43: $(PROG) test43.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar unix - n n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Fc . bar/unix/chroot=y bar/unix/command='aa -stuffobb=cc dd' >test43.tmp 2>&1
+       ./$(PROG) -Mfc. >>test43.tmp 2>&1
+       diff test43.ref test43.tmp
+       rm -f main.cf master.cf test43.tmp
+
+test44: $(PROG) test44.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar unix - n n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mc . bar/unix='xx inet - n n - 0 aa -stuffobb=cc dd' >test44.tmp 2>&1
+       ./$(PROG) -Mfc. >>test44.tmp 2>&1
+       diff test44.ref test44.tmp
+       rm -f main.cf master.cf test44.tmp
+
+test45: $(PROG) test45.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar xxxx - n n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mfc. >test45.tmp 2>&1 || true
+       diff test45.ref test45.tmp
+       rm -f main.cf master.cf test45.tmp
+
+test46: $(PROG) test46.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet X n n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mfc. >test46.tmp 2>&1 || true
+       diff test46.ref test46.tmp
+       rm -f main.cf master.cf test46.tmp
+
+test47: $(PROG) test47.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - X n - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mfc. >test47.tmp 2>&1 || true
+       diff test47.ref test47.tmp
+       rm -f main.cf master.cf test47.tmp
+
+test48: $(PROG) test48.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n X - 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mfc. >test48.tmp 2>&1 || true
+       diff test48.ref test48.tmp
+       rm -f main.cf master.cf test48.tmp
+
+test49: $(PROG) test49.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n X 0 other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mfc. >test49.tmp 2>&1 || true
+       diff test49.ref test49.tmp
+       rm -f main.cf master.cf test49.tmp
+
+test50: $(PROG) test50.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n - X other >> master.cf
+       echo baz unix - n n - 0 other >> master.cf
+       ./$(PROG) -Mfc. >test50.tmp 2>&1 || true
+       diff test50.ref test50.tmp
+       rm -f main.cf master.cf test50.tmp
+
+test51: $(PROG) test51.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n -? 0 other >> master.cf
+       echo bar inet - n n X? 0 other >> master.cf
+       echo baz unix - n n 0? 0 other >> master.cf
+       ./$(PROG) -Mfc. >test51.tmp 2>&1 || true
+       diff test51.ref test51.tmp
+       rm -f main.cf master.cf test51.tmp
+
+test52: $(PROG) test52.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n 0 0 other >> master.cf
+       echo baz unix - n n 0 0 other >> master.cf
+       ./$(PROG) -MXc. bar/inet foo/unix xxx/yyy
+       ./$(PROG) -Mfc. >test52.tmp 2>&1 || true
+       diff test52.ref test52.tmp
+       rm -f main.cf master.cf test52.tmp
+
+test53: $(PROG) test53.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n 0 0 other >> master.cf
+       echo baz unix - n n 0 0 other >> master.cf
+       ./$(PROG) -M#c. bar/inet xxx/yyy
+       diff test53.ref master.cf
+       rm -f main.cf master.cf test53.tmp
+
+test54: $(PROG) test54.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n 0 0 other >> master.cf
+       echo baz unix - n n 0 0 other >> master.cf
+       ./$(PROG) -M#c. bar/inet foo/unix
+       diff test54.ref master.cf
+       rm -f main.cf master.cf test54.tmp
+
+test55: $(PROG) test55.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n 0 0 other >> master.cf
+       echo baz unix - n n 0 0 other >> master.cf
+       ./$(PROG) -M#c. bar/inet baz/unix
+       diff test55.ref master.cf
+       rm -f main.cf master.cf test55.tmp
+
+test56: $(PROG) test56.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo foo unix - n n - 0 other >> master.cf
+       echo bar inet - n n 0 0 other >> master.cf
+       echo " -o first" >> master.cf
+       echo " -o second" >> master.cf
+       echo baz unix - n n 0 0 other >> master.cf
+       ./$(PROG) -M#c. bar/inet xxx/yyy
+       diff test56.ref master.cf
+       rm -f main.cf master.cf test56.tmp
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -568,6 +743,7 @@ postconf.o: ../../include/msg.h
 postconf.o: ../../include/msg_vstream.h
 postconf.o: ../../include/myflock.h
 postconf.o: ../../include/mymalloc.h
+postconf.o: ../../include/name_code.h
 postconf.o: ../../include/name_mask.h
 postconf.o: ../../include/stringops.h
 postconf.o: ../../include/sys_defs.h
@@ -593,6 +769,7 @@ postconf_builtin.o: ../../include/msg.h
 postconf_builtin.o: ../../include/myflock.h
 postconf_builtin.o: ../../include/mymalloc.h
 postconf_builtin.o: ../../include/mynetworks.h
+postconf_builtin.o: ../../include/name_code.h
 postconf_builtin.o: ../../include/server_acl.h
 postconf_builtin.o: ../../include/stringops.h
 postconf_builtin.o: ../../include/sys_defs.h
@@ -634,6 +811,7 @@ postconf_dbms.o: ../../include/mac_expand.h
 postconf_dbms.o: ../../include/mac_parse.h
 postconf_dbms.o: ../../include/mail_conf.h
 postconf_dbms.o: ../../include/myflock.h
+postconf_dbms.o: ../../include/name_code.h
 postconf_dbms.o: ../../include/split_at.h
 postconf_dbms.o: ../../include/stringops.h
 postconf_dbms.o: ../../include/sys_defs.h
@@ -650,7 +828,9 @@ postconf_edit.o: ../../include/mail_params.h
 postconf_edit.o: ../../include/msg.h
 postconf_edit.o: ../../include/myflock.h
 postconf_edit.o: ../../include/mymalloc.h
+postconf_edit.o: ../../include/name_code.h
 postconf_edit.o: ../../include/readlline.h
+postconf_edit.o: ../../include/split_at.h
 postconf_edit.o: ../../include/stringops.h
 postconf_edit.o: ../../include/sys_defs.h
 postconf_edit.o: ../../include/vbuf.h
@@ -668,6 +848,7 @@ postconf_lookup.o: ../../include/mail_conf.h
 postconf_lookup.o: ../../include/msg.h
 postconf_lookup.o: ../../include/myflock.h
 postconf_lookup.o: ../../include/mymalloc.h
+postconf_lookup.o: ../../include/name_code.h
 postconf_lookup.o: ../../include/stringops.h
 postconf_lookup.o: ../../include/sys_defs.h
 postconf_lookup.o: ../../include/vbuf.h
@@ -685,6 +866,7 @@ postconf_main.o: ../../include/mail_params.h
 postconf_main.o: ../../include/msg.h
 postconf_main.o: ../../include/myflock.h
 postconf_main.o: ../../include/mymalloc.h
+postconf_main.o: ../../include/name_code.h
 postconf_main.o: ../../include/readlline.h
 postconf_main.o: ../../include/stringops.h
 postconf_main.o: ../../include/sys_defs.h
@@ -697,11 +879,13 @@ postconf_master.o: ../../include/argv.h
 postconf_master.o: ../../include/dict.h
 postconf_master.o: ../../include/htable.h
 postconf_master.o: ../../include/mail_params.h
-postconf_master.o: ../../include/match_service.h
+postconf_master.o: ../../include/master_proto.h
 postconf_master.o: ../../include/msg.h
 postconf_master.o: ../../include/myflock.h
 postconf_master.o: ../../include/mymalloc.h
+postconf_master.o: ../../include/name_code.h
 postconf_master.o: ../../include/readlline.h
+postconf_master.o: ../../include/split_at.h
 postconf_master.o: ../../include/stringops.h
 postconf_master.o: ../../include/sys_defs.h
 postconf_master.o: ../../include/vbuf.h
@@ -709,6 +893,20 @@ postconf_master.o: ../../include/vstream.h
 postconf_master.o: ../../include/vstring.h
 postconf_master.o: postconf.h
 postconf_master.o: postconf_master.c
+postconf_match.o: ../../include/argv.h
+postconf_match.o: ../../include/dict.h
+postconf_match.o: ../../include/htable.h
+postconf_match.o: ../../include/msg.h
+postconf_match.o: ../../include/myflock.h
+postconf_match.o: ../../include/mymalloc.h
+postconf_match.o: ../../include/name_code.h
+postconf_match.o: ../../include/split_at.h
+postconf_match.o: ../../include/sys_defs.h
+postconf_match.o: ../../include/vbuf.h
+postconf_match.o: ../../include/vstream.h
+postconf_match.o: ../../include/vstring.h
+postconf_match.o: postconf.h
+postconf_match.o: postconf_match.c
 postconf_misc.o: ../../include/argv.h
 postconf_misc.o: ../../include/dict.h
 postconf_misc.o: ../../include/htable.h
@@ -716,6 +914,7 @@ postconf_misc.o: ../../include/mail_conf.h
 postconf_misc.o: ../../include/mail_params.h
 postconf_misc.o: ../../include/myflock.h
 postconf_misc.o: ../../include/mymalloc.h
+postconf_misc.o: ../../include/name_code.h
 postconf_misc.o: ../../include/safe.h
 postconf_misc.o: ../../include/sys_defs.h
 postconf_misc.o: ../../include/vbuf.h
@@ -729,6 +928,7 @@ postconf_node.o: ../../include/htable.h
 postconf_node.o: ../../include/msg.h
 postconf_node.o: ../../include/myflock.h
 postconf_node.o: ../../include/mymalloc.h
+postconf_node.o: ../../include/name_code.h
 postconf_node.o: ../../include/sys_defs.h
 postconf_node.o: ../../include/vbuf.h
 postconf_node.o: ../../include/vstream.h
@@ -740,6 +940,7 @@ postconf_other.o: ../../include/dict.h
 postconf_other.o: ../../include/htable.h
 postconf_other.o: ../../include/mbox_conf.h
 postconf_other.o: ../../include/myflock.h
+postconf_other.o: ../../include/name_code.h
 postconf_other.o: ../../include/sys_defs.h
 postconf_other.o: ../../include/vbuf.h
 postconf_other.o: ../../include/vstream.h
@@ -747,6 +948,18 @@ postconf_other.o: ../../include/vstring.h
 postconf_other.o: ../../include/xsasl.h
 postconf_other.o: postconf.h
 postconf_other.o: postconf_other.c
+postconf_print.o: ../../include/argv.h
+postconf_print.o: ../../include/dict.h
+postconf_print.o: ../../include/htable.h
+postconf_print.o: ../../include/msg.h
+postconf_print.o: ../../include/myflock.h
+postconf_print.o: ../../include/name_code.h
+postconf_print.o: ../../include/sys_defs.h
+postconf_print.o: ../../include/vbuf.h
+postconf_print.o: ../../include/vstream.h
+postconf_print.o: ../../include/vstring.h
+postconf_print.o: postconf.h
+postconf_print.o: postconf_print.c
 postconf_service.o: ../../include/argv.h
 postconf_service.o: ../../include/dict.h
 postconf_service.o: ../../include/htable.h
@@ -754,6 +967,7 @@ postconf_service.o: ../../include/mail_params.h
 postconf_service.o: ../../include/msg.h
 postconf_service.o: ../../include/myflock.h
 postconf_service.o: ../../include/mymalloc.h
+postconf_service.o: ../../include/name_code.h
 postconf_service.o: ../../include/stringops.h
 postconf_service.o: ../../include/sys_defs.h
 postconf_service.o: ../../include/vbuf.h
@@ -768,6 +982,7 @@ postconf_unused.o: ../../include/mail_conf.h
 postconf_unused.o: ../../include/mail_params.h
 postconf_unused.o: ../../include/msg.h
 postconf_unused.o: ../../include/myflock.h
+postconf_unused.o: ../../include/name_code.h
 postconf_unused.o: ../../include/sys_defs.h
 postconf_unused.o: ../../include/vbuf.h
 postconf_unused.o: ../../include/vstream.h
@@ -784,6 +999,7 @@ postconf_user.o: ../../include/mail_params.h
 postconf_user.o: ../../include/msg.h
 postconf_user.o: ../../include/myflock.h
 postconf_user.o: ../../include/mymalloc.h
+postconf_user.o: ../../include/name_code.h
 postconf_user.o: ../../include/stringops.h
 postconf_user.o: ../../include/sys_defs.h
 postconf_user.o: ../../include/vbuf.h
index f8672971c33ec567e9e669f981d312d06b89a190..d46434bae93dec5d980bde07109d647dc0dc67c7 100644 (file)
 /*     [\fB-C \fIclass,...\fR] [\fIparameter ...\fR]
 /*
 /*     \fBpostconf\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
-/*     [\fIparameter=value ...\fR]
+/*     \fIparameter=value ...\fR
 /*
 /*     \fBpostconf\fR [\fB-#vX\fR] [\fB-c \fIconfig_dir\fR]
-/*     [\fIparameter ...\fR]
+/*     \fIparameter ...\fR
 /*
-/*     \fBManaging master.cf:\fR
+/*     \fBManaging master.cf service entries:\fR
 /*
 /*     \fBpostconf\fR [\fB-fMovx\fR] [\fB-c \fIconfig_dir\fR]
-/*     [\fIservice ...\fR]
+/*     [\fIservice\fR[\fB/\fItype\fR]\fI ...\fR]
+/*
+/*     \fBpostconf\fR [\fB-eMv\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fIservice\fB/\fItype=value ...\fR
+/*
+/*     \fBpostconf\fR [\fB-#MvX\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fIservice\fB/\fItype ...\fR
+/*
+/*     \fBManaging master.cf service fields:\fR
+/*
+/*     \fBpostconf\fR [\fB-fFovx\fR] [\fB-c \fIconfig_dir\fR]
+/*     [\fIservice\fR[\fB/\fItype\fR[\fB/\fIfield\fR]]\fI ...\fR]
+/*
+/*     \fBpostconf\fR [\fB-eFv\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fIservice\fB/\fItype\fB/\fIfield=value ...\fR
+/*
+/*     \fBManaging master.cf service parameters:\fR
+/*
+/*     \fBpostconf\fR [\fB-fPovx\fR] [\fB-c \fIconfig_dir\fR]
+/*     [\fIservice\fR[\fB/\fItype\fR[\fB/\fIparameter\fR]]\fI ...\fR]
+/*
+/*     \fBpostconf\fR [\fB-ePv\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fIservice\fB/\fItype\fB/\fIparameter=value ...\fR
+/*
+/*     \fBpostconf\fR [\fB-PXv\fR] [\fB-c \fIconfig_dir\fR]
+/*     \fIservice\fB/\fItype\fB/\fIparameter ...\fR
 /*
 /*     \fBManaging bounce message templates:\fR
 /*
 /*     (Postfix 2.9 and later).
 /* .IP \fB-e\fR
 /*     Edit the \fBmain.cf\fR configuration file, and update
-/*     parameter settings with the "\fIname=value\fR" pairs
-/*     on the \fBpostconf\fR(1) command line. The file is copied
-/*     to a temporary file then renamed into place.
-/*     Specify quotes to protect special characters and whitespace
-/*     on the \fBpostconf\fR(1) command line.
-/*
-/*     The \fB-e\fR is no longer needed with Postfix version 2.8
-/*     and later.
+/*     parameter settings with the "\fIname=value\fR" pairs on the
+/*     \fBpostconf\fR(1) command line.
+/*
+/*     With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+/*     and replace one or more service entries with new values as
+/*     specified with "\fIservice/type=value\fR" on the \fBpostconf\fR(1)
+/*     command line.
+/*
+/*     With \fB-F\fR, edit the \fBmaster.cf\fR configuration file,
+/*     and replace one or more service fields with new values as
+/*     specied with "\fIservice/type/field=value\fR" on the
+/*     \fBpostconf\fR(1) command line. Currently, the "command"
+/*     field contains the command name and command arguments.  this
+/*     may change in the near future, so that the "command" field
+/*     contains only the command name, and a new "arguments"
+/*     pseudofield contains the command arguments.
+/*
+/*     With \fB-P\fR, edit the \fBmaster.cf\fR configuration file,
+/*     and add or update one or more service parameter settings
+/*     (-o parameter=value settings) with new values as specied
+/*     with "\fIservice/type/parameter=value\fR" on the \fBpostconf\fR(1)
+/*     command line.
+/*
+/*     In all cases the file is copied to a temporary file then
+/*     renamed into place.  Specify quotes to protect special
+/*     characters and whitespace on the \fBpostconf\fR(1) command
+/*     line.
+/*
+/*     The \fB-e\fR option is no longer needed with Postfix version
+/*     2.8 and later.
 /* .IP \fB-f\fR
 /*     Fold long lines when printing \fBmain.cf\fR or \fBmaster.cf\fR
 /*     configuration file entries, for human readability.
 /*
 /*     This feature is available with Postfix 2.9 and later.
+/* .IP \fB-F\fR
+/*     Show \fBmaster.cf\fR per-entry field settings (by default
+/*     all services and all fields), formatted as one
+/*     "\fIservice/type/field=value\fR" per line. Specify \fB-Ff\fR
+/*     to fold long lines.
+/*
+/*     Specify one or more "\fIservice/type/field\fR" instances
+/*     on the \fBpostconf\fR(1) command line to limit the output
+/*     to fields of interest.  Trailing parameter name or service
+/*     type fields that are omitted will be handled as "*" wildcard
+/*     fields.
+/*
+/*     This feature is available with Postfix 2.11 and later.
 /* .IP \fB-h\fR
-/*     Show \fBmain.cf\fR parameter values without the "\fIname\fR
+/*     Show parameter or attribute values without the "\fIname\fR
 /*     = " label that normally precedes the value.
 /* .IP \fB-l\fR
 /*     List the names of all supported mailbox locking methods.
 /*     This locking method is available on systems with a BSD
 /*     compatible library.
 /* .IP \fBfcntl\fR
-/*     A kernel-based advisory locking method for local and remote files.
+/*     A kernel-based advisory locking method for local and remote
+/*     files.
 /* .IP \fBdotlock\fR
-/*     An application-level locking method. An application locks a file
-/*     named \fIfilename\fR by creating a file named \fIfilename\fB.lock\fR.
-/*     The application is expected to remove its own lock file, as well as
-/*     stale lock files that were left behind after abnormal program
-/*     termination.
+/*     An application-level locking method. An application locks
+/*     a file named \fIfilename\fR by creating a file named
+/*     \fIfilename\fB.lock\fR.  The application is expected to
+/*     remove its own lock file, as well as stale lock files that
+/*     were left behind after abnormal program termination.
 /* .RE
 /* .IP \fB-m\fR
-/*     List the names of all supported lookup table types. In Postfix
-/*     configuration files,
-/*     lookup tables are specified as \fItype\fB:\fIname\fR, where
-/*     \fItype\fR is one of the types listed below. The table \fIname\fR
-/*     syntax depends on the lookup table type as described in the
-/*     DATABASE_README document.
+/*     List the names of all supported lookup table types. In
+/*     Postfix configuration files, lookup tables are specified
+/*     as \fItype\fB:\fIname\fR, where \fItype\fR is one of the
+/*     types listed below. The table \fIname\fR syntax depends on
+/*     the lookup table type as described in the DATABASE_README
+/*     document.
 /* .RS
 /* .IP \fBbtree\fR
 /*     A sorted, balanced tree structure.  Available on systems
 /* .IP "\fBsqlite\fR (read-only)"
 /*     SQLite database. This is described in \fBsqlite_table\fR(5).
 /* .IP "\fBstatic\fR (read-only)"
-/*     A table that always returns its name as lookup result. For example,
-/*     \fBstatic:foobar\fR always returns the string \fBfoobar\fR as lookup
-/*     result.
+/*     A table that always returns its name as lookup result. For
+/*     example, \fBstatic:foobar\fR always returns the string
+/*     \fBfoobar\fR as lookup result.
 /* .IP "\fBtcp\fR (read-only)"
 /*     TCP/IP client. The protocol is described in \fBtcp_table\fR(5).
 /* .IP "\fBtexthash\fR (read-only)"
-/*     Produces similar results as hash: files, except that you don't
-/*     need to run the \fBpostmap\fR(1) command before you can use the file,
-/*     and that it does not detect changes after the file is read.
+/*     Produces similar results as hash: files, except that you
+/*     don't need to run the \fBpostmap\fR(1) command before you
+/*     can use the file, and that it does not detect changes after
+/*     the file is read.
 /* .IP "\fBunix\fR (read-only)"
 /*     A limited view of the UNIX authentication database. The
 /*     following tables are implemented:
 /* .RS
 /*. IP \fBunix:passwd.byname\fR
-/*     The table is the UNIX password database. The key is a login name.
-/*     The result is a password file entry in \fBpasswd\fR(5) format.
+/*     The table is the UNIX password database. The key is a login
+/*     name.  The result is a password file entry in \fBpasswd\fR(5)
+/*     format.
 /* .IP \fBunix:group.byname\fR
-/*     The table is the UNIX group database. The key is a group name.
-/*     The result is a group file entry in \fBgroup\fR(5) format.
+/*     The table is the UNIX group database. The key is a group
+/*     name.  The result is a group file entry in \fBgroup\fR(5)
+/*     format.
 /* .RE
 /* .RE
 /* .IP
-/*     Other table types may exist depending on how Postfix was built.
+/*     Other table types may exist depending on how Postfix was
+/*     built.
 /* .IP \fB-M\fR
 /*     Show \fBmaster.cf\fR file contents instead of \fBmain.cf\fR
-/*     file contents.
-/*     Specify \fB-Mf\fR to fold long lines for human readability.
+/*     file contents.  Specify \fB-Mf\fR to fold long lines for
+/*     human readability.
 /*
-/*     If \fIservice ...\fR is specified, only the matching services
-/*     will be output. For example, "\fBpostconf -Mf inet\fR"
-/*     will output all services that listen on the network.
+/*     Specify zero or more arguments, each with a \fIservice-name\fR
+/*     or \fIservice-name/service-type\fR pair, where \fIservice-name\fR
+/*     is the first field of a master.cf entry and \fIservice-type\fR
+/*     is one of (\fBinet\fR, \fBunix\fR, \fBfifo\fR, or \fBpass\fR).
 /*
-/*     Specify zero or more arguments, each with a \fIservice-type\fR
-/*     name (\fBinet\fR, \fBunix\fR, \fBfifo\fR, or \fBpass\fR)
-/*     or with a \fIservice-name.service-type\fR pair, where
-/*     \fIservice-name\fR is the first field of a master.cf entry.
+/*     If \fIservice-name\fR or \fIservice-name/service-type\fR
+/*     is specified, only the matching master.cf entries will be
+/*     output. For example, "\fBpostconf -Mf smtp\fR" will output
+/*     all services named "smtp", and "\fBpostconf -Mf smtp/inet\fR"
+/*     will output only the smtp service that listens on the
+/*     network.  Trailing service type fields that are omitted
+/*     will be handled as "*" wildcard fields.
 /*
-/*     This feature is available with Postfix 2.9 and later.
+/*     This feature is available with Postfix 2.9 and later. The
+/*     syntax was changed from "\fIname.type\fR" to "\fIname/type\fR",
+/*     and "*" wildcard support was added with Postfix 2.11.
 /* .IP \fB-n\fR
 /*     Show only configuration parameters that have explicit
-/*     \fIname=value\fR settings in \fBmain.cf\fR.
-/*     Specify \fB-nf\fR to fold long lines for human readability
-/*     (Postfix 2.9 and later).
+/*     \fIname=value\fR settings in \fBmain.cf\fR.  Specify \fB-nf\fR
+/*     to fold long lines for human readability (Postfix 2.9 and
+/*     later).
 /* .IP "\fB-o \fIname=value\fR"
 /*     Override \fBmain.cf\fR parameter settings.
 /*
 /*     This feature is available with Postfix 2.10 and later.
+/* .IP \fB-p\fR
+/*     Show \fBmain.cf\fR parameter settings. This is the default.
+/* .IP \fB-P\fR
+/*     Show \fBmaster.cf\fR service parameter settings (by default
+/*     all services and all parameters).  formatted as one
+/*     "\fIservice/type/parameter=value\fR" per line.  Specify
+/*     \fB-Pf\fR to fold long lines.
+/*
+/*     Specify one or more "\fIservice/type/parameter\fR" instances
+/*     on the \fBpostconf\fR(1) command line to limit the output
+/*     to parameters of interest.  Trailing parameter name or
+/*     service type fields that are omitted will be handled as "*"
+/*     wildcard fields.
+/*
+/*     This feature is available with Postfix 2.11 and later.
 /* .IP "\fB-t\fR [\fItemplate_file\fR]"
 /*     Display the templates for text that appears at the beginning
 /*     of delivery status notification (DSN) messages, without
 /*
 /*     This feature is available with Postfix 2.3 and later.
 /* .IP \fB-v\fR
-/*     Enable verbose logging for debugging purposes. Multiple \fB-v\fR
-/*     options make the software increasingly verbose.
+/*     Enable verbose logging for debugging purposes. Multiple
+/*     \fB-v\fR options make the software increasingly verbose.
 /* .IP \fB-x\fR
 /*     Expand \fI$name\fR in \fBmain.cf\fR or \fBmaster.cf\fR
 /*     parameter values. The expansion is recursive.
 /*
 /*     This feature is available with Postfix 2.10 and later.
 /* .IP \fB-X\fR
-/*     Edit the \fBmain.cf\fR configuration file, and remove
-/*     the parameters named on the \fBpostconf\fR(1) command line.
-/*     The file is copied to a temporary file then renamed into
-/*     place.
+/*     Edit the \fBmain.cf\fR configuration file, and remove the
+/*     parameters named on the \fBpostconf\fR(1) command line.
 /*     Specify a list of parameter names, not "\fIname=value\fR"
-/*     pairs.  There is no \fBpostconf\fR(1) command to perform
-/*     the reverse operation.
+/*     pairs.
+/*
+/*     With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+/*     and remove one or more service entries as specified with
+/*     "\fIservice/type\fR" on the \fBpostconf\fR(1) command line.
+/*
+/*     With \fB-P\fR, edit the \fBmaster.cf\fR configuration file,
+/*     and remove one or more service parameter settings (-o
+/*     parameter=value settings) as specied with
+/*     "\fIservice/type/parameter\fR" on the \fBpostconf\fR(1)
+/*     command line.
+/*
+/*     In all cases the file is copied to a temporary file then
+/*     renamed into place.  Specify quotes to protect special
+/*     characters on the \fBpostconf\fR(1) command line.
+/*
+/*     There is no \fBpostconf\fR(1) command to perform the reverse
+/*     operation.
 /*
 /*     This feature is available with Postfix 2.10 and later.
+/*     Support for -M and -P was added with Postfix 2.11.
 /* .IP \fB-#\fR
 /*     Edit the \fBmain.cf\fR configuration file, and comment out
 /*     the parameters named on the \fBpostconf\fR(1) command line,
 /*     so that those parameters revert to their default values.
-/*     The file is copied to a temporary file then renamed into
-/*     place.
 /*     Specify a list of parameter names, not "\fIname=value\fR"
-/*     pairs.  There is no \fBpostconf\fR(1) command to perform
-/*     the reverse operation.
+/*     pairs.
 /*
-/*     This feature is available with Postfix 2.6 and later.
+/*     With \fB-M\fR, edit the \fBmaster.cf\fR configuration file,
+/*     and comment out one or more service entries as specified
+/*     with "\fIservice/type\fR" on the \fBpostconf\fR(1) command
+/*     line.
+/*
+/*     In all cases the file is copied to a temporary file then
+/*     renamed into place.  Specify quotes to protect special
+/*     characters on the \fBpostconf\fR(1) command line.
+/*
+/*     There is no \fBpostconf\fR(1) command to perform the reverse
+/*     operation.
+/*
+/*     This feature is available with Postfix 2.6 and later. Support
+/*     for -M was added with Postfix 2.11.
 /* DIAGNOSTICS
 /*     Problems are reported to the standard error stream.
 /* ENVIRONMENT
 /* CONFIGURATION PARAMETERS
 /* .ad
 /* .fi
-/*     The following \fBmain.cf\fR parameters are especially relevant to
-/*     this program.
+/*     The following \fBmain.cf\fR parameters are especially
+/*     relevant to this program.
 /*
 /*     The text below provides only a parameter summary. See
 /*     \fBpostconf\fR(5) for more details including examples.
 /*     Pathname of a configuration file with bounce message templates.
 /* FILES
 /*     /etc/postfix/main.cf, Postfix configuration parameters
-/*     /etc/postfix/master.cf, Postfix master daemon configuraton
+/*     /etc/postfix/master.cf, Postfix master daemon configuration
 /* SEE ALSO
-/*     bounce(5), bounce template file format
-/*     master(5), master.cf configuration file syntax
-/*     postconf(5), main.cf configuration file syntax
+/*     bounce(5), bounce template file format master(5), master.cf
+/*     configuration file syntax postconf(5), main.cf configuration
+/*     file syntax
 /* README FILES
 /* .ad
 /* .fi
-/*     Use "\fBpostconf readme_directory\fR" or
-/*     "\fBpostconf html_directory\fR" to locate this information.
+/*     Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+/*     html_directory\fR" to locate this information.
 /* .na
 /* .nf
 /*     DATABASE_README, Postfix lookup table overview
 /* LICENSE
 /* .ad
 /* .fi
-/*     The Secure Mailer license must be distributed with this software.
+/*     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 IBM T.J. Watson Research P.O. Box 704 Yorktown
+/*     Heights, NY 10598, USA
 /*--*/
 
 /* System library. */
@@ -390,6 +500,153 @@ int     cmd_mode = DEF_MODE;
   */
 MAIL_VERSION_STAMP_DECLARE;
 
+ /*
+  * This program has so many command-line options that we have to implement a
+  * compatibility matrix to weed out the conflicting option combinations, and
+  * to alert the user about option combinations that have no effect.
+  */
+
+ /*
+  * Options that are mutually-exclusive. First entry must specify the major
+  * modes. Other entries specify conflicts between option modifiers.
+  */
+static const int incompat_options[] = {
+    /* Major modes. */
+    SHOW_SASL_SERV | SHOW_SASL_CLNT | EXP_DSN_TEMPL | SHOW_LOCKS | SHOW_MAPS \
+    |DUMP_DSN_TEMPL | MAIN_PARAM | MASTER_ENTRY | MASTER_FIELD | MASTER_PARAM,
+    /* Modifiers. */
+    SHOW_DEFS | EDIT_CONF | SHOW_NONDEF | COMMENT_OUT | EDIT_EXCL,
+    FOLD_LINE | EDIT_CONF | COMMENT_OUT | EDIT_EXCL,
+    SHOW_EVAL | EDIT_CONF | COMMENT_OUT | EDIT_EXCL,
+    MAIN_OVER | SHOW_DEFS | EDIT_CONF | COMMENT_OUT | EDIT_EXCL,
+    HIDE_NAME | EDIT_CONF | COMMENT_OUT | EDIT_EXCL,
+    0,
+};
+
+ /*
+  * Options, and the only options that they are compatible with. There must
+  * be one entry for each major mode. Other entries specify compatibility
+  * between option modifiers.
+  */
+static const int compat_options[][2] = {
+    /* Major modes. */
+    {SHOW_SASL_SERV, 0},
+    {SHOW_SASL_CLNT, 0},
+    {EXP_DSN_TEMPL, 0},
+    {SHOW_LOCKS, 0},
+    {SHOW_MAPS, 0,},
+    {DUMP_DSN_TEMPL, 0},
+    {MAIN_PARAM, EDIT_CONF | EDIT_EXCL | COMMENT_OUT | FOLD_LINE | HIDE_NAME \
+    |PARAM_CLASS | SHOW_EVAL | SHOW_DEFS | SHOW_NONDEF | MAIN_OVER},
+    {MASTER_ENTRY, EDIT_CONF | EDIT_EXCL | COMMENT_OUT | FOLD_LINE | MAIN_OVER \
+    |SHOW_EVAL},
+    {MASTER_FIELD, EDIT_CONF | FOLD_LINE | HIDE_NAME | MAIN_OVER | SHOW_EVAL},
+    {MASTER_PARAM, EDIT_CONF | EDIT_EXCL | FOLD_LINE | HIDE_NAME | MAIN_OVER \
+    |SHOW_EVAL},
+    /* Modifiers. */
+    {PARAM_CLASS, MAIN_PARAM | SHOW_DEFS | SHOW_NONDEF},
+    0,
+};
+
+ /*
+  * Compatibility to string conversion support.
+  */
+static const NAME_MASK compat_names[] = {
+    "-a", SHOW_SASL_SERV,
+    "-A", SHOW_SASL_CLNT,
+    "-b", EXP_DSN_TEMPL,
+    "-C", PARAM_CLASS,
+    "-d", SHOW_DEFS,
+    "-e", EDIT_CONF,
+    "-f", FOLD_LINE,
+    "-F", MASTER_FIELD,
+    "-h", HIDE_NAME,
+    "-l", SHOW_LOCKS,
+    "-m", SHOW_MAPS,
+    "-M", MASTER_ENTRY,
+    "-n", SHOW_NONDEF,
+    "-o", MAIN_OVER,
+    "-p", MAIN_PARAM,
+    "-P", MASTER_PARAM,
+    "-t", DUMP_DSN_TEMPL,
+    "-x", SHOW_EVAL,
+    "-X", EDIT_EXCL,
+    "-#", COMMENT_OUT,
+    0,
+};
+
+/* usage - enumerate parameters without compatibility info */
+
+static void usage(const char *progname)
+{
+    msg_fatal("usage: %s"
+             " [-a (server SASL types)]"
+             " [-A (client SASL types)]"
+             " [-b (bounce templates)]"
+             " [-c config_dir]"
+             " [-c param_class]"
+             " [-d (parameter defaults)]"
+             " [-e (edit configuration)]"
+             " [-f (fold lines)]"
+             " [-F (master.cf fields)]"
+             " [-h (no names)]"
+             " [-l (lock types)]"
+             " [-m (map types)]"
+             " [-M (master.cf)]"
+             " [-n (non-default parameters)]"
+             " [-o name=value (override parameter value)]"
+             " [-p (main.cf, default)]"
+             " [-P (master.cf parameters)]"
+             " [-t (bounce templates)]"
+             " [-v (verbose)]"
+             " [-x (expand parameter values)]"
+             " [-X (exclude)]"
+             " [-# (comment-out)]"
+             " [name...]", progname);
+}
+
+/* check_exclusive_options - complain about mutually-exclusive options */
+
+static void check_exclusive_options(int optval)
+{
+    const char *myname = "check_exclusive_options";
+    const int *op;
+    int     oval;
+    unsigned mask;
+
+    for (op = incompat_options; (oval = *op) != 0; op++) {
+       oval &= optval;
+       for (mask = ~0; (mask & oval) != 0; mask >>= 1) {
+           if ((mask & oval) != oval)
+               msg_fatal("specify one of %s",
+                         str_name_mask(myname, compat_names, oval));
+       }
+    }
+}
+
+/* check_compat_options - complain about incompatible options */
+
+static void check_compat_options(int optval)
+{
+    const char *myname = "check_compat_options";
+    VSTRING *buf1 = vstring_alloc(10);
+    VSTRING *buf2 = vstring_alloc(10);
+    const int (*op)[2];
+    int     excess;
+
+    for (op = compat_options; op[0][0] != 0; op++) {
+       if ((optval & *op[0]) != 0
+           && (excess = (optval & ~((*op)[0] | (*op)[1]))) != 0)
+           msg_fatal("with option %s, do not specify %s",
+                     str_name_mask_opt(buf1, myname, compat_names,
+                                       (*op)[0], NAME_MASK_NUMBER),
+                     str_name_mask_opt(buf2, myname, compat_names, excess,
+                                       NAME_MASK_NUMBER));
+    }
+    vstring_free(buf1);
+    vstring_free(buf2);
+}
+
 /* main */
 
 int     main(int argc, char **argv)
@@ -397,7 +654,6 @@ int     main(int argc, char **argv)
     int     ch;
     int     fd;
     struct stat st;
-    int     junk;
     ARGV   *ext_argv = 0;
     int     param_class = PC_PARAM_MASK_CLASS;
     static const NAME_MASK param_class_table[] = {
@@ -437,7 +693,7 @@ int     main(int argc, char **argv)
     /*
      * Parse JCL.
      */
-    while ((ch = GETOPT(argc, argv, "aAbc:C:deEf#hlmMno:tvxX")) > 0) {
+    while ((ch = GETOPT(argc, argv, "aAbc:C:deEfFhlmMno:pPtvxX#")) > 0) {
        switch (ch) {
        case 'a':
            cmd_mode |= SHOW_SASL_SERV;
@@ -446,6 +702,7 @@ int     main(int argc, char **argv)
            cmd_mode |= SHOW_SASL_CLNT;
            break;
        case 'b':
+           cmd_mode |= EXP_DSN_TEMPL;
            if (ext_argv)
                msg_fatal("specify one of -b and -t");
            ext_argv = argv_alloc(2);
@@ -463,16 +720,19 @@ int     main(int argc, char **argv)
            cmd_mode |= SHOW_DEFS;
            break;
        case 'e':
-           cmd_mode |= EDIT_MAIN;
+           cmd_mode |= EDIT_CONF;
            break;
        case 'f':
            cmd_mode |= FOLD_LINE;
            break;
+       case 'F':
+           cmd_mode |= MASTER_FIELD;
+           break;
        case '#':
-           cmd_mode = COMMENT_OUT;
+           cmd_mode |= COMMENT_OUT;
            break;
        case 'h':
-           cmd_mode &= ~SHOW_NAME;
+           cmd_mode |= HIDE_NAME;
            break;
        case 'l':
            cmd_mode |= SHOW_LOCKS;
@@ -481,17 +741,25 @@ int     main(int argc, char **argv)
            cmd_mode |= SHOW_MAPS;
            break;
        case 'M':
-           cmd_mode |= SHOW_MASTER;
+           cmd_mode |= MASTER_ENTRY;
            break;
        case 'n':
            cmd_mode |= SHOW_NONDEF;
            break;
        case 'o':
+           cmd_mode |= MAIN_OVER;
            if (override_params == 0)
                override_params = argv_alloc(2);
            argv_add(override_params, optarg, (char *) 0);
            break;
+       case 'p':
+           cmd_mode |= MAIN_PARAM;
+           break;
+       case 'P':
+           cmd_mode |= MASTER_PARAM;
+           break;
        case 't':
+           cmd_mode |= DUMP_DSN_TEMPL;
            if (ext_argv)
                msg_fatal("specify one of -b and -t");
            ext_argv = argv_alloc(2);
@@ -508,27 +776,27 @@ int     main(int argc, char **argv)
            msg_verbose++;
            break;
        default:
-           msg_fatal("usage: %s [-a (server SASL types)] [-A (client SASL types)] [-b (bounce templates)] [-c config_dir] [-C param_class] [-d (defaults)] [-e (edit)] [-f (fold lines)] [-# (comment-out)] [-h (no names)] [-l (lock types)] [-m (map types)] [-M (master.cf)] [-n (non-defaults)] [-v] [-X (exclude)] [name...]", argv[0]);
+           usage(argv[0]);
        }
     }
 
+    /*
+     * Make all options explicit, before checking their compatibility.
+     */
+    if ((cmd_mode & incompat_options[0]) == 0)
+       cmd_mode |= MAIN_PARAM;
+    if ((cmd_mode & (MAIN_PARAM | MASTER_ENTRY | MASTER_FIELD | MASTER_PARAM))
+       && argv[optind] && strchr(argv[optind], '='))
+       cmd_mode |= EDIT_CONF;
+
     /*
      * Sanity check.
      */
-    junk = (cmd_mode & (SHOW_DEFS | SHOW_MAPS | SHOW_LOCKS | EDIT_MAIN | SHOW_SASL_SERV | SHOW_SASL_CLNT | COMMENT_OUT | SHOW_MASTER | EDIT_EXCL));
-    if (junk != 0 && ((junk != SHOW_DEFS
-            && junk != SHOW_MAPS && junk != SHOW_LOCKS && junk != EDIT_MAIN
-                      && junk != SHOW_SASL_SERV && junk != SHOW_SASL_CLNT
-                      && junk != COMMENT_OUT && junk != SHOW_MASTER
-                      && junk != EDIT_EXCL)
-                     || ext_argv != 0))
-       msg_fatal("specify one of -a, -A, -b, -d, -e, -#, -l, -m, -M, and -X");
-    if ((cmd_mode & SHOW_EVAL) != 0 && junk != 0 && junk != SHOW_DEFS && junk != SHOW_MASTER)
-       msg_fatal("do not specify -x with -a, -A, -b, -e, -#, -l, -m, or -X");
-    if ((cmd_mode & SHOW_NONDEF) != 0 && junk != 0)
-       msg_fatal("do not specify -n with -a, -A, -b, -d, -e, -#, -l, -m, -M, or -X");
-    if (override_params != 0 && junk != 0 && junk != SHOW_MASTER)
-       msg_fatal("do not specify -o with -a, -A, -b, -d, -e, -#, -l, -m, or -X");
+    check_exclusive_options(cmd_mode);
+    check_compat_options(cmd_mode);
+
+    if ((cmd_mode & EDIT_CONF) && argc == optind)
+       msg_fatal("-e requires name=value argument");
 
     /*
      * Display bounce template information and exit.
@@ -569,7 +837,8 @@ int     main(int argc, char **argv)
     /*
      * If showing master.cf entries, show them and exit
      */
-    else if (cmd_mode & SHOW_MASTER) {
+    else if ((cmd_mode & (MASTER_ENTRY | MASTER_FIELD | MASTER_PARAM))
+            && !(cmd_mode & (EDIT_CONF | EDIT_EXCL | COMMENT_OUT))) {
        read_master(FAIL_ON_OPEN_ERROR);
        read_parameters();
        if (override_params)
@@ -577,7 +846,12 @@ int     main(int argc, char **argv)
        register_builtin_parameters(basename(argv[0]), getpid());
        register_service_parameters();
        register_user_parameters();
-       show_master(VSTREAM_OUT, cmd_mode, argv + optind);
+       if (cmd_mode & MASTER_FIELD)
+           show_master_fields(VSTREAM_OUT, cmd_mode, argc - optind, argv + optind);
+       else if (cmd_mode & MASTER_PARAM)
+           show_master_params(VSTREAM_OUT, cmd_mode, argc - optind, argv + optind);
+       else
+           show_master_entries(VSTREAM_OUT, cmd_mode, argc - optind, argv + optind);
     }
 
     /*
@@ -590,13 +864,16 @@ int     main(int argc, char **argv)
     }
 
     /*
-     * Edit main.cf.
+     * Edit main.cf or master.cf.
      */
-    else if (cmd_mode & (EDIT_MAIN | COMMENT_OUT | EDIT_EXCL)) {
-       edit_parameters(cmd_mode, argc - optind, argv + optind);
-    } else if (cmd_mode == DEF_MODE
-              && argv[optind] && strchr(argv[optind], '=')) {
-       edit_parameters(cmd_mode | EDIT_MAIN, argc - optind, argv + optind);
+    else if (cmd_mode & (EDIT_CONF | COMMENT_OUT | EDIT_EXCL)) {
+       if (optind == argc)
+           msg_fatal("missing service argument");
+       if (cmd_mode & (MASTER_ENTRY | MASTER_FIELD | MASTER_PARAM)) {
+           edit_master(cmd_mode, argc - optind, argv + optind);
+       } else {
+           edit_main(cmd_mode, argc - optind, argv + optind);
+       }
     }
 
     /*
index 8b7bca5ad7cd71f427444dfa26a5ea00f7229785..7b7ee8f94bdc8c43a7b63d388fd6ecc32383a49b 100644 (file)
 #include <htable.h>
 #include <argv.h>
 #include <dict.h>
+#include <name_code.h>
 
  /*
   * What we're supposed to be doing.
   */
 #define SHOW_NONDEF    (1<<0)          /* show main.cf non-default settings */
 #define SHOW_DEFS      (1<<1)          /* show main.cf default setting */
-#define SHOW_NAME      (1<<2)          /* show main.cf parameter name */
+#define HIDE_NAME      (1<<2)          /* hide main.cf parameter name */
 #define SHOW_MAPS      (1<<3)          /* show map types */
-#define EDIT_MAIN      (1<<4)          /* edit main.cf */
+#define EDIT_CONF      (1<<4)          /* edit main.cf or master.cf */
 #define SHOW_LOCKS     (1<<5)          /* show mailbox lock methods */
 #define SHOW_EVAL      (1<<6)          /* expand main.cf right-hand sides */
 #define SHOW_SASL_SERV (1<<7)          /* show server auth plugin types */
 #define SHOW_SASL_CLNT (1<<8)          /* show client auth plugin types */
 #define COMMENT_OUT    (1<<9)          /* #-out selected main.cf entries */
-#define SHOW_MASTER    (1<<10)         /* show master.cf entries */
+#define MASTER_ENTRY   (1<<10)         /* manage master.cf entries */
 #define FOLD_LINE      (1<<11)         /* fold long *.cf entries */
 #define EDIT_EXCL      (1<<12)         /* exclude main.cf entries */
+#define MASTER_FIELD   (1<<13)         /* hierarchical pathname */
+#define MAIN_PARAM     (1<<14)         /* manage main.cf entries */
+#define EXP_DSN_TEMPL  (1<<15)         /* expand bounce templates */
+#define PARAM_CLASS    (1<<16)         /* select parameter class */
+#define MAIN_OVER      (1<<17)         /* override parameter values */
+#define DUMP_DSN_TEMPL (1<<18)         /* show bounce templates */
+#define MASTER_PARAM   (1<<19)         /* manage master.cf -o name=value */
 
-#define DEF_MODE       SHOW_NAME       /* default mode */
+#define DEF_MODE       0
 
  /*
   * Structure for one "valid parameter" (built-in, service-defined or valid
@@ -105,7 +113,7 @@ extern VSTRING *param_string_buf;
   * Structure of one master.cf entry.
   */
 typedef struct {
-    char   *name_space;                        /* service.type, parameter name space */
+    char   *name_space;                        /* service/type, parameter name space */
     ARGV   *argv;                      /* null, or master.cf fields */
     DICT   *all_params;                        /* null, or all name=value entries */
     HTABLE *valid_names;               /* null, or "valid" parameter names */
@@ -113,10 +121,31 @@ typedef struct {
 
 #define PC_MASTER_MIN_FIELDS   8       /* mandatory field count */
 
- /*
-  * Lookup table for master.cf entries. The table is terminated with an entry
-  * that has a null argv member.
-  */
+#define PC_MASTER_NAME_SERVICE "service"
+#define PC_MASTER_NAME_TYPE    "type"
+#define PC_MASTER_NAME_PRIVATE "private"
+#define PC_MASTER_NAME_UNPRIV  "unprivileged"
+#define PC_MASTER_NAME_CHROOT  "chroot"
+#define PC_MASTER_NAME_WAKEUP  "wakeup"
+#define PC_MASTER_NAME_MAXPROC "process_limit"
+#define PC_MASTER_NAME_CMD     "command"
+
+#define PC_MASTER_FIELD_SERVICE        0       /* service name */
+#define PC_MASTER_FIELD_TYPE   1       /* service type */
+#define PC_MASTER_FIELD_PRIVATE        2       /* private service */
+#define PC_MASTER_FIELD_UNPRIV 3       /* unprivileged service */
+#define PC_MASTER_FIELD_CHROOT 4       /* chrooted service */
+#define PC_MASTER_FIELD_WAKEUP 5       /* wakeup timer */
+#define PC_MASTER_FIELD_MAXPROC        6       /* process limit */
+#define PC_MASTER_FIELD_CMD    7       /* command */
+
+#define PC_MASTER_FIELD_WILDC  -1      /* wild-card */
+#define PC_MASTER_FIELD_NONE   -2      /* not available
+                                        * 
+                                       /* Lookup table for master.cf
+                                        * entries. The table is terminated
+                                        * with an entry that has a null argv
+                                        * member. */
 PC_MASTER_ENT *master_table;
 
  /*
@@ -147,18 +176,75 @@ extern void show_parameters(VSTREAM *, int, int, char **);
  /*
   * postconf_edit.c
   */
-extern void edit_parameters(int, int, char **);
+extern void edit_main(int, int, char **);
+extern void edit_master(int, int, char **);
 
  /*
   * postconf_master.c.
   */
 extern const char daemon_options_expecting_value[];
 extern void read_master(int);
-extern void show_master(VSTREAM *, int, char **);
+extern void show_master_entries(VSTREAM *, int, int, char **);
+extern const char *parse_master_entry(PC_MASTER_ENT *, const char *);
+extern void print_master_entry(VSTREAM *, int, PC_MASTER_ENT *);
+extern void free_master_entry(PC_MASTER_ENT *);
+extern void show_master_fields(VSTREAM *, int, int, char **);
+extern void edit_master_field(PC_MASTER_ENT *, int, const char *);
+extern void show_master_params(VSTREAM *, int, int, char **);
+extern void edit_master_param(PC_MASTER_ENT *, int, const char *, const char *);
 
 #define WARN_ON_OPEN_ERROR     0
 #define FAIL_ON_OPEN_ERROR     1
 
+#define PC_MASTER_BLANKS       " \t\r\n"       /* XXX */
+
+ /*
+  * Master.cf parameter namespace management. The idea is to manage master.cf
+  * "-o name=value" settings with other tools than text editors.
+  * 
+  * The natural choice is to use "service-name.service-type.parameter-name", but
+  * unfortunately the '.' may appear in service and parameter names.
+  * 
+  * For example, a spawn(8) listener can have a service name 127.0.0.1:10028.
+  * This service name becomes part of a service-dependent parameter name
+  * "127.0.0.1:10028_time_limit". All those '.' characters mean we can't use
+  * '.' as the parameter namespace delimiter.
+  * 
+  * (We could require that such service names are specified as $foo:port with
+  * the value of "foo" defined in main.cf or at the top of master.cf.)
+  * 
+  * But it is easier if we use '/' instead.
+  */
+#define PC_NAMESP_SEP_CH       '/'
+#define PC_NAMESP_SEP_STR      "/"
+
+#define PC_LEGACY_SEP_CH       '.'
+
+ /*
+  * postconf_match.c.
+  */
+#define PC_MATCH_WILDC_STR     "*"
+#define PC_MATCH_ANY(p)                ((p)[0] == PC_MATCH_WILDC_STR[0] && (p)[1] == 0)
+#define PC_MATCH_STRING(p, s)  (PC_MATCH_ANY(p) || strcmp((p), (s)) == 0)
+
+extern ARGV *parse_service_pattern(const char *, int, int);
+extern int parse_field_pattern(const char *);
+
+#define IS_MAGIC_SERVICE_PATTERN(pat) \
+    (PC_MATCH_ANY((pat)->argv[0]) || PC_MATCH_ANY((pat)->argv[1]))
+#define MATCH_SERVICE_PATTERN(pat, name, type) \
+    (PC_MATCH_STRING((pat)->argv[0], (name)) \
+       && PC_MATCH_STRING((pat)->argv[1], (type)))
+
+#define is_magic_field_pattern(pat) ((pat) == PC_MASTER_FIELD_WILDC)
+#define str_field_pattern(pat) ((const char *) (field_name_offset[pat].name))
+
+#define IS_MAGIC_PARAM_PATTERN(pat) PC_MATCH_ANY(pat)
+#define MATCH_PARAM_PATTERN(pat, name) PC_MATCH_STRING((pat), (name))
+
+/* The following is not part of the postconf_match API. */
+extern NAME_CODE field_name_offset[];
+
  /*
   * postconf_builtin.c.
   */
@@ -197,6 +283,11 @@ const char *lookup_parameter_value(int, const char *, PC_MASTER_ENT *,
 
 char   *expand_parameter_value(VSTRING *, int, const char *, PC_MASTER_ENT *);
 
+ /*
+  * postconf_print.c.
+  */
+extern void print_line(VSTREAM *, int, const char *,...);
+
  /*
   * postconf_unused.c.
   */
index 54852d1fe0ee1ea84c627be5b3e704a7645e7503..7607c56874a3b188e7d479c2ed55b4d61c0129da 100644 (file)
@@ -6,20 +6,45 @@
 /* SYNOPSIS
 /*     #include <postconf.h>
 /*
-/*     void    edit_parameters(mode, argc, argv)
+/*     void    edit_main(mode, argc, argv)
+/*     int     mode;
+/*     int     argc;
+/*     char    **argv;
+/*
+/*     void    edit_master(mode, argc, argv)
 /*     int     mode;
 /*     int     argc;
 /*     char    **argv;
 /* DESCRIPTION
-/*     Edit the \fBmain.cf\fR configuration file, and update
-/*     parameter settings with the "\fIname\fR=\fIvalue\fR" pairs
-/*     given on the command line. The file is copied to a temporary
-/*     file then renamed into place.
+/*     edit_main() edits the \fBmain.cf\fR configuration
+/*     file. It replaces or adds parameter settings given as
+/*     "\fIname=value\fR" pairs given on the command line, or
+/*     removes parameter settings given as "\fIname\fR" on the
+/*     command line.  The file is copied to a temporary file
+/*     then renamed into place.
+/*
+/*     edit_master() edits the \fBmaster.cf\fR configuration file.
+/*     The file is copied to a temporary file then renamed into
+/*     place. Depending on the value of \fBmode\fR:
+/* .IP MASTER_ENTRY
+/*     edit_master() replaces or adds entire master.cf entries,
+/*     specified on the command line as "\fIname/type = name type
+/*     private unprivileged chroot wakeup process_limit command...\fR".
+/* ,IP MASTER_FIELD
+/*     edit_master() replaces the value of specific service
+/*     attributes, specified on the command line as
+/*     "\fIname/type/attribute = value\fR".
+/* .IP MASTER_PARAM
+/*     edit_master() replaces the value of specific service
+/*     parameters, specified on the command line as
+/*     "\fIname/type/parameter = value\fR".
 /* DIAGNOSTICS
 /*     Problems are reported to the standard error stream.
 /* FILES
 /*     /etc/postfix/main.cf, Postfix configuration parameters
 /*     /etc/postfix/main.cf.tmp, temporary name
+/*     /etc/postfix/master.cf, Postfix configuration parameters
+/*     /etc/postfix/master.cf.tmp, temporary name
 /* LICENSE
 /* .ad
 /* .fi
@@ -47,6 +72,7 @@
 #include <edit_file.h>
 #include <readlline.h>
 #include <stringops.h>
+#include <split_at.h>
 
 /* Global library. */
 
 
 #define STR(x) vstring_str(x)
 
-/* edit_parameters - edit parameter file */
+/* find_cf_info - pass-through non-content line, return content or null */
 
-void    edit_parameters(int mode, int argc, char **argv)
+static char *find_cf_info(VSTRING *buf, VSTREAM *dst)
+{
+    char   *cp;
+
+    for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
+        /* void */ ;
+    /* Pass-through comment, all-whitespace, or empty line. */
+    if (*cp == '#' || *cp == 0) {
+       vstream_fputs(STR(buf), dst);
+       return (0);
+    } else {
+       return (cp);
+    }
+}
+
+/* next_cf_line - return next content line, pass-through non-content */
+
+static char *next_cf_line(VSTRING *buf, VSTREAM *src, VSTREAM *dst, int *lineno)
+{
+    char   *cp;
+
+    while (vstring_get(buf, src) != VSTREAM_EOF) {
+       if (lineno)
+           *lineno += 1;
+       if ((cp = find_cf_info(buf, dst)) != 0)
+           return (cp);
+    }
+    return (0);
+}
+
+/* gobble_cf_line - accumulate multi-line content, pass-through non-content */
+
+static void gobble_cf_line(VSTRING *full_entry_buf, VSTRING *line_buf,
+                                  VSTREAM *src, VSTREAM *dst, int *lineno)
+{
+    int     ch;
+
+    vstring_strcpy(full_entry_buf, STR(line_buf));
+    for (;;) {
+       if ((ch = VSTREAM_GETC(src)) != VSTREAM_EOF)
+           vstream_ungetc(src, ch);
+       if ((ch != '#' && !ISSPACE(ch))
+           || vstring_get(line_buf, src) == VSTREAM_EOF)
+           break;
+       lineno += 1;
+       if (find_cf_info(line_buf, dst))
+           vstring_strcat(full_entry_buf, STR(line_buf));
+    }
+}
+
+/* edit_main - edit main.cf file */
+
+void    edit_main(int mode, int argc, char **argv)
 {
     char   *path;
     EDIT_FILE *ep;
@@ -69,8 +147,8 @@ void    edit_parameters(int mode, int argc, char **argv)
     VSTRING *buf = vstring_alloc(100);
     VSTRING *key = vstring_alloc(10);
     char   *cp;
-    char   *edit_key;
-    char   *edit_val;
+    char   *pattern;
+    char   *edit_value;
     HTABLE *table;
     struct cvalue {
        char   *value;
@@ -93,24 +171,24 @@ void    edit_parameters(int mode, int argc, char **argv)
            cp++;
        if (*cp == '#')
            msg_fatal("-e, -X, or -# accepts no comment input");
-       if (mode & EDIT_MAIN) {
-           if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
+       if (mode & EDIT_CONF) {
+           if ((err = split_nameval(cp, &pattern, &edit_value)) != 0)
                msg_fatal("%s: \"%s\"", err, cp);
        } else if (mode & (COMMENT_OUT | EDIT_EXCL)) {
            if (*cp == 0)
                msg_fatal("-X or -# requires non-blank parameter names");
            if (strchr(cp, '=') != 0)
-               msg_fatal("-X or -# requires parameter names only");
-           edit_key = cp;
-           trimblanks(edit_key, 0);
-           edit_val = 0;
+               msg_fatal("-X or -# requires parameter names without value");
+           pattern = cp;
+           trimblanks(pattern, 0);
+           edit_value = 0;
        } else {
-           msg_panic("edit_parameters: unknown mode %d", mode);
+           msg_panic("edit_main: unknown mode %d", mode);
        }
        cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
-       cvalue->value = edit_val;
+       cvalue->value = edit_value;
        cvalue->found = 0;
-       htable_enter(table, edit_key, (char *) cvalue);
+       htable_enter(table, pattern, (char *) cvalue);
     }
 
     /*
@@ -139,15 +217,9 @@ void    edit_parameters(int mode, int argc, char **argv)
 #define STR(x) vstring_str(x)
 
     interesting = 0;
-    while (vstring_get(buf, src) != VSTREAM_EOF) {
-       for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
-            /* void */ ;
-       /* Copy comment, all-whitespace, or empty line. */
-       if (*cp == '#' || *cp == 0) {
-           vstream_fputs(STR(buf), dst);
-       }
+    while ((cp = next_cf_line(buf, src, dst, (int *) 0)) != 0) {
        /* Copy, skip or replace continued text. */
-       else if (cp > STR(buf)) {
+       if (cp > STR(buf)) {
            if (interesting == 0)
                vstream_fputs(STR(buf), dst);
            else if (mode & COMMENT_OUT)
@@ -160,7 +232,7 @@ void    edit_parameters(int mode, int argc, char **argv)
            if ((interesting = !!cvalue) != 0) {
                if (cvalue->found++ == 1)
                    msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
-               if (mode & EDIT_MAIN)
+               if (mode & EDIT_CONF)
                    vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
                else if (mode & COMMENT_OUT)
                    vstream_fprintf(dst, "#%s", cp);
@@ -173,7 +245,7 @@ void    edit_parameters(int mode, int argc, char **argv)
     /*
      * Generate new entries for parameters that were not found.
      */
-    if (mode & EDIT_MAIN) {
+    if (mode & EDIT_CONF) {
        for (ht_info = ht = htable_list(table); *ht; ht++) {
            cvalue = (struct cvalue *) ht[0]->value;
            if (cvalue->found == 0)
@@ -198,3 +270,294 @@ void    edit_parameters(int mode, int argc, char **argv)
     vstring_free(key);
     htable_free(table, myfree);
 }
+
+ /*
+  * Data structure to hold a master.cf edit request.
+  */
+typedef struct {
+    int     match_count;               /* hit count */
+    const char *raw_text;              /* unparsed command-line argument */
+    char   *parsed_text;               /* destructive parse */
+    ARGV   *service_pattern;           /* service name, type, ... */
+    int     field_number;              /* attribute field number */
+    const char *param_pattern;         /* parameter name */
+    char   *edit_value;                        /* value substring */
+} PC_MASTER_EDIT_REQ;
+
+/* edit_master - edit master.cf file */
+
+void    edit_master(int mode, int argc, char **argv)
+{
+    const char *myname = "edit_master";
+    char   *path;
+    EDIT_FILE *ep;
+    VSTREAM *src;
+    VSTREAM *dst;
+    VSTRING *line_buf = vstring_alloc(100);
+    VSTRING *parse_buf = vstring_alloc(100);
+    int     lineno;
+    PC_MASTER_ENT *new_entry;
+    VSTRING *full_entry_buf = vstring_alloc(100);
+    char   *cp;
+    char   *pattern;
+    int     service_name_type_matched;
+    const char *err;
+    PC_MASTER_EDIT_REQ *edit_reqs;
+    PC_MASTER_EDIT_REQ *req;
+    int     num_reqs = argc;
+    const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#";
+    char   *service_name;
+    char   *service_type;
+
+    /*
+     * Sanity check.
+     */
+    if (num_reqs <= 0)
+       msg_panic("%s: empty argument list", myname);
+
+    /*
+     * Preprocessing: split pattern=value, then split the pattern components.
+     */
+    edit_reqs = (PC_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs);
+    for (req = edit_reqs; *argv != 0; req++, argv++) {
+       req->match_count = 0;
+       req->raw_text = *argv;
+       cp = req->parsed_text = mystrdup(req->raw_text);
+       if (strchr(cp, '\n') != 0)
+           msg_fatal("%s accept no multi-line input", edit_opts);
+       while (ISSPACE(*cp))
+           cp++;
+       if (*cp == '#')
+           msg_fatal("%s accept no comment input", edit_opts);
+       /* Separate the pattern from the value. */
+       if (mode & EDIT_CONF) {
+           if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0)
+               msg_fatal("%s: \"%s\"", err, req->raw_text);
+       } else if (mode & (COMMENT_OUT | EDIT_EXCL)) {
+           if (strchr(cp, '=') != 0)
+               msg_fatal("-X or -# requires names without value");
+           pattern = cp;
+           trimblanks(pattern, 0);
+           req->edit_value = 0;
+       } else {
+           msg_panic("%s: unknown mode %d", myname, mode);
+       }
+
+#define PC_MASTER_MASK (MASTER_ENTRY | MASTER_FIELD | MASTER_PARAM)
+
+       /*
+        * Split name/type or name/type/field pattern into components.
+        */
+       switch (mode & PC_MASTER_MASK) {
+       case MASTER_ENTRY:
+           if ((req->service_pattern =
+                parse_service_pattern(pattern, 2, 2)) == 0)
+               msg_fatal("-Me, -MX or -M# requires service_name/type");
+           break;
+       case MASTER_FIELD:
+           if ((req->service_pattern =
+                parse_service_pattern(pattern, 3, 3)) == 0)
+               msg_fatal("-Fe or -FX requires service_name/type/field_name");
+           req->field_number =
+               parse_field_pattern(req->service_pattern->argv[2]);
+           if (is_magic_field_pattern(req->field_number))
+               msg_fatal("-Fe does not accept wild-card field name");
+           if ((mode & EDIT_CONF)
+               && req->field_number < PC_MASTER_FIELD_CMD
+            && req->edit_value[strcspn(req->edit_value, PC_MASTER_BLANKS)])
+               msg_fatal("-Fe does not accept whitespace in non-command field");
+           break;
+       case MASTER_PARAM:
+           if ((req->service_pattern =
+                parse_service_pattern(pattern, 3, 3)) == 0)
+               msg_fatal("-Pe or -PX requires service_name/type/parameter");
+           req->param_pattern = req->service_pattern->argv[2];
+           if (IS_MAGIC_PARAM_PATTERN(req->param_pattern))
+               msg_fatal("-Pe does not accept wild-card parameter name");
+           if ((mode & EDIT_CONF)
+            && req->edit_value[strcspn(req->edit_value, PC_MASTER_BLANKS)])
+               msg_fatal("-Pe does not accept whitespace in parameter value");
+           break;
+       default:
+           msg_panic("%s: unknown edit mode %d", myname, mode);
+       }
+    }
+
+    /*
+     * Open a temp file for the result. This uses a deterministic name so we
+     * don't leave behind thrash with random names.
+     */
+    set_config_dir();
+    path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
+    if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
+       msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
+    dst = ep->tmp_fp;
+
+    /*
+     * Open the original file for input.
+     */
+    if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
+       /* OK to delete, since we control the temp file name exclusively. */
+       (void) unlink(ep->tmp_path);
+       msg_fatal("open %s for reading: %m", path);
+    }
+
+    /*
+     * Copy original file to temp file, while replacing service entries on
+     * the fly.
+     */
+    service_name_type_matched = 0;
+    new_entry = 0;
+    lineno = 0;
+    while ((cp = next_cf_line(parse_buf, src, dst, &lineno)) != 0) {
+       vstring_strcpy(line_buf, STR(parse_buf));
+
+       /*
+        * Copy, skip or replace continued text.
+        */
+       if (cp > STR(parse_buf)) {
+           if (service_name_type_matched == 0)
+               vstream_fputs(STR(line_buf), dst);
+           else if (mode & COMMENT_OUT)
+               vstream_fprintf(dst, "#%s", STR(line_buf));
+       }
+
+       /*
+        * Copy or replace (start of) logical line.
+        */
+       else {
+           service_name_type_matched = 0;
+
+           /*
+            * Parse out the service name and type.
+            */
+           if ((service_name = mystrtok(&cp, PC_MASTER_BLANKS)) == 0
+               || (service_type = mystrtok(&cp, PC_MASTER_BLANKS)) == 0)
+               msg_fatal("file %s: line %d: specify service name and type "
+                         "on the same line", path, lineno);
+           if (strchr(service_name, '='))
+               msg_fatal("file %s: line %d: service name syntax \"%s\" is "
+                         "unsupported with %s", path, lineno, service_name,
+                         edit_opts);
+           if (service_type[strcspn(service_type, "=/")] != 0)
+               msg_fatal("file %s: line %d: "
+                       "service type syntax \"%s\" is unsupported with %s",
+                         path, lineno, service_type, edit_opts);
+
+           /*
+            * Match each service pattern.
+            */
+           for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
+               if (MATCH_SERVICE_PATTERN(req->service_pattern, service_name,
+                                         service_type)) {
+                   service_name_type_matched = 1;      /* Sticky flag */
+                   req->match_count += 1;
+
+                   /*
+                    * Generate replacement master.cf entries.
+                    */
+                   if ((mode & EDIT_CONF)
+                       || ((mode & MASTER_PARAM) && (mode & EDIT_EXCL))) {
+                       switch (mode & PC_MASTER_MASK) {
+
+                           /*
+                            * Replace master.cf entry field or parameter
+                            * value.
+                            */
+                       case MASTER_FIELD:
+                       case MASTER_PARAM:
+                           if (new_entry == 0) {
+                               /* Gobble up any continuation lines. */
+                               gobble_cf_line(full_entry_buf, line_buf, src,
+                                              dst, &lineno);
+                               new_entry = (PC_MASTER_ENT *)
+                                   mymalloc(sizeof(*new_entry));
+                               if ((err = parse_master_entry(new_entry,
+                                                STR(full_entry_buf))) != 0)
+                                   msg_fatal("file %s: line %d: %s",
+                                             path, lineno, err);
+                           }
+                           if (mode & MASTER_FIELD) {
+                               edit_master_field(new_entry, req->field_number,
+                                                 req->edit_value);
+                           } else {
+                               edit_master_param(new_entry, mode,
+                                                 req->param_pattern,
+                                                 req->edit_value);
+                           }
+                           break;
+
+                           /*
+                            * Replace entire master.cf entry.
+                            */
+                       case MASTER_ENTRY:
+                           if (new_entry != 0)
+                               free_master_entry(new_entry);
+                           new_entry = (PC_MASTER_ENT *)
+                               mymalloc(sizeof(*new_entry));
+                           if ((err = parse_master_entry(new_entry,
+                                                    req->edit_value)) != 0)
+                               msg_fatal("%s: \"%s\"", err, req->raw_text);
+                           break;
+                       default:
+                           msg_panic("%s: unknown edit mode %d", myname, mode);
+                       }
+                   }
+               }
+           }
+
+           /*
+            * Pass through or replace the current input line.
+            */
+           if (new_entry) {
+               print_master_entry(dst, FOLD_LINE, new_entry);
+               free_master_entry(new_entry);
+               new_entry = 0;
+           } else if (service_name_type_matched == 0) {
+               vstream_fputs(STR(line_buf), dst);
+           } else if (mode & COMMENT_OUT) {
+               vstream_fprintf(dst, "#%s", STR(line_buf));
+           }
+       }
+    }
+
+    /*
+     * Postprocessing: when editing entire service entries, generate new
+     * entries for services not found. Otherwise (editing fields or
+     * parameters), "service not found" is a fatal error.
+     */
+    for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
+       if (req->match_count == 0) {
+           if ((mode & MASTER_ENTRY) && (mode & EDIT_CONF)) {
+               new_entry = (PC_MASTER_ENT *) mymalloc(sizeof(*new_entry));
+               if ((err = parse_master_entry(new_entry, req->edit_value)) != 0)
+                   msg_fatal("%s: \"%s\"", err, req->raw_text);
+               print_master_entry(dst, FOLD_LINE, new_entry);
+               free_master_entry(new_entry);
+           } else if ((mode & MASTER_ENTRY) == 0) {
+               msg_warn("unmatched service_name/type: \"%s\"", req->raw_text);
+           }
+       }
+    }
+
+    /*
+     * When all is well, rename the temp file to the original one.
+     */
+    if (vstream_fclose(src))
+       msg_fatal("read %s: %m", path);
+    if (edit_file_close(ep) != 0)
+       msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);
+
+    /*
+     * Cleanup.
+     */
+    myfree(path);
+    vstring_free(line_buf);
+    vstring_free(parse_buf);
+    vstring_free(full_entry_buf);
+    for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
+       argv_free(req->service_pattern);
+       myfree(req->parsed_text);
+    }
+    myfree((char *) edit_reqs);
+}
index d94c4a6e62d671d7ddff58531cf61429b85808c4..a7bab12e42325b57f30b31282a606679685b0c44 100644 (file)
@@ -129,59 +129,6 @@ void    set_parameters(char **name_val_array)
     }
 }
 
-/* print_line - show line possibly folded, and with normalized whitespace */
-
-static void print_line(VSTREAM *fp, int mode, const char *fmt,...)
-{
-    va_list ap;
-    static VSTRING *buf = 0;
-    char   *start;
-    char   *next;
-    int     line_len = 0;
-    int     word_len;
-
-    /*
-     * One-off initialization.
-     */
-    if (buf == 0)
-       buf = vstring_alloc(100);
-
-    /*
-     * Format the text.
-     */
-    va_start(ap, fmt);
-    vstring_vsprintf(buf, fmt, ap);
-    va_end(ap);
-
-    /*
-     * Normalize the whitespace. We don't use the line_wrap() routine because
-     * 1) that function does not normalize whitespace between words and 2) we
-     * want to normalize whitespace even when not wrapping lines.
-     * 
-     * XXX Some parameters preserve whitespace: for example, smtpd_banner and
-     * smtpd_reject_footer. If we have to preserve whitespace between words,
-     * then perhaps readlline() can be changed to canonicalize whitespace
-     * that follows a newline.
-     */
-    for (start = STR(buf); *(start += strspn(start, SEPARATORS)) != 0; start = next) {
-       word_len = strcspn(start, SEPARATORS);
-       if (*(next = start + word_len) != 0)
-           *next++ = 0;
-       if (word_len > 0 && line_len > 0) {
-           if ((mode & FOLD_LINE) == 0 || line_len + word_len < LINE_LIMIT) {
-               vstream_fputs(" ", fp);
-               line_len += 1;
-           } else {
-               vstream_fputs("\n" INDENT_TEXT, fp);
-               line_len = INDENT_LEN;
-           }
-       }
-       vstream_fputs(start, fp);
-       line_len += word_len;
-    }
-    vstream_fputs("\n", fp);
-}
-
 /* print_parameter - show specific parameter */
 
 static void print_parameter(VSTREAM *fp, int mode, const char *name,
@@ -202,7 +149,7 @@ static void print_parameter(VSTREAM *fp, int mode, const char *name,
        if ((mode & SHOW_EVAL) != 0 && PC_RAW_PARAMETER(node) == 0)
            value = expand_parameter_value((VSTRING *) 0, mode, value,
                                           (PC_MASTER_ENT *) 0);
-       if (mode & SHOW_NAME) {
+       if ((mode & HIDE_NAME) == 0) {
            print_line(fp, mode, "%s = %s\n", name, value);
        } else {
            print_line(fp, mode, "%s\n", value);
index 9a94ce92faf27b3f2c41b77a25133294d50e5703..1409373221bd54d0475e1b8cf4b8738080b9f044 100644 (file)
 /*     void    read_master(fail_on_open)
 /*     int     fail_on_open;
 /*
-/*     void    show_master(fp, mode, filters)
+/*     void    show_master_entries(fp, mode, service_filters)
 /*     VSTREAM *fp;
 /*     int     mode;
-/*     char    **filters;
+/*     char    **service_filters;
+/*
+/*     void    show_master_fields(fp, mode, n_filters, field_filters)
+/*     VSTREAM *fp;
+/*     int     mode;
+/*     int     n_filters;
+/*     char    **field_filters;
+/*
+/*     void    edit_master_field(masterp, field, new_value)
+/*     PC_MASTER_ENT *masterp;
+/*     int     field;
+/*     const char *new_value;
+/*
+/*     void    show_master_params(fp, mode, argc, **param_filters)
+/*     VSTREAM *fp;
+/*     int     mode;
+/*     int     argc;
+/*     char    **param_filters;
+/*
+/*     void    edit_master_param(masterp, mode, param_name, param_value)
+/*     PC_MASTER_ENT *masterp;
+/*     int     mode;
+/*     const char *param_name;
+/*     const char *param_value;
+/* AUXILIARY FUNCTIONS
+/*     const char *parse_master_entry(masterp, buf)
+/*     PC_MASTER_ENT *masterp;
+/*     const char *buf;
+/*
+/*     void    print_master_entry(fp, mode, masterp)
+/*     VSTREAM *fp;
+/*     int mode;
+/*     PC_MASTER_ENT *masterp;
+/*
+/*     void    free_master_entry(masterp)
+/*     PC_MASTER_ENT *masterp;
 /* DESCRIPTION
 /*     read_master() reads entries from master.cf into memory.
 /*
-/*     show_master() writes the entries in the master.cf file
+/*     show_master_entries() writes the entries in the master.cf
+/*     file to the specified stream.
+/*
+/*     show_master_fields() writes name/type/field=value records to
+/*     the specified stream.
+/*
+/*     edit_master_field() updates the value of a single-column
+/*     or multi-column attribute.
+/*
+/*     show_master_params() writes name/type/parameter=value records
 /*     to the specified stream.
 /*
+/*     edit_master_param() updates, removes or adds the named
+/*     parameter in a master.cf entry (the remove request ignores
+/*     the parameter value).
+/*
 /*     daemon_options_expecting_value[] is an array of master.cf
 /*     daemon command-line options that expect an option value.
 /*
+/*     parse_master_entry() parses a (perhaps multi-line) string
+/*     that contains a complete master.cf entry, and normalizes
+/*     daemon command-line options to simplify further handling.
+/*
+/*     print_master_entry() prints a parsed master.cf entry.
+/*
+/*     free_master_entry() returns storage to the heap that was
+/*     allocated by parse_master_entry().
+/*
 /*     Arguments
 /* .IP fail_on_open
 /*     Specify FAIL_ON_OPEN if open failure is a fatal error,
 /* .IP fp
 /*     Output stream.
 /* .IP mode
-/*     If the FOLD_LINE flag is set, show_master() wraps long
-/*     output lines.
-/* .IP filters
-/*     A list of zero or more expressions in master_service(3)
-/*     format. If no list is specified, show_master() outputs
+/*     Bit-wise OR of flags. Flags other than the following are ignored.
+/* .RS
+/* .IP FOLD_LINE
+/*     Wrap long output lines.
+/* .IP SHOW_EVAL
+/*     Expand $name in parameter values.
+/* .IP EDIT_EXCL
+/*     Request that edit_master_param() removes the parameter.
+/* .RE
+/* .IP n_filters
+/*     The number of command-line filters.
+/* .IP field_filters
+/*     A list of zero or more service field patterns (name/type/field).
+/*     The output is formatted as "name/type/field = value".  If
+/*     no filters are specified, show_master_fields() outputs the
+/*     fields of all master.cf entries in the specified order.
+/* .IP param_filters
+/*     A list of zero or more service parameter patterns
+/*     (name/type/parameter).  The output is formatted as
+/*     "name/type/parameter = value".  If no filters are specified,
+/*     show_master_params() outputs the parameters of all master.cf
+/*     entries in sorted order.
+/* .IP service_filters
+/*     A list of zero or more service patterns (name or name/type).
+/*     If no filters are specified, show_master_entries() outputs
 /*     all master.cf entries in the specified order.
+/* .IP field
+/*     Index into parsed master.cf entry.
+/* .IP new_value
+/*     Replacement value for the specified field. It is split
+/*     in whitespace in case of a multi-field attribute.
 /* DIAGNOSTICS
 /*     Problems are reported to the standard error stream.
 /* LICENSE
 
 #include <sys_defs.h>
 #include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
 
 /* Utility library. */
 
 #include <vstream.h>
 #include <readlline.h>
 #include <stringops.h>
+#include <split_at.h>
 
 /* Global library. */
 
 #include <mail_params.h>
-#include <match_service.h>
+
+/* Master library. */
+
+#include <master_proto.h>
 
 /* Application-specific. */
 
 
 const char daemon_options_expecting_value[] = "o";
 
+ /*
+  * Data structure to capture a command-line service field filter.
+  */
+typedef struct {
+    int     match_count;               /* hit count */
+    const char *raw_text;              /* full pattern text */
+    ARGV   *service_pattern;           /* parsed service name, type, ... */
+    int     field_pattern;             /* parsed field pattern */
+    const char *param_pattern;         /* parameter pattern */
+} PC_MASTER_FIELD_REQ;
+
+ /*
+  * Valid inputs.
+  */
+static const char *valid_master_types[] = {
+    MASTER_XPORT_NAME_UNIX,
+    MASTER_XPORT_NAME_FIFO,
+    MASTER_XPORT_NAME_INET,
+    MASTER_XPORT_NAME_PASS,
+    0,
+};
+
+static const char valid_bool_types[] = "yn-";
+
 #define STR(x) vstring_str(x)
 
 /* normalize_options - bring options into canonical form */
@@ -97,7 +209,7 @@ static void normalize_options(ARGV *argv)
        for (cp = arg + 1; *cp; cp++) {
            if (strchr(daemon_options_expecting_value, *cp) != 0
                && cp > arg + 1) {
-               /* Split "-stuffo" into "-stuff" and "-o". */
+               /* Split "-stuffozz" into "-stuff" and "-ozz". */
                junk = concatenate("-", cp, (char *) 0);
                argv_insert_one(argv, field + 1, junk);
                myfree(junk);
@@ -120,9 +232,80 @@ static void normalize_options(ARGV *argv)
     }
 }
 
-/* parse_master_line - parse one master line */
+/* fix_fatal - fix multiline text before release */
+
+static NORETURN PRINTFLIKE(1, 2) fix_fatal(const char *fmt,...)
+{
+    VSTRING *buf = vstring_alloc(100);
+    va_list ap;
+
+    /*
+     * Replace newline with whitespace.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+    translit(STR(buf), "\n", " ");
+    msg_fatal("%s", STR(buf));
+    /* NOTREACHED */
+}
+
+/* check_master_entry - sanity check master.cf entry */
+
+static void check_master_entry(ARGV *argv, const char *raw_text)
+{
+    const char **cpp;
+    char   *cp;
+    int     len;
+    int     field;
+
+    cp = argv->argv[PC_MASTER_FIELD_TYPE];
+    for (cpp = valid_master_types; /* see below */ ; cpp++) {
+       if (*cpp == 0)
+           fix_fatal("invalid " PC_MASTER_NAME_TYPE " field \"%s\" in \"%s\"",
+                     cp, raw_text);
+       if (strcmp(*cpp, cp) == 0)
+           break;
+    }
+
+    for (field = PC_MASTER_FIELD_PRIVATE; field <= PC_MASTER_FIELD_CHROOT; field++) {
+       cp = argv->argv[field];
+       if (cp[1] != 0 || strchr(valid_bool_types, *cp) == 0)
+           fix_fatal("invalid %s field \%s\" in \"%s\"",
+                     str_field_pattern(field), cp, raw_text);
+    }
+
+    cp = argv->argv[PC_MASTER_FIELD_WAKEUP];
+    len = strlen(cp);
+    if (len > 0 && cp[len - 1] == '?')
+       len--;
+    if (!(cp[0] == '-' && len == 1) && strspn(cp, "0123456789") != len)
+       fix_fatal("invalid " PC_MASTER_NAME_WAKEUP " field \%s\" in \"%s\"",
+                 cp, raw_text);
+
+    cp = argv->argv[PC_MASTER_FIELD_MAXPROC];
+    if (strcmp("-", cp) != 0 && cp[strspn(cp, "0123456789")] != 0)
+       fix_fatal("invalid " PC_MASTER_NAME_MAXPROC " field \%s\" in \"%s\"",
+                 cp, raw_text);
+}
+
+/* free_master_entry - destroy parsed entry */
+
+void    free_master_entry(PC_MASTER_ENT *masterp)
+{
+    /* XX Fixme: allocation/deallocation asymmetry. */
+    myfree(masterp->name_space);
+    argv_free(masterp->argv);
+    if (masterp->valid_names)
+       htable_free(masterp->valid_names, myfree);
+    if (masterp->all_params)
+       dict_free(masterp->all_params);
+    myfree((char *) masterp);
+}
+
+/* parse_master_entry - parse one master line */
 
-static const char *parse_master_line(PC_MASTER_ENT *masterp, const char *buf)
+const char *parse_master_entry(PC_MASTER_ENT *masterp, const char *buf)
 {
     ARGV   *argv;
 
@@ -134,17 +317,18 @@ static const char *parse_master_line(PC_MASTER_ENT *masterp, const char *buf)
      * The postconf command needs to show default fields as "-", and needs to
      * know about all service names so that it can generate service-dependent
      * parameter names (transport-dependent etc.).
+     * 
+     * XXX Do per-field sanity checks.
      */
-#define MASTER_BLANKS  " \t\r\n"               /* XXX */
-
-    argv = argv_split(buf, MASTER_BLANKS);
+    argv = argv_split(buf, PC_MASTER_BLANKS);
     if (argv->argc < PC_MASTER_MIN_FIELDS) {
        argv_free(argv);                        /* Coverity 201311 */
        return ("bad field count");
     }
+    check_master_entry(argv, buf);
     normalize_options(argv);
     masterp->name_space =
-       concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0);
+       concatenate(argv->argv[0], PC_NAMESP_SEP_STR, argv->argv[1], (char *) 0);
     masterp->argv = argv;
     masterp->valid_names = 0;
     masterp->all_params = 0;
@@ -194,8 +378,8 @@ void    read_master(int fail_on_open_error)
        while (readlline(buf, fp, &line_count) != 0) {
            master_table = (PC_MASTER_ENT *) myrealloc((char *) master_table,
                                 (entry_count + 2) * sizeof(*master_table));
-           if ((err = parse_master_line(master_table + entry_count,
-                                        STR(buf))) != 0)
+           if ((err = parse_master_entry(master_table + entry_count,
+                                         STR(buf))) != 0)
                msg_fatal("file %s: line %d: %s", path, line_count, err);
            entry_count += 1;
        }
@@ -210,9 +394,9 @@ void    read_master(int fail_on_open_error)
     myfree(path);
 }
 
-/* print_master_line - print one master line */
+/* print_master_entry - print one master line */
 
-static void print_master_line(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp)
+void    print_master_entry(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp)
 {
     char  **argv = masterp->argv->argv;
     const char *arg;
@@ -268,11 +452,11 @@ static void print_master_line(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp)
             */
            if (arg[0] != '-' || strcmp(arg, "--") == 0) {
                in_daemon_options = 0;
-               if ((mode & FOLD_LINE)
-                   && line_len > column_goal[PC_MASTER_MIN_FIELDS - 1]) {
+#if 0
+               if (mode & FOLD_LINE)
                    /* Force line wrap. */
                    line_len = LINE_LIMIT;
-               }
+#endif
            }
 
            /*
@@ -281,6 +465,9 @@ static void print_master_line(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp)
            else if (strchr(daemon_options_expecting_value, arg[1]) != 0
                     && (aval = argv[field + 1]) != 0) {
 
+               /* Force line wrap before option with value. */
+               line_len = LINE_LIMIT;
+
                /*
                 * Optionally, expand $name in parameter value.
                 */
@@ -313,36 +500,497 @@ static void print_master_line(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp)
            ADD_SPACE;
            ADD_TEXT(aval, strlen(aval));
            field += 1;
+
+           /* Force line wrap after option with value. */
+           line_len = LINE_LIMIT;
+
+       }
+    }
+    vstream_fputs("\n", fp);
+
+    if (msg_verbose)
+       vstream_fflush(fp);
+}
+
+/* show_master_entries - show master.cf entries */
+
+void    show_master_entries(VSTREAM *fp, int mode, int argc, char **argv)
+{
+    PC_MASTER_ENT *masterp;
+    PC_MASTER_FIELD_REQ *field_reqs;
+    PC_MASTER_FIELD_REQ *req;
+
+    /*
+     * Parse the filter expressions.
+     */
+    if (argc > 0) {
+       field_reqs = (PC_MASTER_FIELD_REQ *)
+           mymalloc(sizeof(*field_reqs) * argc);
+       for (req = field_reqs; req < field_reqs + argc; req++) {
+           req->match_count = 0;
+           req->raw_text = *argv++;
+           req->service_pattern =
+               parse_service_pattern(req->raw_text, 1, 2);
+           if (req->service_pattern == 0)
+               msg_fatal("-M option requires service_name[/type]");
+       }
+    }
+
+    /*
+     * Iterate over the master table.
+     */
+    for (masterp = master_table; masterp->argv != 0; masterp++) {
+       if (argc > 0) {
+           for (req = field_reqs; req < field_reqs + argc; req++) {
+               if (MATCH_SERVICE_PATTERN(req->service_pattern,
+                                         masterp->argv->argv[0],
+                                         masterp->argv->argv[1])) {
+                   req->match_count++;
+                   print_master_entry(fp, mode, masterp);
+               }
+           }
+       } else {
+           print_master_entry(fp, mode, masterp);
+       }
+    }
+
+    /*
+     * Cleanup.
+     */
+    if (argc > 0) {
+       for (req = field_reqs; req < field_reqs + argc; req++) {
+           if (req->match_count == 0)
+               msg_warn("unmatched request: \"%s\"", req->raw_text);
+           argv_free(req->service_pattern);
+       }
+       myfree((char *) field_reqs);
+    }
+}
+
+/* print_master_field - scaffolding */
+
+static void print_master_field(VSTREAM *fp, int mode,
+                                      PC_MASTER_ENT *masterp,
+                                      int field)
+{
+    char  **argv = masterp->argv->argv;
+    const char *arg;
+    const char *aval;
+    int     arg_len;
+    int     line_len;
+    int     in_daemon_options;
+
+    /*
+     * Show the field value, or the first value in the case of a multi-column
+     * field.
+     */
+#define ADD_CHAR(ch) ADD_TEXT((ch), 1)
+
+    line_len = 0;
+    if ((mode & HIDE_NAME) == 0) {
+       ADD_TEXT(argv[0], strlen(argv[0]));
+       ADD_CHAR(PC_NAMESP_SEP_STR);
+       ADD_TEXT(argv[1], strlen(argv[1]));
+       ADD_CHAR(PC_NAMESP_SEP_STR);
+       ADD_TEXT(str_field_pattern(field), strlen(str_field_pattern(field)));
+       ADD_TEXT(" = ", 3);
+       if (line_len + strlen(argv[field]) > LINE_LIMIT) {
+           vstream_fputs("\n" INDENT_TEXT, fp);
+           line_len = INDENT_LEN;
+       }
+    }
+    ADD_TEXT(argv[field], strlen(argv[field]));
+
+    /*
+     * Format the daemon command-line options and non-option arguments. Here,
+     * we have no data-dependent preference for column positions, but we do
+     * have argument grouping preferences.
+     */
+    if (field == PC_MASTER_FIELD_CMD) {
+       in_daemon_options = 1;
+       for (field += 1; (arg = argv[field]) != 0; field++) {
+           arg_len = strlen(arg);
+           aval = 0;
+           if (in_daemon_options) {
+
+               /*
+                * We make no special case for generic options (-v -D)
+                * options.
+                */
+               if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+                   in_daemon_options = 0;
+               } else if (strchr(daemon_options_expecting_value, arg[1]) != 0
+                          && (aval = argv[field + 1]) != 0) {
+
+                   /* Force line break before option with value. */
+                   line_len = LINE_LIMIT;
+
+                   /*
+                    * Optionally, expand $name in parameter value.
+                    */
+                   if (strcmp(arg, "-o") == 0
+                       && (mode & SHOW_EVAL) != 0)
+                       aval = expand_parameter_value((VSTRING *) 0, mode,
+                                                     aval, masterp);
+
+                   /*
+                    * Keep option and value on the same line.
+                    */
+                   arg_len += strlen(aval) + 1;
+               }
+           }
+
+           /*
+            * Insert a line break when the next item won't fit.
+            */
+           if (line_len > INDENT_LEN) {
+               if ((mode & FOLD_LINE) == 0
+                   || line_len + 1 + arg_len < LINE_LIMIT) {
+                   ADD_SPACE;
+               } else {
+                   vstream_fputs("\n" INDENT_TEXT, fp);
+                   line_len = INDENT_LEN;
+               }
+           }
+           ADD_TEXT(arg, strlen(arg));
+           if (aval) {
+               ADD_SPACE;
+               ADD_TEXT(aval, strlen(aval));
+               field += 1;
+
+               /* Force line break after option with value. */
+               line_len = LINE_LIMIT;
+           }
        }
     }
     vstream_fputs("\n", fp);
+
+    if (msg_verbose)
+       vstream_fflush(fp);
+}
+
+/* show_master_fields - show master.cf fields */
+
+void    show_master_fields(VSTREAM *fp, int mode, int argc, char **argv)
+{
+    const char *myname = "show_master_fields";
+    PC_MASTER_ENT *masterp;
+    PC_MASTER_FIELD_REQ *field_reqs;
+    PC_MASTER_FIELD_REQ *req;
+    int     field;
+
+    /*
+     * Parse the filter expressions.
+     */
+    if (argc > 0) {
+       field_reqs = (PC_MASTER_FIELD_REQ *)
+           mymalloc(sizeof(*field_reqs) * argc);
+       for (req = field_reqs; req < field_reqs + argc; req++) {
+           req->match_count = 0;
+           req->raw_text = *argv++;
+           req->service_pattern =
+               parse_service_pattern(req->raw_text, 1, 3);
+           if (req->service_pattern == 0)
+               msg_fatal("-F option requires service_name[/type[/field]]");
+           field = req->field_pattern =
+               parse_field_pattern(req->service_pattern->argv[2]);
+           if (is_magic_field_pattern(field) == 0
+               && (field < 0 || field > PC_MASTER_FIELD_CMD))
+               msg_panic("%s: bad attribute field index: %d",
+                         myname, field);
+       }
+    }
+
+    /*
+     * Iterate over the master table.
+     */
+    for (masterp = master_table; masterp->argv != 0; masterp++) {
+       if (argc > 0) {
+           for (req = field_reqs; req < field_reqs + argc; req++) {
+               if (MATCH_SERVICE_PATTERN(req->service_pattern,
+                                         masterp->argv->argv[0],
+                                         masterp->argv->argv[1])) {
+                   req->match_count++;
+                   field = req->field_pattern;
+                   if (is_magic_field_pattern(field)) {
+                       for (field = 0; field <= PC_MASTER_FIELD_CMD; field++)
+                           print_master_field(fp, mode, masterp, field);
+                   } else {
+                       print_master_field(fp, mode, masterp, field);
+                   }
+               }
+           }
+       } else {
+           for (field = 0; field <= PC_MASTER_FIELD_CMD; field++)
+               print_master_field(fp, mode, masterp, field);
+       }
+    }
+
+    /*
+     * Cleanup.
+     */
+    if (argc > 0) {
+       for (req = field_reqs; req < field_reqs + argc; req++) {
+           if (req->match_count == 0)
+               msg_warn("unmatched request: \"%s\"", req->raw_text);
+           argv_free(req->service_pattern);
+       }
+       myfree((char *) field_reqs);
+    }
+}
+
+/* edit_master_field - replace master.cf field value. */
+
+void    edit_master_field(PC_MASTER_ENT *masterp, int field,
+                                 const char *new_value)
+{
+
+    /*
+     * Replace multi-column attribute.
+     */
+    if (field == PC_MASTER_FIELD_CMD) {
+       argv_truncate(masterp->argv, PC_MASTER_FIELD_CMD);
+       argv_split_append(masterp->argv, new_value, PC_MASTER_BLANKS);
+    }
+
+    /*
+     * Replace single-column attribute.
+     */
+    else {
+       argv_replace_one(masterp->argv, field, new_value);
+    }
+
+    /*
+     * Do per-field sanity checks.
+     */
+    check_master_entry(masterp->argv, new_value);
+}
+
+/* print_master_param - scaffolding */
+
+static void print_master_param(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp,
+                           const char *param_name, const char *param_value)
+{
+    if ((mode & SHOW_EVAL) != 0)
+       param_value = expand_parameter_value((VSTRING *) 0, mode,
+                                            param_value, masterp);
+    if ((mode & HIDE_NAME) == 0) {
+       print_line(fp, mode, "%s%c%s = %s\n",
+                  masterp->name_space, PC_NAMESP_SEP_CH,
+                  param_name, param_value);
+    } else {
+       print_line(fp, mode, "%s\n", param_value);
+    }
+    if (msg_verbose)
+       vstream_fflush(fp);
+}
+
+/* sort_argv_cb - sort argv call-back */
+
+static int sort_argv_cb(const void *a, const void *b)
+{
+    return (strcmp(*(char **) a, *(char **) b));
+}
+
+/* show_master_any_param - show any parameter in master.cf service entry */
+
+static void show_master_any_param(VSTREAM *fp, int mode, PC_MASTER_ENT *masterp)
+{
+    const char *myname = "show_master_any_param";
+    ARGV   *argv = argv_alloc(10);
+    DICT   *dict = masterp->all_params;
+    const char *param_name;
+    const char *param_value;
+    int     param_count = 0;
+    int     how;
+    char  **cpp;
+
+    /*
+     * Print parameters in sorted order. The number of parameters per
+     * master.cf entry is small, so we optmiize for code simplicity and don't
+     * worry about the cost of double lookup.
+     */
+
+    /* Look up the parameter names and ignore the values. */
+
+    for (how = DICT_SEQ_FUN_FIRST;
+        dict->sequence(dict, how, &param_name, &param_value) == 0;
+        how = DICT_SEQ_FUN_NEXT) {
+       argv_add(argv, param_name, ARGV_END);
+       param_count++;
+    }
+
+    /* Print the parameters in sorted order. */
+
+    qsort(argv->argv, param_count, sizeof(argv->argv[0]), sort_argv_cb);
+    for (cpp = argv->argv; (param_name = *cpp) != 0; cpp++) {
+       if ((param_value = dict_get(dict, param_name)) == 0)
+           msg_panic("%s: parameter name not found: %s", myname, param_name);
+       print_master_param(fp, mode, masterp, param_name, param_value);
+    }
+
+    /*
+     * Clean up.
+     */
+    argv_free(argv);
 }
 
-/* show_master - show master.cf entries */
+/* show_master_params - show master.cf params */
 
-void    show_master(VSTREAM *fp, int mode, char **filters)
+void    show_master_params(VSTREAM *fp, int mode, int argc, char **argv)
 {
     PC_MASTER_ENT *masterp;
-    ARGV   *service_filter = 0;
+    PC_MASTER_FIELD_REQ *field_reqs;
+    PC_MASTER_FIELD_REQ *req;
+    DICT   *dict;
+    const char *param_value;
 
     /*
-     * Initialize the service filter.
+     * Parse the filter expressions.
      */
-    if (filters[0])
-       service_filter = match_service_init_argv(filters);
+    if (argc > 0) {
+       field_reqs = (PC_MASTER_FIELD_REQ *)
+           mymalloc(sizeof(*field_reqs) * argc);
+       for (req = field_reqs; req < field_reqs + argc; req++) {
+           req->match_count = 0;
+           req->raw_text = *argv++;
+           req->service_pattern =
+               parse_service_pattern(req->raw_text, 1, 3);
+           if (req->service_pattern == 0)
+               msg_fatal("-P option requires service_name[/type[/parameter]]");
+           req->param_pattern = req->service_pattern->argv[2];
+       }
+    }
 
     /*
      * Iterate over the master table.
      */
-    for (masterp = master_table; masterp->argv != 0; masterp++)
-       if ((service_filter == 0
-            || match_service_match(service_filter, masterp->name_space))
-           && ((mode & SHOW_NONDEF) == 0 || masterp->all_params != 0))
-           print_master_line(fp, mode, masterp);
+    for (masterp = master_table; masterp->argv != 0; masterp++) {
+       if ((dict = masterp->all_params) != 0) {
+           if (argc > 0) {
+               for (req = field_reqs; req < field_reqs + argc; req++) {
+                   if (MATCH_SERVICE_PATTERN(req->service_pattern,
+                                             masterp->argv->argv[0],
+                                             masterp->argv->argv[1])) {
+                       if (IS_MAGIC_PARAM_PATTERN(req->param_pattern)) {
+                           show_master_any_param(fp, mode, masterp);
+                           req->match_count += 1;
+                       } else if ((param_value = dict_get(dict,
+                                               req->param_pattern)) != 0) {
+                           print_master_param(fp, mode, masterp,
+                                              req->param_pattern,
+                                              param_value);
+                           req->match_count += 1;
+                       }
+                   }
+               }
+           } else {
+               show_master_any_param(fp, mode, masterp);
+           }
+       }
+    }
 
     /*
      * Cleanup.
      */
-    if (service_filter != 0)
-       argv_free(service_filter);
+    if (argc > 0) {
+       for (req = field_reqs; req < field_reqs + argc; req++) {
+           if (req->match_count == 0)
+               msg_warn("unmatched request: \"%s\"", req->raw_text);
+           argv_free(req->service_pattern);
+       }
+       myfree((char *) field_reqs);
+    }
+}
+
+/* edit_master_param - update, add or remove -o parameter=value */
+
+void    edit_master_param(PC_MASTER_ENT *masterp, int mode,
+                                 const char *param_name,
+                                 const char *param_value)
+{
+    const char *myname = "edit_master_param";
+    ARGV   *argv = masterp->argv;
+    const char *arg;
+    const char *aval;
+    int     param_match = 0;
+    int     name_len = strlen(param_name);
+    int     field;
+
+    for (field = PC_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
+       arg = argv->argv[field];
+
+       /*
+        * Stop at the first non-option argument or end-of-list.
+        */
+       if (arg[0] != '-' || strcmp(arg, "--") == 0) {
+           break;
+       }
+
+       /*
+        * Zoom in on command-line options with a value.
+        */
+       else if (strchr(daemon_options_expecting_value, arg[1]) != 0
+                && (aval = argv->argv[field + 1]) != 0) {
+
+           /*
+            * Zoom in on "-o parameter=value".
+            */
+           if (strcmp(arg, "-o") == 0) {
+               if (strncmp(aval, param_name, name_len) == 0
+                   && aval[name_len] == '=') {
+                   param_match = 1;
+                   switch (mode & (EDIT_CONF | EDIT_EXCL)) {
+
+                       /*
+                        * Update parameter=value.
+                        */
+                   case EDIT_CONF:
+                       aval = concatenate(param_name, "=",
+                                          param_value, (char *) 0);
+                       argv_replace_one(argv, field + 1, aval);
+                       myfree((char *) aval);
+                       if (masterp->all_params)
+                           dict_put(masterp->all_params, param_name, param_value);
+                       /* XXX Update parameter "used/defined" status. */
+                       break;
+
+                       /*
+                        * Delete parameter=value.
+                        */
+                   case EDIT_EXCL:
+                       argv_delete(argv, field, 2);
+                       if (masterp->all_params)
+                           dict_del(masterp->all_params, param_name);
+                       /* XXX Update parameter "used/defined" status. */
+                       field -= 2;
+                       break;
+                   default:
+                       msg_panic("%s: unexpected mode: %d", myname, mode);
+                   }
+               }
+           }
+
+           /*
+            * Skip over the command-line option value.
+            */
+           field += 1;
+       }
+    }
+
+    /*
+     * Add unmatched parameter.
+     */
+    if ((mode & EDIT_CONF) && param_match == 0) {
+       /* XXX Generalize: argv_insert(argv, where, list...) */
+       argv_insert_one(argv, field, "-o");
+       aval = concatenate(param_name, "=",
+                          param_value, (char *) 0);
+       argv_insert_one(argv, field + 1, aval);
+       if (masterp->all_params)
+           dict_put(masterp->all_params, param_name, param_value);
+       /* XXX May affect parameter "used/defined" status. */
+       myfree((char *) aval);
+       param_match = 1;
+    }
 }
diff --git a/postfix/src/postconf/postconf_match.c b/postfix/src/postconf/postconf_match.c
new file mode 100644 (file)
index 0000000..67836f3
--- /dev/null
@@ -0,0 +1,188 @@
+/*++
+/* NAME
+/*     postconf_match 3
+/* SUMMARY
+/*     pattern-matching support
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     int     parse_field_pattern(field_expr)
+/*     char    *field_expr;
+/*
+/*     const char *str_field_pattern(field_pattern)
+/*     int     field_pattern;
+/*
+/*     int     is_magic_field_pattern(field_pattern)
+/*     int     field_pattern;
+/*
+/*     ARGV    *parse_service_pattern(service_expr, min_expr, max_expr)
+/*     const char *service_expr;
+/*     int     min_expr;
+/*     int     max_expr;
+/*
+/*     int     IS_MAGIC_SERVICE_PATTERN(service_pattern)
+/*     const ARGV *service_pattern;
+/*
+/*     int     MATCH_SERVICE_PATTERN(service_pattern, service_name,
+/*                                     service_type)
+/*     const ARGV *service_pattern;
+/*     const char *service_name;
+/*     const char *service_type;
+/*
+/*     const char *str_field_pattern(field_pattern)
+/*     int     field_pattern;
+/*
+/*     int     IS_MAGIC_PARAM_PATTERN(param_pattern)
+/*     const char *param_pattern;
+/*
+/*     int     MATCH_PARAM_PATTERN(param_pattern, param_name)
+/*     const char *param_pattern;
+/*     const char *param_name;
+/* DESCRIPTION
+/*     parse_service_pattern() takes an expression and splits it
+/*     up on '/' into an array of sub-expressions, This function
+/*     returns null if the input does fewer than "min" or more
+/*     than "max" sub-expressions.
+/*
+/*     IS_MAGIC_SERVICE_PATTERN() returns non-zero if any of the
+/*     service name or service type sub-expressions contains a
+/*     matching operator (as opposed to string literals that only
+/*     match themselves). This is an unsafe macro that evaluates
+/*     its arguments more than once.
+/*
+/*     MATCH_SERVICE_PATTERN() matches a service name and type
+/*     from master.cf against the parsed pattern. This is an unsafe
+/*     macro that evaluates its arguments more than once.
+/*
+/*     parse_field_pattern() converts a field sub-expression, and
+/*     returns the conversion result.
+/*
+/*     str_field_pattern() converts a result from parse_field_pattern()
+/*     into string form.
+/*
+/*     is_magic_field_pattern() returns non-zero if the field
+/*     pattern sub-expression contained a matching operator (as
+/*     opposed to a string literal that only matches itself).
+/*
+/*     IS_MAGIC_PARAM_PATTERN() returns non-zero if the parameter
+/*     sub-expression contains a matching operator (as opposed to
+/*     a string literal that only matches itself). This is an
+/*     unsafe macro that evaluates its arguments more than once.
+/*
+/*     MATCH_PARAM_PATTERN() matches a parameter name from master.cf
+/*     against the parsed pattern. This is an unsafe macro that
+/*     evaluates its arguments more than once.
+/*
+/*     Arguments
+/* .IP field_expr
+/*     A field expression.
+/* .IP service_expr
+/*     This argument is split on '/' into its constituent
+/*     sub-expressions.
+/* .IP min_expr
+/*     The minimum number of sub-expressions in service_expr.
+/* .IP max_expr
+/*     The maximum number of sub-expressions in service_expr.
+/* .IP service_name
+/*     Service name from master.cf.
+/* .IP service_type
+/*     Service type from master.cf.
+/* .IP param_pattern
+/*     A parameter name expression.
+/* DIAGNOSTICS
+/*     Fatal errors: invalid syntax.
+/* 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <split_at.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+ /*
+  * Conversion table. Each PC_MASTER_NAME_XXX name entry must be stored at
+  * table offset PC_MASTER_FIELD_XXX. So don't mess it up.
+  */
+NAME_CODE field_name_offset[] = {
+    PC_MASTER_NAME_SERVICE, PC_MASTER_FIELD_SERVICE,
+    PC_MASTER_NAME_TYPE, PC_MASTER_FIELD_TYPE,
+    PC_MASTER_NAME_PRIVATE, PC_MASTER_FIELD_PRIVATE,
+    PC_MASTER_NAME_UNPRIV, PC_MASTER_FIELD_UNPRIV,
+    PC_MASTER_NAME_CHROOT, PC_MASTER_FIELD_CHROOT,
+    PC_MASTER_NAME_WAKEUP, PC_MASTER_FIELD_WAKEUP,
+    PC_MASTER_NAME_MAXPROC, PC_MASTER_FIELD_MAXPROC,
+    PC_MASTER_NAME_CMD, PC_MASTER_FIELD_CMD,
+    "*", PC_MASTER_FIELD_WILDC,
+    0, PC_MASTER_FIELD_NONE,
+};
+
+/* parse_field_pattern - parse service attribute pattern */
+
+int     parse_field_pattern(const char *field_name)
+{
+    int     field_pattern;
+
+    if ((field_pattern = name_code(field_name_offset,
+                                  NAME_CODE_FLAG_STRICT_CASE,
+                                  field_name)) == PC_MASTER_FIELD_NONE)
+       msg_fatal("invalid service attribute name: \"%s\"", field_name);
+    return (field_pattern);
+}
+
+/* parse_service_pattern - parse service pattern */
+
+ARGV   *parse_service_pattern(const char *pattern, int min_expr, int max_expr)
+{
+    ARGV   *argv;
+    char  **cpp;
+
+    /*
+     * Work around argv_split() lameness.
+     */
+    if (*pattern == '/')
+       return (0);
+    argv = argv_split(pattern, PC_NAMESP_SEP_STR);
+    if (argv->argc < min_expr || argv->argc > max_expr) {
+       argv_free(argv);
+       return (0);
+    }
+
+    /*
+     * Allow '*' only all by itself.
+     */
+    for (cpp = argv->argv; *cpp; cpp++) {
+       if (!PC_MATCH_ANY(*cpp) && strchr(*cpp, PC_MATCH_WILDC_STR[0]) != 0) {
+           argv_free(argv);
+           return (0);
+       }
+    }
+
+    /*
+     * Provide defaults for missing fields.
+     */
+    while (argv->argc < max_expr)
+       argv_add(argv, PC_MATCH_WILDC_STR, ARGV_END);
+    return (argv);
+}
index 179614debbebe42d08838c85eea6727d48ebea3b..622a9ff7cc97951e87932b2f84d2142c632fa29a 100644 (file)
 
 /* set_config_dir - forcibly override var_config_dir */
 
-void set_config_dir(void)
+void    set_config_dir(void)
 {
     char   *config_dir;
 
     if (var_config_dir)
-        myfree(var_config_dir);
+       myfree(var_config_dir);
     var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
-                              config_dir : DEF_CONFIG_DIR);     /* XXX */
+                             config_dir : DEF_CONFIG_DIR);     /* XXX */
     set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
 }
diff --git a/postfix/src/postconf/postconf_print.c b/postfix/src/postconf/postconf_print.c
new file mode 100644 (file)
index 0000000..a77d8a8
--- /dev/null
@@ -0,0 +1,113 @@
+/*++
+/* NAME
+/*     postconf_print 3
+/* SUMMARY
+/*     basic line printing support
+/* SYNOPSIS
+/*     #include <postconf.h>
+/*
+/*     void    print_line(fp, mode, const char *fmt, ...)
+/*     VSTREAM *fp;
+/*     int     mode;
+/*     const char *fmt;
+/* DESCRIPTION
+/*     print_line() formats text, normalized whitespace, and
+/*     optionally folds long lines.
+/*
+/*     Arguments:
+/* .IP fp
+/*     Output stream.
+/* .IP mode
+/*     Bit-wise OR of zero or more of the following (other flags
+/*     are ignored):
+/* .RS
+/* .IP FOLD_LINE
+/*     Fold long lines.
+/* .RE
+/* .IP fmt
+/*     Format string.
+/* DIAGNOSTICS
+/*     Problems are reported to the standard error stream.
+/* 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Application-specific. */
+
+#include <postconf.h>
+
+/* SLMs. */
+
+#define STR(x) vstring_str(x)
+
+/* print_line - show line possibly folded, and with normalized whitespace */
+
+void    print_line(VSTREAM *fp, int mode, const char *fmt,...)
+{
+    va_list ap;
+    static VSTRING *buf = 0;
+    char   *start;
+    char   *next;
+    int     line_len = 0;
+    int     word_len;
+
+    /*
+     * One-off initialization.
+     */
+    if (buf == 0)
+       buf = vstring_alloc(100);
+
+    /*
+     * Format the text.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+
+    /*
+     * Normalize the whitespace. We don't use the line_wrap() routine because
+     * 1) that function does not normalize whitespace between words and 2) we
+     * want to normalize whitespace even when not wrapping lines.
+     * 
+     * XXX Some parameters preserve whitespace: for example, smtpd_banner and
+     * smtpd_reject_footer. If we have to preserve whitespace between words,
+     * then perhaps readlline() can be changed to canonicalize whitespace
+     * that follows a newline.
+     */
+    for (start = STR(buf); *(start += strspn(start, SEPARATORS)) != 0; start = next) {
+       word_len = strcspn(start, SEPARATORS);
+       if (*(next = start + word_len) != 0)
+           *next++ = 0;
+       if (word_len > 0 && line_len > 0) {
+           if ((mode & FOLD_LINE) == 0 || line_len + word_len < LINE_LIMIT) {
+               vstream_fputs(" ", fp);
+               line_len += 1;
+           } else {
+               vstream_fputs("\n" INDENT_TEXT, fp);
+               line_len = INDENT_LEN;
+           }
+       }
+       vstream_fputs(start, fp);
+       line_len += word_len;
+    }
+    vstream_fputs("\n", fp);
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5bb68913157609c539a0051cba43a19d13b873a7 100644 (file)
@@ -0,0 +1,2 @@
+./postconf: warning: unmatched request: "bar/inet"
+./postconf: warning: unmatched request: "foo/unix"
index 4da3f96cd7a4ea7b9ae521e8aa8989389bd2c695..8ef6d6d23f08384a76d70ff7bb6a3c7d8c87c258 100644 (file)
@@ -1,2 +1,4 @@
 whatever   unix  -       n       n       -       0       other
-    -o mydestination=yyy -o always_bcc=ccc -o aaa=ccc
+    -o mydestination=yyy
+    -o always_bcc=ccc
+    -o aaa=ccc
index d7ec1b9d906835395755e58ab9f871138965905f..1ca585ef6569ccd20d6a24a3724e0ffbb57675f0 100644 (file)
@@ -1,2 +1,6 @@
-foo        unix  -       n       n       -       0       other -v -o aaa=bbb -v
-    -o ccc=bbb -v -o ddd=bbb
+foo        unix  -       n       n       -       0       other -v
+    -o aaa=bbb
+    -v
+    -o ccc=bbb
+    -v
+    -o ddd=bbb
diff --git a/postfix/src/postconf/test41.ref b/postfix/src/postconf/test41.ref
new file mode 100644 (file)
index 0000000..9776e8e
--- /dev/null
@@ -0,0 +1,12 @@
+foo        unix  -       n       n       -       0       other
+bar        unix  -       n       n       -       0       other
+    -o xxx=yyy
+    -o aaa=bbb
+baz        unix  -       n       n       -       0       other
+foo        unix  -       n       n       -       0       other
+bar        unix  -       n       n       -       0       other
+    -o xxx=YYY
+    -o aaa=BBB
+baz        unix  -       n       n       -       0       other
+bar/unix/aaa = BBB
+bar/unix/xxx = YYY
diff --git a/postfix/src/postconf/test42.ref b/postfix/src/postconf/test42.ref
new file mode 100644 (file)
index 0000000..fb95ae9
--- /dev/null
@@ -0,0 +1,10 @@
+foo        unix  -       n       n       -       0       other
+bar        unix  -       n       n       -       0       other
+    -o xxx=yyy
+    -o aaa=bbb
+baz        unix  -       n       n       -       0       other
+bar/unix/aaa = bbb
+bar/unix/xxx = yyy
+foo        unix  -       n       n       -       0       other
+bar        unix  -       n       n       -       0       other
+baz        unix  -       n       n       -       0       other
diff --git a/postfix/src/postconf/test43.ref b/postfix/src/postconf/test43.ref
new file mode 100644 (file)
index 0000000..34707f4
--- /dev/null
@@ -0,0 +1,5 @@
+foo        unix  -       n       n       -       0       other
+bar        unix  -       n       y       -       0       aa -stuff
+    -o bb=cc
+    dd
+baz        unix  -       n       n       -       0       other
diff --git a/postfix/src/postconf/test44.ref b/postfix/src/postconf/test44.ref
new file mode 100644 (file)
index 0000000..e25a238
--- /dev/null
@@ -0,0 +1,5 @@
+foo        unix  -       n       n       -       0       other
+xx         inet  -       n       n       -       0       aa -stuff
+    -o bb=cc
+    dd
+baz        unix  -       n       n       -       0       other
diff --git a/postfix/src/postconf/test45.ref b/postfix/src/postconf/test45.ref
new file mode 100644 (file)
index 0000000..8ae54a0
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid type field "xxxx" in "bar xxxx - n n - 0 other"
diff --git a/postfix/src/postconf/test46.ref b/postfix/src/postconf/test46.ref
new file mode 100644 (file)
index 0000000..7939f93
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid private field X" in "bar inet X n n - 0 other"
diff --git a/postfix/src/postconf/test47.ref b/postfix/src/postconf/test47.ref
new file mode 100644 (file)
index 0000000..a446068
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid unprivileged field X" in "bar inet - X n - 0 other"
diff --git a/postfix/src/postconf/test48.ref b/postfix/src/postconf/test48.ref
new file mode 100644 (file)
index 0000000..ae76ff5
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid chroot field X" in "bar inet - n X - 0 other"
diff --git a/postfix/src/postconf/test49.ref b/postfix/src/postconf/test49.ref
new file mode 100644 (file)
index 0000000..4922155
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid wakeup field X" in "bar inet - n n X 0 other"
diff --git a/postfix/src/postconf/test50.ref b/postfix/src/postconf/test50.ref
new file mode 100644 (file)
index 0000000..8b5af2c
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid process_limit field X" in "bar inet - n n - X other"
diff --git a/postfix/src/postconf/test51.ref b/postfix/src/postconf/test51.ref
new file mode 100644 (file)
index 0000000..d5ff477
--- /dev/null
@@ -0,0 +1 @@
+./postconf: fatal: invalid wakeup field X?" in "bar inet - n n X? 0 other"
diff --git a/postfix/src/postconf/test52.ref b/postfix/src/postconf/test52.ref
new file mode 100644 (file)
index 0000000..8902db9
--- /dev/null
@@ -0,0 +1 @@
+baz        unix  -       n       n       0       0       other
diff --git a/postfix/src/postconf/test53.ref b/postfix/src/postconf/test53.ref
new file mode 100644 (file)
index 0000000..c81cf65
--- /dev/null
@@ -0,0 +1,3 @@
+foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+baz unix - n n 0 0 other
diff --git a/postfix/src/postconf/test54.ref b/postfix/src/postconf/test54.ref
new file mode 100644 (file)
index 0000000..045a9f6
--- /dev/null
@@ -0,0 +1,3 @@
+#foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+baz unix - n n 0 0 other
diff --git a/postfix/src/postconf/test55.ref b/postfix/src/postconf/test55.ref
new file mode 100644 (file)
index 0000000..96c11b0
--- /dev/null
@@ -0,0 +1,3 @@
+foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+#baz unix - n n 0 0 other
diff --git a/postfix/src/postconf/test56.ref b/postfix/src/postconf/test56.ref
new file mode 100644 (file)
index 0000000..ac845f6
--- /dev/null
@@ -0,0 +1,5 @@
+foo unix - n n - 0 other
+#bar inet - n n 0 0 other
+# -o first
+# -o second
+baz unix - n n 0 0 other
index e11a53746eacac751ab3a8c4f3e79bf30cda3608..9067bb730cbfa71fbaaa5ea228310614b379a2c0 100644 (file)
 /* .IP "\fBsmtp_tls_force_insecure_host_tlsa_lookup (no)\fR"
 /*     Lookup the associated DANE TLSA RRset even when a hostname is
 /*     not an alias and its address records lie in an unsigned zone.
-/* .IP "\fBtls_dane_trust_anchor_digest_enable (trust-anchor-assertion)\fR"
+/* .IP "\fBtls_dane_trust_anchor_digest_enable (yes)\fR"
 /*     RFC 6698 trust-anchor digest support in the Postfix TLS library.
 /* .IP "\fBtlsmgr_service_name (tlsmgr)\fR"
 /*     The name of the \fBtlsmgr\fR(8) service entry in master.cf.
index f84955e7e601b65afacccdf0438ed60d10398f2e..1452748e8df10349c0cde9a0cb1d1449ef248c18 100644 (file)
@@ -788,9 +788,9 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
                 props->namaddr, props->protocols);
        return (0);
     }
-    /* The DANE level requires TLS 1.0 or later, not SSLv2 or SSLv3. */
+    /* The DANE level requires SSLv3 or later, not SSLv2. */
     if (props->tls_level == TLS_LEV_DANE)
-       protomask |= TLS_PROTOCOL_SSLv3 | TLS_PROTOCOL_SSLv2;
+       protomask |= TLS_PROTOCOL_SSLv2;
 
     /*
      * Per session cipher selection for sessions with mandatory encryption
index 4fb62ae31f8457f3940f13b6abe7cf79c029576f..92f2b6fa08256b05d437e3f35c0500a7c276c0ef 100644 (file)
@@ -97,7 +97,7 @@
 /*     callback MUST be cleared.
 /*
 /*     tls_dane_resolve() maps a (port, protocol, hostrr) tuple to a
-/*     corresponding TLS_DANE policy structure found in the DNS.  The port
+/*     corresponding TLS_DANE policy structure found in the DNS.  The port
 /*     argument is in network byte order.  A null pointer is returned when
 /*     the DNS query for the TLSA record tempfailed.  In all other cases the
 /*     return value is a pointer to the corresponding TLS_DANE structure.
 #include <timecmp.h>
 #include <ctable.h>
 #include <hex_code.h>
+#include <safe_ultostr.h>
+#include <split_at.h>
+#include <name_code.h>
 
 #define STR(x) vstring_str(x)
 
@@ -246,27 +249,42 @@ static ASN1_OBJECT *serverAuth;
 /*
  * https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml
  */
-typedef struct digest_info {
-    const char *alg;                   /* OpenSSL name */
+typedef struct {
+    const char *mdalg;
+    uint8_t dane_id;
+} iana_digest;
+
+static iana_digest iana_table[] = {
+    {"", DNS_TLSA_MATCHING_TYPE_NO_HASH_USED},
+    {"sha256", DNS_TLSA_MATCHING_TYPE_SHA256},
+    {"sha512", DNS_TLSA_MATCHING_TYPE_SHA512},
+    {0, 0}
+};
+
+typedef struct dane_digest {
+    struct dane_digest *next;          /* linkage */
+    const char *mdalg;                 /* OpenSSL name */
     const EVP_MD *md;                  /* OpenSSL EVP handle */
     int     len;                       /* digest octet length */
     int     pref;                      /* tls_dane_digests index or -1 */
     uint8_t dane_id;                   /* IANA id */
-}       digest_info;
+} dane_digest;
 
 #define MAXDIGESTS 256                 /* RFC limit */
-digest_info digest_table[] = {
-    {"full", 0, 0, 0, DNS_TLSA_MATCHING_TYPE_NO_HASH_USED},
-    {"sha256", 0, 0, -1, DNS_TLSA_MATCHING_TYPE_SHA256},
-    {"sha512", 0, 0, -1, DNS_TLSA_MATCHING_TYPE_SHA512},
-    {0, 0, 0, 0, 0}
+static dane_digest *digest_list;
+static int digest_agility = -1;
+
+#define AGILITY_OFF    0
+#define AGILITY_ON     1
+#define AGILITY_MAYBE  2
+
+static NAME_CODE agility[] = {
+    {TLS_DANE_AGILITY_OFF, AGILITY_OFF},
+    {TLS_DANE_AGILITY_ON, AGILITY_ON},
+    {TLS_DANE_AGILITY_MAYBE, AGILITY_MAYBE},
+    {0, -1}
 };
 
-static int digest_mask;
-
-#define TLS_DANE_ENABLE_CC     (1<<0)  /* ca-constraint digests OK */
-#define TLS_DANE_ENABLE_TAA    (1<<1)  /* trust-anchor-assertion digests OK */
-
 /*
  * This is not intended to be a long-term cache of pre-parsed TLSA data,
  * rather we primarily want to avoid fetching and parsing the TLSA records
@@ -286,27 +304,123 @@ void    tls_dane_verbose(int on)
     dane_verbose = on;
 }
 
-/* dane_digest_info - locate digest_table entry for given IANA id */
+/* add_digest - validate and append digest to digest list */
 
-static digest_info *dane_digest_info(uint8_t dane_id)
+static dane_digest *add_digest(char *mdalg, int pref)
 {
-    digest_info *di;
+    iana_digest *i;
+    dane_digest *d;
+    int     dane_id = -1;
+    const char *dane_mdalg = mdalg;
+    char   *value = split_at(mdalg, '=');
+    const EVP_MD *md = 0;
+    size_t  mdlen = 0;
+
+    if (value && *value) {
+       unsigned long l;
+       char   *endcp;
+
+       /*
+        * XXX: safe_strtoul() does not flag empty or white-space only input.
+        * Since we get idbuf by splitting white-space/comma delimited
+        * tokens, this is not a problem here.
+        */
+       l = safe_strtoul(value, &endcp, 10);
+       if ((l == 0 && (errno == EINVAL || endcp == value))
+           || l >= MAXDIGESTS
+           || *endcp) {
+           msg_warn("Invalid matching type number in %s: %s=%s",
+                    VAR_TLS_DANE_DIGESTS, mdalg, value);
+           return (0);
+       }
+       dane_id = l;
+    }
 
-    for (di = digest_table; di->alg; ++di)
-       if (di->dane_id == dane_id)
-           return (di);
+    /*
+     * Check for known IANA conflicts
+     */
+    for (i = iana_table; i->mdalg; ++i) {
+       if (*mdalg && strcasecmp(i->mdalg, mdalg) == 0) {
+           if (dane_id >= 0 && i->dane_id != dane_id) {
+               msg_warn("Non-standard value in %s: %s%s%s",
+                        VAR_TLS_DANE_DIGESTS, mdalg,
+                        value ? "=" : "", value ? value : "");
+               return (0);
+           }
+           dane_id = i->dane_id;
+       } else if (i->dane_id == dane_id) {
+           if (*mdalg) {
+               msg_warn("Non-standard algorithm in %s: %s%s%s",
+                        VAR_TLS_DANE_DIGESTS, mdalg,
+                        value ? "=" : "", value ? value : "");
+               return (0);
+           }
+           dane_mdalg = i->mdalg;
+       }
+    }
+
+    /*
+     * Check for unknown implicit digest or value
+     */
+    if (dane_id < 0 || (dane_id > 0 && !*dane_mdalg)) {
+       msg_warn("Unknown incompletely specified element in %s: %s%s%s",
+                VAR_TLS_DANE_DIGESTS, mdalg,
+                value ? "=" : "", value ? value : "");
+       return 0;
+    }
+
+    /*
+     * Check for duplicate entries
+     */
+    for (d = digest_list; d; d = d->next) {
+       if (strcasecmp(d->mdalg, dane_mdalg) == 0
+           || d->dane_id == dane_id) {
+           msg_warn("Duplicate element in %s: %s%s%s",
+                    VAR_TLS_DANE_DIGESTS, mdalg,
+                    value ? "=" : "", value ? value : "");
+           return (0);
+       }
+    }
+
+    if (*dane_mdalg
+       && ((md = EVP_get_digestbyname(dane_mdalg)) == 0
+           || (mdlen = EVP_MD_size(md)) <= 0
+           || mdlen > EVP_MAX_MD_SIZE)) {
+       msg_warn("Unimplemented digest algoritm in %s: %s%s%s",
+                VAR_TLS_DANE_DIGESTS, mdalg,
+                value ? "=" : "", value ? value : "");
+       return (0);
+    }
+    d = (dane_digest *) mymalloc(sizeof(*d));
+    d->next = digest_list;
+    d->mdalg = mystrdup(dane_mdalg);
+    d->md = md;
+    d->len = mdlen;
+    d->pref = pref;
+    d->dane_id = dane_id;
+
+    return (digest_list = d);
+}
+
+/* digest_byid - locate digest_table entry for given IANA id */
+
+static dane_digest *digest_byid(uint8_t dane_id)
+{
+    dane_digest *d;
+
+    for (d = digest_list; d; d = d->next)
+       if (d->dane_id == dane_id)
+           return (d);
     return (0);
 }
 
-/* dane_digest_pref - digest preference by IANA id */
+/* digest_pref_byid - digest preference by IANA id */
 
-static int dane_digest_pref(uint8_t dane_id)
+static int digest_pref_byid(uint8_t dane_id)
 {
-    digest_info *di = dane_digest_info(dane_id);
+    dane_digest *d = digest_byid(dane_id);
 
-    if (di && di->pref >= 0)
-       return (di->pref);
-    return (MAXDIGESTS + dane_id);
+    return (d ? (d->pref) : (MAXDIGESTS + dane_id));
 }
 
 /* gencakey - generate interal DANE root CA key */
@@ -343,46 +457,38 @@ static EVP_PKEY *gencakey(void)
 
 static void dane_init(void)
 {
-    static NAME_MASK ta_dgsts[] = {
-       TLS_DANE_CC, TLS_DANE_ENABLE_CC,
-       TLS_DANE_TAA, TLS_DANE_ENABLE_TAA,
-       0,
-    };
     int     digest_pref = 0;
     char   *cp;
     char   *save;
     char   *tok;
-    digest_info *di;
+    static char fullmtype[] = "=0";
+    dane_digest *d;
 
-    digest_mask =
-       name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst,
-                     NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
-
-    save = cp = mystrdup(var_tls_dane_digests);
-    while ((tok = mystrtok(&cp, "\t\n\r ,")) != 0) {
-       for (di = digest_table; di->alg; ++di)
-           if (strcasecmp(tok, di->alg) == 0)
+    /*
+     * Add the full matching type at highest preference and then the users
+     * configured list.
+     * 
+     * The most preferred digest will be used for cert signing and hashing full
+     * values for comparison.
+     */
+    if ((digest_agility = name_code(agility, 0, var_tls_dane_agility)) < 0) {
+       msg_warn("Invalid %s syntax: %s. DANE support disabled.",
+                VAR_TLS_DANE_AGILITY, var_tls_dane_agility);
+    } else if (add_digest(fullmtype, 0)) {
+       save = cp = mystrdup(var_tls_dane_digests);
+       while ((tok = mystrtok(&cp, "\t\n\r ,")) != 0) {
+           if ((d = add_digest(tok, ++digest_pref)) == 0) {
+               signalg = 0;
+               signmd = 0;
                break;
-       if (di->alg != 0
-           && (di->md = EVP_get_digestbyname(di->alg)) != 0
-           && (di->len = EVP_MD_size(di->md)) > 0
-           && di->len <= EVP_MAX_MD_SIZE) {
-
-           /*
-            * The most preferred digest will be used for cert signing and
-            * digesting for comparison.
-            */
-           if ((di->pref = ++digest_pref) == 1) {
-               signalg = di->alg;
-               signmd = di->md;
            }
-       } else {
-           msg_warn("Unsupported DANE digest algorithm: %s", tok);
-           continue;
+           if (digest_pref == 1) {
+               signalg = d->mdalg;
+               signmd = d->md;
+           }
        }
+       myfree(save);
     }
-    myfree(save);
-
     /* Don't report old news */
     ERR_clear_error();
 
@@ -599,14 +705,15 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector,
     ARGV  **argvp;
 
     switch (certusage) {
-    case DNS_TLSA_USAGE_CA_CONSTRAINT:
     case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
-       certusage = TLS_DANE_TA;                /* Collapse 0/2 -> 2 */
+       certusage = TLS_DANE_TA;
        break;
     case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:
     case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
        certusage = TLS_DANE_EE;                /* Collapse 1/3 -> 3 */
        break;
+    default:
+       msg_panic("Unsupported DANE certificate usage: %d", certusage);
     }
 
     switch (selector) {
@@ -616,6 +723,8 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector,
     case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
        selector = TLS_DANE_PKEY;
        break;
+    default:
+       msg_panic("Unsupported DANE selector: %d", selector);
     }
 
     tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
@@ -630,6 +739,60 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector,
 
 #ifdef DANE_TLSA_SUPPORT
 
+#define FILTER_CTX_AGILITY_OK          (1<<0)
+#define FILTER_CTX_CHECK_AGILITY       (1<<1)
+#define FILTER_CTX_APPLY_AGILITY       (1<<2)
+#define FILTER_CTX_PARSE_DATA          (1<<3)
+
+#define FILTER_RR_DROP                 0
+#define FILTER_RR_KEEP                 1
+
+typedef struct filter_ctx {
+    TLS_DANE *dane;                    /* Parsed result */
+    int     count;                     /* Digest mtype count */
+    int     target;                    /* Digest mtype target count */
+    int     flags;                     /* Action/result bitmask */
+} filter_ctx;
+
+typedef int (*tlsa_filter) (DNS_RR *, filter_ctx *);
+
+/* tlsa_apply - apply filter to each rr in turn */
+
+static DNS_RR *tlsa_apply(DNS_RR *rr, tlsa_filter filter, filter_ctx *ctx)
+{
+    DNS_RR *head = 0;                  /* First retained RR */
+    DNS_RR *tail = 0;                  /* Last retained RR */
+    DNS_RR *next;
+
+    for ( /* nop */ ; rr; rr = next) {
+       next = rr->next;
+
+       if (filter(rr, ctx) == FILTER_RR_KEEP) {
+           tail = rr;
+           if (!head)
+               head = rr;
+       } else {
+           if (tail)
+               tail->next = rr->next;
+           rr->next = 0;
+           dns_rr_free(rr);
+       }
+    }
+    return (head);
+}
+
+/* usmdelta - packed usage/selector/mtype bits changing in next record */
+
+static unsigned int usmdelta(uint8_t u, uint8_t s, uint8_t m, DNS_RR *next)
+{
+    uint8_t *ip = (next && next->data_len >= 3) ? (uint8_t *) next->data : 0;
+    uint8_t nu = ip ? *ip++ : ~u;
+    uint8_t ns = ip ? *ip++ : ~s;
+    uint8_t nm = ip ? *ip++ : ~m;
+
+    return (((u ^ nu) << 16) | ((s ^ ns) << 8) | (m ^ nm));
+}
+
 /* tlsa_rr_cmp - qsort TLSA rrs in case shuffled by name server */
 
 static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b)
@@ -645,11 +808,12 @@ static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b)
        uint8_t *ai = (uint8_t *) a->data;
        uint8_t *bi = (uint8_t *) b->data;
 
-#define signedcmp(x, y) (((int)(y)) - ((int)(x)))
+#define signedcmp(x, y) (((int)(x)) - ((int)(y)))
 
        if ((cmp = signedcmp(ai[0], bi[0])) != 0
            || (cmp = signedcmp(ai[1], bi[1])) != 0
-         || (cmp = dane_digest_pref(ai[2]) - dane_digest_pref(bi[2])) != 0)
+           || (cmp = digest_pref_byid(ai[2]) -
+               digest_pref_byid(bi[2])) != 0)
            return (cmp);
     }
     if ((cmp = a->data_len - b->data_len) != 0)
@@ -657,224 +821,244 @@ static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b)
     return (memcmp(a->data, b->data, a->data_len));
 }
 
-/* parse_tlsa_rrs - parse a validated TLSA RRset */
+/* parse_tlsa_rr - parse a validated TLSA RRset */
 
-static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
+static int parse_tlsa_rr(DNS_RR *rr, filter_ctx *ctx)
 {
+    uint8_t *ip;
     uint8_t usage;
     uint8_t selector;
     uint8_t mtype;
-    int     mlen;
+    ssize_t dlen;
+    const unsigned char *data;
     const unsigned char *p;
-    uint32_t prev_usage_selector;      /* Previous (usage<<8|selector) */
-    uint32_t prev_mtype;               /* Previous valid mtype for above */
+    int     iscname = strcasecmp(rr->rname, rr->qname);
+    const char *q = (iscname) ? (rr)->qname : "";
+    const char *a = (iscname) ? " -> " : "";
+    const char *r = rr->rname;
+    unsigned int change;
+
+    if (rr->type != T_TLSA)
+       msg_panic("unexpected non-TLSA RR type %u for %s%s%s", rr->type,
+                 q, a, r);
+
+    /* Drop truncated records */
+    if ((dlen = rr->data_len - 3) < 0) {
+       msg_warn("truncated length %u RR: %s%s%s IN TLSA ...",
+                (unsigned) rr->data_len, q, a, r);
+       ctx->flags &= ~FILTER_CTX_AGILITY_OK;
+       return (FILTER_RR_DROP);
+    }
+    ip = (uint8_t *) rr->data;
+    usage = *ip++;
+    selector = *ip++;
+    mtype = *ip++;
+    change = usmdelta(usage, selector, mtype, rr->next);
+    p = data = (const unsigned char *) ip;
+
+    /*
+     * Handle digest agility for non-zero matching types.
+     */
+    if (mtype) {
+       if (ctx->count && (ctx->flags & FILTER_CTX_APPLY_AGILITY)) {
+           if (change & 0xffff00)              /* New usage/selector, */
+               ctx->count = 0;                 /* disable drop */
+           return (FILTER_RR_DROP);
+       }
+       if ((ctx->flags & FILTER_CTX_CHECK_AGILITY)
+           && (ctx->flags & FILTER_CTX_AGILITY_OK)) {
+           ++ctx->count;
+           if (change) {
+               /*-
+                * Count changed from last mtype for same usage/selector?
+                * Yes, disable agility.
+                * Else, set or (on usage/selector change) reset target.
+                */
+               if (ctx->target && ctx->target != ctx->count)
+                   ctx->flags &= ~FILTER_CTX_AGILITY_OK;
+               else
+                   ctx->target = (change & ~0xff) ? 0 : ctx->count;
+               ctx->count = 0;
+           }
+       }
+    }
+    /*-
+     * Drop unsupported usages.
+     * Note: NO SUPPORT for usage 0 which does not apply to SMTP.
+     * Note: Best-effort support for usage 1, which simply maps to 3.
+     */
+    switch (usage) {
+    case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+    case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:
+    case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
+       break;
+    default:
+       msg_warn("unsupported certificate usage %u in RR: "
+                "%s%s%s IN TLSA %u ...", usage,
+                q, a, r, usage);
+       return (FILTER_RR_DROP);
+    }
 
-#define NO_PREV 0xffffffff                     /* Not any usage|selector or
-                                                * mtype */
-    prev_usage_selector = NO_PREV;
+    /*
+     * Drop unsupported selectors
+     */
+    switch (selector) {
+    case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
+    case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
+       break;
+    default:
+       msg_warn("unsupported selector %u in RR: "
+                "%s%s%s IN TLSA %u %u ...", selector,
+                q, a, r, usage, selector);
+       return (FILTER_RR_DROP);
+    }
 
-    if (rr == 0)
-       msg_panic("null TLSA rr");
+    if (mtype) {
+       dane_digest *d = digest_byid(mtype);
 
-    for ( /* nop */ ; rr; rr = rr->next) {
-       const char *mdalg = 0;
-       char   *digest;
-       int     iscname = strcasecmp(rr->rname, rr->qname);
-       uint8_t *ip = (uint8_t *) rr->data;
-       X509   *x = 0;                  /* OpenSSL tries to re-use *x if x!=0 */
-       EVP_PKEY *k = 0;                /* OpenSSL tries to re-use *k if k!=0 */
-       digest_info *di;
-
-#define rcname(rr) (iscname ? rr->qname : "")
-#define rarrow(rr) (iscname ? " -> " : "")
-
-       if (rr->type != T_TLSA)
-           msg_panic("unexpected non-TLSA RR type %u for %s%s%s", rr->type,
-                     rcname(rr), rarrow(rr), rr->rname);
-
-       /* Skip malformed */
-       if ((mlen = rr->data_len - 3) < 0) {
-           msg_warn("truncated length %u RR: %s%s%s IN TLSA ...",
-               (unsigned) rr->data_len, rcname(rr), rarrow(rr), rr->rname);
-           continue;
+       if (d == 0) {
+           msg_warn("unsupported matching type %u in RR: "
+                    "%s%s%s IN TLSA %u %u %u ...", mtype,
+                    q, a, r, usage, selector, mtype);
+           return (FILTER_RR_DROP);
        }
-       switch (usage = *ip++) {
-       default:
-           msg_warn("unsupported certificate usage %u in RR: "
-                    "%s%s%s IN TLSA %u ...", usage,
-                    rcname(rr), rarrow(rr), rr->rname, usage);
-           continue;
-       case DNS_TLSA_USAGE_CA_CONSTRAINT:
-       case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
-       case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:
-       case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
-           break;
+       if (dlen != d->len) {
+           msg_warn("malformed %s digest, length %u, in RR: "
+                    "%s%s%s IN TLSA %u %u %u ...", d->mdalg, dlen,
+                    q, a, r, usage, selector, mtype);
+           ctx->flags &= ~FILTER_CTX_AGILITY_OK;
+           return (FILTER_RR_DROP);
        }
-
-       switch (selector = *ip++) {
-       default:
-           msg_warn("unsupported selector %u in RR: "
-                    "%s%s%s IN TLSA %u %u ...", selector,
-                    rcname(rr), rarrow(rr), rr->rname, usage, selector);
-           continue;
-       case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
-       case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
-           break;
+       if (!var_tls_dane_taa_dgst
+           && usage == DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION) {
+           msg_warn("trust-anchor digests disabled, ignoring RR: "
+                    "%s%s%s IN TLSA %u %u %u ...", q, a, r,
+                    usage, selector, mtype);
+           return (FILTER_RR_DROP);
        }
+       /* New digest mtype next? Prepare to drop following RRs */
+       if (change && (change & 0xffff00) == 0
+           && (ctx->flags & FILTER_CTX_APPLY_AGILITY))
+           ++ctx->count;
 
-       /*
-        * Skip all but the most preferred matching type for any given
-        * (usage, selector) combination.
-        */
-       mtype = *ip++;
-       if (prev_usage_selector != (usage << 8 | selector))
-           prev_mtype = NO_PREV;
-       else if (prev_mtype != NO_PREV && prev_mtype != mtype)
-           continue;
-
-       switch (mtype) {
-       default:
-           if ((di = dane_digest_info(mtype)) == 0) {
-               msg_warn("unsupported matching type %u in RR: "
-                        "%s%s%s IN TLSA %u %u %u ...", mtype, rcname(rr),
-                        rarrow(rr), rr->rname, usage, selector, mtype);
-               continue;
-           }
-           if (di->pref < 0) {
-               msg_warn("digest algorithm %s locally disabled, in RR: "
-                        "%s%s%s IN TLSA %u %u %u ...", di->alg,
-                        rcname(rr), rarrow(rr), rr->rname,
-                        usage, selector, mtype);
-               continue;
-           }
-           if (mlen != di->len) {
-               msg_warn("malformed %s digest, length %u, in RR: "
-                        "%s%s%s IN TLSA %u %u %u ...", di->alg, mlen,
-                        rcname(rr), rarrow(rr), rr->rname,
-                        usage, selector, mtype);
-               continue;
-           }
-           switch (usage) {
-           case DNS_TLSA_USAGE_CA_CONSTRAINT:
-               if (!(digest_mask & TLS_DANE_ENABLE_CC)) {
-                   msg_warn("%s trust-anchor %s digests disabled, in RR: "
-                            "%s%s%s IN TLSA %u %u %u ...", TLS_DANE_CC,
-                            di->alg, rcname(rr), rarrow(rr), rr->rname,
-                            usage, selector, mtype);
-                   continue;
-               }
-               break;
-           case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
-               if (!(digest_mask & TLS_DANE_ENABLE_TAA)) {
-                   msg_warn("%s trust-anchor %s digests disabled, in RR: "
-                            "%s%s%s IN TLSA %u %u %u ...", TLS_DANE_TAA,
-                            di->alg, rcname(rr), rarrow(rr), rr->rname,
-                            usage, selector, mtype);
-                   continue;
-               }
-               break;
-           }
-           digest = tls_digest_encode((unsigned char *) ip, di->len);
-           dane_add(dane, usage, selector, di->alg, digest);
-           break;
+       if (ctx->flags & FILTER_CTX_PARSE_DATA) {
+           char   *digest = tls_digest_encode(data, dlen);
+
+           dane_add(ctx->dane, usage, selector, d->mdalg, digest);
+           if (msg_verbose || dane_verbose)
+               msg_info("using DANE RR: %s%s%s IN TLSA %u %u %u %s",
+                        q, a, r, usage, selector, mtype, digest);
+           myfree(digest);
+       }
+    } else {
+       X509   *x = 0;                  /* OpenSSL re-uses *x if x!=0 */
+       EVP_PKEY *k = 0;                /* OpenSSL re-uses *k if k!=0 */
 
-       case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
-           p = (unsigned char *) ip;
-
-           /* Validate the cert or public key via d2i_mumble() */
-           switch (selector) {
-           case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
-               if (!d2i_X509(&x, &p, mlen)) {
-                   msg_warn("malformed %s in RR: "
-                            "%s%s%s IN TLSA %u %u %u ...", "certificate",
-                            rcname(rr), rarrow(rr), rr->rname,
-                            usage, selector, mtype);
-                   continue;
-               }
-               /* Also unusable if public key is malformed */
-               if ((k = X509_get_pubkey(x)) == 0) {
-                   msg_warn("%s public key malformed in RR: "
-                            "%s%s%s IN TLSA %u %u %u ...", "certificate",
-                            rcname(rr), rarrow(rr), rr->rname,
-                            usage, selector, mtype);
+       /* Validate the cert or public key via d2i_mumble() */
+       switch (selector) {
+       case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
+           if (!d2i_X509(&x, &p, dlen) || dlen != p - data) {
+               msg_warn("malformed %s in RR: "
+                        "%s%s%s IN TLSA %u %u %u ...", "certificate",
+                        q, a, r, usage, selector, mtype);
+               if (x)
                    X509_free(x);
-                   continue;
-               }
-               EVP_PKEY_free(k);
-
-               /*
-                * When a full trust-anchor certificate is published via DNS,
-                * we may need to use it to validate the server trust chain.
-                * Store it away for later use.  We collapse certificate
-                * usage 0/2 because MTAs don't stock a complete list of the
-                * usual browser-trusted CAs.  Thus, here (and in the public
-                * key case below) we treat the usages identically.
-                */
-               switch (usage) {
-               case DNS_TLSA_USAGE_CA_CONSTRAINT:
-               case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
-                   ta_cert_insert(dane, x);
-                   break;
-               }
+               return (FILTER_RR_DROP);
+           }
+           /* Also unusable if public key is malformed or unsupported */
+           k = X509_get_pubkey(x);
+           EVP_PKEY_free(k);
+           if (k == 0) {
+               msg_warn("%s public key malformed in RR: "
+                        "%s%s%s IN TLSA %u %u %u ...", "certificate",
+                        q, a, r, usage, selector, mtype);
                X509_free(x);
-               break;
-
-           case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
-               if (!d2i_PUBKEY(&k, &p, mlen)) {
-                   msg_warn("malformed %s in RR: "
-                            "%s%s%s IN TLSA %u %u %u ...", "public key",
-                            rcname(rr), rarrow(rr), rr->rname,
-                            usage, selector, mtype);
-                   continue;
-               }
-               /* See full cert case above */
-               switch (usage) {
-               case DNS_TLSA_USAGE_CA_CONSTRAINT:
-               case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
-                   ta_pkey_insert(dane, k);
-                   break;
-               }
-               EVP_PKEY_free(k);
-               break;
+               return (FILTER_RR_DROP);
            }
 
            /*
-            * The cert or key was valid, just digest the raw object, and
-            * encode the digest value.
+            * When a full trust-anchor certificate is published via DNS, we
+            * may need to use it to validate the server trust chain. Store
+            * it away for later use.
             */
-           digest = tls_data_fprint((char *) ip, mlen, signalg);
-           dane_add(dane, usage, selector, mdalg = signalg, digest);
+           if (usage == DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION
+               && (ctx->flags & FILTER_CTX_PARSE_DATA))
+               ta_cert_insert(ctx->dane, x);
+           X509_free(x);
+           break;
+
+       case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
+           if (!d2i_PUBKEY(&k, &p, dlen) || dlen != p - data) {
+               msg_warn("malformed %s in RR: "
+                        "%s%s%s IN TLSA %u %u %u ...", "public key",
+                        q, a, r, usage, selector, mtype);
+               if (k)
+                   EVP_PKEY_free(k);
+               return (FILTER_RR_DROP);
+           }
+           /* See full cert case above */
+           if (usage == DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION
+               && (ctx->flags & FILTER_CTX_PARSE_DATA))
+               ta_pkey_insert(ctx->dane, k);
+           EVP_PKEY_free(k);
            break;
        }
 
        /*
-        * Save state
+        * The cert or key was valid, just digest the raw object, and encode
+        * the digest value.
         */
-       prev_usage_selector = (usage << 8 | selector);
-       prev_mtype = mtype;
+       if (ctx->flags & FILTER_CTX_PARSE_DATA) {
+           char   *digest = tls_data_fprint((char *) data, dlen, signalg);
 
-       if (msg_verbose || dane_verbose) {
-           switch (mtype) {
-           default:
-               msg_info("using DANE RR: %s%s%s IN TLSA %u %u %u %s",
-                        rcname(rr), rarrow(rr), rr->rname,
-                        usage, selector, mtype, digest);
-               break;
-           case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
+           dane_add(ctx->dane, usage, selector, signalg, digest);
+           if (msg_verbose || dane_verbose)
                msg_info("using DANE RR: %s%s%s IN TLSA %u %u %u <%s>; "
-                        "%s digest %s",
-                        rcname(rr), rarrow(rr), rr->rname,
-                        usage, selector, mtype,
+                        "%s digest %s", q, a, r, usage, selector, mtype,
                         (selector == DNS_TLSA_SELECTOR_FULL_CERTIFICATE) ?
-                        "certificate" : "public key", mdalg, digest);
-               break;
-           }
+                        "certificate" : "public key", signalg, digest);
+           myfree(digest);
        }
-       myfree(digest);
     }
+    return (FILTER_RR_KEEP);
+}
+
+/* process_rrs - filter and parse the TLSA RRset */
 
+static DNS_RR *process_rrs(TLS_DANE *dane, DNS_RR *rrset)
+{
+    filter_ctx ctx;
+
+    ctx.dane = dane;
+    ctx.count = ctx.target = 0;
+    ctx.flags = 0;
+
+    switch (digest_agility) {
+    case AGILITY_ON:
+       ctx.flags |= FILTER_CTX_APPLY_AGILITY | FILTER_CTX_PARSE_DATA;
+       break;
+    case AGILITY_OFF:
+       ctx.flags |= FILTER_CTX_PARSE_DATA;
+       break;
+    case AGILITY_MAYBE:
+       ctx.flags |= FILTER_CTX_CHECK_AGILITY | FILTER_CTX_AGILITY_OK;
+       break;
+    }
+
+    rrset = tlsa_apply(rrset, parse_tlsa_rr, &ctx);
+
+    if (digest_agility == AGILITY_MAYBE) {
+       /* Two-pass algorithm */
+       if (ctx.flags & FILTER_CTX_AGILITY_OK)
+           ctx.flags = FILTER_CTX_APPLY_AGILITY | FILTER_CTX_PARSE_DATA;
+       else
+           ctx.flags = FILTER_CTX_PARSE_DATA;
+       rrset = tlsa_apply(rrset, parse_tlsa_rr, &ctx);
+    }
     if (dane->ta == 0 && dane->ee == 0)
        dane->flags |= TLS_DANE_FLAG_EMPTY;
+
+    return (rrset);
 }
 
 /* dane_lookup - TLSA record lookup, ctable style */
@@ -911,11 +1095,12 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
             * same usage and selector.
             */
            rrs = dns_rr_sort(rrs, tlsa_rr_cmp);
-           parse_tlsa_rrs(dane, rrs);
+           rrs = process_rrs(dane, rrs);
        } else
            dane->flags |= TLS_DANE_FLAG_NORRS;
 
-       dns_rr_free(rrs);
+       if (rrs)
+           dns_rr_free(rrs);
        break;
 
     case DNS_NOTFOUND:
index 4bd8b9b03ae5d566c243ef2d67f14fa1f5fff78a..026ef9dc19d5d415ea8f8f1538705bcc0ad2b96f 100644 (file)
 /*     char    *var_tls_null_clist;
 /*     char    *var_tls_eecdh_strong;
 /*     char    *var_tls_eecdh_ultra;
-/*     char    *var_tls_dane_ta_dgst;
+/*     char    *var_tls_dane_agility;
 /*     char    *var_tls_dane_digests;
 /*     int     var_tls_daemon_rand_bytes;
 /*     bool    var_tls_append_def_CA;
+/*     bool    var_tls_dane_taa_dgst;
 /*     bool    var_tls_preempt_clist;
 /*     bool    var_tls_bc_pkey_fprint;
 /*     bool    var_tls_multi_wildcard;
@@ -221,15 +222,15 @@ char   *var_tls_null_clist;
 int     var_tls_daemon_rand_bytes;
 char   *var_tls_eecdh_strong;
 char   *var_tls_eecdh_ultra;
+char   *var_tls_dane_agility;
 char   *var_tls_dane_digests;
-char   *var_tls_dane_ta_dgst;
 bool    var_tls_append_def_CA;
 char   *var_tls_bug_tweaks;
 char   *var_tls_ssl_options;
 bool    var_tls_bc_pkey_fprint;
+bool    var_tls_dane_taa_dgst;
 bool    var_tls_multi_wildcard;
 char   *var_tls_mgr_service;
-char   *tls_dane_digests;
 
 #ifdef VAR_TLS_PREEMPT_CLIST
 bool    var_tls_preempt_clist;
@@ -597,8 +598,8 @@ void    tls_param_init(void)
        VAR_TLS_EECDH_ULTRA, DEF_TLS_EECDH_ULTRA, &var_tls_eecdh_ultra, 1, 0,
        VAR_TLS_BUG_TWEAKS, DEF_TLS_BUG_TWEAKS, &var_tls_bug_tweaks, 0, 0,
        VAR_TLS_SSL_OPTIONS, DEF_TLS_SSL_OPTIONS, &var_tls_ssl_options, 0, 0,
+       VAR_TLS_DANE_AGILITY, DEF_TLS_DANE_AGILITY, &var_tls_dane_agility, 1, 0,
        VAR_TLS_DANE_DIGESTS, DEF_TLS_DANE_DIGESTS, &var_tls_dane_digests, 1, 0,
-       VAR_TLS_DANE_TA_DGST, DEF_TLS_DANE_TA_DGST, &var_tls_dane_ta_dgst, 0, 0,
        VAR_TLS_MGR_SERVICE, DEF_TLS_MGR_SERVICE, &var_tls_mgr_service, 1, 0,
        0,
     };
@@ -609,6 +610,7 @@ void    tls_param_init(void)
     static const CONFIG_BOOL_TABLE bool_table[] = {
        VAR_TLS_APPEND_DEF_CA, DEF_TLS_APPEND_DEF_CA, &var_tls_append_def_CA,
        VAR_TLS_BC_PKEY_FPRINT, DEF_TLS_BC_PKEY_FPRINT, &var_tls_bc_pkey_fprint,
+       VAR_TLS_DANE_TAA_DGST, DEF_TLS_DANE_TAA_DGST, &var_tls_dane_taa_dgst,
        VAR_TLS_PREEMPT_CLIST, DEF_TLS_PREEMPT_CLIST, &var_tls_preempt_clist,
        VAR_TLS_MULTI_WILDCARD, DEF_TLS_MULTI_WILDCARD, &var_tls_multi_wildcard,
        0,
index 5b2eec3eb6934bca50479066c486d145d673ca6b..fe6d81f418fd58867836ead9e4eb6dd17bc758d4 100644 (file)
 /*     ssize_t pos;
 /*     const char *arg;
 /*
+/*     void    argv_delete(argvp, pos, how_many)
+/*     ARGV    *argvp;
+/*     ssize_t pos;
+/*     ssize_t how_many;
+/*
 /*     void    ARGV_FAKE_BEGIN(argv, arg)
 /*     const char *arg;
 /*
 /*     argv_replace_one() replaces one string at the specified
 /*     position.
 /*
+/*     argv_delete() deletes the specified number of elements
+/*     starting at the specified array position. The result is
+/*     null-terminated.
+/*
 /*     ARGV_FAKE_BEGIN/END are an optimization for the case where
 /*     a single string needs to be passed into an ARGV-based
 /*     interface.  ARGV_FAKE_BEGIN() opens a statement block and
@@ -292,3 +301,23 @@ void    argv_replace_one(ARGV *argvp, ssize_t where, const char *arg)
     myfree(argvp->argv[where]);
     argvp->argv[where] = mystrdup(arg);
 }
+
+/* argv_delete - remove string(s) from array */
+
+void    argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many)
+{
+    ssize_t pos;
+
+    /*
+     * Sanity check.
+     */
+    if (first < 0 || how_many < 0 || first + how_many > argvp->argc)
+       msg_panic("argv_delete bad range: (start=%ld count=%ld)",
+                 (long) first, (long) how_many);
+
+    for (pos = first; pos < first + how_many; pos++)
+       myfree(argvp->argv[pos]);
+    for (pos = first; pos <= argvp->argc - how_many; pos++)
+       argvp->argv[pos] = argvp->argv[pos + how_many];
+    argvp->argc -= how_many;
+}
index fe0322422b6d34510b93ed419474d4d8e2387263..fda35c3119e6841309c6a75102e35056d1ab52ee 100644 (file)
@@ -28,6 +28,7 @@ extern void argv_terminate(ARGV *);
 extern void argv_truncate(ARGV *, ssize_t);
 extern void argv_insert_one(ARGV *, ssize_t, const char *);
 extern void argv_replace_one(ARGV *, ssize_t, const char *);
+extern void argv_delete(ARGV *, ssize_t, ssize_t);
 extern ARGV *argv_free(ARGV *);
 
 extern ARGV *argv_split(const char *, const char *);
index 920bf399ac49d1a41d458825d92c4cb1f8976f15..a9f3afbdbaea8b6b99758cd00999beb18506e34e 100644 (file)
@@ -8,9 +8,11 @@
 /*
 /*     ARGV    *argv_split(string, delim)
 /*     const char *string;
+/*     const char *delim;
 /*
 /*     ARGV    *argv_split_count(string, delim, count)
 /*     const char *string;
+/*     const char *delim;
 /*     ssize_t count;
 /*
 /*     ARGV    *argv_split_append(argv, string, delim)
index 7cc932479307ac4554b397b360b2b3c76c6368b2..644d8b7d6cf37f4696e46b60ca484b158768c057 100644 (file)
@@ -115,7 +115,7 @@ const char *host_port(char *buf, char **host, char *def_host,
     char   *cp = buf;
     int     ipv6 = 0;
 
-    /*
+    /*-
      * [host]:port, [host]:, [host].
      * [ipv6:ipv6addr]:port, [ipv6:ipv6addr]:, [ipv6:ipv6addr].
      */
index febeb488ad86f266bae1b8ccb5886306a0f322d5..fcfcf4b7d3dac566433f57e464e629d0a768499b 100644 (file)
@@ -296,8 +296,8 @@ static int slmdb_recover(SLMDB *slmdb, int status)
     MDB_envinfo info;
 
     /*
-     * Recover bulk transactions only if they can be restarted. Limit
-     * the number of recovery attempts per slmdb(3) API request.
+     * Recover bulk transactions only if they can be restarted. Limit the
+     * number of recovery attempts per slmdb(3) API request.
      */
     if ((slmdb->txn != 0 && slmdb->longjmp_fn == 0)
        || ((slmdb->api_retry_count += 1) >= slmdb->api_retry_limit))