]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.11-20131217
authorWietse Venema <wietse@porcupine.org>
Tue, 17 Dec 2013 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Wed, 18 Dec 2013 02:39:16 +0000 (21:39 -0500)
98 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/MULTI_INSTANCE_README
postfix/README_FILES/TLS_README
postfix/RELEASE_NOTES
postfix/conf/postfix-files
postfix/html/DATABASE_README.html
postfix/html/MULTI_INSTANCE_README.html
postfix/html/SMTPD_ACCESS_README.html
postfix/html/SMTPD_POLICY_README.html
postfix/html/TLS_README.html
postfix/html/VIRTUAL_README.html
postfix/html/lmdb_table.5.html
postfix/html/lmtp.8.html
postfix/html/local.8.html
postfix/html/memcache_table.5.html
postfix/html/mysql_table.5.html
postfix/html/pgsql_table.5.html
postfix/html/pipe.8.html
postfix/html/postconf.1.html
postfix/html/postconf.5.html
postfix/html/smtp.8.html
postfix/html/smtpd.8.html
postfix/html/socketmap_table.5.html
postfix/html/trivial-rewrite.8.html
postfix/makedefs
postfix/man/man1/postconf.1
postfix/man/man5/lmdb_table.5
postfix/man/man5/postconf.5
postfix/man/man8/local.8
postfix/man/man8/pipe.8
postfix/man/man8/smtp.8
postfix/man/man8/smtpd.8
postfix/man/man8/trivial-rewrite.8
postfix/mantools/postlink
postfix/proto/MULTI_INSTANCE_README.html
postfix/proto/TLS_README.html
postfix/proto/lmdb_table
postfix/proto/postconf.man.prolog
postfix/proto/postconf.proto
postfix/src/dns/dns.h
postfix/src/global/Makefile.in
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/match_service.c
postfix/src/global/safe_ultostr.c
postfix/src/global/safe_ultostr.in [new file with mode: 0644]
postfix/src/global/safe_ultostr.ref [new file with mode: 0644]
postfix/src/local/local.c
postfix/src/master/master_ent.c
postfix/src/milter/test-milter.c
postfix/src/pipe/pipe.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/posttls-finger/posttls-finger.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp_tls_policy.c
postfix/src/smtpd/smtpd.c
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_dane.c
postfix/src/tls/tls_fprint.c
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_rsa.c
postfix/src/tls/tls_server.c
postfix/src/trivial-rewrite/trivial-rewrite.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..982975fd0b4289d17276ab4124e4fa4c9749d19b 100644 (file)
 -TLMTP_STATE
 -TLOCAL_EXP
 -TLOCAL_STATE
+-TLONG_NAME_MASK
 -TMAC_EXP
 -TMAC_HEAD
 -TMAC_PARSE
 -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
+-Tsockaddr
 -Tssize_t
 -Tssl_cipher_stack_t
 -Tssl_comp_stack_t
 -Ttime_t
+-Ttlsa_filter
 -Tx509_extension_stack_t
 -Tx509_stack_t
index 3e1e222496ae65cb21fba1522443444a6a96ebc3..da9e4c2b10aa0a2bba54e5b2631427af124913af 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,161 @@ 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.
+
+20131129
+
+       Bugfix: Makefile example in MULTI_INSTANCE_README. Viktor
+       Dukhovni. File: proto/MULTI_INSTANCE_README.html.
+
+20131130
+
+       Cleanup: simplify fingerprint security level implementation
+       in new DANE code.  Viktor Dukhovni.  Files: src/tls/tls.h
+       src/smtp/smtp_tls_policy.c src/tls/tls_dane.c
+       src/posttls-finger/posttls-finger.c.
+
+20131209
+
+       Cleanup: safe_strtoul() did not report an error for empty
+       or all-space input (the code to report this was in the wrong
+       place). This was not a problem as long as safe_strtoul()
+       was used only for output from safe_ultostr().  Files:
+       global/safe_ultostr.c, global/safe_ultostr.in,
+       global/safe_ultostr.ref.
+
+20131210
+
+       Documentation: updated description of SSL protocol controls.
+       In particular, enabled protocols are part of a contiguous
+       range. Viktor Dukhovni.  Files: proto/TLS_README.html,
+       proto/postconf.proto.
+
+       Bugfix: DANE support: handle OpenSSL memory allocation
+       error. Viktor Dukhovni.  File: tls/tls_dane.c.
+
+       Cleanup: LMDB_README was not installed. File: conf/postfix-files.
+
+20131214
+
+       Portability: on some platforms posttls-finger now requires
+       explicitly linking libdl.  File: posttls-finger/Makefile.in.
+
+       Cleanup: DANE support: extension gymnastics. Viktor Dukhovni.
+       File: tls/tls_dane.c.
+
+       Bugfix: DANE support: the wrap_cert() and wrap_key() calls
+       should never fail, but some callers ignored the return
+       value.  The only failure is for lack of memory, so we use
+       msg_fatal() internally and change wrap_cert() and wrap_key()
+       to return void. Viktor Dukhovni.  File: tls/tls_dane.c.
+
+       Bugfix: DANE support: avoid making DANE certificates with
+       replaced public-keys appear as if they were self-signed.
+       Viktor Dukhovni.  File: tls/tls_dane.c.
+
+       Cleanup: DANE support: simplify grow_chain() to always apply
+       trust consistently. Viktor Dukhovni.  File: tls/tls_dane.c.
+
+       Bugfix: DANE support: backport fixes from OpenSSL DANE
+       testing.  Discard errors generated by raw TA key signature
+       checks. Record the tadepth as zero with self-signed depth
+       0 TAs. Robustness: Though it should never happen, don't
+       update the tadepth if already set. Viktor Dukhovni.  Files:
+       tls/tls_dane.c, tls/tls_server.c.
+
+20131215
+
+       Cleanup: OpenSSL "const" declarations have changed over
+       time. Viktor Dukhovni. Files: src/tls/tls.h, src/tls/tls_client.c,
+       src/tls/tls_dane.c, src/tls/tls_server.c.
+
+20131216
+
+       Cleanup: TLS support. Eliminate calls of deprecated functions
+       before they are removed from OpenSSL.  CRYPTO_thread_id is
+       deprecated and we don't need it.  Replace the deprecated
+       ERR_remove_state() call with ERR_remove_thread_state(), and
+       use RSA_generate_key_ex().  Viktor Dukhovni. Files:
+       posttls-finger/posttls-finger.c, tls/tls_misc.c, tls/tls_rsa.c.
+
+       Cleanup: DANE support: Reduce #ifdef clutter to improve
+       redability and maintability. Viktor Dukhovni.  File:
+       tls/tls_dane.c.
+
+       Future proofing: Tolerate disappearance of named bug-workaround
+       bits without invalidating user configurations.  When support
+       for a bug workaround is removed from OpenSSL, the corresponding
+       bit is defined as zero (i.e. NOOP) intstead of causing
+       programs to break. Viktor Dukhovni.  File: tls/tls_misc.c.
+
+20131217
+
+       Portability: RSA_generate_key_ex() is not available on all
+       supported platforms, so this change is made conditional.
+       Enforce that this function will be used only for creating
+       a 512-bit ephemeral RSA key.  Viktor Dukhovni. File:
+       tls/tls_rsa.c.
index 6e2fb48c56473cea1e2ab010fcf9884616b8ca50..7e930f25c353124a1a795d19d80cc511a95ed4cc 100644 (file)
@@ -177,7 +177,7 @@ database when none exists.
         generic: Makefile
                 @echo Creating $@
                 @rm -f $@.tmp
-                @printf '%s\t%s+root=%s\n' root $MTAADMIN `uname -n` > $@.tmp
+                @printf '%s\t%s+root=%s\n' root ${MTAADMIN} `uname -n` > $@.tmp
                 @mv $@.tmp generic
 
         %.cdb: %
index 4728a624d2b914426649a1808ea748946d6265dd..552279f728368a4b7db7f43b232ed50ad0968e7a 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
@@ -418,7 +412,7 @@ from scratch.
 
 Since Postfix uses multiple smtpd(8) service processes, an in-memory cache is
 not sufficient for session re-use. Clients store at most one cached session per
-server and are very unlikey to repeatedly connect to the same server process.
+server and are very unlikely to repeatedly connect to the same server process.
 Thus session caching in the Postfix SMTP server generally requires a shared
 cache (an alternative available with Postfix >= 2.11 is described below).
 
@@ -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:
+        # Preferred syntax with Postfix >= 2.5:
         smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
-        # Alternative form.
+        # Legacy syntax:
         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 unlikely 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..23fef36bd85960c92be242d009f85ec513297f0b 100644 (file)
@@ -1,3 +1,5 @@
+This is the Postfix 2.11 (experimental) branch.
+
 The stable Postfix release is called postfix-2.10.x where 2=major
 release number, 10=minor release number, x=patchlevel.  The stable
 release never changes except for patches that address bugs or
@@ -14,6 +16,156 @@ 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 20131217
+===========================================
+
+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 20131217
+=========================================
+
+Support for advanced master.cf query and update operations.  This
+was implemented primarily to support automated system management
+tools.
+
+The goal is to make all Postfix master.cf details accessible as
+lists 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.
+
+Managing master.cf service attributes
+-------------------------------------
+
+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
+    $ postfix reload
+
+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'
+    $ postfix reload
+
+Managing master.cf service "-o parameter=value" settings
+--------------------------------------------------------
+
+For a second example, let's look at the submission service.  This
+service typically has multiple "-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, always execute "postfix reload" after updating master.cf.
+
+Managing master.cf service entries
+----------------------------------
+
+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.
+
+Suppose that you need to configure a Postfix SMTP client that will
+handle slow email deliveries.  To implement this you need to clone
+the smtp/unix service settings and create a new delay/unix service.
+
+First, 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) by hand
+to create the delay/unix service:
+
+    $ postconf -M delay/unix="delay   unix   -   -   n   -   -   smtp"
+
+To combine the above steps 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.
+
+Again, always execute "postfix reload" after updating master.cf.
+
+Deleting or commenting out master.cf entries
+--------------------------------------------
+
+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
+
+As with main.cf, there is no support to "undo" master.cf changes
+that are made with -X or -#.
+
+Again, always execute "postfix reload" after updating master.cf.
+
 Major changes with snapshot 20131031
 ====================================
 
index 2da438efc42a0fa65af276449eea47a8affe606e..3304edf9d62e3b86d80a60e540cff3b76f3cf129 100644 (file)
@@ -264,6 +264,7 @@ $readme_directory/INSTALL:f:root:-:644
 $readme_directory/IPV6_README:f:root:-:644
 $readme_directory/LDAP_README:f:root:-:644
 $readme_directory/LINUX_README:f:root:-:644
+$readme_directory/LMDB_README:f:root:-:644
 $readme_directory/LOCAL_RECIPIENT_README:f:root:-:644
 $readme_directory/MACOSX_README:f:root:-:644:o
 $readme_directory/MAILDROP_README:f:root:-:644
@@ -319,6 +320,7 @@ $html_directory/INSTALL.html:f:root:-:644
 $html_directory/IPV6_README.html:f:root:-:644
 $html_directory/LDAP_README.html:f:root:-:644
 $html_directory/LINUX_README.html:f:root:-:644
+$html_directory/LMDB_README.html:f:root:-:644
 $html_directory/LOCAL_RECIPIENT_README.html:f:root:-:644
 $html_directory/MAILDROP_README.html:f:root:-:644
 $html_directory/MILTER_README.html:f:root:-:644
index 389ed08c7ca6019d47b2d1da74510a51d1122ea6..2bc86d2aa69f7062310129f51342971e5d661617 100644 (file)
@@ -428,13 +428,13 @@ tables are implemented:
 
 <dl>
 
-<dt> <b><a href="DATABASE_README.html#types">unix</a>:passwd.byname</b> </dt>
+<dt> <b>unix:passwd.byname</b> </dt>
 
 <dd>The table is the UNIX password database. The key is a login
 name.  The result is a password file entry in passwd(5) format.
 </dd>
 
-<dt> <b><a href="DATABASE_README.html#types">unix</a>:group.byname</b> </dt>
+<dt> <b>unix:group.byname</b> </dt>
 
 <dd> The table is the UNIX group database. The key is a group name.
 The result is a group file entry in group(5) format. </dd>
index 6aca5f53d4b9276bba450aaa33e6b9226a8bca11..39fc87da10caa4be605272884ffe9fe58c28c35b 100644 (file)
@@ -233,7 +233,7 @@ creates a "generic" database when none exists. </p>
     generic: Makefile
             @echo Creating $@
             @rm -f $@.tmp
-            @printf '%s\t%s+root=%s\n' root $MTAADMIN `uname -n` &gt; $@.tmp
+            @printf '%s\t%s+root=%s\n' root ${MTAADMIN} `uname -n` &gt; $@.tmp
             @mv $@.tmp generic
 
     %.<a href="CDB_README.html">cdb</a>: %
index fc8b53230e5248d572362d60715e39ebe5993232..3c4f5975c8114ce3d95c53cf413342e46a9612c5 100644 (file)
@@ -209,7 +209,7 @@ described in the <a href="postconf.5.html">postconf(5)</a> manual page. </p>
     <a href="postconf.5.html#smtpd_data_restrictions">smtpd_data_restrictions</a> = <a href="postconf.5.html#reject_unauth_pipelining">reject_unauth_pipelining</a>
 
     # Enforce mail volume quota via policy service callouts.
-    <a href="postconf.5.html#smtpd_end_of_data_restrictions">smtpd_end_of_data_restrictions</a> = <a href="postconf.5.html#check_policy_service">check_policy_service</a> <a href="DATABASE_README.html#types">unix</a>:private/policy
+    <a href="postconf.5.html#smtpd_end_of_data_restrictions">smtpd_end_of_data_restrictions</a> = <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/policy
 </pre>
 
 <p> Each restriction list is evaluated from left to right until
index 995db85b17bfe56e7a5d55a5049d2b30ea9137f9..5770535b483d6e66e66a7d4dfc808ce295000516 100644 (file)
@@ -235,8 +235,8 @@ or to a UNIX-domain socket. Examples: </p>
 <blockquote>
 <pre>
 inet:127.0.0.1:9998
-<a href="DATABASE_README.html#types">unix</a>:/some/where/policy
-<a href="DATABASE_README.html#types">unix</a>:private/policy
+unix:/some/where/policy
+unix:private/policy
 </pre>
 </blockquote>
 
@@ -261,7 +261,7 @@ daemon, you would use something like this: </p>
  6     <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
  7         ... 
  8         <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> 
- 9         <a href="postconf.5.html#check_policy_service">check_policy_service</a> <a href="DATABASE_README.html#types">unix</a>:private/policy 
+ 9         <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/policy 
 10         ...
 11     <a href="postconf.5.html#transport_time_limit">policy_time_limit</a> = 3600
 </pre>
@@ -411,7 +411,7 @@ processes only: </p>
  7     <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
  8         ... 
  9         <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> 
-10         <a href="postconf.5.html#check_policy_service">check_policy_service</a> <a href="DATABASE_README.html#types">unix</a>:private/greylist
+10         <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/greylist
 11         ...
 </pre>
 </blockquote>
@@ -454,7 +454,7 @@ a built-in suffix (in the above example: "_time_limit").  </p>
 </ul>
 
 <p> With Solaris &lt; 9, or Postfix &lt; 2.10 on any Solaris
-version, use inet: style sockets instead of <a href="DATABASE_README.html#types">unix</a>:
+version, use inet: style sockets instead of unix:
 style, as detailed in the "<a href="#client_config">Policy
 client/server configuration</a>" section above.  </p>
 
@@ -492,7 +492,7 @@ forged MAIL FROM domains could be found at
  6         <a href="postconf.5.html#check_sender_access">check_sender_access</a> <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/sender_access
  7         ...
  8     <a href="postconf.5.html#smtpd_restriction_classes">smtpd_restriction_classes</a> = greylist
- 9     greylist = <a href="postconf.5.html#check_policy_service">check_policy_service</a> <a href="DATABASE_README.html#types">unix</a>:private/greylist
+ 9     greylist = <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/greylist
 10 
 11 /etc/postfix/sender_access:
 12     aol.com     greylist
@@ -548,7 +548,7 @@ most of the delays and most of the database pollution problem. </p>
  4         ...
  5         <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> 
  6         <a href="postconf.5.html#check_sender_access">check_sender_access</a> <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/sender_access
- 7         <a href="postconf.5.html#check_policy_service">check_policy_service</a> <a href="DATABASE_README.html#types">unix</a>:private/policy
+ 7         <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/policy
  8         ...
  9 
 10 /etc/postfix/sender_access:
index 34939a3b4cfbb5cff40c7613b8bfd25af54f8fa7..cd75ee8ab0ebbac9d0f9406591785f7bb19f27a9 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>
 
@@ -614,7 +607,7 @@ from scratch.  </p>
 
 <p> Since Postfix uses multiple <a href="smtpd.8.html">smtpd(8)</a> service processes, an
 in-memory cache is not sufficient for session re-use.  Clients store
-at most one cached session per server and are very unlikey to
+at most one cached session per server and are very unlikely to
 repeatedly connect to the same server process.  Thus session caching
 in the Postfix SMTP server generally requires a shared cache (an
 alternative available with Postfix &ge; 2.11 is described below).
@@ -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:
+    # Preferred syntax with Postfix &ge; 2.5:
     <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
-    # Alternative form.
+    # Legacy syntax:
     <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 unlikely 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 313ba379f29e4185765a60924eaf6b14ca6221f5..d23c57b402a09f3a9fb1a2e1231d1a6cd4a7ddfd 100644 (file)
@@ -410,7 +410,7 @@ examples (use only one): </p>
 
 <blockquote>
 <pre>
-<a href="postconf.5.html#virtual_transport">virtual_transport</a> = <a href="lmtp.8.html">lmtp</a>:<a href="DATABASE_README.html#types">unix</a>:/path/name (uses UNIX-domain socket)
+<a href="postconf.5.html#virtual_transport">virtual_transport</a> = <a href="lmtp.8.html">lmtp</a>:unix:/path/name (uses UNIX-domain socket)
 <a href="postconf.5.html#virtual_transport">virtual_transport</a> = <a href="lmtp.8.html">lmtp</a>:hostname:port   (uses TCP socket)
 <a href="postconf.5.html#virtual_transport">virtual_transport</a> = maildrop:            (uses <a href="pipe.8.html">pipe(8)</a> to command)
 </pre>
index d8dd19fafd2e7938a863da3e6be8eafaaf99ffa1..52075553fd2fe5f73dd6c04b7d7019ff0dcd1fdd 100644 (file)
@@ -34,16 +34,16 @@ LMDB_TABLE(5)                                                    LMDB_TABLE(5)
        When  a  transaction fails due to a full database, Postfix
        resizes the database and retries the transaction.
 
-       Postfix access, address mapping and  routing  tables  will
-       generate  partial search keys such as domain names without
-       one or more subdomains, network addresses without  one  or
-       more  least-significant octets, or email addresses without
-       the localpart, address extension or domain portion.   This
-       behavior  is  also  found  with  <a href="DATABASE_README.html#types">btree</a>:,  <a href="DATABASE_README.html#types">hash</a>:,  or <a href="ldap_table.5.html">ldap</a>:
-       tables.
+       Postfix access, address mapping and routing table  lookups
+       may  generate  partial  search  keys  such as domain names
+       without one or more subdomains, network addresses  without
+       one  or  more least-significant octets, or email addresses
+       without the localpart, address extension  or  domain  por-
+       tion.   This behavior is also found with <a href="DATABASE_README.html#types">btree</a>:, <a href="DATABASE_README.html#types">hash</a>:, or
+       <a href="ldap_table.5.html">ldap</a>: tables.
 
        Unlike other flat-file based Postfix databases, changes to
-       an  LMDB  database do not require automatic daemon program
+       an  LMDB  database do not trigger automatic daemon program
        restart.
 
 <b>RELIABILITY</b>
@@ -56,10 +56,12 @@ LMDB_TABLE(5)                                                    LMDB_TABLE(5)
        The Postfix LMDB adapter implements locking with  fcntl(2)
        locks  at  whole-file  granularity.  LMDB's native locking
        scheme would require world-writable  lockfiles  and  would
-       therefore violate the Postfix security model.  Unlike some
-       other Postfix  flat-file  databases,  LMDB  databases  can
-       safely be updated without serializing requests through the
-       <a href="proxymap.8.html">proxymap(8)</a> service.
+       therefore violate the Postfix security model.
+
+       Multiple  processes  can  safely  update  an LMDB database
+       without serializing requests through the <a href="proxymap.8.html">proxymap(8)</a>  ser-
+       vice.  This makes LMDB suitable as a shared cache for <a href="verify.8.html">ver-</a>
+       <a href="verify.8.html">ify(8)</a> or <a href="postscreen.8.html">postscreen(8)</a> services.
 
 <b>CONFIGURATION PARAMETERS</b>
        Short-lived programs  automatically  pick  up  changes  to
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 ee4dff4de3fdd3b69e8c79c5a6e02e8c54edfd40..d88e556fcbe1d9509248b8824af79da340cb425e 100644 (file)
@@ -609,7 +609,9 @@ LOCAL(8)                                                              LOCAL(8)
 
        <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
               The set of characters that can separate a user name
-              from its address extension (user+foo).
+              from its extension (example: user+foo), or a  .for-
+              ward  file  name from its extension (example: .for-
+              ward+foo).
 
        <b><a href="postconf.5.html#require_home_directory">require_home_directory</a> (no)</b>
               Require that a <a href="local.8.html"><b>local</b>(8)</a> recipient's home  directory
index 46822efd522ee5f6c66d607387a2ae738cc2889d..238509bc6f41e941946d866c3a5daa51640445c7 100644 (file)
@@ -41,12 +41,12 @@ MEMCACHE_TABLE(5)                                            MEMCACHE_TABLE(5)
               "inet:" followed by a hostname or address, ":", and
               a  port  name  or  number.  Specify an IPv6 address
               inside "[]".   For  a  UNIX-domain  server  specify
-              "<a href="DATABASE_README.html#types">unix</a>:" followed by the socket pathname. Examples:
+              "unix:" followed by the socket pathname. Examples:
 
                   memcache = inet:memcache.example.com:11211
                   memcache = inet:127.0.0.1:11211
                   memcache = inet:[fc00:8d00:189::3]:11211
-                  memcache = <a href="DATABASE_README.html#types">unix</a>:/path/to/socket
+                  memcache = unix:/path/to/socket
 
               NOTE: to access a UNIX-domain socket with the <a href="proxymap.8.html">prox-</a>
               <a href="proxymap.8.html">ymap(8)</a> server, the socket must  be  accessible  by
index 2e7e68d9fa50cab922c60028c5261646a27f0f10..f8264f6098f89210386937ebbd8c9f59f125d39e 100644 (file)
@@ -80,10 +80,10 @@ MYSQL_TABLE(5)                                                  MYSQL_TABLE(5)
 
 <b>MYSQL PARAMETERS</b>
        <b>hosts</b>  The  hosts  that Postfix will try to connect to and
-              query from.  Specify <i><a href="DATABASE_README.html#types">unix</a>:</i> for UNIX domain sockets,
+              query from.  Specify <i>unix:</i> for UNIX domain sockets,
               <i>inet:</i> for TCP connections (default).  Example:
                   hosts = host1.some.domain host2.some.domain:port
-                  hosts = <a href="DATABASE_README.html#types">unix</a>:/file/name
+                  hosts = unix:/file/name
 
               The  hosts are tried in random order, with all con-
               nections  over  UNIX  domain  sockets  being  tried
index bb1cb36d996922fa094a7493155ae61d47717e38..427629e0935d2a18d72026d73e302442c6c0cda0 100644 (file)
@@ -86,10 +86,10 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
 
 <b>PGSQL PARAMETERS</b>
        <b>hosts</b>  The  hosts  that Postfix will try to connect to and
-              query from.  Specify <i><a href="DATABASE_README.html#types">unix</a>:</i> for UNIX-domain sockets,
+              query from.  Specify <i>unix:</i> for UNIX-domain sockets,
               <i>inet:</i> for TCP connections (default).  Example:
                   hosts = host1.some.domain host2.some.domain:port
-                  hosts = <a href="DATABASE_README.html#types">unix</a>:/file/name
+                  hosts = unix:/file/name
 
               The  hosts are tried in random order, with all con-
               nections  over  UNIX  domain  sockets  being  tried
@@ -97,7 +97,7 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
               matically closed  after  being  idle  for  about  1
               minute, and are re-opened as necessary.
 
-              NOTE: the <i><a href="DATABASE_README.html#types">unix</a>:</i> and <i>inet:</i> prefixes are accepted for
+              NOTE: the <i>unix:</i> and <i>inet:</i> prefixes are accepted for
               backwards compatibility reasons, but  are  actually
               ignored.  The PostgreSQL client library will always
               try to connect to an UNIX socket if the name starts
index 9705d00e5e84f9851038923e5ee88a9d526aad36..90e831fe200851a4ec4032591bf8523adb706ae3 100644 (file)
@@ -488,7 +488,9 @@ PIPE(8)                                                                PIPE(8)
 
        <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
               The set of characters that can separate a user name
-              from its address extension (user+foo).
+              from its extension (example: user+foo), or a  .for-
+              ward  file  name from its extension (example: .for-
+              ward+foo).
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
index 44fc4a22a87b23a8583a44a4c3ad6992b8a86733..06501c540048b1ec867d82dc8b1087cb0d56171a 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
@@ -274,13 +327,13 @@ POSTCONF(1)                                                        POSTCONF(1)
                      database. The following  tables  are  imple-
                      mented:
 
-                     <b><a href="DATABASE_README.html#types">unix</a>:passwd.byname</b>
+                     <b>unix:passwd.byname</b>
                             The  table is the UNIX password data-
                             base. The key is a login  name.   The
                             result  is  a  password file entry in
                             <b>passwd</b>(5) format.
 
-                     <b><a href="DATABASE_README.html#types">unix</a>:group.byname</b>
+                     <b>unix:group.byname</b>
                             The table is the UNIX group database.
                             The  key is a group name.  The result
                             is a group  file  entry  in  <b>group</b>(5)
@@ -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..2f3914ffddaf75e48ad544bb21aa92329c923991 100644 (file)
@@ -5220,7 +5220,7 @@ system.  </p>
 </DD>
 
 <DT><b><a name="local_recipient_maps">local_recipient_maps</a>
-(default: <a href="proxymap.8.html">proxy</a>:<a href="DATABASE_README.html#types">unix</a>:passwd.byname $<a href="postconf.5.html#alias_maps">alias_maps</a>)</b></DT><DD>
+(default: <a href="proxymap.8.html">proxy</a>:unix:passwd.byname $<a href="postconf.5.html#alias_maps">alias_maps</a>)</b></DT><DD>
 
 <p> Lookup tables with all names or addresses of local recipients:
 a recipient address is local when its domain matches $<a href="postconf.5.html#mydestination">mydestination</a>,
@@ -8558,23 +8558,27 @@ Example:
 (default: empty)</b></DT><DD>
 
 <p> The set of characters that can separate a user name from its
-address extension (user+foo).  See <a href="canonical.5.html">canonical(5)</a>, <a href="local.8.html">local(8)</a>, <a href="relocated.5.html">relocated(5)</a>
-and <a href="virtual.5.html">virtual(5)</a> for the effects this has on aliases, canonical,
-virtual, and relocated lookups.  Basically, the software tries
-user+foo and .forward+foo before trying user and .forward.  </p>
-
-<p> This implementation recognizes one delimiter character per email
-address, and one address extension per email address.  </p>
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).  Basically, the software tries
+user+foo and .forward+foo before trying user and .forward.  This
+implementation recognizes one delimiter character and one extension
+per email address or .forward file name. </p>
 
 <p> When the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> set contains multiple characters
-(Postfix 2.11 and later), a user name is separated from its address
-extension by the first character that matches the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>
-set. </p>
+(Postfix 2.11 and later), a user name or .forward file name is
+separated from its extension by the first character that matches
+the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> set. </p>
+
+<p> See <a href="canonical.5.html">canonical(5)</a>, <a href="local.8.html">local(8)</a>, <a href="relocated.5.html">relocated(5)</a> and <a href="virtual.5.html">virtual(5)</a> for the
+effects of <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> on lookups in aliases, canonical,
+virtual, and relocated maps, and see the <a href="postconf.5.html#propagate_unmatched_extensions">propagate_unmatched_extensions</a>
+parameter for propagating an extension from one email address to
+another.  </p>
 
 <p> When used in <a href="postconf.5.html#command_execution_directory">command_execution_directory</a>, <a href="postconf.5.html#forward_path">forward_path</a>, or
-<a href="postconf.5.html#luser_relay">luser_relay</a>, ${<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>} is replaced
-with the recipient delimiter that was found in the recipient email
-address (Postfix 2.11 and later), or it is replaced with the <a href="postconf.5.html">main.cf</a>
+<a href="postconf.5.html#luser_relay">luser_relay</a>, ${<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>} is replaced with the actual
+recipient delimiter that was found in the recipient email address
+(Postfix 2.11 and later), or it is replaced with the <a href="postconf.5.html">main.cf</a>
 <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> parameter value (Postfix 2.10 and earlier).
 </p>
 
@@ -9550,7 +9554,7 @@ IP address),
 transport map,
 
 <li> if mail is sent via a UNIX-domain socket: a pathname (without
-the <a href="DATABASE_README.html#types">unix</a>: prefix),
+the unix: prefix),
 
 <li> a /file/name with domain names and/or <a href="postconf.5.html#relayhost">relay host</a> names as
 defined above,
@@ -11479,6 +11483,35 @@ 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> With Postfix &ge; 2.5 the parameter syntax was expanded to support
+protocol exclusions. One can explicitly exclude "SSLv2" by setting
+"<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2". To exclude both "SSLv2" and
+"SSLv3" set "<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is
+supported, but not recommended. The exclusion form more closely
+matches the underlying OpenSSL interface semantics.
+</p>
+
+<p> The range of protocols advertised by an SSL/TLS client must be
+contiguous.  When a protocol version is enabled, disabling any
+higher version implicitly disables all versions above that higher
+version.  Thus, for example: </p>
+<blockquote>
+<pre>
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !TLSv1
+</pre>
+</blockquote>
+<p> also disables any protocols version higher than TLSv1 leaving
+only "SSLv3" enabled.  </p>
+
+<p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
+and "TLSv1.2". When Postfix &le; 2.5 is linked against OpenSSL 1.0.1
+or later, these, or any other new protocol versions, cannot be
+disabled except by also disabling "TLSv1" (typically leaving just
+"SSLv3").  The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can explicitly disable support for
+"TLSv1.1" or "TLSv1.2". </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
@@ -11486,24 +11519,10 @@ 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
+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
-unconditionally enabled. </p>
-
-<p> With Postfix &ge; 2.5 the parameter syntax is expanded to support
-protocol exclusions. One can now explicitly exclude SSLv2 by setting
-"<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2". To exclude both SSLv2 and
-SSLv3 set "<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3". Listing
-the protocols to include, rather than protocols to exclude, is
-supported, but not recommended. The exclusion form more closely
-matches the behavior when the OpenSSL library is newer than Postfix.
-</p>
+you are sure will support "TLSv1.1" or "TLSv1.2".  </p>
 
 <p> Since SSL version 2 has known protocol weaknesses and is now
 deprecated, the default setting excludes "SSLv2".  This means that by
@@ -11516,9 +11535,9 @@ and higher. </p>
 <p> Example: </p>
 
 <pre>
-# Preferred form with Postfix &ge; 2.5:
+# Preferred syntax with Postfix &ge; 2.5:
 <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
-# Alternative form.
+# Legacy syntax:
 <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = TLSv1
 </pre>
 
@@ -11637,7 +11656,7 @@ which is either the recipient domain, or the verbatim next-hop
 specified in the transport table, $<a href="postconf.5.html#local_transport">local_transport</a>, $<a href="postconf.5.html#virtual_transport">virtual_transport</a>,
 $<a href="postconf.5.html#relay_transport">relay_transport</a> or $<a href="postconf.5.html#default_transport">default_transport</a>. This includes any enclosing
 square brackets and any non-default destination server port suffix. The
-LMTP socket type prefix (inet: or <a href="DATABASE_README.html#types">unix</a>:) is not included in the lookup
+LMTP socket type prefix (inet: or unix:) is not included in the lookup
 key. </p>
 
 <p> Only the next-hop domain, or $<a href="postconf.5.html#myhostname">myhostname</a> with LMTP over UNIX-domain
@@ -11690,23 +11709,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>
 
@@ -11807,22 +11820,34 @@ separator is colon. An empty value means allow all protocols. The valid
 protocol names, (see <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3"
 and "TLSv1". </p>
 
+<p> The range of protocols advertised by an SSL/TLS client must be
+contiguous.  When a protocol version is enabled, disabling any
+higher version implicitly disables all versions above that higher
+version.  Thus, for example: </p>
+<blockquote>
+<pre>
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !TLSv1
+</pre>
+</blockquote>
+<p> also disables any protocols version higher than TLSv1 leaving
+only "SSLv3" enabled.  </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
-unconditionally enabled. </p>
+and "TLSv1.2". The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can explicitly disable support for
+"TLSv1.1" or "TLSv1.2"</p>
 
 <p> To include a protocol list its name, to exclude it, prefix the name
 with a "!" character. To exclude SSLv2 even for opportunistic TLS set
 "<a href="postconf.5.html#smtp_tls_protocols">smtp_tls_protocols</a> = !SSLv2". To exclude both "SSLv2" and "SSLv3" set
 "<a href="postconf.5.html#smtp_tls_protocols">smtp_tls_protocols</a> = !SSLv2, !SSLv3". Explicitly listing the protocols to
 include, rather than protocols to exclude, is supported, but not
-recommended.  The exclusion form more closely matches the behavior
-when the OpenSSL library is newer than Postfix.  </p>
+recommended.  The exclusion form more closely matches the underlying
+OpenSSL interface semantics. </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 +11980,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.
@@ -13785,7 +13807,7 @@ supposed to give the result to another Postfix SMTP server process.
 </p>
 
 <p> Specify "host:port" or "inet:host:port" for a TCP endpoint, or
-"<a href="DATABASE_README.html#types">unix</a>:pathname" for a UNIX-domain endpoint. The host can be specified
+"unix:pathname" for a UNIX-domain endpoint. The host can be specified
 as an IP address or as a symbolic name; no MX lookups are done.
 When no "host" or "host:"  are specified, the local machine is
 assumed.  Pathname interpretation is relative to the Postfix queue
@@ -13793,7 +13815,7 @@ directory.  </p>
 
 <p> This feature is available in Postfix 2.1 and later.  </p>
 
-<p> The "inet:" and "<a href="DATABASE_README.html#types">unix</a>:" prefixes are available in Postfix 2.3
+<p> The "inet:" and "unix:" prefixes are available in Postfix 2.3
 and later.  </p>
 
 
@@ -15615,25 +15637,26 @@ 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>
-
-<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
-unconditionally enabled. </p>
-
-<p> With Postfix &ge; 2.5 the parameter syntax is expanded to support
-protocol exclusions. One can now explicitly exclude SSLv2 by setting
-"<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2". To exclude both SSLv2 and
-SSLv3 set "<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3". Listing
+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> With Postfix &ge; 2.5 the parameter syntax was expanded to support
+protocol exclusions. One can explicitly exclude "SSLv2" by setting
+"<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2". To exclude both "SSLv2" and
+"SSLv3" set "<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3". Listing
 the protocols to include, rather than protocols to exclude, is
 supported, but not recommended. The exclusion form more closely
-matches the behavior when the OpenSSL library is newer than Postfix.
-</p>
+matches the underlying OpenSSL interface semantics.  </p>
+
+<p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
+and "TLSv1.2". When Postfix &le; 2.5 is linked against OpenSSL 1.0.1
+or later, these, or any other new protocol versions, cannot be
+disabled.  The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can disable support for "TLSv1.1" or
+"TLSv1.2". </p>
 
 <p> Since SSL version 2 has known protocol weaknesses and is now
 deprecated, the default setting excludes "SSLv2".  This means that
@@ -15643,9 +15666,10 @@ 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:
+# Preferred syntax with Postfix &ge; 2.5:
 <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
+# Legacy syntax:
+<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>
@@ -15657,28 +15681,25 @@ level. </p>
 (default: empty)</b></DT><DD>
 
 <p> List of TLS protocols that the Postfix SMTP server will exclude
-or include with opportunistic TLS encryption. This parameter SHOULD be
-left at its default empty value, allowing all protocols to be used with
-opportunistic TLS. </p>
-
-<p> In <a href="postconf.5.html">main.cf</a> the values are separated by whitespace, commas or
-colons. An empty value means allow all protocols. The valid protocol
-names, (see <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and
-"TLSv1". In <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> table entries, "protocols" attribute
-values are separated by a colon. </p>
+or include with opportunistic TLS encryption. This parameter SHOULD
+be left at its default empty value, allowing all protocols to be
+used with opportunistic TLS.  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
-1.0.1 or later, these, or any other new protocol versions, are
-unconditionally enabled. </p>
+and "TLSv1.2". The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can disable support for "TLSv1.1" or
+"TLSv1.2". </p>
 
 <p> To include a protocol list its name, to exclude it, prefix the name
 with a "!" character. To exclude SSLv2 even for opportunistic TLS set
 "<a href="postconf.5.html#smtpd_tls_protocols">smtpd_tls_protocols</a> = !SSLv2". To exclude both "SSLv2" and "SSLv3" set
 "<a href="postconf.5.html#smtpd_tls_protocols">smtpd_tls_protocols</a> = !SSLv2, !SSLv3". Explicitly listing the protocols to
 include, rather than protocols to exclude, is supported, but not
-recommended.  The exclusion form more closely matches the behavior
-when the OpenSSL library is newer than Postfix.  </p>
+recommended.  The exclusion form more closely matches the underlying
+OpenSSL interface semantics. </p>
 
 <p> Example: </p>
 <pre>
@@ -16212,6 +16233,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>).
+</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 +16305,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 +16367,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 +16635,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 fe7f161526b163b760121070099ff83ab9ef1440..e9e23ff472b9419f62426b57a68c91b3e28ba07e 100644 (file)
@@ -1287,7 +1287,9 @@ SMTPD(8)                                                              SMTPD(8)
 
        <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
               The set of characters that can separate a user name
-              from its address extension (user+foo).
+              from its extension (example: user+foo), or a  .for-
+              ward  file  name from its extension (example: .for-
+              ward+foo).
 
        <b><a href="postconf.5.html#smtpd_banner">smtpd_banner</a> ($<a href="postconf.5.html#myhostname">myhostname</a> ESMTP $<a href="postconf.5.html#mail_name">mail_name</a>)</b>
               The text that follows the 220 status  code  in  the
index 0941283209f53050b2f1d9cd63975ad9b74cb5d0..d2008c24c8766529ece84debdc1a1ed3fe881473 100644 (file)
@@ -11,10 +11,10 @@ SOCKETMAP_TABLE(5)                                          SOCKETMAP_TABLE(5)
 
 <b>SYNOPSIS</b>
        <b>postmap -q "</b><i>string</i><b>" <a href="socketmap_table.html">socketmap</a>:inet:</b><i>host</i><b>:</b><i>port</i><b>:</b><i>name</i>
-       <b>postmap -q "</b><i>string</i><b>" <a href="socketmap_table.html">socketmap</a>:<a href="DATABASE_README.html#types">unix</a>:</b><i>pathname</i><b>:</b><i>name</i>
+       <b>postmap -q "</b><i>string</i><b>" <a href="socketmap_table.html">socketmap</a>:unix:</b><i>pathname</i><b>:</b><i>name</i>
 
        <b>postmap -q - <a href="socketmap_table.html">socketmap</a>:inet:</b><i>host</i><b>:</b><i>port</i><b>:</b><i>name</i> &lt;<i>inputfile</i>
-       <b>postmap -q - <a href="socketmap_table.html">socketmap</a>:<a href="DATABASE_README.html#types">unix</a>:</b><i>pathname</i><b>:</b><i>name</i> &lt;<i>inputfile</i>
+       <b>postmap -q - <a href="socketmap_table.html">socketmap</a>:unix:</b><i>pathname</i><b>:</b><i>name</i> &lt;<i>inputfile</i>
 
 <b>DESCRIPTION</b>
        The  Postfix  mail system uses optional tables for address
@@ -22,7 +22,7 @@ SOCKETMAP_TABLE(5)                                          SOCKETMAP_TABLE(5)
 
        The Postfix socketmap client expects TCP endpoint names of
        the  form  <b>inet:</b><i>host</i><b>:</b><i>port</i><b>:</b><i>name</i>, or UNIX-domain endponts of
-       the form <b><a href="DATABASE_README.html#types">unix</a>:</b><i>pathname</i><b>:</b><i>name</i>.  In both cases,  <i>name</i>  speci-
+       the form <b>unix:</b><i>pathname</i><b>:</b><i>name</i>.  In both cases,  <i>name</i>  speci-
        fies  the  name  field  in a socketmap client request (see
        "REQUEST FORMAT" below).
 
index e417e9d082171f0ce7a1d4b6927cd8ea2299ace8..4d2fdac3d5e7f26005f2dde1f4141eb2db3c2ab4 100644 (file)
@@ -138,7 +138,9 @@ TRIVIAL-REWRITE(8)                                          TRIVIAL-REWRITE(8)
 
        <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
               The set of characters that can separate a user name
-              from its address extension (user+foo).
+              from its extension (example: user+foo), or a  .for-
+              ward  file  name from its extension (example: .for-
+              ward+foo).
 
        <b><a href="postconf.5.html#swap_bangpath">swap_bangpath</a> (yes)</b>
               Enable   the   rewriting   of   "site!user"    into
index b75c572dd6114d7c5b323a7e0edb374439e0f715..7ea559081d6a7b87d396786cc5c1d14ac1e797f5 100644 (file)
@@ -201,7 +201,7 @@ case "$SYSTEM.$RELEASE" in
                ;;
     SunOS.5*)  SYSTYPE=SUNOS5
                RANLIB=echo
-               SYSLIBS="-lresolv -lsocket -lnsl"
+               SYSLIBS="-lresolv -lsocket -lnsl -ldl"
                # Stock awk breaks with >10 files.
                test -x /usr/xpg4/bin/awk && AWK=/usr/xpg4/bin/awk
                # Solaris 2.5 added usleep(), POSIX regexp, POSIX getpwnam/uid_r
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 a45d9de9bd68b481fad2ce272aa7f09531579902..45b9a4441d97f00a63809ef2cb893405d6255a0a 100644 (file)
@@ -38,8 +38,8 @@ can be stored under a fixed lookup key.
 When a transaction fails due to a full database, Postfix
 resizes the database and retries the transaction.
 
-Postfix access, address mapping and routing tables will
-generate partial search keys such as domain names without
+Postfix access, address mapping and routing table lookups
+may generate partial search keys such as domain names without
 one or more subdomains, network addresses without one or
 more least-significant octets, or email addresses without
 the localpart, address extension or domain portion.
@@ -47,7 +47,7 @@ This behavior is also found with btree:, hash:, or ldap:
 tables.
 
 Unlike other flat-file based Postfix databases, changes to
-an LMDB database do not require automatic daemon program
+an LMDB database do not trigger automatic daemon program
 restart.
 .SH "RELIABILITY"
 .na
@@ -63,10 +63,12 @@ curruption due stray pointer bugs.
 The Postfix LMDB adapter implements locking with fcntl(2)
 locks at whole-file granularity. LMDB's native locking
 scheme would require world-writable lockfiles and would
-therefore violate the Postfix security model.  Unlike some
-other Postfix flat-file databases, LMDB databases can safely
-be updated without serializing requests through the proxymap(8)
-service.
+therefore violate the Postfix security model.
+
+Multiple processes can safely update an LMDB database without
+serializing requests through the proxymap(8) service.  This
+makes LMDB suitable as a shared cache for verify(8) or
+postscreen(8) services.
 .SH "CONFIGURATION PARAMETERS"
 .na
 .nf
index 4da8a488bbb4c5c34127bbca6be4edeaf2ef59c3..a46106d64cc37310d06c0c069f6f01309e1537c2 100644 (file)
@@ -12,10 +12,10 @@ Postfix configuration parameters
 .SH DESCRIPTION
 .ad
 .fi
-The Postfix main.cf configuration file specifies a small subset
-of all the parameters that control the operation of the Postfix
-mail system. Parameters not specified in main.cf are left at their
-default values.
+The Postfix main.cf configuration file specifies parameters that
+control the operation of the Postfix mail system. Typically the
+file contains only a small subset of all parameters; parameters
+not specified are left at their default values.
 .PP
 The general format of the main.cf file is as follows:
 .IP \(bu
@@ -5131,23 +5131,27 @@ recipient_canonical_maps = hash:/etc/postfix/recipient_canonical
 .ft R
 .SH recipient_delimiter (default: empty)
 The set of characters that can separate a user name from its
-address extension (user+foo).  See \fBcanonical\fR(5), \fBlocal\fR(8), \fBrelocated\fR(5)
-and \fBvirtual\fR(5) for the effects this has on aliases, canonical,
-virtual, and relocated lookups.  Basically, the software tries
-user+foo and .forward+foo before trying user and .forward.
-.PP
-This implementation recognizes one delimiter character per email
-address, and one address extension per email address.
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).  Basically, the software tries
+user+foo and .forward+foo before trying user and .forward.  This
+implementation recognizes one delimiter character and one extension
+per email address or .forward file name.
 .PP
 When the recipient_delimiter set contains multiple characters
-(Postfix 2.11 and later), a user name is separated from its address
-extension by the first character that matches the recipient_delimiter
-set.
+(Postfix 2.11 and later), a user name or .forward file name is
+separated from its extension by the first character that matches
+the recipient_delimiter set.
+.PP
+See \fBcanonical\fR(5), \fBlocal\fR(8), \fBrelocated\fR(5) and \fBvirtual\fR(5) for the
+effects of recipient_delimiter on lookups in aliases, canonical,
+virtual, and relocated maps, and see the propagate_unmatched_extensions
+parameter for propagating an extension from one email address to
+another.
 .PP
 When used in command_execution_directory, forward_path, or
-luser_relay, ${recipient_delimiter} is replaced
-with the recipient delimiter that was found in the recipient email
-address (Postfix 2.11 and later), or it is replaced with the main.cf
+luser_relay, ${recipient_delimiter} is replaced with the actual
+recipient delimiter that was found in the recipient email address
+(Postfix 2.11 and later), or it is replaced with the main.cf
 recipient_delimiter parameter value (Postfix 2.10 and earlier).
 .PP
 The recipient_delimiter is not applied to the mailer-daemon
@@ -7220,6 +7224,39 @@ 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
+With Postfix >= 2.5 the parameter syntax was expanded to support
+protocol exclusions. One can explicitly exclude "SSLv2" by setting
+"smtp_tls_mandatory_protocols = !SSLv2". To exclude both "SSLv2" and
+"SSLv3" set "smtp_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is
+supported, but not recommended. The exclusion form more closely
+matches the underlying OpenSSL interface semantics.
+.PP
+The range of protocols advertised by an SSL/TLS client must be
+contiguous.  When a protocol version is enabled, disabling any
+higher version implicitly disables all versions above that higher
+version.  Thus, for example:
+.sp
+.in +4
+.nf
+.na
+.ft C
+smtp_tls_mandatory_protocols = !SSLv2, !TLSv1
+.fi
+.ad
+.ft R
+.in -4
+also disables any protocols version higher than TLSv1 leaving
+only "SSLv3" enabled.
+.PP
+Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
+and "TLSv1.2". When Postfix <= 2.5 is linked against OpenSSL 1.0.1
+or later, these, or any other new protocol versions, cannot be
+disabled except by also disabling "TLSv1" (typically leaving just
+"SSLv3").  The latest patch levels of Postfix >= 2.6, and all
+versions of Postfix >= 2.10 can explicitly disable support for
+"TLSv1.1" or "TLSv1.2".
+.PP
 At the dane and
 dane-only security
 levels, when usable TLSA records are obtained for the remote SMTP
@@ -7227,23 +7264,10 @@ 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
+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
-unconditionally enabled.
-.PP
-With Postfix >= 2.5 the parameter syntax is expanded to support
-protocol exclusions. One can now explicitly exclude SSLv2 by setting
-"smtp_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
-SSLv3 set "smtp_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
-the protocols to include, rather than protocols to exclude, is
-supported, but not recommended. The exclusion form more closely
-matches the behavior when the OpenSSL library is newer than Postfix.
+you are sure will support "TLSv1.1" or "TLSv1.2".
 .PP
 Since SSL version 2 has known protocol weaknesses and is now
 deprecated, the default setting excludes "SSLv2".  This means that by
@@ -7258,9 +7282,9 @@ Example:
 .nf
 .na
 .ft C
-# Preferred form with Postfix >= 2.5:
+# Preferred syntax with Postfix >= 2.5:
 smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
-# Alternative form.
+# Legacy syntax:
 smtp_tls_mandatory_protocols = TLSv1
 .fi
 .ad
@@ -7415,23 +7439,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
@@ -7532,24 +7550,41 @@ separator is colon. An empty value means allow all protocols. The valid
 protocol names, (see \\fBfBSSL_get_version\fR(3)\fR), are "SSLv2", "SSLv3"
 and "TLSv1".
 .PP
+The range of protocols advertised by an SSL/TLS client must be
+contiguous.  When a protocol version is enabled, disabling any
+higher version implicitly disables all versions above that higher
+version.  Thus, for example:
+.sp
+.in +4
+.nf
+.na
+.ft C
+smtp_tls_mandatory_protocols = !SSLv2, !TLSv1
+.fi
+.ad
+.ft R
+.in -4
+also disables any protocols version higher than TLSv1 leaving
+only "SSLv3" enabled.
+.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
-unconditionally enabled.
+and "TLSv1.2". The latest patch levels of Postfix >= 2.6, and all
+versions of Postfix >= 2.10 can explicitly disable support for
+"TLSv1.1" or "TLSv1.2"
 .PP
 To include a protocol list its name, to exclude it, prefix the name
 with a "!" character. To exclude SSLv2 even for opportunistic TLS set
 "smtp_tls_protocols = !SSLv2". To exclude both "SSLv2" and "SSLv3" set
 "smtp_tls_protocols = !SSLv2, !SSLv3". Explicitly listing the protocols to
 include, rather than protocols to exclude, is supported, but not
-recommended.  The exclusion form more closely matches the behavior
-when the OpenSSL library is newer than Postfix.
+recommended.  The exclusion form more closely matches the underlying
+OpenSSL interface semantics.
 .PP
 Example:
 .nf
 .na
 .ft C
-# TLSv1 only!
+# TLSv1 or better:
 smtp_tls_protocols = !SSLv2, !SSLv3
 .fi
 .ad
@@ -7666,20 +7701,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,24 +10630,26 @@ 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.
-.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
-unconditionally enabled.
-.PP
-With Postfix >= 2.5 the parameter syntax is expanded to support
-protocol exclusions. One can now explicitly exclude SSLv2 by setting
-"smtpd_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
-SSLv3 set "smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+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
+With Postfix >= 2.5 the parameter syntax was expanded to support
+protocol exclusions. One can explicitly exclude "SSLv2" by setting
+"smtpd_tls_mandatory_protocols = !SSLv2". To exclude both "SSLv2" and
+"SSLv3" set "smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
 the protocols to include, rather than protocols to exclude, is
 supported, but not recommended. The exclusion form more closely
-matches the behavior when the OpenSSL library is newer than Postfix.
+matches the underlying OpenSSL interface semantics.
+.PP
+Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
+and "TLSv1.2". When Postfix <= 2.5 is linked against OpenSSL 1.0.1
+or later, these, or any other new protocol versions, cannot be
+disabled.  The latest patch levels of Postfix >= 2.6, and all
+versions of Postfix >= 2.10 can disable support for "TLSv1.1" or
+"TLSv1.2".
 .PP
 Since SSL version 2 has known protocol weaknesses and is now
 deprecated, the default setting excludes "SSLv2".  This means that
@@ -10628,9 +10661,10 @@ Example:
 .nf
 .na
 .ft C
-smtpd_tls_mandatory_protocols = TLSv1
-# Alternative form with Postfix >= 2.5:
+# Preferred syntax with Postfix >= 2.5:
 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+# Legacy syntax:
+smtpd_tls_mandatory_protocols = TLSv1
 .fi
 .ad
 .ft R
@@ -10638,28 +10672,25 @@ smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
 This feature is available in Postfix 2.3 and later.
 .SH smtpd_tls_protocols (default: empty)
 List of TLS protocols that the Postfix SMTP server will exclude
-or include with opportunistic TLS encryption. This parameter SHOULD be
-left at its default empty value, allowing all protocols to be used with
-opportunistic TLS.
-.PP
-In main.cf the values are separated by whitespace, commas or
-colons. An empty value means allow all protocols. The valid protocol
-names, (see \\fBfBSSL_get_version\fR(3)\fR), are "SSLv2", "SSLv3" and
-"TLSv1". In smtp_tls_policy_maps table entries, "protocols" attribute
-values are separated by a colon.
+or include with opportunistic TLS encryption. This parameter SHOULD
+be left at its default empty value, allowing all protocols to be
+used with opportunistic TLS.  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
-1.0.1 or later, these, or any other new protocol versions, are
-unconditionally enabled.
+and "TLSv1.2". The latest patch levels of Postfix >= 2.6, and all
+versions of Postfix >= 2.10 can disable support for "TLSv1.1" or
+"TLSv1.2".
 .PP
 To include a protocol list its name, to exclude it, prefix the name
 with a "!" character. To exclude SSLv2 even for opportunistic TLS set
 "smtpd_tls_protocols = !SSLv2". To exclude both "SSLv2" and "SSLv3" set
 "smtpd_tls_protocols = !SSLv2, !SSLv3". Explicitly listing the protocols to
 include, rather than protocols to exclude, is supported, but not
-recommended.  The exclusion form more closely matches the behavior
-when the OpenSSL library is newer than Postfix.
+recommended.  The exclusion form more closely matches the underlying
+OpenSSL interface semantics.
 .PP
 Example:
 .nf
@@ -11007,78 +11038,110 @@ 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).
+.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 +11151,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 +11368,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 b872ce6fe8b061a727865d4ba01cf5db50abecd7..fb50757e3d65f7a5b27a3723e5239d81275be068 100644 (file)
@@ -576,7 +576,8 @@ key to the lookup result.
 The location of the Postfix top-level queue directory.
 .IP "\fBrecipient_delimiter (empty)\fR"
 The set of characters that can separate a user name from its
-address extension (user+foo).
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).
 .IP "\fBrequire_home_directory (no)\fR"
 Require that a \fBlocal\fR(8) recipient's home directory exists
 before mail delivery is attempted.
index dc594a814bd55d7f88ab56d8fd7b6a213510d855..41d5159648bb122a1e1db3d5591d9b2775340162 100644 (file)
@@ -417,7 +417,8 @@ The process name of a Postfix command or daemon process.
 The location of the Postfix top-level queue directory.
 .IP "\fBrecipient_delimiter (empty)\fR"
 The set of characters that can separate a user name from its
-address extension (user+foo).
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).
 .IP "\fBsyslog_facility (mail)\fR"
 The syslog facility of Postfix logging.
 .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
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 3651a05382642cf2de6778ad98e6bed94a9b959f..ec95a4bca5862b9ee94c8a26c3507d027c292b35 100644 (file)
@@ -1009,7 +1009,8 @@ The process name of a Postfix command or daemon process.
 The location of the Postfix top-level queue directory.
 .IP "\fBrecipient_delimiter (empty)\fR"
 The set of characters that can separate a user name from its
-address extension (user+foo).
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).
 .IP "\fBsmtpd_banner ($myhostname ESMTP $mail_name)\fR"
 The text that follows the 220 status code in the SMTP greeting
 banner.
index ff0b25a9965d82454178f4abc0d7eddf057d015e..27a7bb3f88f427b0f4d2bc8465c4bc6f3a7dfd49 100644 (file)
@@ -135,7 +135,8 @@ With locally submitted mail, append the string ".$mydomain" to
 addresses that have no ".domain" information.
 .IP "\fBrecipient_delimiter (empty)\fR"
 The set of characters that can separate a user name from its
-address extension (user+foo).
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).
 .IP "\fBswap_bangpath (yes)\fR"
 Enable the rewriting of "site!user" into "user@site".
 .PP
index f1dfe3f89b2dad0c78db60cbde4d453d17030d46..d7a87e1067a7a28653add090ea17130a1128ff5e 100755 (executable)
@@ -1112,7 +1112,7 @@ while (<>) {
     s/\b(static):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
     s/\b(tcp):/<a href="tcp_table.5.html">$1<\/a>:/g;
     s/\b(texthash):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
-    s/\b(unix):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
+    #s/\b(unix):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
 
     # Do nice links for smtp:host:port etc.
 
index 59bb4fbd9301c3334bd0db6719d466a7ab3219f3..a2574df8921fba9c24af58e36e7604d06226c499 100644 (file)
@@ -233,7 +233,7 @@ creates a "generic" database when none exists. </p>
     generic: Makefile
            @echo Creating $@
            @rm -f $@.tmp
-           @printf '%s\t%s+root=%s\n' root $MTAADMIN `uname -n` &gt; $@.tmp
+           @printf '%s\t%s+root=%s\n' root ${MTAADMIN} `uname -n` &gt; $@.tmp
            @mv $@.tmp generic
 
     %.cdb: %
index ad5177224ff70c6c891c4132d18109721045de09..0e538d47b99501d44eebcfb3b52c8f778046c0ef 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>
 
@@ -614,7 +607,7 @@ from scratch.  </p>
 
 <p> Since Postfix uses multiple smtpd(8) service processes, an
 in-memory cache is not sufficient for session re-use.  Clients store
-at most one cached session per server and are very unlikey to
+at most one cached session per server and are very unlikely to
 repeatedly connect to the same server process.  Thus session caching
 in the Postfix SMTP server generally requires a shared cache (an
 alternative available with Postfix &ge; 2.11 is described below).
@@ -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:
+    # Preferred syntax with Postfix &ge; 2.5:
     smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
-    # Alternative form.
+    # Legacy syntax:
     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 unlikely 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 b1cc8169a5e355d114e026621632177ba7f723b4..8f2829753f3e13c6bbc7310474b102a925445d79 100644 (file)
@@ -30,8 +30,8 @@
 #      When a transaction fails due to a full database, Postfix
 #      resizes the database and retries the transaction.
 #
-#      Postfix access, address mapping and routing tables will
-#      generate partial search keys such as domain names without
+#      Postfix access, address mapping and routing table lookups
+#      may generate partial search keys such as domain names without
 #      one or more subdomains, network addresses without one or
 #      more least-significant octets, or email addresses without
 #      the localpart, address extension or domain portion.
@@ -39,7 +39,7 @@
 #      tables.
 #
 #      Unlike other flat-file based Postfix databases, changes to
-#      an LMDB database do not require automatic daemon program
+#      an LMDB database do not trigger automatic daemon program
 #      restart.
 # RELIABILITY
 # .ad
 #      The Postfix LMDB adapter implements locking with fcntl(2)
 #      locks at whole-file granularity. LMDB's native locking
 #      scheme would require world-writable lockfiles and would
-#      therefore violate the Postfix security model.  Unlike some
-#      other Postfix flat-file databases, LMDB databases can safely
-#      be updated without serializing requests through the proxymap(8)
-#      service.
+#      therefore violate the Postfix security model.
+#
+#      Multiple processes can safely update an LMDB database without
+#      serializing requests through the proxymap(8) service.  This
+#      makes LMDB suitable as a shared cache for verify(8) or
+#      postscreen(8) services.
 # CONFIGURATION PARAMETERS
 # .ad
 # .fi
index 37afa4e88fe98307ac19819fdd048d1f736cadc6..170838fe64ab1e78724f86853d48712a75ae0b3c 100644 (file)
@@ -12,10 +12,10 @@ Postfix configuration parameters
 .SH DESCRIPTION
 .ad
 .fi
-The Postfix main.cf configuration file specifies a small subset
-of all the parameters that control the operation of the Postfix
-mail system. Parameters not specified in main.cf are left at their
-default values.
+The Postfix main.cf configuration file specifies parameters that
+control the operation of the Postfix mail system. Typically the
+file contains only a small subset of all parameters; parameters
+not specified are left at their default values.
 .PP
 The general format of the main.cf file is as follows:
 .IP \(bu
index 32925f00acad7994fcaaf954238e7b67b54c83f3..b4204fc6ceddd636e7dea5f83baff8284c492998 100644 (file)
@@ -3505,23 +3505,27 @@ recipient_canonical_maps = hash:/etc/postfix/recipient_canonical
 %PARAM recipient_delimiter
 
 <p> The set of characters that can separate a user name from its
-address extension (user+foo).  See canonical(5), local(8), relocated(5)
-and virtual(5) for the effects this has on aliases, canonical,
-virtual, and relocated lookups.  Basically, the software tries
-user+foo and .forward+foo before trying user and .forward.  </p>
-
-<p> This implementation recognizes one delimiter character per email
-address, and one address extension per email address.  </p>
+extension (example: user+foo), or a .forward file name from its
+extension (example: .forward+foo).  Basically, the software tries
+user+foo and .forward+foo before trying user and .forward.  This
+implementation recognizes one delimiter character and one extension
+per email address or .forward file name. </p>
 
 <p> When the recipient_delimiter set contains multiple characters
-(Postfix 2.11 and later), a user name is separated from its address
-extension by the first character that matches the recipient_delimiter
-set. </p>
+(Postfix 2.11 and later), a user name or .forward file name is
+separated from its extension by the first character that matches
+the recipient_delimiter set. </p>
+
+<p> See canonical(5), local(8), relocated(5) and virtual(5) for the
+effects of recipient_delimiter on lookups in aliases, canonical,
+virtual, and relocated maps, and see the propagate_unmatched_extensions
+parameter for propagating an extension from one email address to
+another.  </p>
 
 <p> When used in command_execution_directory, forward_path, or
-luser_relay, ${recipient_delimiter} is replaced
-with the recipient delimiter that was found in the recipient email
-address (Postfix 2.11 and later), or it is replaced with the main.cf
+luser_relay, ${recipient_delimiter} is replaced with the actual
+recipient delimiter that was found in the recipient email address
+(Postfix 2.11 and later), or it is replaced with the main.cf
 recipient_delimiter parameter value (Postfix 2.10 and earlier).
 </p>
 
@@ -10668,23 +10672,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,6 +10775,35 @@ 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> With Postfix &ge; 2.5 the parameter syntax was expanded to support
+protocol exclusions. One can explicitly exclude "SSLv2" by setting
+"smtp_tls_mandatory_protocols = !SSLv2". To exclude both "SSLv2" and
+"SSLv3" set "smtp_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is
+supported, but not recommended. The exclusion form more closely
+matches the underlying OpenSSL interface semantics.
+</p>
+
+<p> The range of protocols advertised by an SSL/TLS client must be
+contiguous.  When a protocol version is enabled, disabling any
+higher version implicitly disables all versions above that higher
+version.  Thus, for example: </p>
+<blockquote>
+<pre>
+smtp_tls_mandatory_protocols = !SSLv2, !TLSv1
+</pre>
+</blockquote>
+<p> also disables any protocols version higher than TLSv1 leaving
+only "SSLv3" enabled.  </p>
+
+<p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
+and "TLSv1.2". When Postfix &le; 2.5 is linked against OpenSSL 1.0.1
+or later, these, or any other new protocol versions, cannot be
+disabled except by also disabling "TLSv1" (typically leaving just
+"SSLv3").  The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can explicitly disable support for
+"TLSv1.1" or "TLSv1.2". </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
@@ -10784,24 +10811,10 @@ 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
+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
-unconditionally enabled. </p>
-
-<p> With Postfix &ge; 2.5 the parameter syntax is expanded to support
-protocol exclusions. One can now explicitly exclude SSLv2 by setting
-"smtp_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
-SSLv3 set "smtp_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
-the protocols to include, rather than protocols to exclude, is
-supported, but not recommended. The exclusion form more closely
-matches the behavior when the OpenSSL library is newer than Postfix.
-</p>
+you are sure will support "TLSv1.1" or "TLSv1.2".  </p>
 
 <p> Since SSL version 2 has known protocol weaknesses and is now
 deprecated, the default setting excludes "SSLv2".  This means that by
@@ -10814,9 +10827,9 @@ TLS_README for more information about security levels. </p>
 <p> Example: </p>
 
 <pre>
-# Preferred form with Postfix &ge; 2.5:
+# Preferred syntax with Postfix &ge; 2.5:
 smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
-# Alternative form.
+# Legacy syntax:
 smtp_tls_mandatory_protocols = TLSv1
 </pre>
 
@@ -10970,25 +10983,26 @@ 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>
-
-<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
-unconditionally enabled. </p>
-
-<p> With Postfix &ge; 2.5 the parameter syntax is expanded to support
-protocol exclusions. One can now explicitly exclude SSLv2 by setting
-"smtpd_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
-SSLv3 set "smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+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> With Postfix &ge; 2.5 the parameter syntax was expanded to support
+protocol exclusions. One can explicitly exclude "SSLv2" by setting
+"smtpd_tls_mandatory_protocols = !SSLv2". To exclude both "SSLv2" and
+"SSLv3" set "smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
 the protocols to include, rather than protocols to exclude, is
 supported, but not recommended. The exclusion form more closely
-matches the behavior when the OpenSSL library is newer than Postfix.
-</p>
+matches the underlying OpenSSL interface semantics.  </p>
+
+<p> Note: As of OpenSSL 1.0.1 two new protocols are defined, "TLSv1.1"
+and "TLSv1.2". When Postfix &le; 2.5 is linked against OpenSSL 1.0.1
+or later, these, or any other new protocol versions, cannot be
+disabled.  The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can disable support for "TLSv1.1" or
+"TLSv1.2". </p>
 
 <p> Since SSL version 2 has known protocol weaknesses and is now
 deprecated, the default setting excludes "SSLv2".  This means that
@@ -10998,9 +11012,10 @@ level. </p>
 <p> Example: </p>
 
 <pre>
-smtpd_tls_mandatory_protocols = TLSv1
-# Alternative form with Postfix &ge; 2.5:
+# Preferred syntax with Postfix &ge; 2.5:
 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+# Legacy syntax:
+smtpd_tls_mandatory_protocols = TLSv1
 </pre>
 
 <p> This feature is available in Postfix 2.3 and later. </p>
@@ -11063,21 +11078,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.
@@ -12127,22 +12139,34 @@ separator is colon. An empty value means allow all protocols. The valid
 protocol names, (see <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3"
 and "TLSv1". </p>
 
+<p> The range of protocols advertised by an SSL/TLS client must be
+contiguous.  When a protocol version is enabled, disabling any
+higher version implicitly disables all versions above that higher
+version.  Thus, for example: </p>
+<blockquote>
+<pre>
+smtp_tls_mandatory_protocols = !SSLv2, !TLSv1
+</pre>
+</blockquote>
+<p> also disables any protocols version higher than TLSv1 leaving
+only "SSLv3" enabled.  </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
-unconditionally enabled. </p>
+and "TLSv1.2". The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can explicitly disable support for
+"TLSv1.1" or "TLSv1.2"</p>
 
 <p> To include a protocol list its name, to exclude it, prefix the name
 with a "!" character. To exclude SSLv2 even for opportunistic TLS set
 "smtp_tls_protocols = !SSLv2". To exclude both "SSLv2" and "SSLv3" set
 "smtp_tls_protocols = !SSLv2, !SSLv3". Explicitly listing the protocols to
 include, rather than protocols to exclude, is supported, but not
-recommended.  The exclusion form more closely matches the behavior
-when the OpenSSL library is newer than Postfix.  </p>
+recommended.  The exclusion form more closely matches the underlying
+OpenSSL interface semantics. </p>
 
 <p> Example: </p>
 <pre>
-# TLSv1 only!
+# TLSv1 or better:
 smtp_tls_protocols = !SSLv2, !SSLv3
 </pre>
 
@@ -12151,28 +12175,25 @@ smtp_tls_protocols = !SSLv2, !SSLv3
 %PARAM smtpd_tls_protocols
 
 <p> List of TLS protocols that the Postfix SMTP server will exclude
-or include with opportunistic TLS encryption. This parameter SHOULD be
-left at its default empty value, allowing all protocols to be used with
-opportunistic TLS. </p>
-
-<p> In main.cf the values are separated by whitespace, commas or
-colons. An empty value means allow all protocols. The valid protocol
-names, (see <b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and
-"TLSv1". In smtp_tls_policy_maps table entries, "protocols" attribute
-values are separated by a colon. </p>
+or include with opportunistic TLS encryption. This parameter SHOULD
+be left at its default empty value, allowing all protocols to be
+used with opportunistic TLS.  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
-1.0.1 or later, these, or any other new protocol versions, are
-unconditionally enabled. </p>
+and "TLSv1.2". The latest patch levels of Postfix &ge; 2.6, and all
+versions of Postfix &ge; 2.10 can disable support for "TLSv1.1" or
+"TLSv1.2". </p>
 
 <p> To include a protocol list its name, to exclude it, prefix the name
 with a "!" character. To exclude SSLv2 even for opportunistic TLS set
 "smtpd_tls_protocols = !SSLv2". To exclude both "SSLv2" and "SSLv3" set
 "smtpd_tls_protocols = !SSLv2, !SSLv3". Explicitly listing the protocols to
 include, rather than protocols to exclude, is supported, but not
-recommended.  The exclusion form more closely matches the behavior
-when the OpenSSL library is newer than Postfix.  </p>
+recommended.  The exclusion form more closely matches the underlying
+OpenSSL interface semantics. </p>
 
 <p> Example: </p>
 <pre>
@@ -14265,7 +14286,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 +15281,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 +15302,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 +15454,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).
+</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 d85eabdce71d87edd157881d714019e73eb9296c..f008a0d073f2d86ef5b4520519af8309ce38efeb 100644 (file)
@@ -312,6 +312,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
        xtext_test scache_multi_test ehlo_mask_test \
        namadr_list_test mail_conf_time_test header_body_checks_tests \
        mail_version_test server_acl_test resolve_local_test maps_test
+       safe_ultostr_test
 
 mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
        mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
@@ -516,6 +517,11 @@ mail_conf_time_test: mail_conf_time mail_conf_time.ref
        diff mail_conf_time.ref mail_conf_time.tmp
        rm -f mail_conf_time.tmp
 
+safe_ultostr_test: safe_ultostr safe_ultostr.in safe_ultostr.ref
+       ./safe_ultostr <safe_ultostr.in >safe_ultostr.tmp 2>&1
+       diff safe_ultostr.ref safe_ultostr.tmp
+       rm -f safe_ultostr.tmp
+
 header_body_checks_null_test: header_body_checks header_body_checks_null.ref
        ./header_body_checks "" "" "" "" \
                <mime_test.in >header_body_checks_null.tmp 2>&1
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 00973bb0198e31632e8b3c5599443d496c79c4c0..191ad53188b8e4906b0f7ce4313c40059cd605b7 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20131126"
+#define MAIL_RELEASE_DATE      "20131217"
 #define MAIL_VERSION_NUMBER    "2.11"
 
 #ifdef SNAPSHOT
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 1457e1fa72f7e06cf73e9067b0e0fa6674ba2720..910c2ee68c5468e2df07ce85af7603ab8dc96909 100644 (file)
@@ -97,7 +97,7 @@ static unsigned char safe_chars[] =
 /* safe_ultostr - convert unsigned long to safe alphanumerical string */
 
 char   *safe_ultostr(VSTRING *buf, unsigned long ulval, int base,
-                              int padlen, int padchar)
+                            int padlen, int padchar)
 {
     const char *myname = "safe_ultostr";
     char   *start;
@@ -171,6 +171,8 @@ unsigned long safe_strtoul(const char *start, char **end, int base)
     /*
      * Skip leading whitespace. We don't implement sign/base prefixes.
      */
+    if (end)
+       *end = (char *) start;
     while (ISSPACE(*start))
        ++start;
 
@@ -178,13 +180,7 @@ unsigned long safe_strtoul(const char *start, char **end, int base)
      * Start the conversion.
      */
     errno = 0;
-    for (cp = (unsigned char *) start; *cp; cp++) {
-       /* Return (0, EINVAL) if no conversion was made. */
-       if ((char_val = char_map[*cp]) >= base) {
-           if (cp == (unsigned char *) start)
-               errno = EINVAL;
-           break;
-       }
+    for (cp = (unsigned char *) start; (char_val = char_map[*cp]) < base; cp++) {
        /* Return (ULONG_MAX, ERANGE) if the result is too large. */
        if (sum > div_limit
            || (sum == div_limit && char_val > mod_limit)) {
@@ -197,7 +193,10 @@ unsigned long safe_strtoul(const char *start, char **end, int base)
        }
        sum = sum * base + char_val;
     }
-    if (end)
+    /* Return (0, EINVAL) after no conversion. Test moved here 20131209. */
+    if (cp == (unsigned char *) start)
+       errno = EINVAL;
+    else if (end)
        *end = (char *) cp;
     return (sum);
 }
@@ -225,6 +224,16 @@ int     main(int unused_argc, char **unused_argv)
 #define strtoul strtol
 #endif
 
+    /*
+     * Hard-coded string-to-number test.
+     */
+    ulval2 = safe_strtoul("  ", &junk, 10);
+    if (*junk == 0 || errno != EINVAL)
+       msg_warn("input=' ' result=%lu errno=%m", ulval2);
+
+    /*
+     * Configurable number-to-string-to-number test.
+     */
     while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
        ch = 0;
        if (sscanf(STR(buf), "%lu %d%c", &ulval, &base, &ch) != 2 || ch) {
diff --git a/postfix/src/global/safe_ultostr.in b/postfix/src/global/safe_ultostr.in
new file mode 100644 (file)
index 0000000..49e2743
--- /dev/null
@@ -0,0 +1,4 @@
+4294967295 2
+4294967295 10
+4294967295 16
+4294967295 52
diff --git a/postfix/src/global/safe_ultostr.ref b/postfix/src/global/safe_ultostr.ref
new file mode 100644 (file)
index 0000000..829b79e
--- /dev/null
@@ -0,0 +1,4 @@
+4294967295 = 11111111111111111111111111111111
+4294967295 = 4294967295
+4294967295 = HHHHHHHH
+4294967295 = CHPgSv
index 32da88bcabe854ec6e5f99807e1e609c73360b44..4b69a3032d124541909e2a7f2316222057c4a698 100644 (file)
 /*     The location of the Postfix top-level queue directory.
 /* .IP "\fBrecipient_delimiter (empty)\fR"
 /*     The set of characters that can separate a user name from its
-/*     address extension (user+foo).
+/*     extension (example: user+foo), or a .forward file name from its
+/*     extension (example: .forward+foo).
 /* .IP "\fBrequire_home_directory (no)\fR"
 /*     Require that a \fBlocal\fR(8) recipient's home directory exists
 /*     before mail delivery is attempted.
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 6c4da668cb82795a2f914c5fa4b14d10d394436a..b2e59e105bff908dce87a5133e049bf4c61e4dc3 100644 (file)
 /*     The location of the Postfix top-level queue directory.
 /* .IP "\fBrecipient_delimiter (empty)\fR"
 /*     The set of characters that can separate a user name from its
-/*     address extension (user+foo).
+/*     extension (example: user+foo), or a .forward file name from its
+/*     extension (example: .forward+foo).
 /* .IP "\fBsyslog_facility (mail)\fR"
 /*     The syslog facility of Postfix logging.
 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
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..49114b902b9b524da4cc9bdefb397ed8c4ae3ae9 100644 (file)
@@ -2,24 +2,57 @@
 /* NAME
 /*     postconf_edit 3
 /* SUMMARY
-/*     edit main.cf
+/*     edit main.cf or master.cf
 /* 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 flags in \fBmode\fR:
+/* .IP MASTER_ENTRY
+/*     With EDIT_CONF, 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".
+/*
+/*     With EDIT_EXCL or COMMENT_OUT, edit_master() removes or
+/*     comments out entries specified on the command line as
+/*     "\fIname/type\fR.
+/* .IP MASTER_FIELD
+/*     With EDIT_CONF, edit_master() replaces the value of specific
+/*     service attributes, specified on the command line as
+/*     "\fIname/type/attribute = value\fR".
+/* .IP MASTER_PARAM
+/*     With EDIT_CONF, edit_master() replaces or adds the value
+/*     of service parameters, specified on the command line as
+/*     "\fIname/type/parameter = value\fR".
+/*
+/*     With EDIT_EXCL, edit_master() removes service parameters
+/*     specified on the command line as "\fIparametername\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 +80,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 +155,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 +179,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 +225,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 +240,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 +253,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 +278,298 @@ 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);
+           if ((mode & MASTER_PARAM)
+            && req->edit_value[strcspn(req->edit_value, PC_MASTER_BLANKS)])
+               msg_fatal("whitespace in parameter value: \"%s\"",
+                         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/whatever 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 82d3aeb7d8cafec6261c27ad56a38b91e2eeef00..28aa5f1f197e1dcacd8e9189d3a4c297af5fe7e1 100644 (file)
@@ -798,7 +798,7 @@ static int doproto(STATE *state)
 
 /* connect_sock - connect a socket over some transport */
 
-static VSTREAM *connect_sock(int sock, struct sockaddr * sa, int salen,
+static VSTREAM *connect_sock(int sock, struct sockaddr *sa, int salen,
                           const char *name, const char *addr, STATE *state)
 {
     DSN_BUF *why = state->why;
@@ -895,7 +895,7 @@ static VSTREAM *connect_unix(STATE *state, const char *path)
     if (msg_verbose)
        msg_info("%s: trying: %s...", myname, path);
 
-    return (connect_sock(sock, (struct sockaddr *) & sock_un, sizeof(sock_un),
+    return (connect_sock(sock, (struct sockaddr *) &sock_un, sizeof(sock_un),
                         var_myhostname, path, state));
 }
 
@@ -906,7 +906,7 @@ static VSTREAM *connect_addr(STATE *state, DNS_RR *addr)
     static const char *myname = "connect_addr";
     DSN_BUF *why = state->why;
     struct sockaddr_storage ss;                /* remote */
-    struct sockaddr *sa = (struct sockaddr *) & ss;
+    struct sockaddr *sa = (struct sockaddr *) &ss;
     SOCKADDR_SIZE salen = sizeof(ss);
     MAI_HOSTADDR_STR hostaddr;
     int     sock;
@@ -1392,7 +1392,7 @@ static int finger(STATE *state)
     state->why = dsb_create();
 
     if (!(err = connect_dest(state))) {
-       if (state->pass == 1)
+       if (state->pass == 1 && !state->nochat)
            msg_info("Connected to %s", state->namaddrport);
        err = doproto(state);
     }
@@ -1430,7 +1430,11 @@ static int finger(STATE *state)
 
 static void ssl_cleanup(void)
 {
-    ERR_remove_state(0);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+    ERR_remove_thread_state(0);                /* Thread-id is now a pointer */
+#else
+    ERR_remove_state(0);               /* Deprecated with OpenSSL 1.0.0 */
+#endif
     ENGINE_cleanup();
     CONF_modules_unload(1);
     ERR_free_strings();
@@ -1745,10 +1749,10 @@ static void parse_match(STATE *state, int argc, char *argv[])
            argv_add(state->match, "hostname", ARGV_END);
        break;
     case TLS_LEV_FPRINT:
-       state->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+       state->dane = tls_dane_alloc();
        while (*argv)
-           tls_dane_split((TLS_DANE *) state->dane, TLS_DANE_EE, TLS_DANE_PKEY,
-                          state->mdalg, *argv++, "");
+           tls_dane_add_ee_digests((TLS_DANE *) state->dane,
+                                   state->mdalg, *argv++, "");
        break;
     case TLS_LEV_DANE:
        state->match = argv_alloc(2);
@@ -1773,7 +1777,7 @@ static void parse_tas(STATE *state)
        return;
     case TLS_LEV_SECURE:
     case TLS_LEV_VERIFY:
-       state->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+       state->dane = tls_dane_alloc();
        for (file = state->options.tas->argv; *file; ++file) {
            if (!tls_dane_load_trustfile((TLS_DANE *) state->dane, *file))
                break;
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 4f3fae10b92bc6fcb30107e6fbf6ff0c1159839a..7c93e464d44d9d2c5c66c88a091820b530c8e239 100644 (file)
@@ -308,9 +308,9 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
                break;
            case TLS_LEV_FPRINT:
                if (!tls->dane)
-                   tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
-               tls_dane_split(tls->dane, TLS_DANE_EE, TLS_DANE_PKEY,
-                              var_smtp_tls_fpt_dgst, val, "|");
+                   tls->dane = tls_dane_alloc();
+               tls_dane_add_ee_digests(tls->dane,
+                                       var_smtp_tls_fpt_dgst, val, "|");
                break;
            case TLS_LEV_VERIFY:
            case TLS_LEV_SECURE:
@@ -345,7 +345,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
                INVALID_RETURN(tls->why, site_level);
            }
            if (!tls->dane)
-               tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+               tls->dane = tls_dane_alloc();
            if (!tls_dane_load_trustfile(tls->dane, val)) {
                INVALID_RETURN(tls->why, site_level);
            }
@@ -559,11 +559,10 @@ static void *policy_create(const char *unused_key, void *context)
        break;
     case TLS_LEV_FPRINT:
        if (tls->dane == 0)
-           tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+           tls->dane = tls_dane_alloc();
        if (!TLS_DANE_HASEE(tls->dane)) {
-           tls_dane_split(tls->dane, TLS_DANE_EE, TLS_DANE_PKEY,
-                          var_smtp_tls_fpt_dgst, var_smtp_tls_fpt_cmatch,
-                          "\t\n\r, ");
+           tls_dane_add_ee_digests(tls->dane, var_smtp_tls_fpt_dgst,
+                                   var_smtp_tls_fpt_cmatch, "\t\n\r, ");
            if (!TLS_DANE_HASEE(tls->dane)) {
                msg_warn("nexthop domain %s: configured at fingerprint "
                       "security level, but with no fingerprints to match.",
@@ -582,7 +581,7 @@ static void *policy_create(const char *unused_key, void *context)
                           "\t\n\r, :");
        if (*var_smtp_tls_tafile) {
            if (tls->dane == 0)
-               tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+               tls->dane = tls_dane_alloc();
            if (!TLS_DANE_HASTA(tls->dane)
                && !load_tas(tls->dane, var_smtp_tls_tafile)) {
                MARK_INVALID(tls->why, &tls->level);
index c3cc37c68b93cb8f3012064d528ffc39967d6859..08aa2e2b45a4ee70903df8a74d1e3ace9d822a10 100644 (file)
 /*     The location of the Postfix top-level queue directory.
 /* .IP "\fBrecipient_delimiter (empty)\fR"
 /*     The set of characters that can separate a user name from its
-/*     address extension (user+foo).
+/*     extension (example: user+foo), or a .forward file name from its
+/*     extension (example: .forward+foo).
 /* .IP "\fBsmtpd_banner ($myhostname ESMTP $mail_name)\fR"
 /*     The text that follows the 220 status code in the SMTP greeting
 /*     banner.
index 577d530a7a673a54554f7254fa4669d35f5c4e39..3e452c10d073448ca93836dc953e5caff9cb471f 100644 (file)
@@ -17,7 +17,7 @@ DEFS  = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 INCL   =
 LIB    = libtls.a
-TESTPROG= tls_dh tls_mgr
+TESTPROG= tls_dh tls_mgr tls_rsa
 
 LIBS   = ../../lib/libglobal.a ../../lib/libutil.a
 LIB_DIR        = ../../lib
@@ -81,6 +81,11 @@ tls_mgr: $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
        mv junk $@.o
 
+tls_rsa: $(LIB) $(LIBS)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+       mv junk $@.o
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -151,7 +156,9 @@ tls_dane.o: ../../include/myaddrinfo.h
 tls_dane.o: ../../include/mymalloc.h
 tls_dane.o: ../../include/name_code.h
 tls_dane.o: ../../include/name_mask.h
+tls_dane.o: ../../include/safe_ultostr.h
 tls_dane.o: ../../include/sock_addr.h
+tls_dane.o: ../../include/split_at.h
 tls_dane.o: ../../include/stringops.h
 tls_dane.o: ../../include/sys_defs.h
 tls_dane.o: ../../include/timecmp.h
@@ -320,6 +327,7 @@ tls_proxy_scan.o: tls_proxy.h
 tls_proxy_scan.o: tls_proxy_scan.c
 tls_rsa.o: ../../include/argv.h
 tls_rsa.o: ../../include/dns.h
+tls_rsa.o: ../../include/msg.h
 tls_rsa.o: ../../include/myaddrinfo.h
 tls_rsa.o: ../../include/name_code.h
 tls_rsa.o: ../../include/name_mask.h
index cd4c2fcd2eff2881f3c5acc1ff64916d601f4b80..5b4851d3987e5137e2ae573a5464e6f440233155 100644 (file)
@@ -82,6 +82,20 @@ extern const NAME_CODE tls_level_table[];
 
 #if (OPENSSL_VERSION_NUMBER < 0x00090700f)
 #error "need OpenSSL version 0.9.7 or later"
+#endif
+
+/* SSL_CIPHER_get_name() got constified in 0.9.7g */
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL      /* constification */
+#define SSL_CIPHER_const const
+#else
+#define SSL_CIPHER_const
+#endif
+
+/* d2i_X509() got constified in 0.9.8a */
+#if OPENSSL_VERSION_NUMBER >= 0x0090801fL
+#define D2I_const const
+#else
+#define D2I_const
 #endif
 
  /*
@@ -108,10 +122,9 @@ extern const NAME_CODE tls_level_table[];
 #define TLS_DANE_CERT  0               /* Match the certificate digest */
 #define TLS_DANE_PKEY  1               /* Match the public key digest */
 
-#define TLS_DANE_FLAG_MIXED    (1<<0)  /* Combined pkeys and certs */
-#define TLS_DANE_FLAG_NORRS    (1<<1)  /* Nothing found in DNS */
-#define TLS_DANE_FLAG_EMPTY    (1<<2)  /* Nothing usable found in DNS */
-#define TLS_DANE_FLAG_ERROR    (1<<3)  /* TLSA record lookup error */
+#define TLS_DANE_FLAG_NORRS    (1<<0)  /* Nothing found in DNS */
+#define TLS_DANE_FLAG_EMPTY    (1<<1)  /* Nothing usable found in DNS */
+#define TLS_DANE_FLAG_ERROR    (1<<2)  /* TLSA record lookup error */
 
 #define tls_dane_unusable(dane)        ((dane)->flags & TLS_DANE_FLAG_EMPTY)
 #define tls_dane_notfound(dane)        ((dane)->flags & TLS_DANE_FLAG_NORRS)
@@ -146,10 +159,6 @@ typedef struct TLS_PKEYS {
     struct TLS_PKEYS *next;
 } TLS_PKEYS;
 
- /*
-  * When TLS_DANE_FLAG_MIXED is set, the pkeys digest list is not allocated
-  * separately, and aliases the certs digest list for each algorithm.
-  */
 typedef struct TLS_DANE {
     TLS_TLSA *ta;                      /* Trust-anchor cert/pubkey digests */
     TLS_TLSA *ee;                      /* End-entity cert/pubkey digests */
@@ -170,9 +179,9 @@ typedef struct TLS_DANE {
 extern int tls_dane_avail(void);
 extern void tls_dane_flush(void);
 extern void tls_dane_verbose(int);
-extern TLS_DANE *tls_dane_alloc(int);
-extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *,
-                                  const char *);
+extern TLS_DANE *tls_dane_alloc(void);
+extern void tls_dane_add_ee_digests(TLS_DANE *, const char *, const char *,
+                                           const char *);
 extern void tls_dane_free(TLS_DANE *);
 extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int);
 extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
index f84955e7e601b65afacccdf0438ed60d10398f2e..7732cfa7b6f088d4fee30aee85958207fb41b432 100644 (file)
@@ -756,7 +756,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     int     protomask;
     const char *cipher_list;
     SSL_SESSION *session = 0;
-    const SSL_CIPHER *cipher;
+    SSL_CIPHER_const SSL_CIPHER *cipher;
     X509   *peercert;
     TLS_SESS_STATE *TLScontext;
     TLS_APPL_STATE *app_ctx = props->ctx;
@@ -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..1f8edcb4736cdbc819561f7533560c349fcfb8cb 100644 (file)
 /*     void    tls_dane_verbose(on)
 /*     int     on;
 /*
-/*     TLS_DANE *tls_dane_alloc(flags)
-/*     int     flags;
+/*     TLS_DANE *tls_dane_alloc()
 /*
 /*     void    tls_dane_free(dane)
 /*     TLS_DANE *dane;
 /*
-/*     void    tls_dane_split(dane, certusage, selector, mdalg, digest, delim)
+/*     void    tls_dane_add_ee_digests(dane, mdalg, digest, delim)
 /*     TLS_DANE *dane;
-/*     int     certusage;
-/*     int     selector;
 /*     const char *mdalg;
 /*     const char *digest;
 /*     const char *delim;
 /*     tls_dane_verbose() turns on verbose logging of TLSA record lookups.
 /*
 /*     tls_dane_alloc() returns a pointer to a newly allocated TLS_DANE
-/*     structure with null ta and ee digest sublists.  If "flags" includes
-/*     TLS_DANE_FLAG_MIXED, the cert and pkey digests are stored together on
-/*     the pkeys list with the certs list empty, otherwise they are stored
-/*     separately.
+/*     structure with null ta and ee digest sublists.
 /*
 /*     tls_dane_free() frees the structure allocated by tls_dane_alloc().
 /*
-/*     tls_dane_split() splits "digest" using the characters in "delim" as
-/*     delimiters and stores the results with the requested "certusage"
-/*     and "selector".  This is an incremental interface, that builds a
-/*     TLS_DANE structure outside the cache by manually adding entries.
+/*     tls_dane_add_ee_digests() splits "digest" using the characters in
+/*     "delim" as delimiters and stores the results on the EE match list
+/*     to match either a certificate or a public key.  This is an incremental
+/*     interface, that builds a TLS_DANE structure outside the cache by
+/*     manually adding entries.
 /*
 /*     tls_dane_load_trustfile() imports trust-anchor certificates and
 /*     public keys from a file (rather than DNS TLSA records).
@@ -97,7 +92,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.
 /*     When true, TLSA lookups are performed even when the qname and rname
 /*     are insecure.  This is only useful in the unlikely case that DLV is
 /*     used to secure the TLSA RRset in an otherwise insecure zone.
-/* .IP flags
-/*     Only one flag is part of the public interface at this time:
 /* .IP TLScontext
 /*     Client context with TA/EE matching data and related state.
 /* .IP usage
 /* .IP ssl_ctx
 /*     The global SSL_CTX structure used to initialize child SSL
 /*     conenctions.
-/* .RS
-/* .IP TLS_DANE_FLAG_MIXED
-/*     Don't distinguish between certificate and public-key digests.
-/*     A single digest list for both certificates and keys with be
-/*     stored for each algorithm in the "pkeys" field, the "certs"
-/*     field will be null.
-/* .RE
-/* .IP certusage
-/*     Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests?
-/* .IP selector
-/*     Full certificate (TLS_DANE_CERT) or pubkey (TLS_DANE_PKEY) digests?
 /* .IP mdalg
 /*     Name of a message digest algorithm suitable for computing secure
 /*     (1st pre-image resistant) message digests of certificates. For now,
 #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)
 
 
 #endif                                 /* OPENSSL_VERSION_NUMBER ... */
 
+#ifdef TRUST_ANCHOR_SUPPORT
+static int ta_support = 1;
+
+#else
+static int ta_support = 0;
+
+#endif
+
 #ifdef WRAP_SIGNED
 static int wrap_signed = 1;
 
@@ -237,36 +230,59 @@ static int wrap_signed = 1;
 static int wrap_signed = 0;
 
 #endif
+
+#ifdef DANE_TLSA_SUPPORT
+static int dane_tlsa_support = 1;
+
+#else
+static int dane_tlsa_support = 0;
+
+#endif
+
+static EVP_PKEY *signkey;
 static const EVP_MD *signmd;
 static const char *signalg;
-
-static EVP_PKEY *danekey;
 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 +302,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;
 
-    for (di = digest_table; di->alg; ++di)
-       if (di->dane_id == dane_id)
-           return (di);
+       /*
+        * 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. Fixed as of 210131209.
+        */
+       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;
+    }
+
+    /*
+     * 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 */
@@ -335,7 +447,7 @@ static EVP_PKEY *gencakey(void)
        EC_GROUP_free(group);
     if (eckey)
        EC_KEY_free(eckey);
-#endif
+#endif                                         /* WRAP_SIGNED */
     return (key);
 }
 
@@ -343,56 +455,57 @@ 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();
 
-#ifdef TRUST_ANCHOR_SUPPORT
-    if ((wrap_signed && (danekey = gencakey()) == 0)
+    /*
+     * DANE TLSA support requires trust-anchor support plus working DANE
+     * digests.
+     */
+    if (!ta_support
+       || (wrap_signed && (signkey = gencakey()) == 0)
        || (serverAuth = OBJ_nid2obj(NID_server_auth)) == 0) {
-       msg_warn("cannot generate TA certificates, no DANE support");
+       msg_warn("cannot generate TA certificates, "
+                "no trust-anchor or DANE support");
+       tls_print_errors();
+       dane_tlsa_support = ta_support = 0;
+    } else if (signmd == 0) {
+       msg_warn("digest algorithm initializaton failed, no DANE support");
        tls_print_errors();
+       dane_tlsa_support = 0;
     }
-#endif
     dane_initialized = 1;
 }
 
@@ -402,12 +515,7 @@ int     tls_dane_avail(void)
 {
     if (!dane_initialized)
        dane_init();
-
-#ifdef DANE_TLSA_SUPPORT
-    return (signalg && serverAuth);
-#else
-    return (0);
-#endif
+    return (dane_tlsa_support);
 }
 
 /* tls_dane_flush - flush the cache */
@@ -421,7 +529,7 @@ void    tls_dane_flush(void)
 
 /* tls_dane_alloc - allocate a TLS_DANE structure */
 
-TLS_DANE *tls_dane_alloc(int flags)
+TLS_DANE *tls_dane_alloc(void)
 {
     TLS_DANE *dane = (TLS_DANE *) mymalloc(sizeof(*dane));
 
@@ -430,7 +538,7 @@ TLS_DANE *tls_dane_alloc(int flags)
     dane->certs = 0;
     dane->pkeys = 0;
     dane->base_domain = 0;
-    dane->flags = flags;
+    dane->flags = 0;
     dane->expires = 0;
     dane->refs = 1;
     return (dane);
@@ -557,36 +665,49 @@ static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg)
     return (tlsap);
 }
 
-/* tls_dane_split - split and append digests */
+/* tls_dane_add_ee_digests - split and append digests */
 
-void    tls_dane_split(TLS_DANE *dane, int certusage, int selector,
-                  const char *mdalg, const char *digest, const char *delim)
+void    tls_dane_add_ee_digests(TLS_DANE *dane, const char *mdalg,
+                                     const char *digest, const char *delim)
 {
-    TLS_TLSA **tlsap;
-    TLS_TLSA *tlsa;
-    ARGV  **argvp;
-
-    tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
-    tlsa = *(tlsap = dane_locate(tlsap, mdalg));
-    argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
-       &tlsa->pkeys : &tlsa->certs;
+    TLS_TLSA **tlsap = dane_locate(&dane->ee, mdalg);
+    TLS_TLSA *tlsa = *tlsap;
 
     /* Delimited append, may append nothing */
-    if (*argvp == 0)
-       *argvp = argv_split(digest, delim);
+    if (tlsa->pkeys == 0)
+       tlsa->pkeys = argv_split(digest, delim);
     else
-       argv_split_append(*argvp, digest, delim);
+       argv_split_append(tlsa->pkeys, digest, delim);
 
-    if ((*argvp)->argc == 0) {
-       argv_free(*argvp);
-       *argvp = 0;
+    /* Remove empty elements from the list */
+    if (tlsa->pkeys->argc == 0) {
+       argv_free(tlsa->pkeys);
+       tlsa->pkeys = 0;
 
-       /* Remove empty elements from the list */
-       if (tlsa->pkeys == 0 && tlsa->certs == 0) {
+       if (tlsa->certs == 0) {
            *tlsap = tlsa->next;
            tlsa_free(tlsa);
        }
+       return;
     }
+
+    /*
+     * At the "fingerprint" security level certificate digests and public key
+     * digests are interchangeable.  Each leaf certificate is matched via
+     * either the public key digest or full certificate digest.  The DER
+     * encoding of a certificate is not a valid public key, and conversely,
+     * the DER encoding of a public key is not a valid certificate.  An
+     * attacker would need a 2nd-preimage that is feasible across types
+     * (given cert digest == some pkey digest) and yet presumably difficult
+     * within a type (e.g. given cert digest == some other cert digest).  No
+     * such attacks are known at this time, and it is expected that if any
+     * are found they would work within as well as across the cert/pkey data
+     * types.
+     */
+    if (tlsa->certs == 0)
+       tlsa->certs = argv_split(digest, delim);
+    else
+       argv_split_append(tlsa->certs, digest, delim);
 }
 
 /* dane_add - add a digest entry */
@@ -599,14 +720,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,19 +738,76 @@ 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;
     tlsa = *(tlsap = dane_locate(tlsap, mdalg));
-    argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
-       &tlsa->pkeys : &tlsa->certs;
+    argvp = (selector == TLS_DANE_PKEY) ? &tlsa->pkeys : &tlsa->certs;
 
     if (*argvp == 0)
        *argvp = argv_alloc(1);
     argv_add(*argvp, digest, ARGV_END);
 }
 
-#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;
+
+    /*
+     * XXX Code that modifies or destroys DNS_RR lists or entries belongs in
+     * the DNS library, not here.
+     */
+    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 */
 
@@ -645,11 +824,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 +837,249 @@ 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;
-    const unsigned char *p;
-    uint32_t prev_usage_selector;      /* Previous (usage<<8|selector) */
-    uint32_t prev_mtype;               /* Previous valid mtype for above */
+    ssize_t dlen;
+    D2I_const unsigned char *data;
+    D2I_const unsigned char *p;
+    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 = (D2I_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 & 0xffff00) ? 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 %lu, in RR: "
+                    "%s%s%s IN TLSA %u %u %u ...",
+                    d->mdalg, (unsigned long) dlen,
+                    q, a, r, usage, selector, mtype);
+           ctx->flags &= ~FILTER_CTX_AGILITY_OK;
+           return (FILTER_RR_DROP);
+       }
+       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;
 
-       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 (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 */
 
-       /*
-        * 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;
+       /* 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);
+               return (FILTER_RR_DROP);
            }
-           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;
+           /* Also unusable if public key is malformed or unsupported */
+           k = X509_get_pubkey(x);
+           EVP_PKEY_free(k);
+           if (k == 0) {
+               msg_warn("malformed %s in RR: %s%s%s IN TLSA %u %u %u ...",
+                        "or unsupported certificate public key",
+                        q, a, r, usage, selector, mtype);
+               X509_free(x);
+               return (FILTER_RR_DROP);
            }
-           digest = tls_digest_encode((unsigned char *) ip, di->len);
-           dane_add(dane, usage, selector, di->alg, digest);
-           break;
 
-       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);
-                   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;
-               }
-               X509_free(x);
-               break;
+           /*
+            * 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.
+            */
+           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, 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;
+       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);
            }
 
            /*
-            * The cert or key was valid, just digest the raw object, and
-            * encode the digest value.
+            * When a full trust-anchor public key 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_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 */
@@ -889,7 +1094,7 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
     if (why == 0)
        why = vstring_alloc(10);
 
-    dane = tls_dane_alloc(0);
+    dane = tls_dane_alloc();
     ret = dns_lookup(tlsa_fqdn, T_TLSA, RES_USE_DNSSEC, &rrs, 0, why);
 
     switch (ret) {
@@ -911,11 +1116,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:
@@ -954,8 +1160,6 @@ static TLS_DANE *resolve_host(const char *host, const char *proto,
     return (dane);
 }
 
-#endif
-
 /* tls_dane_resolve - cached map: (name, proto, port) -> TLS_DANE */
 
 TLS_DANE *tls_dane_resolve(unsigned port, const char *proto, DNS_RR *hostrr,
@@ -964,7 +1168,6 @@ TLS_DANE *tls_dane_resolve(unsigned port, const char *proto, DNS_RR *hostrr,
     TLS_DANE *dane = 0;
     int     iscname;
 
-#ifdef DANE_TLSA_SUPPORT
     if (!tls_dane_avail())
        return (dane);
 
@@ -978,7 +1181,7 @@ TLS_DANE *tls_dane_resolve(unsigned port, const char *proto, DNS_RR *hostrr,
      */
     iscname = strcasecmp(hostrr->rname, hostrr->qname);
     if (!forcetlsa && !hostrr->dnssec_valid && !iscname) {
-       dane = tls_dane_alloc(0);
+       dane = tls_dane_alloc();
        dane->flags = TLS_DANE_FLAG_NORRS;
     } else {
 
@@ -998,7 +1201,6 @@ TLS_DANE *tls_dane_resolve(unsigned port, const char *proto, DNS_RR *hostrr,
        }
     }
 
-#endif
     return (dane);
 }
 
@@ -1023,7 +1225,7 @@ int     tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
     if (!dane_initialized)
        dane_init();
 
-    if (serverAuth == 0) {
+    if (!ta_support) {
        msg_warn("trust-anchor files not supported");
        return (0);
     }
@@ -1045,7 +1247,7 @@ int     tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
     for (tacount = 0;
         errtype == 0 && PEM_read_bio(bp, &name, &header, &data, &len);
         ++tacount) {
-       const unsigned char *p = data;
+       D2I_const unsigned char *p = data;
        int     usage = DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION;
        int     selector;
        char   *digest;
@@ -1116,12 +1318,10 @@ int     tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
     TLS_TLSA *tlsa = (usage == TLS_DANE_EE) ? dane->ee : dane->ta;
     const char *namaddr = TLScontext->namaddr;
     const char *ustr = (usage == TLS_DANE_EE) ? "end entity" : "trust anchor";
-    int     mixed = (dane->flags & TLS_DANE_FLAG_MIXED);
     int     matched;
 
     for (matched = 0; tlsa && !matched; tlsa = tlsa->next) {
        char  **dgst;
-       ARGV   *certs;
 
        /*
         * Note, set_trust() needs to know whether the match was for a pkey
@@ -1143,26 +1343,10 @@ int     tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
                         namaddr, depth, ustr, tlsa->mdalg, pkey_dgst);
            myfree(pkey_dgst);
        }
-
-       /*
-        * Backwards compatible "fingerprint" security level interface:
-        * 
-        * Certificate digests and public key digests are interchangeable, each
-        * leaf certificate is matched via either the public key digest or
-        * full certificate digest when "mixed" is true.  The combined set of
-        * digests is stored on the pkeys digest list and the certs list is
-        * empty.  An attacker would need a 2nd-preimage (not just a
-        * collision) that is feasible across types (given cert digest ==
-        * some key digest) while difficult within a type (e.g. given cert
-        * some other cert digest).  No such attacks are know at this time,
-        * and it is expected that if any are found they would work within as
-        * well as across the cert/key data types.
-        */
-       certs = mixed ? tlsa->pkeys : tlsa->certs;
-       if (certs != 0 && !matched) {
+       if (tlsa->certs != 0 && !matched) {
            char   *cert_dgst = tls_cert_fprint(cert, tlsa->mdalg);
 
-           for (dgst = certs->argv; !matched && *dgst; ++dgst)
+           for (dgst = tlsa->certs->argv; !matched && *dgst; ++dgst)
                if (strcasecmp(cert_dgst, *dgst) == 0)
                    matched = MATCHED_CERT;
            if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
@@ -1176,24 +1360,30 @@ int     tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
     return (matched);
 }
 
+/* push_ext - push extension onto certificate's stack, else free it */
+
+static int push_ext(X509 *cert, X509_EXTENSION *ext)
+{
+    x509_extension_stack_t *exts;
+
+    if (ext) {
+       if ((exts = cert->cert_info->extensions) == 0)
+           exts = cert->cert_info->extensions = sk_X509_EXTENSION_new_null();
+       if (exts && sk_X509_EXTENSION_push(exts, ext))
+           return 1;
+       X509_EXTENSION_free(ext);
+    }
+    return 0;
+}
+
 /* add_ext - add simple extension (no config section references) */
 
 static int add_ext(X509 *issuer, X509 *subject, int ext_nid, char *ext_val)
 {
     X509V3_CTX v3ctx;
-    X509_EXTENSION *ext;
-    x509_extension_stack_t *exts;
 
     X509V3_set_ctx(&v3ctx, issuer, subject, 0, 0, 0);
-    if ((exts = subject->cert_info->extensions) == 0)
-       exts = subject->cert_info->extensions = sk_X509_EXTENSION_new_null();
-
-    if ((ext = X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val)) != 0
-       && sk_X509_EXTENSION_push(exts, ext))
-       return (1);
-    if (ext)
-       X509_EXTENSION_free(ext);
-    return (0);
+    return push_ext(subject, X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val));
 }
 
 /* set_serial - set serial number to match akid or use subject's plus 1 */
@@ -1226,6 +1416,7 @@ static int add_akid(X509 *cert, AUTHORITY_KEYID *akid)
 {
     ASN1_STRING *id;
     unsigned char c = 0;
+    int     nid = NID_authority_key_identifier;
     int     ret = 0;
 
     /*
@@ -1242,7 +1433,7 @@ static int add_akid(X509 *cert, AUTHORITY_KEYID *akid)
     if ((akid = AUTHORITY_KEYID_new()) != 0
        && (akid->keyid = ASN1_OCTET_STRING_new()) != 0
        && M_ASN1_OCTET_STRING_set(akid->keyid, (void *) &c, 1)
-       && X509_add1_ext_i2d(cert, NID_authority_key_identifier, akid, 0, 0))
+       && X509_add1_ext_i2d(cert, nid, akid, 0, X509V3_ADD_DEFAULT) > 0)
        ret = 1;
     if (akid)
        AUTHORITY_KEYID_free(akid);
@@ -1253,20 +1444,12 @@ static int add_akid(X509 *cert, AUTHORITY_KEYID *akid)
 
 static int add_skid(X509 *cert, AUTHORITY_KEYID *akid)
 {
-    int     ret;
-
-    if (akid && akid->keyid) {
-       VSTRING *hexid = vstring_alloc(2 * EVP_MAX_MD_SIZE);
-       ASN1_STRING *id = (ASN1_STRING *) (akid->keyid);
+    int     nid = NID_subject_key_identifier;
 
-       hex_encode(hexid, (char *) M_ASN1_STRING_data(id),
-                  M_ASN1_STRING_length(id));
-       ret = add_ext(0, cert, NID_subject_key_identifier, STR(hexid));
-       vstring_free(hexid);
-    } else {
-       ret = add_ext(0, cert, NID_subject_key_identifier, "hash");
-    }
-    return (ret);
+    if (!akid || !akid->keyid)
+       return add_ext(0, cert, nid, "hash");
+    else
+       return X509_add1_ext_i2d(cert, nid, akid, 0, X509V3_ADD_DEFAULT) > 0;
 }
 
 /* akid_issuer_name - get akid issuer directory name */
@@ -1302,32 +1485,42 @@ static int set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid)
     return (X509_set_issuer_name(cert, X509_get_subject_name(cert)));
 }
 
-/* grow_chain - add certificate to chain */
+/* grow_chain - add certificate to trusted or untrusted chain */
 
-static void grow_chain(x509_stack_t **skptr, X509 *cert, ASN1_OBJECT *trust)
+static void grow_chain(TLS_SESS_STATE *TLScontext, int trusted, X509 *cert)
 {
-    if (!*skptr && (*skptr = sk_X509_new_null()) == 0)
+    x509_stack_t **xs = trusted ? &TLScontext->trusted : &TLScontext->untrusted;
+
+#define UNTRUSTED 0
+#define TRUSTED 1
+
+    if (!*xs && (*xs = sk_X509_new_null()) == 0)
        msg_fatal("out of memory");
     if (cert) {
-       if (trust && !X509_add1_trust_object(cert, trust))
+       if (trusted && !X509_add1_trust_object(cert, serverAuth))
            msg_fatal("out of memory");
        CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509);
-       if (!sk_X509_push(*skptr, cert))
+       if (!sk_X509_push(*xs, cert))
            msg_fatal("out of memory");
     }
 }
 
 /* wrap_key - wrap TA "key" as issuer of "subject" */
 
-static int wrap_key(TLS_SESS_STATE *TLScontext, int depth,
-                           EVP_PKEY *key, X509 *subject)
+static void wrap_key(TLS_SESS_STATE *TLScontext, int depth,
+                            EVP_PKEY *key, X509 *subject)
 {
-    int     ret = 1;
-    int     selfsigned = 0;
     X509   *cert = 0;
     AUTHORITY_KEYID *akid;
     X509_NAME *name = X509_get_issuer_name(subject);
-    X509_NAME *akid_name;
+
+    /*
+     * The subject name is never a NULL object unless we run out of memory.
+     * It may be an empty sequence, but the containing object always exists
+     * and its storage is owned by the certificate itself.
+     */
+    if (name == 0 || (cert = X509_new()) == 0)
+       msg_fatal("Out of memory");
 
     /*
      * Record the depth of the intermediate wrapper certificate, logged in
@@ -1339,65 +1532,53 @@ static int wrap_key(TLS_SESS_STATE *TLScontext, int depth,
            msg_info("%s: depth=%d chain is trust-anchor signed",
                     TLScontext->namaddr, depth);
     }
-
-    /*
-     * If key is NULL generate a self-signed root CA, with key "danekey",
-     * otherwise an intermediate CA signed by above.
-     */
-    if ((cert = X509_new()) == 0)
-       return (0);
-
     akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0);
-    if ((akid_name = akid_issuer_name(akid)) == 0
-       || X509_NAME_cmp(name, akid_name) == 0)
-       selfsigned = 1;
 
     ERR_clear_error();
 
-    /* CA cert valid for +/- 30 days */
+    /*
+     * If key is NULL generate a self-signed root CA, with key "signkey",
+     * otherwise an intermediate CA signed by above.
+     * 
+     * CA cert valid for +/- 30 days.
+     */
     if (!X509_set_version(cert, 2)
        || !set_serial(cert, akid, subject)
        || !X509_set_subject_name(cert, name)
        || !set_issuer_name(cert, akid)
        || !X509_gmtime_adj(X509_get_notBefore(cert), -30 * 86400L)
        || !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L)
-       || !X509_set_pubkey(cert, key ? key : danekey)
+       || !X509_set_pubkey(cert, key ? key : signkey)
        || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE")
-       || (key && !selfsigned && !add_akid(cert, akid))
+       || (key && !add_akid(cert, akid))
        || !add_skid(cert, akid)
-       || (wrap_signed
-           && (!X509_sign(cert, danekey, signmd)
-               || (key && !selfsigned
-                   && !wrap_key(TLScontext, depth + 1, 0, cert))))) {
-       msg_warn("error generating DANE wrapper certificate");
+       || (wrap_signed && !X509_sign(cert, signkey, signmd))) {
        tls_print_errors();
-       ret = 0;
+       msg_fatal("error generating DANE wrapper certificate");
     }
     if (akid)
        AUTHORITY_KEYID_free(akid);
-    if (ret) {
-       if (key && !selfsigned && wrap_signed)
-           grow_chain(&TLScontext->untrusted, cert, 0);
-       else
-           grow_chain(&TLScontext->trusted, cert, serverAuth);
-    }
+    if (key && wrap_signed) {
+       wrap_key(TLScontext, depth + 1, 0, cert);
+       grow_chain(TLScontext, UNTRUSTED, cert);
+    } else
+       grow_chain(TLScontext, TRUSTED, cert);
     if (cert)
        X509_free(cert);
-    return (ret);
 }
 
-/* wrap_cert - wrap "tacert" as issuer of "subject" */
+/* wrap_cert - wrap "tacert" as trust-anchor. */
 
-static int wrap_cert(TLS_SESS_STATE *TLScontext, int depth,
-                            X509 *tacert, X509 *subject)
+static void wrap_cert(TLS_SESS_STATE *TLScontext, X509 *tacert, int depth)
 {
-    int     ret = 1;
     X509   *cert;
     int     len;
     unsigned char *asn1;
     unsigned char *buf;
 
-    TLScontext->tadepth = depth;
+    if (TLScontext->tadepth < 0)
+       TLScontext->tadepth = depth + 1;
+
     if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
        msg_info("%s: depth=%d trust-anchor certificate",
                 TLScontext->namaddr, depth);
@@ -1405,10 +1586,9 @@ static int wrap_cert(TLS_SESS_STATE *TLScontext, int depth,
     /*
      * If the TA certificate is self-issued, use it directly.
      */
-    if (!wrap_signed
-       || X509_check_issued(tacert, tacert) == X509_V_OK) {
-       grow_chain(&TLScontext->trusted, tacert, serverAuth);
-       return (ret);
+    if (!wrap_signed || X509_check_issued(tacert, tacert) == X509_V_OK) {
+       grow_chain(TLScontext, TRUSTED, tacert);
+       return;
     }
     /* Deep-copy tacert by converting to ASN.1 and back */
     len = i2d_X509(tacert, NULL);
@@ -1418,22 +1598,20 @@ static int wrap_cert(TLS_SESS_STATE *TLScontext, int depth,
        msg_panic("i2d_X509 failed to encode TA certificate");
 
     buf = asn1;
-    cert = d2i_X509(0, (unsigned const char **) &buf, len);
+    cert = d2i_X509(0, (D2I_const unsigned char **) &buf, len);
     if (!cert || (buf - asn1) != len)
        msg_panic("d2i_X509 failed to decode TA certificate");
     myfree((char *) asn1);
 
-    grow_chain(&TLScontext->untrusted, cert, 0);
+    grow_chain(TLScontext, UNTRUSTED, cert);
 
-    /* Sign and wrap TA cert with internal "danekey" */
-    if (!X509_sign(cert, danekey, signmd)
-       || !wrap_key(TLScontext, depth + 1, danekey, cert)) {
-       msg_warn("error generating DANE wrapper certificate");
+    /* Sign and wrap TA cert with internal "signkey" */
+    if (!X509_sign(cert, signkey, signmd)) {
        tls_print_errors();
-       ret = 0;
+       msg_fatal("error generating DANE wrapper certificate");
     }
+    wrap_key(TLScontext, depth + 1, signkey, cert);
     X509_free(cert);
-    return (ret);
 }
 
 /* ta_signed - is certificate signed by a TLSA cert or pkey */
@@ -1458,8 +1636,8 @@ static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
            if ((pk = X509_get_pubkey(x->cert)) == 0)
                continue;
            /* Check signature, since some other TA may work if not this. */
-           if (X509_verify(cert, pk) > 0)
-               done = wrap_cert(TLScontext, depth + 1, x->cert, cert);
+           if ((done = (X509_verify(cert, pk) > 0)) != 0)
+               wrap_cert(TLScontext, x->cert, depth);
            EVP_PKEY_free(pk);
        }
     }
@@ -1480,10 +1658,15 @@ static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
      * ASN1 tag and length thus also excluding the unused bits field that is
      * logically part of the length).  However, some CAs have a non-standard
      * authority keyid, so we lose.  Too bad.
+     * 
+     * This may push errors onto the stack when the certificate signature is not
+     * of the right type or length, throw these away.
      */
     for (k = dane->pkeys; !done && k; k = k->next)
-       if (X509_verify(cert, k->pkey) > 0)
-           done = wrap_key(TLScontext, depth, k->pkey, cert);
+       if ((done = (X509_verify(cert, k->pkey) > 0)) != 0)
+           wrap_key(TLScontext, depth, k->pkey, cert);
+       else
+           ERR_clear_error();
 
     return (done);
 }
@@ -1533,7 +1716,7 @@ static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx)
        if (match) {
            switch (match) {
            case MATCHED_CERT:
-               wrap_cert(TLScontext, depth, ca, cert);
+               wrap_cert(TLScontext, ca, depth);
                break;
            case MATCHED_PKEY:
                if ((takey = X509_get_pubkey(ca)) == 0)
@@ -1548,7 +1731,7 @@ static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx)
            break;
        }
        /* Add untrusted ca. */
-       grow_chain(&TLScontext->untrusted, ca, 0);
+       grow_chain(TLScontext, UNTRUSTED, ca);
 
        /* Final untrusted self-signed element? */
        if (X509_check_issued(ca, ca) == X509_V_OK) {
@@ -1567,7 +1750,7 @@ static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx)
      */
     if (!cert || !ta_signed(TLScontext, cert, depth)) {
        /* Create empty trust list if null, else NOP */
-       grow_chain(&TLScontext->trusted, 0, 0);
+       grow_chain(TLScontext, TRUSTED, 0);
     }
     /* shallow free */
     if (in)
@@ -1596,11 +1779,12 @@ static int dane_cb(X509_STORE_CTX *ctx, void *app_ctx)
         * Empty untrusted chain, could be NULL, but then ABI check less
         * reliable, we may zero some other field, ...
         */
-       grow_chain(&TLScontext->untrusted, 0, 0);
-       if (tls_dane_match(TLScontext, TLS_DANE_TA, cert, 0))
-           grow_chain(&TLScontext->trusted, cert, serverAuth);
-       else
-           grow_chain(&TLScontext->trusted, 0, 0);
+       grow_chain(TLScontext, UNTRUSTED, 0);
+       if (tls_dane_match(TLScontext, TLS_DANE_TA, cert, 0)) {
+           TLScontext->tadepth = 0;
+           grow_chain(TLScontext, TRUSTED, cert);
+       } else
+           grow_chain(TLScontext, TRUSTED, 0);
     } else {
        set_trust(TLScontext, ctx);
     }
@@ -1621,10 +1805,10 @@ static int dane_cb(X509_STORE_CTX *ctx, void *app_ctx)
 
 void    tls_dane_set_callback(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext)
 {
-    if (!serverAuth || !TLS_DANE_HASTA(TLScontext->dane))
-       SSL_CTX_set_cert_verify_callback(ctx, 0, 0);
-    else
+    if (ta_support && TLS_DANE_HASTA(TLScontext->dane))
        SSL_CTX_set_cert_verify_callback(ctx, dane_cb, (void *) TLScontext);
+    else
+       SSL_CTX_set_cert_verify_callback(ctx, 0, 0);
 }
 
 #endif                                 /* USE_TLS */
index 40746ea55fb4af8f25cbb47ce8a079d5b8e07f1c..abd5ff7d42f6b35acc9809ac38424c99a6dd4363 100644 (file)
@@ -223,9 +223,6 @@ char   *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
      * we must include the SNI name in the session id.
      */
     if (props->dane) {
-       int     mixed = (props->dane->flags & TLS_DANE_FLAG_MIXED);
-
-       digest_object(&mixed);
        digest_dane(props->dane, ta);
 #if 0
        digest_dane(props->dane, ee);           /* See above */
index 4bd8b9b03ae5d566c243ef2d67f14fa1f5fff78a..65addd2eddcdd5b1a645ae7ff442d75b95d0cb97 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;
@@ -264,59 +265,72 @@ static const NAME_CODE protocol_table[] = {
 #define NAMEBUG(x)     #x, SSL_OP_##x
 static const LONG_NAME_MASK ssl_bug_tweaks[] = {
 
-#if defined(SSL_OP_MICROSOFT_SESS_ID_BUG)
-    NAMEBUG(MICROSOFT_SESS_ID_BUG),    /* 0x00000001L */
+#ifndef SSL_OP_MICROSOFT_SESS_ID_BUG
+#define SSL_OP_MICROSOFT_SESS_ID_BUG           0
 #endif
+    NAMEBUG(MICROSOFT_SESS_ID_BUG),
 
-#if defined(SSL_OP_NETSCAPE_CHALLENGE_BUG)
-    NAMEBUG(NETSCAPE_CHALLENGE_BUG),   /* 0x00000002L */
+#ifndef SSL_OP_NETSCAPE_CHALLENGE_BUG
+#define SSL_OP_NETSCAPE_CHALLENGE_BUG          0
 #endif
+    NAMEBUG(NETSCAPE_CHALLENGE_BUG),
 
-#if defined(SSL_OP_LEGACY_SERVER_CONNECT)
-    NAMEBUG(LEGACY_SERVER_CONNECT),    /* 0x00000004L */
+#ifndef SSL_OP_LEGACY_SERVER_CONNECT
+#define SSL_OP_LEGACY_SERVER_CONNECT           0
 #endif
+    NAMEBUG(LEGACY_SERVER_CONNECT),
 
-#if defined(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG)
-    NAMEBUG(NETSCAPE_REUSE_CIPHER_CHANGE_BUG), /* 0x00000008L */
-    "CVE-2010-4180", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
+#ifndef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+#define SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG 0
 #endif
+    NAMEBUG(NETSCAPE_REUSE_CIPHER_CHANGE_BUG),
+    "CVE-2010-4180", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
 
-#if defined(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG)
-    NAMEBUG(SSLREF2_REUSE_CERT_TYPE_BUG),      /* 0x00000010L */
+#ifndef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+#define SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG     0
 #endif
+    NAMEBUG(SSLREF2_REUSE_CERT_TYPE_BUG),
 
-#if defined(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
-    NAMEBUG(MICROSOFT_BIG_SSLV3_BUFFER),/* 0x00000020L  */
+#ifndef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
+#define SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER      0
 #endif
+    NAMEBUG(MICROSOFT_BIG_SSLV3_BUFFER),
 
-#if defined(SSL_OP_MSIE_SSLV2_RSA_PADDING)
-    NAMEBUG(MSIE_SSLV2_RSA_PADDING),   /* 0x00000040L */
-    "CVE-2005-2969", SSL_OP_MSIE_SSLV2_RSA_PADDING,
+#ifndef SSL_OP_MSIE_SSLV2_RSA_PADDING
+#define SSL_OP_MSIE_SSLV2_RSA_PADDING          0
 #endif
+    NAMEBUG(MSIE_SSLV2_RSA_PADDING),
+    "CVE-2005-2969", SSL_OP_MSIE_SSLV2_RSA_PADDING,
 
-#if defined(SSL_OP_SSLEAY_080_CLIENT_DH_BUG)
-    NAMEBUG(SSLEAY_080_CLIENT_DH_BUG), /* 0x00000080L */
+#ifndef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+#define SSL_OP_SSLEAY_080_CLIENT_DH_BUG                0
 #endif
+    NAMEBUG(SSLEAY_080_CLIENT_DH_BUG),
 
-#if defined(SSL_OP_TLS_D5_BUG)
-    NAMEBUG(TLS_D5_BUG),               /* 0x00000100L   */
+#ifndef SSL_OP_TLS_D5_BUG
+#define SSL_OP_TLS_D5_BUG                      0
 #endif
+    NAMEBUG(TLS_D5_BUG),
 
-#if defined(SSL_OP_TLS_BLOCK_PADDING_BUG)
-    NAMEBUG(TLS_BLOCK_PADDING_BUG),    /* 0x00000200L */
+#ifndef SSL_OP_TLS_BLOCK_PADDING_BUG
+#define SSL_OP_TLS_BLOCK_PADDING_BUG           0
 #endif
+    NAMEBUG(TLS_BLOCK_PADDING_BUG),
 
-#if defined(SSL_OP_TLS_ROLLBACK_BUG)
-    NAMEBUG(TLS_ROLLBACK_BUG),         /* 0x00000400L */
+#ifndef SSL_OP_TLS_ROLLBACK_BUG
+#define SSL_OP_TLS_ROLLBACK_BUG                        0
 #endif
+    NAMEBUG(TLS_ROLLBACK_BUG),
 
-#if defined(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)
-    NAMEBUG(DONT_INSERT_EMPTY_FRAGMENTS),      /* 0x00000800L */
+#ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+#define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS     0
 #endif
+    NAMEBUG(DONT_INSERT_EMPTY_FRAGMENTS),
 
-#if defined(SSL_OP_CRYPTOPRO_TLSEXT_BUG)
-    NAMEBUG(CRYPTOPRO_TLSEXT_BUG),     /* 0x80000000L */
+#ifndef SSL_OP_CRYPTOPRO_TLSEXT_BUG
+#define SSL_OP_CRYPTOPRO_TLSEXT_BUG            0
 #endif
+    NAMEBUG(CRYPTOPRO_TLSEXT_BUG),
     0, 0,
 };
 
@@ -328,17 +342,20 @@ static const LONG_NAME_MASK ssl_bug_tweaks[] = {
 #define NAME_SSL_OP(x) #x, SSL_OP_##x
 static const LONG_NAME_MASK ssl_op_tweaks[] = {
 
-#if defined(SSL_OP_LEGACY_SERVER_CONNECT)
-    NAME_SSL_OP(LEGACY_SERVER_CONNECT),
+#ifndef SSL_OP_LEGACY_SERVER_CONNECT
+#define SSL_OP_LEGACY_SERVER_CONNECT   0
 #endif
+    NAME_SSL_OP(LEGACY_SERVER_CONNECT),
 
-#if defined(SSL_OP_NO_TICKET)
-    NAME_SSL_OP(NO_TICKET),
+#ifndef SSL_OP_NO_TICKET
+#define SSL_OP_NO_TICKET               0
 #endif
+    NAME_SSL_OP(NO_TICKET),
 
-#if defined(SSL_OP_NO_COMPRESSION)
-    NAME_SSL_OP(NO_COMPRESSION),
+#ifndef SSL_OP_NO_COMPRESSION
+#define SSL_OP_NO_COMPRESSION          0
 #endif
+    NAME_SSL_OP(NO_COMPRESSION),
     0, 0,
 };
 
@@ -597,8 +614,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 +626,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,
@@ -934,7 +952,8 @@ long    tls_bug_bits(void)
 {
     long    bits = SSL_OP_ALL;         /* Work around all known bugs */
 
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L && \
+       OPENSSL_VERSION_NUMBER < 0x10000000L
     long    lib_version = SSLeay();
 
     /*
@@ -960,6 +979,10 @@ long    tls_bug_bits(void)
        bits &= ~long_name_mask_opt(VAR_TLS_BUG_TWEAKS, ssl_bug_tweaks,
                                    var_tls_bug_tweaks, NAME_MASK_ANY_CASE |
                                    NAME_MASK_NUMBER | NAME_MASK_WARN);
+#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
+       /* Not relevant to SMTP */
+       bits &= ~SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
+#endif
     }
 
     /*
@@ -988,17 +1011,14 @@ void    tls_print_errors(void)
     const char *data;
     int     line;
     int     flags;
-    unsigned long thread;
 
-    thread = CRYPTO_thread_id();
     while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
        ERR_error_string_n(err, buffer, sizeof(buffer));
        if (flags & ERR_TXT_STRING)
-           msg_warn("TLS library problem: %lu:%s:%s:%d:%s:",
-                    thread, buffer, file, line, data);
+           msg_warn("TLS library problem: %s:%s:%d:%s:",
+                    buffer, file, line, data);
        else
-           msg_warn("TLS library problem: %lu:%s:%s:%d:",
-                    thread, buffer, file, line);
+           msg_warn("TLS library problem: %s:%s:%d:", buffer, file, line);
     }
 }
 
index cb1476b8a44366ee320a0528c92332a1eaaa475d..1dfe266b38c30f4c34a366a289148a091f60206e 100644 (file)
 /*     int     export;
 /*     int     keylength;
 /* DESCRIPTION
-/*     This module maintains parameters for Diffie-Hellman key generation.
-/*
 /*     tls_tmp_rsa_cb() is a call-back routine for the
 /*     SSL_CTX_set_tmp_rsa_callback() function.
+/*
+/*     This implementation will generate only 512-bit ephemeral
+/*     RSA keys for export ciphersuites. It will log a warning in
+/*     all other usage contexts.
 /* LICENSE
 /* .ad
 /* .fi
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni.
 /*--*/
 
 /* System library. */
 
 #include <sys_defs.h>
+#include <msg.h>
 
 #ifdef USE_TLS
 
 
 /* tls_tmp_rsa_cb - call-back to generate ephemeral RSA key */
 
-RSA *tls_tmp_rsa_cb(SSL *unused_ssl, int unused_export, int keylength)
+RSA    *tls_tmp_rsa_cb(SSL *unused_ssl, int export, int keylength)
 {
     static RSA *rsa_tmp;
 
-    /* Code adapted from OpenSSL apps/s_cb.c */
+    /*
+     * We generate ephemeral RSA keys only for export ciphersuites.  In all
+     * other contexts use of ephemeral RSA keys violates the SSL/TLS
+     * protocol, and only takes place when applications ask for trouble and
+     * set the SSL_OP_EPHEMERAL_RSA option.  Postfix should never do that.
+     */
+    if (!export || keylength != 512) {
+       msg_warn("%sexport %d-bit ephemeral RSA key requested",
+                export ? "" : "non-", keylength);
+       return 0;
+    }
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+    if (rsa_tmp == 0) {
+       BIGNUM *e = BN_new();
 
+       if (e != 0 && BN_set_word(e, RSA_F4) && (rsa_tmp = RSA_new()) != 0)
+           if (!RSA_generate_key_ex(rsa_tmp, keylength, e, 0)) {
+               RSA_free(rsa_tmp);
+               rsa_tmp = 0;
+           }
+       if (e)
+           BN_free(e);
+    }
+#else
     if (rsa_tmp == 0)
        rsa_tmp = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
+#endif
+
     return (rsa_tmp);
 }
 
 #ifdef TEST
 
-int main(int unused_argc, char **unused_argv)
+#include <msg_vstream.h>
+
+int     main(int unused_argc, char *const argv[])
 {
-    tls_tmp_rsa_cb(0, 1, 512);
-    tls_tmp_rsa_cb(0, 1, 1024);
-    tls_tmp_rsa_cb(0, 1, 2048);
-    tls_tmp_rsa_cb(0, 0, 512);
+    RSA    *rsa;
+    int     ok;
+
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+
+    /* Export at 512-bits should work */
+    rsa = tls_tmp_rsa_cb(0, 1, 512);
+    ok = rsa != 0 && RSA_size(rsa) == 512 / 8;
+    ok = ok && PEM_write_RSAPrivateKey(stdout, rsa, 0, 0, 0, 0, 0);
+    tls_print_errors();
+
+    /* Non-export or unexpected bit length should fail */
+    ok = ok && tls_tmp_rsa_cb(0, 0, 512) == 0;
+    ok = ok && tls_tmp_rsa_cb(0, 1, 1024) == 0;
+
+    return ok ? 0 : 1;
 }
 
 #endif
index f6de965c25a41e60146f0a2100707bd56512c99a..279ba376d50f2a80e045bb005c07d35104e02f14 100644 (file)
@@ -183,8 +183,8 @@ static SSL_SESSION *get_server_session_cb(SSL *ssl, unsigned char *session_id,
     do { \
        buf = vstring_alloc(2 * (len + strlen(service))); \
        hex_encode(buf, (char *) (id), (len)); \
-       vstring_sprintf_append(buf, "&s=%s", (service)); \
-       vstring_sprintf_append(buf, "&l=%ld", (long) SSLeay()); \
+       vstring_sprintf_append(buf, "&s=%s", (service)); \
+       vstring_sprintf_append(buf, "&l=%ld", (long) SSLeay()); \
     } while (0)
 
 
@@ -797,7 +797,7 @@ TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props)
 
 TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
 {
-    const SSL_CIPHER *cipher;
+    SSL_CIPHER_const SSL_CIPHER *cipher;
     X509   *peer;
     char    buf[CCERT_BUFSIZ];
 
index c5dbe066333a4d49b41e7d720031837a2344ace4..e3889ac24b6c85b91e9b04947c0f249d352cab64 100644 (file)
 /*     addresses that have no ".domain" information.
 /* .IP "\fBrecipient_delimiter (empty)\fR"
 /*     The set of characters that can separate a user name from its
-/*     address extension (user+foo).
+/*     extension (example: user+foo), or a .forward file name from its
+/*     extension (example: .forward+foo).
 /* .IP "\fBswap_bangpath (yes)\fR"
 /*     Enable the rewriting of "site!user" into "user@site".
 /* .PP
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))