]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3-20060629
authorWietse Venema <wietse@porcupine.org>
Thu, 29 Jun 2006 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:32:21 +0000 (06:32 +0000)
259 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/Makefile.in
postfix/README_FILES/AAAREADME
postfix/README_FILES/CONTENT_INSPECTION_README
postfix/README_FILES/FILTER_README
postfix/README_FILES/MILTER_README [new file with mode: 0644]
postfix/README_FILES/TLS_README
postfix/RELEASE_NOTES
postfix/TLS_TODO
postfix/auxiliary/qshape/qshape.pl
postfix/conf/access
postfix/conf/postfix-files
postfix/examples/smtpd-policy/postfix-policyd-spf.pl [moved from postfix/examples/smtpd-policy/spf.pl with 75% similarity, mode: 0755]
postfix/html/CONTENT_INSPECTION_README.html
postfix/html/FILTER_README.html
postfix/html/MILTER_README.html [new file with mode: 0644]
postfix/html/TLS_README.html
postfix/html/access.5.html
postfix/html/cleanup.8.html
postfix/html/index.html
postfix/html/postconf.5.html
postfix/html/qshape.1.html
postfix/html/smtp.8.html
postfix/html/smtpd.8.html
postfix/html/spawn.8.html
postfix/html/tlsmgr.8.html
postfix/man/man5/access.5
postfix/man/man5/postconf.5
postfix/man/man8/cleanup.8
postfix/man/man8/smtp.8
postfix/man/man8/smtpd.8
postfix/man/man8/tlsmgr.8
postfix/mantools/postlink
postfix/mantools/xpostdef
postfix/proto/CONTENT_INSPECTION_README.html
postfix/proto/FILTER_README.html
postfix/proto/MILTER_README.html [new file with mode: 0644]
postfix/proto/Makefile.in
postfix/proto/TLS_README.html
postfix/proto/access
postfix/proto/postconf.proto
postfix/proto/stop
postfix/src/anvil/anvil.c
postfix/src/bounce/bounce.c
postfix/src/bounce/bounce_cleanup.c
postfix/src/bounce/bounce_notify_verp.c
postfix/src/cleanup/Makefile.in
postfix/src/cleanup/cleanup.c
postfix/src/cleanup/cleanup.h
postfix/src/cleanup/cleanup_addr.c
postfix/src/cleanup/cleanup_api.c
postfix/src/cleanup/cleanup_bounce.c
postfix/src/cleanup/cleanup_envelope.c
postfix/src/cleanup/cleanup_extracted.c
postfix/src/cleanup/cleanup_init.c
postfix/src/cleanup/cleanup_message.c
postfix/src/cleanup/cleanup_milter.c [new file with mode: 0644]
postfix/src/cleanup/cleanup_milter.in1 [new file with mode: 0644]
postfix/src/cleanup/cleanup_milter.ref1 [new file with mode: 0644]
postfix/src/cleanup/cleanup_out.c
postfix/src/cleanup/cleanup_state.c
postfix/src/cleanup/test-queue-file [new file with mode: 0755]
postfix/src/discard/discard.c
postfix/src/error/error.c
postfix/src/flush/flush.c
postfix/src/global/Makefile.in
postfix/src/global/bounce.c
postfix/src/global/cleanup_strerror.c
postfix/src/global/cleanup_strflags.c
postfix/src/global/cleanup_user.h
postfix/src/global/db_common.c
postfix/src/global/debug_peer.c
postfix/src/global/deliver_completed.c
postfix/src/global/deliver_pass.c
postfix/src/global/deliver_request.c
postfix/src/global/dict_ldap.c
postfix/src/global/dict_mysql.c
postfix/src/global/dict_pgsql.c
postfix/src/global/flush_clnt.c
postfix/src/global/input_transp.c
postfix/src/global/input_transp.h
postfix/src/global/mail_addr_find.c
postfix/src/global/mail_addr_map.c
postfix/src/global/mail_copy.c
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_queue.c
postfix/src/global/mail_stream.c
postfix/src/global/mail_version.h
postfix/src/global/maps.c
postfix/src/global/mark_corrupt.c
postfix/src/global/mime_state.c
postfix/src/global/mynetworks.c
postfix/src/global/own_inet_addr.c
postfix/src/global/pipe_command.c
postfix/src/global/post_mail.c
postfix/src/global/rec_type.c
postfix/src/global/rec_type.h
postfix/src/global/record.c
postfix/src/global/record.h
postfix/src/global/resolve_clnt.c
postfix/src/local/alias.c
postfix/src/local/command.c
postfix/src/local/dotforward.c
postfix/src/local/file.c
postfix/src/local/forward.c
postfix/src/local/include.c
postfix/src/local/local.c
postfix/src/local/mailbox.c
postfix/src/local/maildir.c
postfix/src/local/recipient.c
postfix/src/local/resolve.c
postfix/src/local/unknown.c
postfix/src/master/mail_flow.c
postfix/src/master/master_avail.c
postfix/src/master/master_ent.c
postfix/src/master/master_flow.c
postfix/src/master/master_listen.c
postfix/src/master/master_proto.c
postfix/src/master/master_sig.c
postfix/src/master/master_spawn.c
postfix/src/master/master_status.c
postfix/src/master/master_wakeup.c
postfix/src/master/multi_server.c
postfix/src/master/single_server.c
postfix/src/master/trigger_server.c
postfix/src/milter/.indent.pro [new symlink]
postfix/src/milter/Makefile.in [new file with mode: 0644]
postfix/src/milter/milter.c [new file with mode: 0644]
postfix/src/milter/milter.h [new file with mode: 0644]
postfix/src/milter/milter8.c [new file with mode: 0644]
postfix/src/milter/test-list [new file with mode: 0644]
postfix/src/milter/test-milter.c [new file with mode: 0644]
postfix/src/oqmgr/Makefile.in
postfix/src/oqmgr/qmgr_active.c
postfix/src/oqmgr/qmgr_deliver.c
postfix/src/oqmgr/qmgr_entry.c
postfix/src/oqmgr/qmgr_message.c
postfix/src/oqmgr/qmgr_move.c
postfix/src/oqmgr/qmgr_queue.c
postfix/src/oqmgr/qmgr_scan.c
postfix/src/oqmgr/qmgr_transport.c
postfix/src/pickup/pickup.c
postfix/src/pipe/pipe.c
postfix/src/postcat/postcat.c
postfix/src/postconf/extract.awk
postfix/src/postdrop/postdrop.c
postfix/src/qmgr/qmgr_active.c
postfix/src/qmgr/qmgr_deliver.c
postfix/src/qmgr/qmgr_entry.c
postfix/src/qmgr/qmgr_job.c
postfix/src/qmgr/qmgr_message.c
postfix/src/qmgr/qmgr_move.c
postfix/src/qmgr/qmgr_peer.c
postfix/src/qmgr/qmgr_queue.c
postfix/src/qmgr/qmgr_scan.c
postfix/src/qmgr/qmgr_transport.c
postfix/src/qmqpd/qmqpd.c
postfix/src/qmqpd/qmqpd.h
postfix/src/qmqpd/qmqpd_peer.c
postfix/src/sendmail/sendmail.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/levels.c
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_chat.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_reuse.c
postfix/src/smtp/smtp_sasl_glue.c
postfix/src/smtp/smtp_sasl_proto.c
postfix/src/smtp/smtp_session.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_chat.c
postfix/src/smtpd/smtpd_chat.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_milter.c [new file with mode: 0644]
postfix/src/smtpd/smtpd_milter.h [new file with mode: 0644]
postfix/src/smtpd/smtpd_peer.c
postfix/src/smtpd/smtpd_sasl_proto.c
postfix/src/smtpd/smtpd_state.c
postfix/src/smtpstone/smtp-sink.c
postfix/src/smtpstone/smtp-source.c
postfix/src/spawn/spawn.c
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_certkey.c
postfix/src/tls/tls_client.c
postfix/src/tls/tls_mgr.c
postfix/src/tls/tls_mgr.h
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_scache.c
postfix/src/tls/tls_scache.h
postfix/src/tls/tls_server.c
postfix/src/tls/tls_stream.c
postfix/src/tlsmgr/Makefile.in
postfix/src/tlsmgr/tlsmgr.c
postfix/src/trivial-rewrite/resolve.c
postfix/src/util/attr_clnt.c
postfix/src/util/ctable.c
postfix/src/util/dict.c
postfix/src/util/dict_db.c
postfix/src/util/dict_dbm.c
postfix/src/util/dict_nis.c
postfix/src/util/dict_open.c
postfix/src/util/dict_sdbm.c
postfix/src/util/dict_tcp.c
postfix/src/util/dir_forest.c
postfix/src/util/events.c
postfix/src/util/fifo_listen.c
postfix/src/util/fifo_trigger.c
postfix/src/util/fsspace.c
postfix/src/util/inet_addr_list.c
postfix/src/util/inet_addr_local.c
postfix/src/util/inet_proto.c
postfix/src/util/inet_trigger.c
postfix/src/util/mac_parse.c
postfix/src/util/match_list.c
postfix/src/util/match_ops.c
postfix/src/util/msg_syslog.c
postfix/src/util/msg_vstream.c
postfix/src/util/name_mask.c
postfix/src/util/name_mask.h
postfix/src/util/netstring.c
postfix/src/util/rand_sleep.c
postfix/src/util/sane_link.c
postfix/src/util/sane_rename.c
postfix/src/util/scan_dir.c
postfix/src/util/spawn_command.c
postfix/src/util/stream_connect.c
postfix/src/util/stream_send_fd.c
postfix/src/util/stream_trigger.c
postfix/src/util/sys_defs.h
postfix/src/util/timed_read.c
postfix/src/util/timed_wait.c
postfix/src/util/timed_write.c
postfix/src/util/unix_recv_fd.c
postfix/src/util/unix_send_fd.c
postfix/src/util/unix_trigger.c
postfix/src/util/valid_hostname.c
postfix/src/util/vstream.c
postfix/src/util/vstream_popen.c
postfix/src/util/watchdog.c
postfix/src/util/write_buf.c
postfix/src/virtual/mailbox.c
postfix/src/virtual/maildir.c
postfix/src/virtual/recipient.c
postfix/src/virtual/unknown.c
postfix/src/virtual/virtual.c
postfix/src/xsasl/xsasl_cyrus_client.c
postfix/src/xsasl/xsasl_cyrus_server.c
postfix/src/xsasl/xsasl_dovecot_server.c

index 61b229edd68a4f90de82c731c9d159a40ed4d62d..86d18924c44ffde4e21bd1272055edb5ca96ed0d 100644 (file)
 -THTABLE_INFO
 -TINET_ADDR_LIST
 -TINET_PROTO_INFO
+-TINT32_TYPE
 -TINTV
 -TINT_TABLE
 -TJMP_BUF_WRAPPER
 -TMASTER_STATUS
 -TMBLOCK
 -TMBOX
+-TMILTER
+-TMILTER8
+-TMILTERS
+-TMILTER_MSG_CONTEXT
 -TMIME_ENCODING
 -TMIME_INFO
 -TMIME_STACK
 -TSINGLE_SERVER
 -TSINK_COMMAND
 -TSINK_STATE
+-TSMFICTX
 -TSMTPD_CMD
 -TSMTPD_DEFER
 -TSMTPD_RBL_EXPAND_CONTEXT
 -TSTRING_LIST
 -TSTRING_TABLE
 -TSYS_EXITS_DETAIL
+-TTLSMGR_SCACHE
 -TTLS_PRNG_SEED_INFO
 -TTLS_PRNG_SRC
 -TTLS_SCACHE
 -Tregmatch_t
 -Tsasl_conn_t
 -Tsasl_secret_t
+-Tsize_t
+-Tssize_t
+-Ttls_client_init_props
+-Ttls_client_start_props
 -Ttls_info_t
+-Ttls_server_props
index f8b6f3d4e866dab996a02a7c4a29850a6e38d893..df09f5efdf9d6447f5ebd08862edc7d0b84afd77 100644 (file)
@@ -12071,6 +12071,10 @@ Apologies for any names omitted.
        to wonder why Postfix doesn't find all the database entries.
        File: global/db_common.c.
 
+       Moved SMTP/LMTP parameter initialization from global/mail_params.c
+       to the combined smtp/lmtp delivery agent. Added missing
+       lmtp parameters.
+
 20060328
 
        Feature: configurable chroot directive for the pipe(8)
@@ -12081,6 +12085,15 @@ Apologies for any names omitted.
        was left with the name of smtp_connection_cache_limit.
        Reported by Victor? File: src/global/mail_params.h.
 
+20060329
+
+       More extensible interface for TLS client/server library,
+       now passes property structures that combine all the relevant
+       parameters in one type-safe structure.
+
+       TLS session cache activity logging now takes place at TLS
+       log level 2 or greater.
+
 20060403
 
        Cleanup: made fcntl/flock handling consistent with respect
@@ -12148,11 +12161,56 @@ Apologies for any names omitted.
        null terminate the address before logging a warning. Reported
        by Kris Kennaway. File: global/tok822_parse.c.
 
+20060301-20060515
+
+       Sendmail 8 Milter support, distributed across the smtpd(8)
+       server for SMTP commands, and the cleanup(8) server for
+       content inspection and manipulation. The code supports all
+       requests to add/delete recipients, and to add/delete/replace
+       message headers, but does not yet support requests to replace
+       the message body.  See MILTER_README for more. Files:
+       smtpd/smtpd.c, smtpd/smtpd_milter.c, cleanup/cleanup_api.c,
+       cleanup/cleanup_envelope.c, cleanup/cleanup_extracted.c,
+       cleanup/cleanup_milter.c, milter/milter.c, milter/milter8.c.
+
+       That's 89 lines in smtpd, 1010 lines in cleanup, and 2449
+       lines of library support, comments not included.  
+
+       A simple test Milter application for use in regression tests
+       is in src/milter/test-milter.c. Queue file modifications are
+       tested with a driver at the end src/cleanup/cleanup_milter.c
+       that reads commands from a script.
+
+       To make debugging easier, uncomment the "#define msg_verbose
+       2" lines at the top of cleanup_milter.c or milter8.c. This
+       produces logging without making everything else verbose.
+
+20060510
+
+       Preliminary TLS_README and postconf(5) changes completed.
+
+       Added smtp_tls_policy_maps and smtp_tls_protocols features
+       to the smtp/lmtp client, changed smtp_tls_cipherlist to
+       only apply when TLS is mandatory.
+
+20060512
+
+       Destinations that share a common server may have distinct
+       TLS protocol and cipherlist requirements, with mandatory
+       TLS add the protocol and cipherlist values to the TLS session
+       lookup key.
+
 20060516
 
        Portability: __float80 alignment, by Albert Chin.  File:
        util/sys_defs.h.
 
+       Further testing of Milter support uncovered typos; a missing
+       null pointer test while cleaning up after content miltering;
+       the need for a workaround to not bounce+delete local
+       submission after it triggers a temporary reject Milter
+       action.
+
        Workaround: don't bounce+delete a local submission after
        it triggers a "reject 4.x.x" action in header/body_checks.
        This means an SMTP client now sees "queue file write error"
@@ -12163,6 +12221,29 @@ Apologies for any names omitted.
        problem. Victor Duchovni. Files: tls/tls_client.c,
        tls/tls_misc.c, tls/tls_server.c.
 
+       Added smtpd_tls_protocols parameter to complement
+       smtp_tls_protocols.
+
+20060517
+
+       The smtp_tls_policy_maps table now implements parent domain
+       matching for destinations that are bare domains (without
+       enclosin [] or optional :port suffix). This allows one to
+       set TLS policy for a domain and all sub-domains.
+
+20060519
+
+       The same parameter can bind to different variables in
+       different daemons, ignore the variable name when eliminating
+       duplicates in extract.awk.
+
+20060523
+
+       Improved handling of smtp_tls_protocols and smtpd_tls_protocols,
+       names now processed via name_mask(3) and canonicalized prior
+       to use in the SMTP/LMTP client TLS session lookup key. Also
+       simplifies the corresponding code in the TLS driver.
+
 20060524
 
        Cleanup: send ETRN command parameter when using check_policy
@@ -12175,6 +12256,19 @@ Apologies for any names omitted.
        domains without secondary MX records.  Joshua Goodall. File:
        smtpd/smtpd_check.c.
 
+20060601
+
+       Fixed default value of LMTP TLS client certificate parameters,
+       using the SMTP values as a default was wrong.
+
+20060603
+
+       Different transports may have different CAfile or CApath
+       settings. We need to add the transport name to the TLS
+       session lookup key so that sessions verified with one set
+       of trusted roots are not inadvertantly considered verified
+       for another.
+
 20060604
 
        Cleanup: minor fluff found with the BEAM source code analyzer.
@@ -12184,6 +12278,17 @@ Apologies for any names omitted.
 
 20060606
 
+       Safety: mail receiving daemons (smtpd, qmqpd) now pass
+       actual client name/addres/helo attributes in addition to
+       the attributes used for logging (xforward). This prevents
+       Milter applications from treating qmqpd mail as if it
+       originated locally, and prevents incorrect Milter decisions
+       after "postsuper -r". Files: smtpd/smtpd.c, qmqpd/qmqpd.c,
+       cleanup/cleanup_envelope.c, cleanup/cleanup_milter.c,
+       cleanup/cleanup_state.c, global/post_mail.c, *qmgr/qmgr_message.c,
+       *qmgr/qmgr_deliver.c, global/deliver_request.c,
+       global/deliver_pass.c, local/forward.c.
+
        Bugfix: qmgr panic after queue file corruption by Mailscanner.
        Files: *qmgr/qmgr_message.c.
 
@@ -12193,6 +12298,16 @@ Apologies for any names omitted.
        applications, the SMTP server now jumps back to the very
        start (the 220 phase) of an SMTP session.  File: smtpd/smtpd.c.
 
+20060606
+
+       Portability: Some systems no longer support the traditional
+       "sort +0 -2 +3".
+
+20060607
+
+       Portability: Found by BEAM static code analyzer. SSL options
+       (long) were stored as int.
+
 20060610
 
        Cleanup: XCLIENT and XFORWARD attribute values are now sent
@@ -12223,21 +12338,126 @@ Apologies for any names omitted.
        be mis-interpreted as an empty result set.  Fixes by Leandro
        Santi. File: global/dict_pgsql.c.
 
+20060612
+
+       Changed smtp security level parsing and level to name
+       convertion to use name_code(3).
+
+       Implemented new smtp_tls_security_level parameter, to replace
+       the unnecessarily complex smtp_use_tls, smtp_enforce_tls
+       and smtp_tls_enforce_peername parameters. The main.cf
+       security level settings are now consistent with the new
+       policy table.
+
+       The smtp_sasl_tls_verified_security_options feature is not
+       yet complete, added #ifdef SNAPSHOT and changed documentation
+       to delay introduction until Postfix 2.4.
+
+20060614
+
+       Merged in Victor's work including the new TLS policy table
+       and a complete set of configuration parameters for the LMTP
+       personality of the unified SMTP/LMTP client.
+
+       Allow mandatory TLS encryption with LMTP over UNIX-domain
+       sockets.
+
+       Safety: improved code to avoid I/O on connections after the
+       TLS handshake fails.
+
+20060615
+
+       Cosmetic patch for const strings. Stefan Huehner.
+
+       Other cosmetic changes, mainly whitespace.
+
+20060616
+
+       The qshape.pl script was updated for the pointer records
+       that were introduced to support message content modification
+       by Milter applications.
+
+20060620
+
+       Feature: Substantially better cipherlist specification
+       interface and support for anonymous ciphers when certificates
+       are not needed. The primary interface in main.cf and the
+       policy table selects one of 5 grades for mandatory TLS with
+       smtp(8) or lmtp(8) or for all TLS sessions with smtpd(8).
+       The levels are "high", "medium" (or better), "low" (or
+       better), "export" (or better) and "null".  The underlying
+       definitions of these levels are configurable, but users are
+       strongly encouraged to not change those definitions.
+
+20060626
+
+       Bugfix: the Milter reply syntax checker was off by one.
+       File: milter/milter8.c.
+
+       Workaround: disable SMTP connection cache lookup by server
+       IP address when the tls_per_site policy table is enabled.
+       This is a workaround for a shortcoming in the SMTP connection
+       cache implementation, which retrieves the server hostname
+       from the cached connection. Since this server name is not
+       obtained in a secure manner, it must not be allowed to
+       control the tls_per_site policy. File: smtp/smtp_reuse.c.
+
+20060627
+
+       Cleanup: mumble_mandatory_tls_mumble parameters renamed to
+       mumble_tls_mandatory_mumble; added _mandatory_ qualifier
+       to names of parameters that affect only mandatory TLS.
+
 Wish list:
 
+       In the SMTPD policy client (encode or strip) non-printable
+       non-ASCII in (TLS or all) attributes.
+
+       run real sendmail through test-milter and check the data
+       for bit-wise compatibility with Postfix.
+
+       Are transport:nexthop null fields the same as in the case
+       of default_transport etc. parameters?
+
+       Introduce the notion of required security level into smtpd(8)
+       just like with smtp(8): if the level is specified, ignore
+       the legacy boolean parameters.
+
+       Introduce structured API for tls_server_mumble() just like
+       with smtp(8): this eliminates ever-growing lists of arguments.
+
+       Cleanup: declare smtp_tls_levels[] in a header file, probably
+       one that is owned by the Postfix TLS library instead of
+       smtp(8).  Better, encapsulate the name to code conversion
+       as a Postfix TLS library service routine.
+
        With (non)delivery notifications, prepend an "Auto-Submitted:
        auto-replied" header, as per RFC 3834.
 
        Defer delivery when a SASL password exists but the server
        does not offer SASL authentication, as mail might otherwise
-       be bounced. Make this configurable so people can get the
-       old behavior.
+       be bounced. This may become an issue now that Postfix will
+       retry in plaintext after optional TLS fails. Make this
+       configurable so people can get the old behavior.
 
        Don't lose bits when converting st_dev into maildir file
        name. It's 64 bits on Linux. Found with the BEAM source
        code analyzer.
 
-       Do or don't introduce unknown_reverse_client_reject_code. 
+       Do or don't introduce unknown_reverse_client_reject_code.
+
+       mail_addr/rcpt_addr should be externalized as they are in
+       Sendmail. Likewise, addresses in add/delete requests should
+       be internalized before updating the queue file.
+
+       Check that UINT32 == in choice is ok (i.e. LP64 UNIX).
+
+       Fix milter_argv() so it does not forget how much memory it
+       has.
+
+       Tempfail when a Milter application wants content access,
+       while it is configured in an SMTP server that runs before
+       the smtpd_proxy filter.
 
        Don't send xforward attributes to every site that announces
        xforward support.
@@ -12265,10 +12485,11 @@ Wish list:
 
        Investigate what it would take to eliminate oqmgr, and to
        make the old behavior configurable in a unified queue
-       manager.
-       This would shave another 2.7 KLOC from the source footprint.
+       manager.  This would shave another 2.7 KLOC from the source
+       footprint.
 
-       Document the case folding strategy for match_list like features.
+       Document the case folding strategy for match_list like
+       features.
 
        Eliminate the (incoming,deferred)->active rename operation.
 
index 3d72b4de42fe3543117e04c3d5dce21855bf6fa1..e3d75e6ef6bdc9783a7c65e96cac69d50ec5060e 100644 (file)
@@ -1,7 +1,8 @@
 SHELL  = /bin/sh
 WARN    = -Wmissing-prototypes -Wformat
 OPTS   = 'CC=$(CC)'
-DIRS   = src/util src/global src/dns src/tls src/xsasl src/master src/postfix src/smtpstone \
+DIRS   = src/util src/global src/dns src/tls src/xsasl src/milter src/master \
+       src/postfix src/smtpstone \
        src/sendmail src/error src/pickup src/cleanup src/smtpd src/local \
        src/trivial-rewrite src/qmgr src/oqmgr src/smtp src/bounce \
        src/pipe src/showq src/postalias src/postcat src/postconf src/postdrop \
index 7ae0c4f8ba8fbe502f2bfa35c61c29c24309c044..86046121efb49fabb7d4bb271557c6ab42f86fdc 100644 (file)
@@ -25,7 +25,8 @@ C\bCo\bon\bnt\bte\ben\bnt\bt i\bin\bns\bsp\bpe\bec\bct\bti\bio\bon\bn
   * BACKSCATTER_README: Stopping backscatter mail
   * BUILTIN_FILTER_README: Built-in content inspection
   * FILTER_README: After-queue content filter
-  * SMTPD_PROXY_README: Before-queue content Filter
+  * SMTPD_PROXY_README: Before-queue content filter
+  * MILTER_README: Before-queue Milter applications
 
 S\bSM\bMT\bTP\bP R\bRe\bel\bla\bay\by a\ban\bnd\bd a\bac\bcc\bce\bes\bss\bs c\bco\bon\bnt\btr\bro\bol\bl
 
index aa7b64452d3f2c144604ea75aca24783a59e350a..a6a6fd87bc3725b7d92b99912b295c997ca89c8b 100644 (file)
@@ -6,7 +6,7 @@ one-line-at-a-time scanning before mail is queued, to heavy duty machinery that
 does sophisticated content analysis after mail is queued. Each approach serves
 a different purpose.
 
-b\bbu\bui\bil\blt\bt-\b-i\bin\bn,\b, l\bli\big\bgh\bht\bt-\b-w\bwe\bei\big\bgh\bht\bt,\b, r\bre\bea\bal\bl-\b-t\bti\bim\bme\be
+b\bbe\bef\bfo\bor\bre\be q\bqu\bue\beu\bue\be,\b, b\bbu\bui\bil\blt\bt-\b-i\bin\bn,\b, l\bli\big\bgh\bht\bt-\b-w\bwe\bei\big\bgh\bht\bt
     This method inspects mail BEFORE it is stored in the queue, and uses
     Postfix's built-in message header and message body inspection. Although the
     main purpose is to stop a specific flood of mail from worms or viruses, it
@@ -17,7 +17,7 @@ b\bbu\bui\bil\blt\bt-\b-i\bin\bn,\b, l\bli\big\bgh\bht\bt-\b-w\bwe\bei\big\bgh\bht\bt,\b, r\bre\bea\bal\bl
     below. Details are described in the BUILTIN_FILTER_README and
     BACKSCATTER_README documents.
 
-e\bex\bxt\bte\ber\brn\bna\bal\bl,\b, h\bhe\bea\bav\bvy\by-\b-w\bwe\bei\big\bgh\bht\bt,\b, n\bno\bot\bt r\bre\bea\bal\bl t\bti\bim\bme\be
+a\baf\bft\bte\ber\br q\bqu\bue\beu\bue\be,\b, e\bex\bxt\bte\ber\brn\bna\bal\bl,\b, h\bhe\bea\bav\bvy\by-\b-w\bwe\bei\big\bgh\bht\bt
     This method inspects mail AFTER it is stored in the queue, and uses
     standard protocols such as SMTP or "pipe to command and wait for exit
     status". After-queue inspection allows you to use content filters of
@@ -25,18 +25,26 @@ e\bex\bxt\bte\ber\brn\bna\bal\bl,\b, h\bhe\bea\bav\bvy\by-\b-w\bwe\bei\big\bgh\bht\bt,\b, n\bno\bot\bt r\b
     without running out of memory resources under a peak load. Details of this
     approach are in the FILTER_README document.
 
-e\bex\bxt\bte\ber\brn\bna\bal\bl,\b, m\bme\bed\bdi\biu\bum\bm-\b-w\bwe\bei\big\bgh\bht\bt,\b, r\bre\bea\bal\bl-\b-t\bti\bim\bme\be
-    This method inspects mail BEFORE it is stored in the queue, and uses the
-    SMTP protocol. Although this approach appears to be the more attractive
-    one, it really combines the worst of the other two. Because mail is
-    inspected before it is queued, content inspection software must finish in a
-    limited amount of time, and must run in a limited amount of memory. If
-    content inspection needs too much time then incoming mail deliveries will
-    time out, and if content inspection needs too much memory then software
-    will crash under a peak load. Before-queue inspection limits the peak load
-    that your system can handle, and limits the sophistication of the content
-    filter that you can use. Details are in the SMTPD_PROXY_README document.
-    This approach is available only with Postfix version 2.1 and later.
+b\bbe\bef\bfo\bor\bre\be q\bqu\bue\beu\bue\be,\b, e\bex\bxt\bte\ber\brn\bna\bal\bl,\b, m\bme\bed\bdi\biu\bum\bm-\b-w\bwe\bei\big\bgh\bht\bt
+    The following two methods inspect mail BEFORE it is stored in the queue.
+
+      * The first method uses the SMTP protocol, and is described in the
+        SMTPD_PROXY_README document. This approach is available with Postfix
+        version 2.1 and later.
+
+      * The second method uses the Sendmail 8 Milter protocol, and is described
+        in the MILTER_README document. This approach is available with Postfix
+        version 2.3 and later.
+
+    Although these approaches appear to be attractive, they have some serious
+    limitations that you need to be aware of. First, content inspection
+    software must finish in a limited amount of time; if content inspection
+    needs too much time then incoming mail deliveries will time out. Second,
+    content inspection software must run in a limited amount of memory; if
+    content inspection needs too much memory then software will crash under a
+    peak load. Before-queue inspection limits the peak load that your system
+    can handle, and limits the sophistication of the content filter that you
+    can use.
 
 The more sophisticated content filtering software is not built into Postfix for
 good reasons: writing an MTA requires different skills than writing a SPAM or
index c6246bc8eac8c743b7332f0a3caca5798933a5c4..3a70f3c2be805ab6d9470ed75c776ece57d284e0 100644 (file)
@@ -386,7 +386,7 @@ without sending `.' on the connection that injects mail back into Postfix.
             -o content_filter=
             -
     o
-    receive_override_options=no_unknown_recipient_checks,no_header_body_checks
+    receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
             -o smtpd_helo_restrictions=
             -o smtpd_client_restrictions=
             -o smtpd_sender_restrictions=
@@ -403,15 +403,23 @@ without sending `.' on the connection that injects mail back into Postfix.
     content filtering for mail from the content filter. This is required or
     else mail will stay in the content filtering loop.
 
-  * The "-o receive_override_options" overrides main.cf settings. It is
-    complementary to the options that are specified in main.cf:
+  * The "-o receive_override_options" overrides main.cf settings to avoid
+    duplicating work that was already done before the content filter. These
+    options are complementary to the options that are specified in main.cf:
 
-      o Disable attempts to find out if a recipient is unknown, and disable
-        header/body checks. This work was already done before the content
-        filter and repeating it would be wasteful.
+      o We specify "no_unknown_recipient_checks" to disable attempts to find
+        out if a recipient is unknown.
 
-      o Enable virtual alias expansion, canonical mappings, address
-        masquerading, and other address mappings.
+      o We specify "no_header_body_checks" to disable header/body checks.
+
+      o We specify "no_milters" to disable Milter applications (this option is
+        available only in Postfix 2.3 and later).
+
+      o We don't specify "no_address_mapping" here. This enables virtual alias
+        expansion, canonical mappings, address masquerading, and other address
+        mappings after the content filter. The main.cf setting of
+        "receive_override_options" disables these mappings before the content
+        filter.
 
     These receive override options are either implemented by the SMTP server
     itself, or they are passed on to the cleanup server.
diff --git a/postfix/README_FILES/MILTER_README b/postfix/README_FILES/MILTER_README
new file mode 100644 (file)
index 0000000..5a036a8
--- /dev/null
@@ -0,0 +1,486 @@
+P\bPo\bos\bst\btf\bfi\bix\bx b\bbe\bef\bfo\bor\bre\be-\b-q\bqu\bue\beu\bue\be M\bMi\bil\blt\bte\ber\br s\bsu\bup\bpp\bpo\bor\brt\bt
+
+-------------------------------------------------------------------------------
+
+I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
+
+Postfix version 2.3 introduces support for the Sendmail version 8 Milter (mail
+filter) protocol. This protocol is used by applications that run outside the
+MTA to inspect SMTP events (CONNECT, DISCONNECT), SMTP commands (HELO, MAIL
+FROM, etc.) as well as mail content. All this happens before mail is queued.
+
+The reason for adding Milter support to Postfix is that there exists a large
+collection of applications, not only to block unwanted mail, but also to verify
+authenticity (examples: SenderID+SPF and Domain keys) or to digitally sign mail
+(example: Domain keys). Having yet another MTA-specific version of all that
+software is a poor use of human and system resources.
+
+Postfix 2.3 implements all the requests of Sendmail version 8 Milter protocols
+up to version 4, except one: message body replacement. See, however, the
+limitations section at the end of this document.
+
+This document provides information on the following topics:
+
+  * How Milter applications plug into Postfix
+  * Building Milter applications
+  * Running Milter applications
+  * Configuring Postfix
+  * Workarounds
+  * Limitations
+
+H\bHo\bow\bw M\bMi\bil\blt\bte\ber\br a\bap\bpp\bpl\bli\bic\bca\bat\bti\bio\bon\bns\bs p\bpl\blu\bug\bg i\bin\bnt\bto\bo P\bPo\bos\bst\btf\bfi\bix\bx
+
+The Postfix Milter implementation uses two different lists of mail filters: one
+list of filters that are used for SMTP mail only, and one list of filters that
+are used for non-SMTP mail. The two lists have different capabilities, which is
+unfortunate. Avoiding this would require major restructuring of Postfix.
+
+  * The SMTP-only filters handle mail that arrives via the Postfix smtpd(8)
+    server. They are typically used to filter unwanted mail and to sign mail
+    from authorized SMTP clients. You specify SMTP-only Milter applications
+    with the smtpd_milters parameter as described in a later section. Mail that
+    arrives via the Postfix smtpd(8) server is not filtered by the non-SMTP
+    filters that are described next.
+
+  * The non-SMTP filters handle mail that arrives via the Postfix sendmail(1)
+    command-line or via the Postfix qmqpd(8) server. They are typically used to
+    digitally sign mail only. Although non-SMTP filters can be used to filter
+    unwanted mail, they have limitations compared to the SMTP-only filters. You
+    specify non-SMTP Milter applications with the non_smtpd_milters parameter
+    as described in a later section.
+
+For those who are familiar with the Postfix architecture, the figure below
+shows how Milter applications plug into Postfix. Names followed by a number are
+Postfix commands or server programs, while unnumbered names inside shaded areas
+represent Postfix queues. To avoid clutter, the path for local submission is
+simplified (the OVERVIEW document has a more complete description).
+
+               SMTP-only      non-SMTP
+                 filters       filters
+
+                     ^ |
+                     | v
+                                   ^ |
+                                   | |
+    Network ->   smtpd(8)          | |
+                                   | v
+
+                           \
+
+    Network ->   qmqpd(8)  -> cleanup(8) -> incoming
+
+                           /
+
+                pickup(8)
+
+                      :
+
+    Local   -> sendmail(1)
+
+B\bBu\bui\bil\bld\bdi\bin\bng\bg M\bMi\bil\blt\bte\ber\br a\bap\bpp\bpl\bli\bic\bca\bat\bti\bio\bon\bns\bs
+
+Milter applications have been written in C, JAVA and Perl, but this document
+deals with C applications only. For these, you need an object library that
+implements the Sendmail 8 Milter protocol. Postfix currently does not provide
+such a library, but Sendmail does.
+
+On some Linux and *BSD distributions, the Sendmail libmilter library is
+installed by default. With this, applications such as dk-milter and sid-milter
+build out of the box without requiring any tinkering:
+
+    $ g\bgz\bzc\bca\bat\bt d\bdk\bk-\b-m\bmi\bil\blt\bte\ber\br-\b-x\bx.\b.y\by.\b.z\bz.\b.t\bta\bar\br.\b.g\bgz\bz |\b| t\bta\bar\br x\bxf\bf -\b-
+    $ c\bcd\bd d\bdk\bk-\b-m\bmi\bil\blt\bte\ber\br-\b-x\bx.\b.y\by.\b.z\bz
+    $ m\bma\bak\bke\be
+    [...lots of output omitted...]
+
+On other platforms you have two options:
+
+  * Install the Sendmail libmilter object library and include files. On Linux
+    systems, libmilter may be provided by the sendmail-devel package. After
+    installing libmilter, build the Milter applications as described in the
+    preceding paragraph.
+
+  * Don't install the Sendmail libmilter library, but build the library from
+    Sendmail source code instead:
+
+        $ g\bgz\bzc\bca\bat\bt s\bse\ben\bnd\bdm\bma\bai\bil\bl-\b-x\bx.\b.y\by.\b.z\bz.\b.t\bta\bar\br.\b.g\bgz\bz |\b| t\bta\bar\br x\bxf\bf -\b-
+        $ c\bcd\bd s\bse\ben\bnd\bdm\bma\bai\bil\bl-\b-x\bx.\b.y\by.\b.z\bz
+        $ m\bma\bak\bke\be
+        [...lots of output omitted...]
+
+    After building your own libmilter library, follow the installation
+    instructions in the Milter application source distribution to specify the
+    location of the libmilter include files and object library. Typically,
+    these settings are configured in a file named sid-filter/Makefile.m4 or
+    similar:
+
+        APPENDDEF(`confINCDIRS', `-I/some/where/sendmail-x.y.z/include')
+        APPENDDEF(`confLIBDIRS', `-L/some/where/sendmail-x.y.z/obj.systemtype/
+        libmilter')
+
+    Then build the Milter application.
+
+R\bRu\bun\bnn\bni\bin\bng\bg M\bMi\bil\blt\bte\ber\br a\bap\bpp\bpl\bli\bic\bca\bat\bti\bio\bon\bns\bs
+
+To run a Milter application, see the documentation of the filter for options. A
+typical command looks like this:
+
+    $ /\b/s\bso\bom\bme\be/\b/w\bwh\bhe\ber\bre\be/\b/d\bdk\bk-\b-f\bfi\bil\blt\bte\ber\br -\b-p\bp i\bin\bne\bet\bt:\b:p\bpo\bor\brt\btn\bnu\bum\bmb\bbe\ber\br@\b@l\blo\boc\bca\bal\blh\bho\bos\bst\bt .\b..\b..\b.o\bot\bth\bhe\ber\br o\bop\bpt\bti\bio\bon\bns\bs.\b..\b..\b.
+
+C\bCo\bon\bnf\bfi\big\bgu\bur\bri\bin\bng\bg P\bPo\bos\bst\btf\bfi\bix\bx
+
+Like Sendmail, Postfix has a lot of configuration options that control how it
+talks to Milter applications. With the initial Postfix Milter protocol
+implementation, many options are global, that is, they apply to all Milter
+applications. Future Postfix versions may support per-Milter timeouts, per-
+Milter error handling, etc.
+
+Information in this section:
+
+  * SMTP-Only Milter applications
+  * Non-SMTP Milter applications
+  * Milter error handling
+  * Milter protocol version
+  * Milter protocol timeouts
+  * Sendmail macro emulation
+
+S\bSM\bMT\bTP\bP-\b-O\bOn\bnl\bly\by M\bMi\bil\blt\bte\ber\br a\bap\bpp\bpl\bli\bic\bca\bat\bti\bio\bon\bns\bs
+
+The SMTP-only Milter applications handle mail that arrives via the Postfix
+smtpd(8) server. They are typically used to filter unwanted mail, and to sign
+mail from authorized SMTP clients. Mail that arrives via the Postfix smtpd(8)
+server is not filtered by the non-SMTP filters that are described in the next
+section.
+
+You specify SMTP-only Milter applications (there can be more than one) with the
+smtpd_milters parameter. Each Milter application is identified by the name of
+its listening socket; other Milter configuration options will be discussed in
+later sections. Milter applications are applied in the order as specified, and
+the first Milter application that rejects a command will override the responses
+from other Milter applications.
+
+    /etc/postfix/main.cf:
+        # Milters for mail that arrives via the smtpd(8) server.
+        # See below for socket address syntax.
+        smtpd_milters = inet:localhost:portnumber ...other filters...
+
+The general syntax for listening sockets is as follows:
+
+    u\bun\bni\bix\bx:\b:pathname
+        Connect to the local UNIX-domain server that is bound to the specified
+        pathname. If the smtpd(8) or cleanup(8) process runs chrooted, an
+        absolute pathname is interpreted relative to the Postfix queue
+        directory.
+
+    i\bin\bne\bet\bt:\b:host:\b:port
+        Connect to the specified TCP port on the specified local or remote
+        host. The host and port can be specified in numeric or symbolic form.
+
+        Note: Postfix syntax differs from Milter syntax which has the form
+        i\bin\bne\bet\bt:\b:port@\b@host.
+
+N\bNo\bon\bn-\b-S\bSM\bMT\bTP\bP M\bMi\bil\blt\bte\ber\br a\bap\bpp\bpl\bli\bic\bca\bat\bti\bio\bon\bns\bs
+
+The non-SMTP Milter applications handle mail that arrives via the Postfix
+sendmail(1) command-line or via the Postfix qmqpd(8) server. They are typically
+used to digitally sign mail. Although non-SMTP filters can be used to filter
+unwanted mail, there are limitations as discussed later in this section. Mail
+that arrives via the Postfix smtpd(8) server is not filtered by the non-SMTP
+filters.
+
+You specify non-SMTP Milter applications with the non_smtpd_milters parameter.
+This parameter uses the same syntax as the smtpd_milters parameter in the
+previous section. As with the SMTP-only filters, you can specify more than one
+Milter application; they are applied in the order as specified, and the first
+Milter application that rejects a command will override the responses from the
+other applications.
+
+    /etc/postfix/main.cf:
+        # Milters for non-SMTP mail.
+        # See below for socket address syntax.
+        non_smtpd_milters = inet:localhost:portnumber ...other filters...
+
+There's one small complication when using Milter applications for non-SMTP
+mail: there is no SMTP session. To keep Milter applications happy, the Postfix
+cleanup(8) server actually has to simulate the SMTP client CONNECT and
+DISCONNECT events, and the SMTP client EHLO, MAIL FROM, RCPT TO and DATA
+commands.
+
+  * When new mail arrives via the sendmail(1) command line, the Postfix cleanup
+    (8) server pretends that the mail arrives with ESMTP from "localhost" with
+    IP address "127.0.0.1". The result is very similar to what happens with
+    command line submissions in Sendmail version 8.12 and later, although
+    Sendmail uses a different mechanism to achieve this result.
+
+  * When new mail arrives via the qmqpd(8) server, the Postfix cleanup(8)
+    server pretends that the mail arrives with ESMTP, and uses the QMQPD client
+    hostname and IP address.
+
+  * When old mail is re-injected into the queue with "postsuper -r", the
+    Postfix cleanup(8) server uses the same client information that was used
+    when the mail arrived as new mail.
+
+This generally works as expected, with only one exception: non-SMTP filters
+must not REJECT or TEMPFAIL simulated RCPT TO commands. When a
+non_smtpd_milters application REJECTs or TEMPFAILs a recipient, Postfix will
+report a configuration error, and mail will stay in the queue.
+
+None of this is a problem for mail filters that digitally sign mail.
+
+M\bMi\bil\blt\bte\ber\br e\ber\brr\bro\bor\br h\bha\ban\bnd\bdl\bli\bin\bng\bg
+
+The milter_default_action parameter specifies how Postfix handles Milter
+application errors. The default action is to respond with a temporary error
+status, so that the client will try again later. Specify "accept" if you want
+to receive mail as if the filter does not exist, and "reject" to reject mail
+with a permanent status.
+
+        # What to do in case of errors? Specify accept, reject, or tempfail.
+        milter_default_action = tempfail
+
+M\bMi\bil\blt\bte\ber\br p\bpr\bro\bot\bto\boc\bco\bol\bl v\bve\ber\brs\bsi\bio\bon\bn
+
+As Postfix is not built with the Sendmail libmilter library, you may need to
+configure the Milter protocol version that Postfix should use. The default
+version is 2.
+
+    milter_protocol = 2
+
+If the Postfix milter_protocol setting specifies a too low version, the
+libmilter library will log an error message like this:
+
+    application name: st_optionneg[xxxxx]: 0xyy does not fulfill action
+    requirements 0xzz
+
+The remedy is to increase the Postfix milter_protocol version number. See,
+however, the limitations section below for features that aren't supported by
+Postfix.
+
+If the Postfix milter_protocol setting specifies a too high version, the
+libmilter library simply hangs up without logging a warning, and you see a
+Postfix warning message like one of the following:
+
+    postfix/smtpd[21045]: warning: milter inet:host:port: can't read packet
+    header: Unknown error : 0
+    postfix/cleanup[15190]: warning: milter inet:host:port: can't read packet
+    header: Success
+
+The remedy is to lower the Postfix milter_protocol version number.
+
+M\bMi\bil\blt\bte\ber\br p\bpr\bro\bot\bto\boc\bco\bol\bl t\bti\bim\bme\beo\bou\but\bts\bs
+
+Postfix uses different time limits at different Milter protocol stages. The
+table shows wich timeouts are used and when (EOH = end of headers; EOM = end of
+message).
+
+     _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b 
+    |P\bPa\bar\bra\bam\bme\bet\bte\ber\br             |T\bTi\bim\bme\be l\bli\bim\bmi\bit\bt|P\bPr\bro\bot\bto\boc\bco\bol\bl s\bst\bta\bag\bge\be                 |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_connect_timeout|30s       |CONNECT                        |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_command_timeout|30s       |HELO, MAIL, RCPT, DATA, UNKNOWN|
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_content_timeout|300s      |HEADER, EOH, BODY, EOM         |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+
+Beware: 30s is not a lot for applications that do a lot of DNS lookups.
+However, if you increase the above timeouts too much, remote SMTP clients may
+hang up and mail may be delivered multiple times. This is an inherent problem
+with before-queue filtering.
+
+S\bSe\ben\bnd\bdm\bma\bai\bil\bl m\bma\bac\bcr\bro\bo e\bem\bmu\bul\bla\bat\bti\bio\bon\bn
+
+Postfix emulates a limited number of Sendmail macros, as shown in the table.
+Different macros are available at different SMTP protocol stages (EOM = end-of-
+message); their availability is not always the same as in Sendmail. See the
+workarounds section below for solutions.
+
+     _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b 
+    |N\bNa\bam\bme\be                |A\bAv\bva\bai\bil\bla\bab\bbi\bil\bli\bit\bty\by             |D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn               |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |i                   |DATA, EOM                |Queue ID                  |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |j                   |Always                   |value of myhostname       |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{auth_authen}       |MAIL, DATA, EOM          |SASL login name           |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{auth_author}       |MAIL, DATA, EOM          |SASL sender               |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{auth_type}         |MAIL, DATA, EOM          |SASL login method         |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{client_addr}       |Always                   |Client IP address         |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{client_connections}|CONNECT                  |Connection concurrency for|
+    |                    |                         |this client               |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |                    |                         |Client hostname, "unknown"|
+    |{client_name}       |Always                   |when lookup or            |
+    |                    |                         |verification fails        |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |                    |                         |Client name from reverse  |
+    |{client_ptr}        |CONNECT, HELO, MAIL, DATA|lookup, "unknown" when    |
+    |                    |                         |lookup fails              |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{cert_issuer}       |HELO, MAIL, DATA, EOM    |TLS client certificate    |
+    |                    |                         |issuer                    |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{cert_subject}      |HELO, MAIL, DATA, EOM    |TLS client certificate    |
+    |                    |                         |subject                   |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{cipher_bits}       |HELO, MAIL, DATA, EOM    |TLS session key size      |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{cipher}            |HELO, MAIL, DATA, EOM    |TLS cipher                |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{daemon_name}       |Always                   |value of                  |
+    |                    |                         |milter_macro_daemon_name  |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{mail_addr}         |MAIL                     |Sender address            |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{rcpt_addr}         |RCPT                     |Recipient address         |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |{tls_version}       |HELO, MAIL, DATA, EOM    |TLS protocol version      |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |v                   |Always                   |value of milter_macro_v   |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+
+Postfix sends specific sets of macros at different SMTP protocol stages. The
+sets are configured with the parameters as described in the table (EOM = end of
+message).
+
+     _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b 
+    |P\bPa\bar\bra\bam\bme\bet\bte\ber\br n\bna\bam\bme\be               |P\bPr\bro\bot\bto\boc\bco\bol\bl v\bve\ber\brs\bsi\bio\bon\bn|P\bPr\bro\bot\bto\boc\bco\bol\bl s\bst\bta\bag\bge\be |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_connect_macros        |2 or higher     |CONNECT        |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_helo_macros           |2 or higher     |HELO/EHLO      |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_mail_macros           |2 or higher     |MAIL FROM      |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_rcpt_macros           |2 or higher     |RCPT TO        |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_data_macros           |4 or higher     |DATA           |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_end_of_data_macros    |2 or higher     |EOM            |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |milter_unknown_command_macros|3 or higher     |unknown command|
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+
+W\bWo\bor\brk\bka\bar\bro\bou\bun\bnd\bds\bs
+
+Sendmail Milter applications were originally developed for the Sendmail version
+8 MTA, which has a different architecture than Postfix. The result is that some
+Milter applications make assumptions that aren't true in a Postfix environment.
+
+  * Some Milter applications log a warning that looks like this:
+
+        sid-filter[36540]: WARNING: sendmail symbol 'i' not available
+
+    And they may insert a message header with "unknown-msgid" like this:
+
+        X-SenderID: Sendmail Sender-ID Filter vx.y.z host.example.com <unknown-
+        msgid>
+
+    This happens because the Milter application expects that the queue ID is
+    known before the MTA accepts the MAIL FROM (sender) command. Postfix, on
+    the other hand, does not create a queue file until after Postfix accepts
+    the first valid RCPT TO (recipient) command. This queue file name must be
+    globally unique across multiple queue directories, so it cannot be chosen
+    until the file is actually created.
+
+    To work around the ugly message header from Milter applications, we add a
+    little code to the Milter source to look up the queue ID after Postfix
+    receives the end of the message.
+
+      o Edit the filter source file (typically named dk-filter/dk-filter.c or
+        similar).
+
+      o Look up the mlfi_eom() function and add code near the top shown as b\bbo\bol\bld\bd
+        text below:
+
+        sic = (Context) smfi_getpriv(ctx);
+        assert(sic != NULL);
+
+        /\b/*\b*
+        *\b**\b*  D\bDe\bet\bte\ber\brm\bmi\bin\bne\be t\bth\bhe\be j\bjo\bob\bb I\bID\bD f\bfo\bor\br l\blo\bog\bgg\bgi\bin\bng\bg.\b.
+        *\b*/\b/
+        i\bif\bf (\b(s\bsi\bic\bc-\b->\b>c\bct\btx\bx_\b_j\bjo\bob\bbi\bid\bd =\b==\b= 0\b0 |\b||\b| s\bst\btr\brc\bcm\bmp\bp(\b(s\bsi\bic\bc-\b->\b>c\bct\btx\bx_\b_j\bjo\bob\bbi\bid\bd,\b, M\bMS\bSG\bGI\bID\bDU\bUN\bNK\bKN\bNO\bOW\bWN\bN)\b) =\b==\b= 0\b0)\b) {\b{
+                c\bch\bha\bar\br *\b*j\bjo\bob\bbi\bid\bd =\b= s\bsm\bmf\bfi\bi_\b_g\bge\bet\bts\bsy\bym\bmv\bva\bal\bl(\b(c\bct\btx\bx,\b, "\b"i\bi"\b")\b);\b;
+                i\bif\bf (\b(j\bjo\bob\bbi\bid\bd !\b!=\b= 0\b0)\b)
+                        s\bsi\bic\bc-\b->\b>c\bct\btx\bx_\b_j\bjo\bob\bbi\bid\bd =\b= j\bjo\bob\bbi\bid\bd;\b;
+        }\b}
+
+    This does not remove the WARNING message, however.
+
+    With some Milter applications we can fix both the WARNING and the "unknown-
+    msgid" by postponing the call of mlfi_eoh() (or whatever routine logs the
+    WARNING) until the end of the message.
+
+      o Edit the filter source file (typically named sid-filter/sid-filter.c or
+        similar).
+
+      o Look up the smfilter table and replace mlfi_eoh (or whatever routine
+        logs the WARNING) by NULL.
+
+      o Look up the mlfi_eom() function and add code near the top that calls
+        mlfi_eoh() as shown by the b\bbo\bol\bld\bd text below:
+
+                assert(ctx != NULL);
+        #endif /* !DEBUG */
+
+                r\bre\bet\bt =\b= m\bml\blf\bfi\bi_\b_e\beo\boh\bh(\b(c\bct\btx\bx)\b);\b;
+                i\bif\bf (\b(r\bre\bet\bt !\b!=\b= S\bSM\bMF\bFI\bIS\bS_\b_C\bCO\bON\bNT\bTI\bIN\bNU\bUE\bE)\b)
+                        r\bre\bet\btu\bur\brn\bn r\bre\bet\bt;\b;
+
+    This works with sid-milter-0.2.10. Other Milter applications will dump core
+    when you do this.
+
+L\bLi\bim\bmi\bit\bta\bat\bti\bio\bon\bns\bs
+
+This section lists limitations of the Postfix Milter implementation. Some
+limitations will be removed disappear as support is extended over time. Of
+course the usual limitations of before-queue filtering will always apply. See
+the CONTENT_INSPECTION_README document for a discussion.
+
+  * Postfix currently supports only applications that speak the Sendmail 8
+    Milter protocol versions 2..4. Support for other protocol types or protocol
+    versions may be added later.
+
+  * For applications that are written in C, you need to use the Sendmail
+    libmilter library. A Postfix replacement may be provided in the future.
+
+  * There are TWO sets of mail filters: filters that are used for SMTP mail
+    only (specified with the smtpd_milters parameter), and filters for non-SMTP
+    mail (specified with the non_smtpd_milters parameter). The non-SMTP filters
+    are primarily for local submissions.
+
+  * When mail is filtered by non-SMTP filters, the Postfix cleanup(8) server
+    has to simulate the SMTP client CONNECT and DISCONNECT events, and the SMTP
+    client EHLO, MAIL FROM, RCPT TO and DATA commands. This works as expected,
+    with only one exception: non-SMTP filters must not REJECT or TEMPFAIL
+    simulated RCPT TO commands. When a non-SMTP filter REJECTs or TEMPFAILs a
+    recipient, Postfix will report a configuration error, and mail will stay in
+    the queue.
+
+  * Postfix currently does not apply content filters to mail that is forwarded
+    or aliased internally, or to mail that is generated internally such as
+    bounces or Postmaster notifications. This may be a problem when you want to
+    apply a signing Milter to such mail.
+
+  * When you use the before-queue content filter for incoming SMTP mail (see
+    SMTPD_PROXY_README), Milter applications have access only to the SMTP
+    command information; they have no access to the message header or body, and
+    cannot make modifications to the message or to the envelope.
+
+  * Postfix 2.3 does not support Milter requests to replace the message body.
+    Milter applications that request this unsupported operation will log a
+    warning like this:
+
+        application name: st_optionneg[134563840]: 0x3d does not fulfill action
+        requirements 0x1e
+
+    The solution is (to wait for) a Postfix version that supports the missing
+    functionality.
+
+  * Most Milter configuration options are global. Future Postfix versions may
+    support per-Milter timeouts, per-Milter error handling, etc.
+
index 474fc7577bccdd83bb2acb07c843a92afd2c09dc..868615078ff0f3bdf3799569ffb86750ef7fa310 100644 (file)
@@ -95,7 +95,7 @@ a\bap\bpp\bpr\bro\bop\bpr\bri\bia\bat\bte\be.\b.
             A\bAU\bUX\bXL\bLI\bIB\bBS\bS=\b="\b"-\b-R\bR/\b/u\bus\bsr\br/\b/l\blo\boc\bca\bal\bl/\b/l\bli\bib\bb -\b-L\bL/\b/u\bus\bsr\br/\b/l\blo\boc\bca\bal\bl/\b/l\bli\bib\bb -\b-l\bls\bss\bsl\bl -\b-l\blc\bcr\bry\byp\bpt\bto\bo"\b"
 
 If you need to apply other customizations (such as Berkeley DB databases,
-MySQL, PosgreSQL, LDAP or SASL), see the respective Postfix README documents,
+MySQL, PostgreSQL, LDAP or SASL), see the respective Postfix README documents,
 and combine their "make makefiles" instructions with the instructions above:
 
     % m\bma\bak\bke\be t\bti\bid\bdy\by # if you have left-over files from a previous build
@@ -125,10 +125,30 @@ Topics covered in this section:
 
 S\bSe\ber\brv\bve\ber\br-\b-s\bsi\bid\bde\be c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be a\ban\bnd\bd p\bpr\bri\biv\bva\bat\bte\be k\bke\bey\by c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
 
-In order to use TLS, the Postfix SMTP server needs a certificate and a private
-key. Both must be in "pem" format. The private key must not be encrypted,
-meaning: the key must be accessible without password. Both certificate and
-private key may be in the same file.
+In order to use TLS, the Postfix SMTP server generally needs a certificate and
+a private key. Both must be in "PEM" format. The private key must not be
+encrypted, meaning: the key must be accessible without password. Both
+certificate and private key may be in the same file, in which case the
+certificate file should be owned by "root" and not be readable by any other
+user. If the key is stored separately, this applies to the key file only, and
+the certificate file may be "world-readable".
+
+Public Internet MX hosts without certificates signed by a "reputable" CA must
+generate, and be prepared to present to most clients, a self-signed or private-
+CA signed certificate. The client will not be able to authenticate the server,
+but unless it is running Postfix 2.3 or similar software, it will still insist
+on a server certificate.
+
+For servers that are n\bno\bot\bt public Internet MX hosts, Postfix 2.3 supports
+configurations with no certificates. This entails the use of just the anonymous
+TLS ciphers, which are not supported by typical SMTP clients. Since such
+clients will not, as a rule, fall back to plain text after a TLS handshake
+failure, the server will be unable to receive email from most TLS enabled
+clients. To avoid accidental configurations with no certificates, Postfix 2.3
+enables certificate-less operation only when the administrator explicitly sets
+"smtpd_tls_cert_file = none". This ensures that new Postfix configurations with
+just "smtpd_use_tls = yes" added, will not accidentally run with no
+certificates.
 
 Both RSA and DSA certificates are supported. Typically you will only have RSA
 certificates issued by a commercial CA. In addition, the tools supplied with
@@ -139,8 +159,8 @@ certificate is preferred.
 
 In order for remote SMTP clients to check the Postfix SMTP server certificates,
 the CA certificate (in case of a certificate chain, all CA certificates) must
-be available. You should add these certificates to the server certificate, the
-server certificate first, then the issuing CA(s).
+be available. You should add any intermediate CA certificates to the server
+certificate: the server certificate first, then the intermediate CA(s).
 
 Example: the certificate for "server.dom.ain" was issued by "intermediate CA"
 which itself has a certificate issued by "root CA". Create the server.pem file
@@ -178,9 +198,15 @@ Their DSA counterparts:
         smtpd_tls_dcert_file = /etc/postfix/server-dsa.pem
         smtpd_tls_dkey_file = $smtpd_tls_dcert_file
 
+Postfix 2.3 and later, TLS without certificates for servers serving exclusively
+anonymous-cipher capable clients:
+
+    /etc/postfix/main.cf:
+        smtpd_tls_cert_file = none
+
 To verify a remote SMTP client certificate, the Postfix SMTP server needs to
 trust the certificates of the issuing certification authorities. These
-certificates in "pem" format can be stored in a single $smtpd_tls_CAfile or in
+certificates in "PEM" format can be stored in a single $smtpd_tls_CAfile or in
 multiple files, one CA per file in the $smtpd_tls_CApath directory. If you use
 a directory, don't forget to create the necessary "hash" links with:
 
@@ -196,15 +222,14 @@ files in the directory when the information is needed. Thus, the
 $smtpd_tls_CApath directory needs to be accessible inside the optional chroot
 jail.
 
-When you configure Postfix to request client certificates (by setting
-$smtpd_tls_ask_ccert = yes), any certificates in $smtpd_tls_CAfile are sent to
-the client, in order to allow it to choose an identity signed by a CA you
-trust. If no $smtpd_tls_CAfile is specified, no preferred CA list is sent, and
-the client is free to choose an identity signed by any CA. Many clients use a
-fixed identity regardless of the preferred CA list and you may be able to
-reduce TLS negotiation overhead by installing client CA certificates mostly or
-only in $smtpd_tls_CApath. In the latter case you need not specify a
-$smtpd_tls_CAfile.
+When you configure Postfix to request client certificates, any CA certificates
+in $smtpd_tls_CAfile are sent to the client, in order to allow it to choose an
+identity signed by a CA you trust. If no $smtpd_tls_CAfile is specified, no
+preferred CA list is sent, and the client is free to choose an identity signed
+by any CA. Many clients use a fixed identity regardless of the preferred CA
+list and you may be able to reduce TLS negotiation overhead by installing
+client CA certificates mostly or only in $smtpd_tls_CApath. In the latter case
+you need not specify a $smtpd_tls_CAfile.
 
 Note, that unless client certificates are used to allow greater access to TLS
 authenticated clients, it is best to not ask for client certificates at all, as
@@ -221,7 +246,7 @@ Example:
 S\bSe\ber\brv\bve\ber\br-\b-s\bsi\bid\bde\be T\bTL\bLS\bS a\bac\bct\bti\biv\bvi\bit\bty\by l\blo\bog\bgg\bgi\bin\bng\bg
 
 To get additional information about Postfix SMTP server TLS activity you can
-increase the loglevel from 0..4. Each logging level also includes the
+increase the log level from 0..4. Each logging level also includes the
 information that is logged at a lower logging level.
 
     0 Disable logging of TLS activity.
@@ -234,7 +259,7 @@ information that is logged at a lower logging level.
 
     4 Log hexadecimal and ASCII dump of complete transmission after STARTTLS
 
-Use loglevel 3 only in case of problems. Use of loglevel 4 is strongly
+Use log level 3 only in case of problems. Use of log level 4 is strongly
 discouraged.
 
 Example:
@@ -310,29 +335,27 @@ Additionally some MTAs (notably some versions of qmail) are unable to complete
 TLS negotiation when client certificates are requested, and abort the SMTP
 session. So this option is "off" by default. You will however need the
 certificate if you want to use certificate based relaying with, for example,
-the permit_tls_clientcerts feature.
+the permit_tls_clientcerts feature. A server that wants client certificates
+must first present its own certificate. While Postfix 2.3 by default offers
+anonymous ciphers to clients, these are automatically suppressed when the
+server is configured to ask for client certificates.
 
 Example:
 
     /etc/postfix/main.cf:
-        smtpd_tls_ask_ccert = no
-
-You may also decide to REQUIRE a remote SMTP client certificate before allowing
-TLS connections. This feature is included for completeness, and implies
-"smtpd_tls_ask_ccert = yes".
-
-Please be aware, that this will inhibit TLS connections without a proper client
-certificate and that it makes sense only when non-TLS submission is disabled
-(smtpd_enforce_tls = yes). Otherwise, clients could bypass the restriction by
-simply not using STARTTLS at all.
+        smtpd_use_tls = yes
+        smtpd_tls_ask_ccert = yes
 
-When TLS is not enforced, the connection will be handled as if only
-"smtpd_tls_ask_ccert = yes" is specified, and a warning is logged.
+When TLS is enforced you may also decide to REQUIRE a remote SMTP client
+certificate for all TLS connections, by setting "smtpd_tls_req_ccert = yes".
+This feature implies "smtpd_tls_ask_ccert = yes". When TLS is not enforced,
+"smtpd_tls_req_ccert = yes" is ignored and a warning is logged.
 
 Example:
 
     /etc/postfix/main.cf:
-        smtpd_tls_req_ccert = no
+        smtpd_enforce_tls = yes
+        smtpd_tls_req_ccert = yes
 
 A client certificate verification depth of 1 is sufficient if the certificate
 is directly issued by a CA listed in the CA file. The default value (5) should
@@ -443,24 +466,42 @@ Example:
 
 S\bSe\ber\brv\bve\ber\br-\b-s\bsi\bid\bde\be c\bci\bip\bph\bhe\ber\br c\bco\bon\bnt\btr\bro\bol\bls\bs
 
-To influence the Postfix SMTP server cipher selection scheme, you can give
-cipherlist string. A detailed description would go to far here; please refer to
-the OpenSSL documentation. If you don't know what to do with it, simply don't
-touch it and leave the (openssl-)compiled in default!
+The description below is for Postfix 2.3; for Postfix < 2.3 the
+smtpd_tls_cipherlist parameter specifies the acceptable ciphers as an explicit
+OpenSSL cipherlist.
 
-DO NOT USE " to enclose the string, specify just the string!!!
+The Postfix SMTP server supports 5 distinct cipher security levels as specified
+by the smtpd_tls_ciphers configuration parameter. The default value is "export"
+which is the only one appropriate for public MX hosts. On private MX hosts or
+MSAs one can further restrict the OpenSSL cipherlist selection.
 
-Example:
+By default anonymous ciphers are allowed, and automatically disabled when
+client certificates are requested. If clients are expected to always verify the
+server certificate you may want to exclude anonymous ciphers by setting
+"smtpd_tls_exclude_ciphers = aNULL". One can't force a client to check the
+server certificate, so excluding anonymous ciphers is generally unnecessary.
+
+For a server that is not a public Internet MX host, Postfix 2.3 supports
+configurations with no server certificates that use o\bon\bnl\bly\by the anonymous ciphers.
+This is enabled by explicitly setting "smtpd_tls_cert_file = none" and not
+specifying an smtpd_tls_dcert_file.
+
+Example: (MSA that requires TLS with reasonably secure ciphers)
 
     /etc/postfix/main.cf:
-        smtpd_tls_cipherlist = DEFAULT
+        smtpd_use_tls = yes
+        smtpd_enforce_tls = yes
+        smtpd_tls_cert_file = /etc/postfix/cert.pem
+        smtpd_tls_key_file = /etc/postfix/key.pem
+        smtpd_tls_ciphers = medium
+        smtpd_tls_exclude_ciphers = aNULL, MD5
 
 If you want to take advantage of ciphers with EDH, DH parameters are needed.
 Instead of using the built-in DH parameters for both 1024bit and 512bit, it is
-better to generate "own" parameters, since otherwise it would "pay" for a
+better to generate your own parameters, since otherwise it would "pay" for a
 possible attacker to start a brute force attack against parameters that are
-used by everybody. For this reason, the parameters chosen are already different
-from those distributed with other TLS packages.
+used by everybody. For this reason, the default parameters chosen by OpenSSL
+are already different from those distributed with other TLS packages.
 
 To generate your own set of DH parameters, use:
 
@@ -488,21 +529,59 @@ S\bSM\bMT\bTP\bP C\bCl\bli\bie\ben\bnt\bt s\bsp\bpe\bec\bci\bif\bfi\bic\bc s\bse\bet\btt\bti\bin\bng\bgs\b
 
 Topics covered in this section:
 
+  * TLS support in the LMTP delivery agent
   * Client-side certificate and private key configuration
   * Client-side TLS activity logging
   * Client-side TLS session cache
-  * Enabling TLS in the Postfix SMTP client
-  * Requiring TLS encryption
-  * Disabling server certificate verification
-  * Per-site TLS policies
-  * Closing a DNS loophole with  per-site TLS policies
+  * Client TLS limitations
+  * Client TLS security levels
+  * Disabling TLS in the SMTP/LMTP client
+  * Enabling TLS in the SMTP/LMTP client
+  * Mandating TLS encryption
+  * Mandating server certificate verification
+  * Secure server certificate verification
+  * Per-destination TLS policy
+  * Obsolete per-site TLS policy support
+  * Closing a DNS loophole with obsolete per-site TLS policies
   * Discovering servers that support TLS
   * Server certificate verification depth
   * Client-side cipher controls
   * Miscellaneous client controls
 
+T\bTL\bLS\bS s\bsu\bup\bpp\bpo\bor\brt\bt i\bin\bn t\bth\bhe\be L\bLM\bMT\bTP\bP d\bde\bel\bli\biv\bve\ber\bry\by a\bag\bge\ben\bnt\bt
+
+In Postfix 2.3, the smtp(8) and lmtp(8) delivery agents have been merged into a
+single dual-purpose program. As a result the lmtp(8) delivery agent is no
+longer the poor cousin of the more extensively used smtp(8). Specifically, as
+of Postfix 2.3, all the TLS features described below apply equally to SMTP and
+LMTP, after replacing the "smtp_" prefix of the each parameter name with
+"lmtp_".
+
+The LMTP delivery agent can communicate with LMTP servers listening on UNIX-
+domain sockets. When server certificate verification is enabled and the server
+is listening on a UNIX-domain socket, the $myhostname parameter is used to set
+the TLS verification nexthop and hostname. Note, opportunistic encryption of
+LMTP traffic over UNIX-domain sockets is futile. TLS is only useful in this
+context when it is mandatory, typically to allow at least one of the server or
+the client to authenticate the other. The "null" cipher grade may be
+appropriate in this context, when available on both client and server. The
+"null" ciphers provide authentication without encryption.
+
 C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be a\ban\bnd\bd p\bpr\bri\biv\bva\bat\bte\be k\bke\bey\by c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
 
+Do not configure client certificates unless you m\bmu\bus\bst\bt present client TLS
+certificates to one or more servers. Client certificates are not usually
+needed, and can cause problems in configurations that work well without them.
+The recommended setting is to let the defaults stand:
+
+        smtp_tls_cert_file =
+        smtp_tls_dcert_file =
+        smtp_tls_key_file =
+        smtp_tls_dkey_file =
+
+The best way to use the default settings is to comment out the above parameters
+in main.cf if present.
+
 During TLS startup negotiation the Postfix SMTP client may present a
 certificate to the remote SMTP server. The Netscape client is rather clever
 here and lets the user select between only those certificates that match CA
@@ -516,7 +595,7 @@ time, in which case the cipher used determines which certificate is presented.
 
 It is possible for the Postfix SMTP client to use the same key/certificate pair
 as the Postfix SMTP server. If a certificate is to be presented, it must be in
-"pem" format. The private key must not be encrypted, meaning: it must be
+"PEM" format. The private key must not be encrypted, meaning: it must be
 accessible without password. Both parts (certificate and private key) may be in
 the same file.
 
@@ -639,93 +718,608 @@ Example:
     /etc/postfix/main.cf:
         smtp_tls_session_cache_timeout = 3600s
 
-E\bEn\bna\bab\bbl\bli\bin\bng\bg T\bTL\bLS\bS i\bin\bn t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx S\bSM\bMT\bTP\bP c\bcl\bli\bie\ben\bnt\bt
+C\bCl\bli\bie\ben\bnt\bt T\bTL\bLS\bS l\bli\bim\bmi\bit\bta\bat\bti\bio\bon\bns\bs
+
+The security properties of TLS communication channels are application specific.
+While the TLS protocol can provide a confidential, tamper-resistant, mutually
+authenticated channel between client and server, not all of these security
+features are applicable to every communication.
+
+For example, while mutual TLS authentication between browsers and web servers
+is possible, it is not practical, or even useful, for web-servers that serve
+the public to verify the identity of every potential user. In practice, most
+HTTPS transactions are asymmetric: the browser verifies the HTTPS server's
+identity, but the user remains anonymous. Much of the security policy is up to
+the client. If the client chooses to not verify the server's name, the server
+is not aware of this. There are many interesting browser security topics, but
+we shall not dwell on them here. Rather, our goal is to understand the security
+features of TLS in conjunction with SMTP.
+
+An important SMTP-specific observation is that a public MX host is even more at
+the mercy of the SMTP client than is an HTTPS server. Not only can it not
+enforce due care in the client's use of TLS, but it cannot even enforce the use
+of TLS, because TLS support in SMTP clients is still the exception rather than
+the rule. One cannot, in practice, limit access to one's MX hosts to just TLS-
+enabled clients. Such a policy would result in a vast reduction in one's
+ability to communicate by email with the world at large.
+
+One may be tempted to try enforcing TLS for mail from specific sending
+organizations, but this, too, runs into obstacles. One such obstacle is that we
+don't know who is (allegedly) sending mail until we see the "MAIL FROM:" SMTP
+command, and at that point, if TLS is not already in use, a potentially
+sensitive sender address (and with SMTP PIPELINING one or more of the
+recipients) has (have) already been leaked in the clear. Another obstacle is
+that mail from the sender to the recipient may be forwarded, and the forwarding
+organization may not have any security arrangements with the final destination.
+Bounces also need to be protected. These can only be identified by the IP
+address and HELO name of the connecting client, and it is difficult to keep
+track of all the potential IP addresses or HELO names of the outbound email
+servers of the sending organization.
+
+Consequently, TLS security for mail delivery to public MX hosts is almost
+entirely the client's responsibility. The server is largely a passive enabler
+of TLS security, the rest is up to the client. While the 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.
+
+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.
+
+If clients instead attempted to verify the recipient domain name, an SMTP
+server for multiple domains would need to list all its email domain names in
+its certificate, and generate a new certificate each time a new domain were
+added. At least some CAs set fairly low limits (20 for one prominent CA) on the
+number of names that server certificates can contain. This approach is not
+consistent with current practice and does not scale.
+
+It is regrettably the case that TLS secure-channels (fully authenticated and
+immune to man-in-the-middle attacks) impose constraints on the sending and
+receiving sites that preclude ubiquitous deployment. One needs to manually
+configure this type of security for each destination domain, and in many cases
+implement non-default TLS policy table entries for additional domains hosted at
+a common secured destination. With Postfix 2.3, we make secure-channel
+configurations substantially easier to configure, but they will never be the
+norm. For the generic domain with which you have made no specific security
+arrangements, this security level is not a good fit.
+
+Given that strong authentication is not generally possible, and that verifiable
+certificates cost time and money, many servers that implement TLS use self-
+signed certificates or private CAs. This further limits the applicability of
+verified TLS on the public Internet.
+
+Historical note: while the documentation of these issues and many of the
+related features are new with Postfix 2.3, the issue was well understood before
+Postfix 1.0, when Lutz Jänicke was designing the first unofficial Postfix TLS
+patch. See his original post http://www.imc.org/ietf-apps-tls/mail-archive/
+msg00304.html and the first response http://www.imc.org/ietf-apps-tls/mail-
+archive/msg00305.html. The problem is not even unique to SMTP or even TLS,
+similar issues exist for secure connections via aliases for HTTPS and Kerberos.
+SMTP merely uses indirect naming (via MX records) more frequently.
+
+C\bCl\bli\bie\ben\bnt\bt T\bTL\bLS\bS s\bse\bec\bcu\bur\bri\bit\bty\by l\ble\bev\bve\bel\bls\bs
+
+The TLS security levels listed below are described in more detail in the
+sections that follow.
+
+n\bno\bon\bne\be
+    No TLS.
+m\bma\bay\by
+    Opportunistic TLS.
+e\ben\bnc\bcr\bry\byp\bpt\bt
+    Mandatory TLS encryption.
+v\bve\ber\bri\bif\bfy\by
+    Mandatory server certificate verification.
+s\bse\bec\bcu\bur\bre\be
+    Secure-channel TLS.
+
+D\bDi\bis\bsa\bab\bbl\bli\bin\bng\bg T\bTL\bLS\bS i\bin\bn t\bth\bhe\be S\bSM\bMT\bTP\bP/\b/L\bLM\bMT\bTP\bP c\bcl\bli\bie\ben\bnt\bt
+
+At the "none" TLS security level, TLS encryption is disabled. This is the
+default security level. With Postfix 2.3 and later, it can be configured
+explicitly by setting "smtp_tls_security_level = none".
+
+With Postfix 2.2 and earlier, or when smtp_tls_security_level is set to its
+default (backwards compatible) empty value, the appropriate configuration
+settings are "smtp_use_tls = no" and "smtp_enforce_tls = no". With either
+approach, TLS is not used even if supported by the server. For LMTP, use the
+corresponding "lmtp_" parameters.
+
+Per destination settings may override this default setting, in which case TLS
+is used selectively, only with destinations explicitly configured for TLS.
+
+You can disable TLS for a subset of destinations, while leaving it enabled for
+the rest. With the Postfix 2.3+ TLS policy table, specify the "none" security
+level. With the obsolete per-site table, specify the "NONE" keyword.
+
+O\bOp\bpp\bpo\bor\brt\btu\bun\bni\bis\bst\bti\bic\bc T\bTL\bLS\bS
+
+At the "may" TLS security level, TLS encryption is opportunistic. The SMTP
+transaction is encrypted if the STARTTLS ESMTP feature is supported by the
+server. Otherwise, messages are sent in the clear. With Postfix 2.3 and later,
+opportunistic TLS can be configured by setting "smtp_tls_security_level = may".
+
+Since sending in the clear is acceptable, demanding stronger than default TLS
+security merely reduces inter-operability. For this reason, Postfix 2.3 and
+later ignore the smtp_tls_mandatory_ciphers and smtp_tls_mandatory_protocols
+parameters at the "may" security level: all protocols are allowed, and "export"
+grade or better ciphers are used.
+
+With Postfix 2.2 and earlier, or when smtp_tls_security_level is set to its
+default (backwards compatible) empty value, the appropriate configuration
+settings are "smtp_use_tls = yes" and "smtp_enforce_tls = no". For LMTP use the
+corresponding "lmtp" parameters.
+
+With opportunistic TLS, mail delivery continues even if the server certificate
+is untrusted or bears the wrong name. Starting with Postfix 2.3, when the TLS
+handshake fails for an opportunistic TLS session, rather than give up on mail
+delivery, the transaction is retried with TLS disabled. Trying an unencrypted
+connection makes it possible to deliver mail to sites with non-interoperable
+server TLS implementations.
+
+Opportunistic encryption is never used for LMTP over UNIX-domain sockets. The
+communications channel is already confidential without TLS, so the only
+potential benefit of TLS is authentication. Do not configure opportunistic TLS
+for LMTP deliveries over UNIX-domain sockets. Only configure TLS for LMTP over
+UNIX-domain sockets at the encrypt security level or higher. Attempts to
+configure opportunistic encryption of LMTP sessions will be ignored with a
+warning written to the mail logs.
+
+You can enable opportunistic TLS just for selected destinations. With the
+Postfix 2.3+ TLS policy table, specify the "may" security level. With the
+obsolete per-site table, specify the "MAY" keyword.
+
+This is the most common security level for TLS protected SMTP sessions,
+stronger security is not generally available and, if needed, is typically only
+configured on a per-destination basis. See the section on TLS limitations
+above.
 
-By default, TLS is disabled in the Postfix SMTP client, so no difference to
-plain Postfix is visible. If you enable TLS, the Postfix SMTP client will send
-STARTTLS when TLS support is announced by the remote SMTP server.
+Example:
 
-When the server accepts the STARTTLS command, but the subsequent TLS handshake
-fails, and no other server is available, the Postfix SMTP client defers the
-delivery attempt, and the mail stays in the queue. After a handshake failure,
-the communications channel is in an indeterminate state and cannot be used for
-non-TLS deliveries.
+    /etc/postfix/main.cf:
+        smtp_tls_security_level = may
 
-Example:
+Postfix 2.2 syntax:
 
     /etc/postfix/main.cf:
         smtp_use_tls = yes
+        smtp_enforce_tls = no
+
+M\bMa\ban\bnd\bda\bat\bto\bor\bry\by T\bTL\bLS\bS e\ben\bnc\bcr\bry\byp\bpt\bti\bio\bon\bn
+
+At the "encrypt" TLS security level, messages are sent only over TLS encrypted
+sessions. The SMTP transaction is aborted unless the STARTTLS ESMTP feature is
+supported by the server. If no suitable servers are found, the message will be
+deferred. With Postfix 2.3 and later, mandatory TLS encryption can be
+configured by setting "smtp_tls_security_level = encrypt". Even though TLS
+encryption is always used, mail delivery continues if the server certificate is
+untrusted or bears the wrong name.
+
+At this security level and higher, the smtp_tls_mandatory_protocols and
+smtp_tls_mandatory_ciphers configuration parameters determine the list of
+sufficiently secure SSL protocol versions and the minimum cipher strength. If
+the protocol or cipher requirements are not met, the mail transaction is
+aborted. The documentation for these parameters includes useful
+interoperability and security guidelines.
+
+With Postfix 2.2 and earlier, or when smtp_tls_security_level is set to its
+default (backwards compatible) empty value, the appropriate configuration
+settings are "smtp_enforce_tls = yes" and "smtp_tls_enforce_peername = no". For
+LMTP use the corresponding lmtp_ parameters.
+
+Despite the potential for eliminating passive eavesdropping attacks, mandatory
+TLS encryption is not viable as a default security level for mail delivery to
+the public Internet. Most MX hosts do not support TLS at all, and some of those
+that do have broken implementations. On a host that delivers mail to the
+Internet, you should not configure mandatory TLS encryption as the default
+security level.
+
+You can enable mandatory TLS encryption just for specific destinations. With
+the Postfix 2.3+ TLS policy table, specify the "encrypt" security level. With
+the obsolete per-site table, specify the "MUST_NOPEERMATCH" keyword. While the
+obsolete approach still works with Postfix 2.3, it is strongly discouraged:
+users of Postfix 2.3+ should use the new TLS policy settings.
+
+Examples:
 
-R\bRe\beq\bqu\bui\bir\bri\bin\bng\bg T\bTL\bLS\bS e\ben\bnc\bcr\bry\byp\bpt\bti\bio\bon\bn
+In the example below, traffic to example.com and its sub-domains via the
+corresponding MX hosts always uses TLS. The protocol version will be "SSLv3" or
+"TLSv1" (the default setting of smtp_tls_mandatory_protocols excludes "SSLv2").
+Only high or medium strength (i.e. 128 bit or better) ciphers will be used by
+default for all "encrypt" security level sessions.
 
-You can ENFORCE the use of TLS, so that the Postfix SMTP client will not
-deliver mail over unencrypted connections. In this mode, the remote SMTP server
-hostname must match the information in the remote server certificate, and the
-server certificate must be issued by a CA that is trusted by the Postfix SMTP
-client. If the remote server certificate doesn't verify or the remote SMTP
-server hostname doesn't match, and no other server is available, the delivery
-attempt is deferred and the mail stays in the queue.
+    /etc/postfix/main.cf:
+        smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
 
-The remote SMTP server hostname is verified against all names provided as
-dNSNames in the SubjectAlternativeName. If no dNSNames are specified, the
-CommonName is checked. Verification may be turned off with the
-smtp_tls_enforce_peername option which is discussed below.
+    /etc/postfix/tls_policy:
+        example.com       encrypt
+        .example.com      encrypt
 
-Enforcing the use of TLS is useful if you know that you will only connect to
-servers that support RFC 2487 _and_ that present server certificates that meet
-the above requirements. An example would be a client only sends email to one
-specific mailhub that offers the necessary STARTTLS support.
+Postfix 2.2 syntax (no support for sub-domains without resorting to regexp
+tables). With Postfix 2.3+, do not use the obsolete per-site table.
 
-Example:
+    /etc/postfix/main.cf:
+        smtp_tls_per_site = hash:/etc/postfix/tls_per_site
+
+    /etc/postfix/tls_per_site:
+        example.com       MUST_NOPEERMATCH
+
+In the next example, secure message submission is configured via the MSA "
+[example.net]:587". TLS sessions are encrypted without authentication, because
+this MSA does not possess an acceptable certificate. This MSA is known to be
+capable of "TLSv1" and "high" grade ciphers, so these are selected via the
+policy table.
+
+N\bNo\bot\bte\be:\b: the policy table lookup key is the verbatim next-hop specification from
+the recipient domain, transport(5) table or relayhost parameter, with any
+enclosing square brackets and optional port. Take care to be consistent: the
+suffixes ":smtp" or ":25" or no port suffix result in different policy table
+lookup keys, even though they are functionally equivalent nexthop
+specifications. Use at most one of these forms for all destinations. Below, the
+policy table has multiple keys, just in case the transport table entries are
+not specified consistently.
 
     /etc/postfix/main.cf:
-        smtp_enforce_tls = yes
+        smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
 
-D\bDi\bis\bsa\bab\bbl\bli\bin\bng\bg s\bse\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be v\bve\ber\bri\bif\bfi\bic\bca\bat\bti\bio\bon\bn
+    /etc/services:
+        submission      587/tcp         msa             # mail message
+    submission
+
+    /etc/postfix/tls_policy:
+        [example.net]:587 encrypt protocols=TLSv1 ciphers=high
+        [example.net]:msa encrypt protocols=TLSv1 ciphers=high
+        [example.net]:submission encrypt protocols=TLSv1 ciphers=high
+
+Postfix 2.2 syntax:
+
+N\bNo\bot\bte\be:\b: Avoid policy lookups with the bare hostname (for example, "example.net").
+Instead, use the destination (for example, "[example.net]:587"), as the per-
+site table lookup key (a recipient domain or MX-enabled transport nexthop with
+no port suffix may look like a bare hostname, but is still a suitable
+destination). With Postfix 2.3+, do not use the obsolete per-site table; use
+the new policy table instead.
+
+    /etc/postfix/main.cf:
+        smtp_tls_per_site = hash:/etc/postfix/tls_per_site
 
-As of RFC 2487 the requirements for hostname checking for MTA clients are not
-set. When TLS is required (smtp_enforce_tls = yes), the option
-smtp_tls_enforce_peername can be set to "no" to disable strict remote SMTP
-server hostname checking. In this case, the mail delivery will proceed
-regardless of the CommonName etc. listed in the certificate.
+    /etc/postfix/tls_per_site:
+        [example.net]:587   MUST_NOPEERMATCH
+
+M\bMa\ban\bnd\bda\bat\bto\bor\bry\by s\bse\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be v\bve\ber\bri\bif\bfi\bic\bca\bat\bti\bio\bon\bn
+
+At the "verify" TLS security level, messages are sent only over TLS encrypted
+sessions for which server certificate verification succeeds. If no suitable
+servers are found, the message will be deferred. With Postfix 2.3 and later,
+mandatory server certificate verification can be configured by setting
+"smtp_tls_security_level = verify", the smtp_tls_verify_cert_match parameter
+can override the default "hostname" certificate match strategy. Fine-tuning the
+matching strategy is generally only appropriate for secure-channel
+destinations.
+
+With Postfix 2.2 and earlier, or when smtp_tls_security_level is set to its
+default (backwards compatible) empty value, the appropriate configuration
+settings are "smtp_enforce_tls = yes" and "smtp_tls_enforce_peername = yes".
+For LMTP use the corresponding lmtp_ parameters.
+
+If the server certificate chain is trusted (see smtp_tls_CAfile and
+smtp_tls_CApath), any DNS names in the SubjectAlternativeName certificate
+extension are used to verify the server name. If no DNS names are specified,
+the certificate CommonName is checked. If you want mandatory encryption without
+server certificate verification, see above.
 
 Despite the potential for eliminating "man-in-the-middle" and other attacks,
-mandatory certificate/peername verification is not viable as a default Internet
-mail delivery policy at this time. A significant fraction of TLS enabled MTAs
-uses self-signed certificates, or certificates that are signed by a private
-certificate authority. On a machine that delivers mail to the Internet, if you
-set smtp_enforce_tls = yes, you should probably also set
-smtp_tls_enforce_peername = no. You can use the per-site TLS policies (see
-below) to enable full peer verification for specific destinations that are
-known to have verifiable TLS server certificates.
+mandatory certificate trust chain and subject name verification is not viable
+as a default Internet mail delivery policy. Most MX hosts do not support TLS at
+all, and a significant portion of TLS enabled MTAs use self-signed
+certificates, or certificates that are signed by a private certificate
+authority. On a machine that delivers mail to the Internet, you should not
+configure mandatory server certificate verification as a default policy.
+
+Mandatory server certificate verification as a default security level may be
+appropriate if you know that you will only connect to servers that support RFC
+2487 and that present verifiable server certificates. An example would be a
+client that sends all email to a central mailhub that offers the necessary
+STARTTLS support. In such cases, you can often use a secure-channel
+configuration instead.
+
+You can enable mandatory server certificate verification just for specific
+destinations. With the Postfix 2.3+ TLS policy table, specify the "verify"
+security level. With the obsolete per-site table, specify the "MUST" keyword.
+While the obsolete approach still works with Postfix 2.3, it is strongly
+discouraged: users of Postfix 2.3+ should use the new TLS policy settings.
 
 Example:
 
+In this example, the client encrypts all traffic to the example.com domain. The
+peer hostname is verified, but verification is vulnerable to DNS response
+forgery. Mail transmission to example.com recipients uses "high" grade ciphers.
+
+    /etc/postfix/main.cf:
+        indexed = ${default_database_type}:${config_directory}/
+        smtp_tls_CAfile = ${config_directory}/CAfile.pem
+        smtp_tls_policy_maps = ${indexed}tls_policy
+
+    /etc/postfix/tls_policy:
+        example.com       verify ciphers=high
+
+Postfix 2.2 syntax:
+
+    /etc/postfix/main.cf:
+        indexed = ${default_database_type}:${config_directory}/
+        smtp_tls_CAfile = ${config_directory}/CAfile.pem
+        smtp_tls_per_site = ${indexed}tls_per_site
+
+    /etc/postfix/tls_per_site:
+        example.com         MUST
+
+S\bSe\bec\bcu\bur\bre\be s\bse\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be v\bve\ber\bri\bif\bfi\bic\bca\bat\bti\bio\bon\bn
+
+At the secure TLS security level, messages are sent only over secure-channel
+TLS sessions where DNS forgery resistant server certificate verification
+succeeds. If no suitable servers are found, the message will be deferred. With
+Postfix 2.3 and later, secure-channels can be configured by setting
+"smtp_tls_security_level = secure". The smtp_tls_secure_cert_match parameter
+can override the default "nexthop, dot-nexthop" certificate match strategy.
+
+With Postfix 2.2 and earlier, or when smtp_tls_security_level is set to its
+default (backwards compatible) empty value, the appropriate configuration
+settings are "smtp_enforce_tls = yes" and "smtp_tls_enforce_peername = yes"
+with additional settings to harden peer certificate verification against forged
+DNS data. For LMTP, use the corresponding lmtp_ parameters.
+
+If the server certificate chain is trusted (see smtp_tls_CAfile and
+smtp_tls_CApath), any DNS names in the SubjectAlternativeName certificate
+extension are used to verify the server name. If no DNS names are specified,
+the CommonName is checked. If you want mandatory encryption without server
+certificate verification, see above.
+
+Despite the potential for eliminating "man-in-the-middle" and other attacks,
+mandatory secure server certificate verification is not viable as a default
+Internet mail delivery policy. Most MX hosts do not support TLS at all, and a
+significant portion of TLS enabled MTAs use self-signed certificates, or
+certificates that are signed by a private certificate authority. On a machine
+that delivers mail to the Internet, you should not configure secure TLS
+verification as a default policy.
+
+Mandatory secure server certificate verification as a default security level
+may be appropriate if you know that you will only connect to servers that
+support RFC 2487 and that present verifiable server certificates. An example
+would be a client that sends all email to a central mailhub that offers the
+necessary STARTTLS support.
+
+You can enable secure TLS verification just for specific destinations. With the
+Postfix 2.3+ TLS policy table, specify the "secure" security level. With the
+obsolete per-site table, specify the "MUST" keyword and harden the certificate
+verification against DNS forgery. While the obsolete approach still works with
+Postfix 2.3, it is strongly discouraged: users of Postfix 2.3+ should use the
+new TLS policy settings.
+
+Examples:
+
+Secure-channel TLS without transport(5) table overrides:
+
+The client will encrypt all traffic and verify the destination name immune from
+forged DNS responses. MX lookups are still used to find the SMTP servers for
+example.com, but these are not used when checking the names in the server
+certificate(s). Rather, the requirement is that the MX hosts for example.com
+have trusted certificates with a subject name of example.com or a sub-domain,
+see the documentation for the smtp_tls_secure_cert_match parameter.
+
+The related domains example.co.uk and example.co.jp are hosted on the same MX
+hosts as the primary example.com domain, and traffic to these is secured by
+verifying the primary example.com domain in the server certificates. This frees
+the server administrator from needing the CA to sign certificates that list all
+the secondary domains. The downside is that clients that want secure channels
+to the secondary domains need explicit TLS policy table entries.
+
+Note, there are two ways to handle related domains. The first is to use the
+default routing for each domain, but add policy table entries to override the
+expected certificate subject name. The second is to override the next-hop in
+the transport table, and use a single policy table entry for the common
+nexthop. We choose the first approach, because it works better when domain
+ownership changes. With the second approach we securely deliver mail to the
+wrong destination, with the first approach, authentication fails and mail stays
+in the local queue, the first approach is more appropriate in most cases.
+
+    /etc/postfix/main.cf:
+        smtp_tls_CAfile = /etc/postfix/CAfile.pem
+        smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+
+    /etc/postfix/transport:
+
+    /etc/postfix/tls_policy:
+        example.com     secure
+        example.co.uk   secure match=example.com:.example.com
+        example.co.jp   secure match=example.com:.example.com
+
+Secure-channel TLS with transport(5) table overrides:
+
+In this case traffic to example.com and its related domains is sent to a single
+logical gateway (to avoid a single point of failure, its name may resolve to
+one or more load-balancer addresses, or to the combined addresses of multiple
+physical hosts). All the physical hosts reachable via the gateway's IP
+addresses have the logical gateway name listed in their certificates. This
+secure-channel configuration can also be implemented via a hardened variant of
+the MUST policy in the obsolete per-site table. As stated above, this approach
+has the potential to mis-deliver email if the related domains change hands.
+
     /etc/postfix/main.cf:
-        smtp_enforce_tls = yes
-        smtp_tls_enforce_peername = no
+        smtp_tls_CAfile = /etc/postfix/CAfile.pem
+        transport_maps = hash:/etc/postfix/transport
+        smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+
+    /etc/postfix/transport:
+        example.com     smtp:[tls.example.com]
+        example.co.uk   smtp:[tls.example.com]
+        example.co.jp   smtp:[tls.example.com]
+
+    /etc/postfix/tls_policy:
+        [tls.example.com] secure match=tls.example.com
+
+Postfix 2.2.9+ syntax:
+
+N\bNo\bot\bte\be:\b: Avoid policy lookups with the bare hostname (for example,
+"tls.example.com"). Instead, use the destination (for example, "
+[tls.example.com]") as the per-site table lookup key (a recipient domain or MX-
+enabled transport nexthop with no port suffix may look like a bare hostname,
+but is still a suitable destination). With Postfix 2.3+, do not use the
+obsolete per-site table; use the new policy table instead.
+
+    /etc/postfix/main.cf:
+        smtp_cname_overrides_servername = no
+        smtp_tls_CAfile = /etc/postfix/CAfile.pem
+        transport_maps = hash:/etc/postfix/transport
+        smtp_tls_per_site = hash:/etc/postfix/tls_per_site
 
-P\bPe\ber\br-\b-s\bsi\bit\bte\be T\bTL\bLS\bS p\bpo\bol\bli\bic\bci\bie\bes\bs
+    /etc/postfix/transport:
+        example.com     smtp:[tls.example.com]
+        example.co.uk   smtp:[tls.example.com]
+        example.co.jp   smtp:[tls.example.com]
+
+    /etc/postfix/tls_per_site:
+        [tls.example.com]       MUST
+
+T\bTL\bLS\bS p\bpo\bol\bli\bic\bcy\by t\bta\bab\bbl\ble\be
+
+Postfix 2.3 introduces a new more flexible TLS policy table. For earlier
+releases, read the description of the obsolete Postfix 2.2 per-site table.
 
 A small fraction of servers offer STARTTLS but the negotiation consistently
-fails, leading to mail aging out of the queue and bouncing back to the sender.
-In such cases, you can use the per-site policies to disable TLS for the problem
-sites. Alternatively, you can enable TLS for just a few specific sites and not
-enable it for all sites.
+fails. With Postfix 2.3, so long as encryption is not enforced, the delivery is
+immediately retried with TLS disabled. You no longer need to explicitly disable
+TLS for the problem destinations. As soon as their TLS software or
+configuration is repaired, encryption will be used.
+
+The new policy table is specified via the smtp_tls_policy_maps parameter. This
+lists optional lookup tables with the Postfix SMTP client TLS security policy
+by next-hop destination. It supersedes the obsolete smtp_tls_per_site
+parameter. When $smtp_tls_policy_maps is not empty, the smtp_tls_per_site
+parameter is ignored (a warning is written to the logs if it is also non-
+empty).
+
+The TLS policy table is indexed by the full next-hop destination, which is
+either the recipient domain, or the verbatim next-hop specified in the
+transport table, $local_transport, $virtual_transport, $relay_transport or
+$default_transport. This includes any enclosing square brackets and any non-
+default destination server port suffix. The LMTP socket type prefix (inet: or
+unix:) is not included in the lookup key.
+
+Only the next-hop domain, or $myhostname with LMTP over UNIX-domain sockets, is
+used as the nexthop name for certificate verification. The port and any
+enclosing square brackets are used in the table lookup key, but are not used
+for server name verification.
+
+When the lookup key is a domain name without enclosing square brackets or any :
+port suffix (typically the recipient domain), and the full domain is not found
+in the table, just as with the transport(5) table, the parent domain starting
+with a leading "." is matched recursively. This allows one to specify a
+security policy for a recipient domain and all its sub-domains.
+
+The lookup result is a security level, followed by an optional list of
+whitespace and/or comma separated name=value attributes that override related
+main.cf settings. The TLS security levels are described above. Below, we
+describe the corresponding table syntax:
+
+n\bno\bon\bne\be
+    No TLS. No additional attributes are supported at this level.
+m\bma\bay\by
+    Opportunistic TLS. No additional attributes are supported at this level.
+e\ben\bnc\bcr\bry\byp\bpt\bt
+    Mandatory TLS encryption. At this level and higher the optional "ciphers"
+    attribute overrides the main.cf smtp_tls_mandatory_ciphers parameter and
+    the optional "protocols" keyword overrides the main.cf
+    smtp_tls_mandatory_protocols parameter. In the policy table, multiple
+    protocols must be separated by colons, as attribute values may not contain
+    whitespace or commas.
+v\bve\ber\bri\bif\bfy\by
+    Mandatory server certificate verification. The optional "match" attribute
+    overrides the main.cf smtp_tls_verify_cert_match parameter. In the policy
+    table, multiple match patterns and strategies must be separated by colons.
+s\bse\bec\bcu\bur\bre\be
+    Secure-channel TLS. The optional "match" attribute overrides the main.cf
+    smtp_tls_secure_cert_match parameter. In the policy table, multiple match
+    patterns and strategies must be separated by colons. The match attribute is
+    useful when additional domains are supported by common server, the policy
+    entries for the additional domains specify matching rules for the primary
+    domain certificate. While transport table overrides routing secondary
+    domains to the primary nexthop also allow secure verification, they risk
+    delivery to the wrong destination when domains change hands or are re-
+    assigned to new gateways. With the "match" attribute approach, routing is
+    not perturbed, and mail is deferred if verification of a new MX host fails.
+Example:
 
-The smtp_tls_per_site table is searched for a policy that matches the following
+    /etc/postfix/main.cf:
+        smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+    /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.net             secure
+        .example.net            secure match=.example.net:example.net
+        [mail.example.org]:587  secure match=nexthop
+
+N\bNo\bot\bte\be:\b: The "hostname" strategy if listed in a non-default setting of
+smtp_tls_secure_cert_match or in the "match" attribute in the policy table can
+render the "secure" level vulnerable to DNS forgery. Do not use the "hostname"
+strategy for secure-channel configurations in environments where DNS security
+is not assured.
+
+O\bOb\bbs\bso\bol\ble\bet\bte\be p\bpe\ber\br-\b-s\bsi\bit\bte\be T\bTL\bLS\bS p\bpo\bol\bli\bic\bcy\by s\bsu\bup\bpp\bpo\bor\brt\bt
+
+This section describes an obsolete per-site TLS policy mechanism. Unlike the
+Postfix 2.3 policy table mechanism, this uses as a policy lookup key a
+potentially untrusted server hostname, and lacks control over what names can
+appear in server certificates. Because of this, the obsolete mechanism is
+typically vulnerable to false DNS hostname information in MX or CNAME records.
+These attacks can be eliminated only with great difficulty. The new policy
+table makes secure-channel configurations easier and provides more control over
+the cipher and protocol selection for sessions with mandatory encryption.
+
+Avoid policy lookups with the bare hostname. Instead, use the full destination
+nexthop (enclosed in [] with a possible ":port" suffix) as the per-site table
+lookup key (a recipient domain or MX-enabled transport nexthop with no port
+suffix may look like a bare hostname, but is still a suitable destination).
+With Postfix 2.3+, use of the obsolete approach documented here is strongly
+discouraged: use the new policy table instead.
+
+Starting with Postfix 2.3, the underlying TLS enforcement levels are common to
+the obsolete per-site table and the new policy table. The main.cf
+smtp_tls_mandatory_ciphers and smtp_tls_mandatory_protocols parameters control
+the TLS ciphers and protocols for mandatory encryption regardless of which
+table is used. The smtp_tls_verify_cert_match parameter determines the match
+strategy for the obsolete "MUST" keyword in the same way as for the "verify"
+level in the new policy.
+
+With Postfix < 2.3, the obsolete smtp_tls_cipherlist parameter is also applied
+for opportunistic TLS sessions, and should be used with care, or not at all.
+Setting cipherlist restrictions that are incompatible with a remote SMTP server
+render that server unreachable, TLS handshakes are always attempted and always
+fail.
+
+When smtp_tls_policy_maps is empty (default) and smtp_tls_per_site is not
+empty, the per-site table is searched for a policy that matches the following
 information:
 
     remote SMTP server hostname
         This is simply the DNS name of the server that the Postfix SMTP client
         connects to; this name may be obtained from other DNS lookups, such as
-        MX lookups or CNAME lookups.
+        MX lookups or CNAME lookups. Use of the hostname lookup key is
+        discouraged; always use the next-hop destination instead.
     next-hop destination
         This is normally the domain portion of the recipient address, but it
-        may be overruled by information from the transport(5) table, from the
+        may be overridden by information from the transport(5) table, from the
         relayhost parameter setting, or from the relay_transport setting. When
-        it's not the recipient domain, the next-hop destination can have the
-        Postfix-specific form "[name]", [name]:port", "name" or "name:port".
+        it is not the recipient domain, the next-hop destination can have the
+        Postfix-specific form "[name]", "[name]:port", "name" or "name:port".
+        This is the recommended lookup key for per-site policy lookups (and
+        incidentally for SASL password lookups).
 
 When both the hostname lookup and the next-hop lookup succeed, the host policy
 does not automatically override the next-hop policy. Instead, precedence is
@@ -737,31 +1331,25 @@ Specify host names or next-hop destinations on the left-hand side; no wildcards
 are allowed. On the right hand side specify one of the following keywords:
 
     NONE
-        Don't use TLS at all. This overrides a less specific M\bMA\bAY\bY lookup result
-        from the alternate host or next-hop lookup key, and overrides the
-        global smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername
-        settings.
+        No TLS. This overrides a less specific "MAY" lookup result from the
+        alternate host or next-hop lookup key, and overrides the global
+        smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername settings.
     MAY
-        Try to use TLS if the server announces support, otherwise use the
-        unencrypted connection. This has less precedence than a more specific
-        result (including N\bNO\bON\bNE\bE) from the alternate host or next-hop lookup key,
-        and has less precedence than the more specific global "smtp_enforce_tls
-        = yes" or "smtp_tls_enforce_peername = yes".
+        Opportunistic TLS. This has less precedence than a more specific result
+        (including "NONE") from the alternate host or next-hop lookup key, and
+        has less precedence than the more specific global
+        "smtp_enforce_tls = yes" or "smtp_tls_enforce_peername = yes".
     MUST_NOPEERMATCH
-        Require TLS encryption, but do not require that the remote SMTP server
-        hostname matches the information in the remote SMTP server certificate,
-        or that the server certificate was issued by a trusted CA. This
-        overrides a less secure N\bNO\bON\bNE\bE or a less specific M\bMA\bAY\bY lookup result from
-        the alternate host or next-hop lookup key, and overrides the global
-        smtp_use_tls, smtp_enforce_tls and smtp_tls_enforce_peername settings.
-    MUST
-        Require TLS encryption, require that the remote SMTP server hostname
-        matches the information in the remote SMTP server certificate, and
-        require that the remote SMTP server certificate was issued by a trusted
-        CA. This overrides a less secure N\bNO\bON\bNE\bE and M\bMU\bUS\bST\bT_\b_N\bNO\bOP\bPE\bEE\bER\bRM\bMA\bAT\bTC\bCH\bH or a less
-        specific M\bMA\bAY\bY lookup result from the alternate host or next-hop lookup
+        Mandatory TLS encryption. This overrides a less secure "NONE" or a less
+        specific "MAY" lookup result from the alternate host or next-hop lookup
         key, and overrides the global smtp_use_tls, smtp_enforce_tls and
         smtp_tls_enforce_peername settings.
+    MUST
+        Mandatory server certificate verification. This overrides a less secure
+        "NONE" and "MUST_NOPEERMATCH" or a less specific "MAY" lookup result
+        from the alternate host or next-hop lookup key, and overrides the
+        global smtp_use_tls, smtp_enforce_tls and smtp_tls_enforce_peername
+        settings.
 
 The precedences between global (main.cf) and per-site TLS policies can be
 summarized as follows:
@@ -778,49 +1366,67 @@ summarized as follows:
     less secure one (NONE).
 
   * After the per-site policy lookups are combined, the result generally
-    overrides the global policy. The exception is the less specific M\bMA\bAY\bY per-
+    overrides the global policy. The exception is the less specific "MAY" per-
     site policy, which is overruled by the more specific global
     "smtp_enforce_tls = yes" with server certificate verification as specified
     with the smtp_tls_enforce_peername parameter.
 
-C\bCl\blo\bos\bsi\bin\bng\bg a\ba D\bDN\bNS\bS l\blo\boo\bop\bph\bho\bol\ble\be w\bwi\bit\bth\bh  p\bpe\ber\br-\b-s\bsi\bit\bte\be T\bTL\bLS\bS p\bpo\bol\bli\bic\bci\bie\bes\bs
+C\bCl\blo\bos\bsi\bin\bng\bg a\ba D\bDN\bNS\bS l\blo\boo\bop\bph\bho\bol\ble\be w\bwi\bit\bth\bh o\bob\bbs\bso\bol\ble\bet\bte\be p\bpe\ber\br-\b-s\bsi\bit\bte\be T\bTL\bLS\bS p\bpo\bol\bli\bic\bci\bie\bes\bs
+
+For a general discussion of TLS security for SMTP see TLS limitations above.
+What follows applies only to Postfix 2.2.9 and subsequent Postfix 2.2 patch
+levels. Do not use this approach with Postfix 2.3+; instead see the
+instructions under secure server certificate verification.
 
 As long as no secure DNS lookup mechanism is available, false hostnames in MX
-or CNAME responses can change the server hostname that Postfix uses for TLS
-policy lookup and server certificate verification. Even with a perfect match
-between the server hostname and the server certificate, there is no guarantee
-that Postfix is connected to the right server. To avoid this loophole take the
-following steps:
+or CNAME responses can change Postfix's notion of the server hostname that is
+used for TLS policy lookup and server certificate verification. Even with a
+perfect match between the server hostname and the server certificate, there is
+no guarantee that Postfix is connected to the right server. To avoid this
+loophole, take all of the following steps:
+
+ 1. Use a dedicated transport for all secure-channel deliveries.
 
 * Eliminate MX lookups. Specify local transport(5) table entries for
2. Eliminate MX lookups. Specify local transport(5) table entries for
     sensitive domains with explicit smtp:[mailhost] or smtp:[mailhost]:port
     destinations (you can assure security of this table unlike DNS); in the
-    smtp_tls_per_site table specify the value M\bMU\bUS\bST\bT for the key [mailhost] or
+    smtp_tls_per_site table, specify the value "MUST" for the key [mailhost] or
     smtp:[mailhost]:port. This prevents false hostname information in DNS MX
-    records from changing the server hostname that Postfix uses for TLS policy
-    lookup and server certificate verification.
+    records from changing Postfix's notion of the server hostname that is used
+    for TLS policy lookup and server certificate verification.
 
 * Disallow CNAME hostname overrides. In main.cf specify
3. Disallow CNAME hostname overrides. In main.cf, specify
     "smtp_cname_overrides_servername = no". This prevents false hostname
     information in DNS CNAME records from changing the server hostname that
     Postfix uses for TLS policy lookup and server certificate verification.
-    This feature requires Postfix 2.2.9 or later.
+    This feature requires Postfix 2.2.9 or later. The default value is "no"
+    starting with Postfix 2.3.
 
 Example:
 
-    /etc/postfix/main.cf:
-        smtp_tls_per_site = hash:/etc/postfix/tls_per_site
-        relayhost = [msa.example.net]:587
+We give the non-default "securetls" transport an explicit master.cf process
+limit, so that we don't raise its process limit when raising
+$default_process_limit. The total process limit for *all* transports should
+stay somewhat under 1024 (the typical select() file descriptor limit);
+otherwise transports may be throttled under steady high load, compounding
+congestion. It is not uncommon at high volume sites to set the default process
+limit to 500 or more.
 
-    /etc/postfix/tls_per_site:
-        # relayhost exact nexthop match
-        [msa.example.net]:587       MUST
+We also default the "securetls" transport TLS security level to MUST, obviating
+the need for per-site table entries for secure-channel destinations.
 
-        # TLS should not be used with the example.org MX hosts.
-        example.org                 NONE
+    /etc/postfix/main.cf:
+        transport_maps = hash:/etc/postfix/transport
+
+    /etc/postfix/transport:
+        example.com         securetls:[tls.example.com]
 
-        # TLS should not be used with the host smtp.example.com.
-        smtp.example.com             NONE
+    /etc/postfix/master.cf:
+        securetls unix  -       -       n       -       100     smtp
+            -o smtp_connection_cache_on_demand=no
+            -o smtp_connection_cache_destinations=
+            -o smtp_enforce_tls=yes
+            -o smtp_tls_enforce_peername=yes
 
 D\bDi\bis\bsc\bco\bov\bve\ber\bri\bin\bng\bg s\bse\ber\brv\bve\ber\brs\bs t\bth\bha\bat\bt s\bsu\bup\bpp\bpo\bor\brt\bt T\bTL\bLS\bS
 
@@ -844,8 +1450,8 @@ S\bSe\ber\brv\bve\ber\br c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be v\bve\ber\bri\bif\bfi\bic\bca\bat\bt
 When verifying a remote SMTP server certificate, a verification depth of 1 is
 sufficient if the certificate is directly issued by a CA specified with
 smtp_tls_CAfile or smtp_tls_CApath. The default value of 5 should also suffice
-for longer chains (root CA issues special CA which then issues the actual
-certificate...)
+for longer chains (where the root CA issues a special CA certificate which then
+issues the actual certificate).
 
 Example:
 
@@ -854,17 +1460,28 @@ Example:
 
 C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be c\bci\bip\bph\bhe\ber\br c\bco\bon\bnt\btr\bro\bol\bls\bs
 
-To influence the Postfix SMTP client cipher selection scheme, you can give
-cipherlist string. A detailed description would go to far here; please refer to
-the OpenSSL documentation. If you don't know what to do with it, simply don't
-touch it and leave the (openssl-)compiled in default!
-
-DO NOT USE " to enclose the string, specify just the string!!!
+The Postfix SMTP client supports 5 distinct cipher security levels as specified
+by the smtp_tls_mandatory_ciphers configuration parameter. This setting
+controls the minimum acceptable SMTP client TLS cipher grade for use with
+mandatory TLS encryption. The default value "medium" is suitable for most
+destinations with which you may want to enforce TLS, and is beyond the reach of
+today's crypt-analytic methods. See smtp_tls_policy_maps for information on how
+to configure ciphers on a per-destination basis.
+
+By default anonymous ciphers are allowed, and automatically disabled when
+server certificates are verified. If you want to disable even at the "encrypt"
+security level, set "smtp_tls_mandatory_exclude_ciphers = aNULL", to disable
+anonymous ciphers even with opportunistic TLS, set
+"smtp_tls_exclude_ciphers = aNULL". There is generally no need to take these
+measures. Anonymous ciphers save bandwidth and TLS session cache space, if
+certificates are ignored, there is little point in requesting them.
 
 Example:
 
     /etc/postfix/main.cf:
-        smtp_tls_cipherlist = DEFAULT
+        smtp_tls_mandatory_ciphers = medium
+        smtp_tls_mandatory_exclude_ciphers = RC4, MD5
+        smtp_tls_exclude_ciphers = aNULL
 
 M\bMi\bis\bsc\bce\bel\bll\bla\ban\bne\beo\bou\bus\bs c\bcl\bli\bie\ben\bnt\bt c\bco\bon\bnt\btr\bro\bol\bls\bs
 
@@ -1011,7 +1628,7 @@ indicates a super-user shell.
     Authority private key that we created a few steps ago.
 
         % o\bop\bpe\ben\bns\bss\bsl\bl c\bca\ba -\b-o\bou\but\bt F\bFO\bOO\bO-\b-c\bce\ber\brt\bt.\b.p\bpe\bem\bm -\b-i\bin\bnf\bfi\bil\ble\bes\bs F\bFO\bOO\bO-\b-r\bre\beq\bq.\b.p\bpe\bem\bm
-        Uing configuration from /etc/ssl/openssl.cnf
+        Using configuration from /etc/ssl/openssl.cnf
         Enter PEM pass phrase:w\bwh\bha\bat\bte\bev\bve\ber\br
         Check that the request matches the signature
         Signature ok
@@ -1038,19 +1655,24 @@ indicates a super-user shell.
         # c\bch\bhm\bmo\bod\bd 6\b64\b44\b4 /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/F\bFO\bOO\bO-\b-c\bce\ber\brt\bt.\b.p\bpe\bem\bm /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/c\bca\bac\bce\ber\brt\bt.\b.p\bpe\bem\bm
         # c\bch\bhm\bmo\bod\bd 4\b40\b00\b0 /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/F\bFO\bOO\bO-\b-k\bke\bey\by.\b.p\bpe\bem\bm
 
-  * Configure Postfix, by adding the following to /etc/postfix/main.cf.
+  * Configure Postfix, by adding the following to /etc/postfix/main.cf. It is
+    generally best to not configure client certificates, unless there are
+    servers which authenticate your mail submission via client certificates.
+    Often servers that perform TLS client authentication will issue the
+    required certificates signed by their own CA. If you configure the client
+    certificate and key incorrectly, you will be unable to send mail to sites
+    that request client certificate, but don't require them from all clients.
 
         smtp_tls_CAfile = /etc/postfix/cacert.pem
-        smtp_tls_cert_file = /etc/postfix/FOO-cert.pem
-        smtp_tls_key_file = /etc/postfix/FOO-key.pem
-        smtp_tls_session_cache_database = btree:/var/run/smtp_tls_session_cache
+        smtp_tls_session_cache_database =
+            btree:/var/spool/postfix/smtp_tls_session_cache
         smtp_use_tls = yes
         smtpd_tls_CAfile = /etc/postfix/cacert.pem
         smtpd_tls_cert_file = /etc/postfix/FOO-cert.pem
         smtpd_tls_key_file = /etc/postfix/FOO-key.pem
         smtpd_tls_received_header = yes
-        smtpd_tls_session_cache_database = btree:/var/run/
-        smtpd_tls_session_cache
+        smtpd_tls_session_cache_database =
+            btree:/var/spool/postfix/smtpd_tls_session_cache
         smtpd_use_tls = yes
         tls_random_source = dev:/dev/urandom
 
@@ -1076,7 +1698,7 @@ J
     kbytes or more, and that implements the sequence operation. In most cases,
     btree databases should be adequate.
 
-    NOTE: You cannot use dbm databases. TLS session objects are too large.
+    NOTE: You cannot use DBM databases. TLS session objects are too large.
 
   * master.cf: Specify "unix" instead of "fifo" as the tlsmgr service type.
 
index f5186e1318d26c8fac30f16da7a65c20f00ad74b..7409e76505f5dcc1e9228e9a9a7a1f2d73ddaf9c 100644 (file)
@@ -17,6 +17,102 @@ Incompatibility with Postfix 2.1 and earlier
 If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
 before proceeding.
 
+Major changes with snapshot 20060626
+====================================
+
+Both the SMTP client and server can be configured without a client
+or server certificate. An SMTP server without certificate can use
+only anonymous ciphers, and will not interoperate with most clients.
+
+The SMTP server supports anonymous ciphers when client certificates
+are not requested or required, and the administrator has not excluded
+the "aNULL" OpenSSL cipher type.
+
+The SMTP client supports anonymous ciphers when no server certificate
+is required (notably Postfix 2.3 in "opportunistic" mode) and the
+administrator has not excluded the "aNULL" OpenSSL cipher type.
+
+Instead of cipher lists you can now specify cipher grades.  The
+smtp_tls_ciphers, lmtp_tls_ciphers and smtpd_tls_ciphers parameters
+specify one of "high", "medium", "low", "export" or "null". See the
+documentation for details.
+
+Incompatibility with Postfix snapshot 20060614
+==============================================
+
+The smtp_sasl_tls_verified_security_options feature is not yet
+complete, and will therefore not appear in the stable Postfix 2.3
+release.
+
+New smtp_tls_mandatory_protocols feature used for mandatory TLS
+destinations.  The default value is "SSLv3, TLSv1". SSLv2 is by
+default no longer used with mandatory TLS.
+
+The smtp_tls_cipherlist parameter only applies when TLS is mandatory,
+it is ignored for opportunistic TLS sessions.
+
+At (lmtp|smtp|smtpd)_tls_loglevel >= 2, Postfix now also logs TLS
+session cache activity. Use level 2 and higher for debugging only,
+use levels 0 or 1 as production settings.
+
+Major changes with snapshot 20060614
+====================================
+
+New design of the TLS policy interface (minimum security levels,
+cipher and protocol selection).
+
+New smtp_tls_security_level parameter obsoletes the unnecessarily
+complex smtp_use_tls, smtp_enforce_tls and smtp_tls_enforce_peername
+parameters. Use smtp_tls_security_level instead. The old parameters
+will be removed in a future Postfix release.
+
+New smtp_tls_policy_maps feature obsoletes smtp_tls_per_site, the
+old feature is only used when smtp_tls_policy_maps is not set.  Use
+smtp_tls_policy_maps instead. This also implements parent domain
+matching for destinations that are bare domains (without enclosing
+[] or optional :port suffix). One can now set TLS policy for a
+domain and all sub-domains.
+
+New smtpd_tls_protocols parameter complements the
+smtp_tls_mandatory_protocols parameter, only recommended for MSA
+configurations, not MX hosts.
+
+The unified SMTP/LMTP client now has complete sets of configuration
+parameters for each protocol.
+
+Incompatibility with snapshot 20060611
+======================================
+
+Postfix internal protocols have has changed. You need to "postfix
+reload" or restart Postfix, otherwise the cleanup server or delivery
+agents will log "unexpected attribute" warnings and mail will not
+be delivered.
+
+Incompatibility with snapshot 20060515
+======================================
+
+Milter support introduces a three new queue file record types. Queue
+files created with this Postfix version will be understood by older
+Postfix versions ONLY if Milter support is turned off, which is
+the default.
+
+Milter support introduces new logging event types: milter-reject,
+milter-discard and milter-hold, that identify actions from Milter
+applications. This may affect logfile processing software.
+
+Major changes with snapshot 20060515
+====================================
+
+Milter (mail filter) application support, compatible with Sendmail
+version 8. This allows you to run a large number of plug-ins to
+reject unwanted mail and to sign mail with, for example, domain
+keys. All Milter functions are implemented except replacing the
+message body, which will be added later. Milters are before-queue
+filters, so they don't change the queue ID.
+
+See the MILTER_README document for a discussion of how to use Milter
+support with Postfix.
+
 Incompatibility with snapshot 20060611
 ======================================
 
index 81389c8303a2310b84ae74720ac63b5fa74b489f..575c25ff352829d7e2ace21e6d55e9cd3df4d4c6 100644 (file)
@@ -13,17 +13,6 @@ This list does not really follow priority.
   "warning alert" issued on normal shutdown. Why is a warning issued for
   a normal shutdown??
 
-* Allow to specify the protocol used globally: SSLv2, SSLv3, TLSv1.
-
-* Enhance tls_per_site feature, such that not only MAY, MUST, NONE flags
-  are supported. It should also be possible to influence the behaviour:
-  choose the SSLv2/SSLv3/TLSv1 protocols.
-  [A compatible way to upgrad the tls_per_site table would be to add the
-  keywords:
-  MUST,SSLv2
-  MAY,NO_TLSv1
-  ]
-
 * Introduce new tls_per_client table to achieve the same selective behaviour
   for incoming connections.
 
index 9eac7130b984cc4043381b0bfa9a8f2a15b20bb3..51112656983dc26f1713481067567256848788f4 100644 (file)
@@ -223,15 +223,22 @@ sub qenv {
        return undef;
     }
     while (my ($r, $l, $d) = rec_get($h)) {
+       if ($r eq "p" && $d > 0) {
+           seek($h, $d, 0) or return (); # follow pointer
+       }
        if ($r eq "R") { push(@r, $d); }
        elsif ($r eq "S") { $s = $d; }
        elsif ($r eq "M") {
            last unless (defined($s));
            if (defined($dlen)) {
-               seek($h, $dlen, 1);
+               seek($h, $dlen, 1) or return (); # skip content
                ($r, $l, $d) = rec_get($h);
            } else {
-               1 while ((($r, $l, $d) = rec_get($h)) && ($r =~ /^[NL]$/));
+               while ((($r, $l, $d) = rec_get($h)) && ($r =~ /^[NLp]$/)) {
+                   if ($r eq "p" && $d > 0) {
+                       seek($h, $d, 0) or return (); # follow pointer
+                   }
+               }
            }
            return unless (defined($r) && $r eq "X");
        }
index 95f904349614842cdb9262b9b71f63bd0747cba4..975bf7c169591f09395705ca4589ba4ba83ce0bf 100644 (file)
 # 
 # REJECT ACTIONS
 #        Postfix  version  2.3  and  later  support enhanced status
-#        codes.  When no code is specified at the beginning of  the
-#        text below, Postfix inserts a default enhanced status code
-#        of "5.7.1" in the case of reject actions, and  "4.7.1"  in
-#        the  case  of  defer  actions. See "ENHANCED STATUS CODES"
-#        below.
+#        codes as defined in RFC 3463.  When no code  is  specified
+#        at  the  beginning  of  the  text below, Postfix inserts a
+#        default enhanced status code of "5.7.1"  in  the  case  of
+#        reject  actions, and "4.7.1" in the case of defer actions.
+#        See "ENHANCED STATUS CODES" below.
 # 
 #        4NN text
 # 
 #               text. 4NN means "try again later", while 5NN  means
 #               "do not try again".
 # 
+#               The  reply  code "421" causes Postfix to disconnect
+#               immediately (Postfix version 2.3 and later).
+# 
 #        REJECT optional text...
-#               Reject  the  address etc. that matches the pattern.
-#               Reply with $reject_code optional text...  when  the
-#               optional  text is specified, otherwise reply with a
+#               Reject the address etc. that matches  the  pattern.
+#               Reply  with  $reject_code optional text... when the
+#               optional text is specified, otherwise reply with  a
 #               generic error response message.
 # 
 #        DEFER_IF_REJECT optional text...
-#               Defer the request if some later  restriction  would
+#               Defer  the  request if some later restriction would
 #               result in a REJECT action. Reply with "450 optional
 #               text... when the optional text is specified, other-
 #               wise reply with a generic error response message.
 #               This feature is available in Postfix 2.1 and later.
 # 
 #        DEFER_IF_PERMIT optional text...
-#               Defer the request if some later  restriction  would
-#               result  in a an explicit or implicit PERMIT action.
-#               Reply with "450 optional text... when the  optional
-#               text  is  specified, otherwise reply with a generic
+#               Defer  the  request if some later restriction would
+#               result in a an explicit or implicit PERMIT  action.
+#               Reply  with "450 optional text... when the optional
+#               text is specified, otherwise reply with  a  generic
 #               error response message.
 # 
 #               This feature is available in Postfix 2.1 and later.
 #               reject_unauth_destination, and so on).
 # 
 #        DISCARD optional text...
-#               Claim successful delivery and silently discard  the
-#               message.   Log the optional text if specified, oth-
+#               Claim  successful delivery and silently discard the
+#               message.  Log the optional text if specified,  oth-
 #               erwise log a generic message.
 # 
-#               Note: this action currently affects all  recipients
-#               of  the  message.   To  discard  only one recipient
-#               without discarding  the  entire  message,  use  the
+#               Note:  this action currently affects all recipients
+#               of the message.   To  discard  only  one  recipient
+#               without  discarding  the  entire  message,  use the
 #               transport(5) table to direct mail to the discard(8)
 #               service.
 # 
 #               This feature is available in Postfix 2.0 and later.
 # 
-#        DUNNO  Pretend  that  the  lookup  key was not found. This
-#               prevents Postfix  from  trying  substrings  of  the
-#               lookup  key (such as a subdomain name, or a network
+#        DUNNO  Pretend that the lookup key  was  not  found.  This
+#               prevents  Postfix  from  trying  substrings  of the
+#               lookup key (such as a subdomain name, or a  network
 #               address subnetwork).
 # 
 #               This feature is available in Postfix 2.0 and later.
 # 
 #        FILTER transport:destination
-#               After  the  message is queued, send the entire mes-
+#               After the message is queued, send the  entire  mes-
 #               sage through the specified external content filter.
-#               The  transport:destination  syntax  is described in
-#               the transport(5)  manual  page.   More  information
-#               about  external  content  filters is in the Postfix
+#               The transport:destination syntax  is  described  in
+#               the  transport(5)  manual  page.   More information
+#               about external content filters is  in  the  Postfix
 #               FILTER_README file.
 # 
-#               Note:  this  action  overrides  the  main.cf   con-
+#               Note:   this  action  overrides  the  main.cf  con-
 #               tent_filter  setting,  and  currently  affects  all
 #               recipients of the message.
 # 
 #               This feature is available in Postfix 2.0 and later.
 # 
 #        HOLD optional text...
-#               Place  the message on the hold queue, where it will
-#               sit until someone either deletes it or releases  it
-#               for  delivery.  Log the optional text if specified,
+#               Place the message on the hold queue, where it  will
+#               sit  until someone either deletes it or releases it
+#               for delivery.  Log the optional text if  specified,
 #               otherwise log a generic message.
 # 
-#               Mail that is placed on hold can  be  examined  with
-#               the  postcat(1)  command,  and  can be destroyed or
+#               Mail  that  is  placed on hold can be examined with
+#               the postcat(1) command, and  can  be  destroyed  or
 #               released with the postsuper(1) command.
 # 
-#               Note: use "postsuper -r" to release mail  that  was
-#               kept  on  hold for a significant fraction of $maxi-
+#               Note:  use  "postsuper -r" to release mail that was
+#               kept on hold for a significant fraction  of  $maxi-
 #               mal_queue_lifetime  or  $bounce_queue_lifetime,  or
 #               longer.
 # 
-#               Note:  this action currently affects all recipients
+#               Note: this action currently affects all  recipients
 #               of the message.
 # 
 #               This feature is available in Postfix 2.0 and later.
 # 
 #        PREPEND headername: headervalue
-#               Prepend  the  specified  message header to the mes-
+#               Prepend the specified message header  to  the  mes-
 #               sage.  When this action is used multiple times, the
-#               first  prepended  header  appears before the second
+#               first prepended header appears  before  the  second
 #               etc. prepended header.
 # 
-#               Note: this action does not support multi-line  mes-
+#               Note:  this action does not support multi-line mes-
 #               sage headers.
 # 
-#               Note:  this  action must be used before the message
-#               content  is  received;  it  cannot   be   used   in
+#               Note: this action must be used before  the  message
+#               content   is   received;   it  cannot  be  used  in
 #               smtpd_end_of_data_restrictions.
 # 
 #               This feature is available in Postfix 2.1 and later.
 # 
 #        REDIRECT user@domain
-#               After the message is queued, send  the  message  to
+#               After  the  message  is queued, send the message to
 #               the  specified  address  instead  of  the  intended
 #               recipient(s).
 # 
-#               Note: this action overrides the FILTER action,  and
+#               Note:  this action overrides the FILTER action, and
 #               currently affects all recipients of the message.
 # 
 #               This feature is available in Postfix 2.1 and later.
 # 
 #        WARN optional text...
 #               Log a warning with the optional text, together with
-#               client  information  and  if  available, with helo,
+#               client information and  if  available,  with  helo,
 #               sender, recipient and protocol information.
 # 
 #               This feature is available in Postfix 2.1 and later.
 # 
 # ENHANCED STATUS CODES
-#        When an enhanced status code is specified in an access ta-
-#        ble, it is subject to modification. The  following  trans-
-#        formations  are  needed when the same access table is used
-#        for client, helo, sender,  or  recipient  access  restric-
-#        tions;  they  happen regardless of whether Postfix replies
-#        to a MAIL FROM, RCPT TO or other SMTP command.
-# 
-#        o      When a sender address matches a REJECT action,  the
-#               Postfix  SMTP server will transform a recipient DSN
-#               status (e.g., 4.1.1-4.1.6) into  the  corresponding
+#        Postfix version 2.3  and  later  support  enhanced  status
+#        codes  as  defined  in  RFC 3463.  When an enhanced status
+#        code is specified in an access table,  it  is  subject  to
+#        modification.  The  following  transformations  are needed
+#        when the same access  table  is  used  for  client,  helo,
+#        sender,  or  recipient  access  restrictions;  they happen
+#        regardless of whether Postfix replies to a MAIL FROM, RCPT
+#        TO or other SMTP command.
+# 
+#        o      When  a sender address matches a REJECT action, the
+#               Postfix SMTP server will transform a recipient  DSN
+#               status  (e.g.,  4.1.1-4.1.6) into the corresponding
 #               sender DSN status, and vice versa.
 # 
-#        o      When   non-address  information  matches  a  REJECT
-#               action (such as the HELO command  argument  or  the
-#               client  hostname/address),  the Postfix SMTP server
-#               will transform a sender  or  recipient  DSN  status
-#               into   a  generic  non-address  DSN  status  (e.g.,
+#        o      When  non-address  information  matches  a   REJECT
+#               action  (such  as  the HELO command argument or the
+#               client hostname/address), the Postfix  SMTP  server
+#               will  transform  a  sender  or recipient DSN status
+#               into  a  generic  non-address  DSN  status   (e.g.,
 #               4.0.0).
 # 
 # REGULAR EXPRESSION TABLES
-#        This section describes how the table lookups  change  when
+#        This  section  describes how the table lookups change when
 #        the table is given in the form of regular expressions. For
-#        a description of regular expression lookup  table  syntax,
+#        a  description  of regular expression lookup table syntax,
 #        see regexp_table(5) or pcre_table(5).
 # 
-#        Each  pattern  is  a regular expression that is applied to
+#        Each pattern is a regular expression that  is  applied  to
 #        the entire string being looked up. Depending on the appli-
-#        cation,  that  string  is  an  entire  client hostname, an
+#        cation, that string  is  an  entire  client  hostname,  an
 #        entire client IP address, or an entire mail address. Thus,
 #        no  parent  domain  or  parent  network  search  is  done,
-#        user@domain mail addresses are not broken  up  into  their
+#        user@domain  mail  addresses  are not broken up into their
 #        user@ and domain constituent parts, nor is user+foo broken
 #        up into user and foo.
 # 
-#        Patterns are applied in the order as specified in the  ta-
-#        ble,  until  a  pattern  is  found that matches the search
+#        Patterns  are applied in the order as specified in the ta-
+#        ble, until a pattern is  found  that  matches  the  search
 #        string.
 # 
-#        Actions are the same as with indexed  file  lookups,  with
-#        the  additional feature that parenthesized substrings from
+#        Actions  are  the  same as with indexed file lookups, with
+#        the additional feature that parenthesized substrings  from
 #        the pattern can be interpolated as $1, $2 and so on.
 # 
 # TCP-BASED TABLES
-#        This section describes how the table lookups  change  when
+#        This  section  describes how the table lookups change when
 #        lookups are directed to a TCP-based server. For a descrip-
 #        tion of the TCP client/server lookup protocol, see tcp_ta-
 #        ble(5).  This feature is not available up to and including
 #        Postfix version 2.2.
 # 
-#        Each lookup operation uses the entire query  string  once.
-#        Depending  on  the  application,  that string is an entire
+#        Each  lookup  operation uses the entire query string once.
+#        Depending on the application, that  string  is  an  entire
 #        client hostname, an entire client IP address, or an entire
-#        mail  address.   Thus,  no parent domain or parent network
-#        search is done, user@domain mail addresses are not  broken
-#        up  into  their user@ and domain constituent parts, nor is
+#        mail address.  Thus, no parent domain  or  parent  network
+#        search  is done, user@domain mail addresses are not broken
+#        up into their user@ and domain constituent parts,  nor  is
 #        user+foo broken up into user and foo.
 # 
 #        Actions are the same as with indexed file lookups.
 # 
 # EXAMPLE
-#        The following example uses an indexed file,  so  that  the
-#        order  of  table entries does not matter. The example per-
-#        mits access by the client at address 1.2.3.4  but  rejects
-#        all  other  clients  in 1.2.3.0/24. Instead of hash lookup
-#        tables, some systems use dbm.  Use the  command  "postconf
-#        -m"  to  find  out  what lookup tables Postfix supports on
+#        The  following  example  uses an indexed file, so that the
+#        order of table entries does not matter. The  example  per-
+#        mits  access  by the client at address 1.2.3.4 but rejects
+#        all other clients in 1.2.3.0/24. Instead  of  hash  lookup
+#        tables,  some  systems use dbm.  Use the command "postconf
+#        -m" to find out what lookup  tables  Postfix  supports  on
 #        your system.
 # 
 #        /etc/postfix/main.cf:
 #        editing the file.
 # 
 # BUGS
-#        The table format does not understand quoting  conventions.
+#        The  table format does not understand quoting conventions.
 # 
 # SEE ALSO
 #        postmap(1), Postfix lookup table manager
 #        transport(5), transport:nexthop syntax
 # 
 # README FILES
-#        Use  "postconf  readme_directory" or "postconf html_direc-
+#        Use "postconf readme_directory" or  "postconf  html_direc-
 #        tory" to locate this information.
 #        SMTPD_ACCESS_README, built-in SMTP server access control
 #        DATABASE_README, Postfix lookup table overview
 # 
 # LICENSE
-#        The Secure Mailer license must be  distributed  with  this
+#        The  Secure  Mailer  license must be distributed with this
 #        software.
 # 
 # AUTHOR(S)
index 118988a13291b3a990af6ee2ade3947e02ae6937..714163f7a3bc95d0b0aef32cae1eb0c7390bb1d5 100644 (file)
@@ -246,6 +246,7 @@ $readme_directory/LMTP_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
+$readme_directory/MILTER_README:f:root:-:644
 $readme_directory/MYSQL_README:f:root:-:644
 $readme_directory/NFS_README:f:root:-:644
 $readme_directory/OVERVIEW:f:root:-:644
@@ -293,6 +294,7 @@ $html_directory/LINUX_README.html:f:root:-:644
 $html_directory/LMTP_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
 $html_directory/MYSQL_README.html:f:root:-:644
 $html_directory/NFS_README.html:f:root:-:644
 $html_directory/OVERVIEW.html:f:root:-:644
old mode 100644 (file)
new mode 100755 (executable)
similarity index 75%
rename from postfix/examples/smtpd-policy/spf.pl
rename to postfix/examples/smtpd-policy/postfix-policyd-spf.pl
index eb5d368..729092e
@@ -1,26 +1,25 @@
 #!/usr/bin/perl
 
-# mengwong@pobox.com
-# Wed Dec 10 03:52:04 EST 2003
 # postfix-policyd-spf
-# version 1.06
-# see http://spf.pobox.com/
+# http://www.openspf.org
+# version 1.07
+# $Id$
 
 use Fcntl;
 use Sys::Syslog qw(:DEFAULT setlogsock);
 use strict;
 
 # ----------------------------------------------------------
-#                     configuration
+#                      configuration
 # ----------------------------------------------------------
 
-# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://spf.pobox.com/downloads.html
+# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://www.openspf.org/downloads.html
 
   my @HANDLERS;
   push @HANDLERS, "testing";
   push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query;
 
-my $VERBOSE = 1;
+my $VERBOSE = 0;
 
 my $DEFAULT_RESPONSE = "DUNNO";
 
@@ -37,7 +36,7 @@ my $syslog_priority = "info";
 my $syslog_ident    = "postfix/policy-spf";
 
 # ----------------------------------------------------------
-#                 minimal documentation
+#                  minimal documentation
 # ----------------------------------------------------------
 
 #
@@ -70,11 +69,10 @@ my $syslog_ident    = "postfix/policy-spf";
 # To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
 #
 #    smtpd_recipient_restrictions =
-#      ...
-#       reject_unknown_sender_domain
-#      reject_unauth_destination
-#      check_policy_service unix:private/policy
-#      ...
+#       ...
+#       reject_unauth_destination
+#       check_policy_service unix:private/policy
+#       ...
 #
 # NOTE: specify check_policy_service AFTER reject_unauth_destination
 # or else your system can become an open relay.
@@ -115,16 +113,16 @@ my $syslog_ident    = "postfix/policy-spf";
 # Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: sender=mengwong@newbabe.mengwong.com
 
 # ----------------------------------------------------------
-#                     initialization
+#                      initialization
 # ----------------------------------------------------------
 
 #
 # Log an error and abort.
 #
 sub fatal_exit {
-  syslog(err  => "fatal_exit: @_");
+  syslog(err     => "fatal_exit: @_");
   syslog(warning => "fatal_exit: @_");
-  syslog(info => "fatal_exit: @_");
+  syslog(info    => "fatal_exit: @_");
   die "fatal: @_";
 }
 
@@ -141,7 +139,7 @@ setlogsock $syslog_socktype;
 openlog $syslog_ident, $syslog_options, $syslog_facility;
 
 # ----------------------------------------------------------
-#                          main
+#                           main
 # ----------------------------------------------------------
 
 #
@@ -151,11 +149,11 @@ my %attr;
 while (<STDIN>) {
   chomp;
   if (/=/)       { my ($k, $v) = split (/=/, $_, 2); $attr{$k} = $v; next }
-  elsif (length) { syslog(warning=>sprintf("warning: ignoring garbage: %.100s", $_)); next; }
+  elsif (length) { syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); next; }
 
   if ($VERBOSE) {
     for (sort keys %attr) {
-      syslog(debug=> "Attribute: %s=%s", $_, $attr{$_});
+      syslog(debug => "Attribute: %s=%s", $_, $attr{$_});
     }
   }
 
@@ -166,50 +164,46 @@ while (<STDIN>) {
   foreach my $handler (@HANDLERS) {
     no strict 'refs';
     my $response = $handler->(attr=>\%attr);
-    syslog(debug=> "handler %s: %s", $handler, $response);
+    syslog(debug => "handler %s: %s", $handler, $response);
     if ($response and $response !~ /^dunno/i) {
-      syslog(info=> "handler %s: %s is decisive.", $handler, $response);
+      syslog(info => "handler %s: %s is decisive.", $handler, $response);
       $action = $response; last;
     }
   }
 
-  syslog(info=> "decided action=%s", $action);
+  syslog(info => "decided action=%s", $action);
 
   print STDOUT "action=$action\n\n";
   %attr = ();
 }
 
 # ----------------------------------------------------------
-#                    plugin: SPF
+#                     plugin: SPF
 # ----------------------------------------------------------
 sub sender_permitted_from {
   local %_ = @_;
   my %attr = %{ $_{attr} };
 
   my $query = eval { new Mail::SPF::Query (ip    =>$attr{client_address},
-                                          sender=>$attr{sender},
-                                          helo  =>$attr{helo_name}) };
+                                           sender=>$attr{sender},
+                                           helo  =>$attr{helo_name}) };
   if ($@) {
-    syslog(info=>"%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s",
-          $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); 
+    syslog(info => "%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s",
+           $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); 
     return "DUNNO";
   }
   my ($result, $smtp_comment, $header_comment) = $query->result();
 
-  syslog(info=>"%s: SPF %s: smtp_comment=%s, header_comment=%s",
-        $attr{queue_id}, $result, $smtp_comment, $header_comment); 
+  syslog(info => "%s: SPF %s: smtp_comment=%s, header_comment=%s",
+         $attr{queue_id}, $result, $smtp_comment, $header_comment); 
 
-  if    ($result eq "pass")  { return "DUNNO"; }
-  elsif ($result eq "fail")  { return "REJECT " . ($smtp_comment || $header_comment); }
-  elsif ($result eq "error") { return "450 temporary failure: $smtp_comment"; }
-  else                       { return "DUNNO"; }
-  # unknown, softfail, neutral and none all return DUNNO
-
-  # TODO XXX: prepend Received-SPF header.  Wietse says he will add that functionality soon.
+  if    ($result eq "fail")     { return "REJECT $smtp_comment"; }
+  elsif ($result eq "error")    { return "DEFER_IF_PERMIT $smtp_comment"; }
+  else                          { return "PREPEND Received-SPF: $result ($header_comment)"; }
 }
 
 # ----------------------------------------------------------
-#                    plugin: testing
+#                     plugin: testing
 # ----------------------------------------------------------
 sub testing {
   local %_ = @_;
@@ -220,16 +214,15 @@ sub testing {
       and
       $attr{recipient} =~ /policyblock/) {
 
-    syslog(info=>"%s: testing: will block as requested",
-          $attr{queue_id}); 
+    syslog(info => "%s: testing: will block as requested", $attr{queue_id}); 
     return "REJECT smtpd-policy blocking $attr{recipient}";
   }
   else {
-    syslog(info=>"%s: testing: stripped sender=%s, stripped rcpt=%s",
-          $attr{queue_id},
-          address_stripped($attr{sender}),
-          address_stripped($attr{recipient}),
-          ); 
+    syslog(info => "%s: testing: stripped sender=%s, stripped rcpt=%s",
+           $attr{queue_id},
+           address_stripped($attr{sender}),
+           address_stripped($attr{recipient}),
+           ); 
     
   }
   return "DUNNO";
@@ -243,4 +236,3 @@ sub address_stripped {
   }
   return $string;
 }
-
index 0fb7c0d7f9527f6f97b3e047323884d513ace791..d44c54bcc42c962d218c97b05ea2d95247323005 100644 (file)
@@ -25,7 +25,7 @@ mail is queued. Each approach serves a different purpose.  </p>
 
 <dl>
 
-<dt> <b> built-in, light-weight, real-time </b> </dt>
+<dt> <b> before queue, built-in, light-weight</b> </dt>
 
 <dd> <p> This method inspects mail BEFORE it is stored in the queue,
 and uses Postfix's built-in message header and message body
@@ -38,7 +38,7 @@ the content inspection methods described below. Details are described
 in the <a href="BUILTIN_FILTER_README.html">BUILTIN_FILTER_README</a> and <a href="BACKSCATTER_README.html">BACKSCATTER_README</a> documents.
 </p>
 
-<dt> <b> external, heavy-weight, not real time </b> </dt>
+<dt> <b> after queue, external, heavy-weight</b> </dt>
 
 <dd> <p> This method inspects mail AFTER it is stored in the queue,
 and uses standard protocols such as SMTP or "pipe to command and
@@ -48,21 +48,33 @@ while receiving mail, and without running out of memory resources
 under a peak load. Details of this approach are in the <a href="FILTER_README.html">FILTER_README</a>
 document. </p>
 
-<dt> <b> external, medium-weight, real-time </b> </dt>
+<dt> <b> before queue, external, medium-weight</b> </dt>
 
-<dd> <p> This method inspects mail BEFORE it is stored in the queue,
-and uses the SMTP protocol. Although this approach appears to be
-the more attractive one, it really combines the worst of the other
-two.  Because mail is inspected before it is queued, content
-inspection software must finish in a limited amount of time, and
-must run in a limited amount of memory.  If content inspection
-needs too much time then incoming mail deliveries will time out,
-and if content inspection needs too much memory then software will
-crash under a peak load. Before-queue inspection limits the peak
-load that your system can handle, and limits the sophistication of
-the content filter that you can use.  Details are in the
-<a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> document.  This approach is available only with
-Postfix version 2.1 and later.  </p>
+<dd> <p> The following two methods inspect mail BEFORE it is stored in the
+queue.  </p>
+
+<ul>
+
+<li> <p> The first method uses the SMTP protocol, and is described
+in the <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> document.  This approach is available
+with Postfix version 2.1 and later.  </p>
+
+<li> <p> The second method uses the Sendmail 8 Milter protocol, and
+is described in the <a href="MILTER_README.html">MILTER_README</a> document.  This approach is
+available with Postfix version 2.3 and later.  </p>
+
+</ul>
+
+<p> Although these approaches appear to be attractive, they have
+some serious limitations that you need to be aware of.  First,
+content inspection software must finish in a limited amount of time;
+if content inspection needs too much time then incoming mail
+deliveries will time out.  Second, content inspection software must
+run in a limited amount of memory; if content inspection needs too
+much memory then software will crash under a peak load.  Before-queue
+inspection limits the peak load that your system can handle, and
+limits the sophistication of the content filter that you can use.
+</p>
 
 </dl>
 
index a89f31ac0c2e595eb260ae7bb70d7d68d787a9aa..10c8f2c266e8f25451d2039d6eed16f9943f686a 100644 (file)
@@ -688,7 +688,7 @@ that injects mail back into Postfix. </p>
     # ===================================================================
     localhost:10026 inet  n       -       n       -       10      smtpd
         -o <a href="postconf.5.html#content_filter">content_filter</a>= 
-        -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_unknown_recipient_checks">no_unknown_recipient_checks</a>,<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>
+        -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_unknown_recipient_checks">no_unknown_recipient_checks</a>,<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>,<a href="postconf.5.html#no_milters">no_milters</a>
         -o <a href="postconf.5.html#smtpd_helo_restrictions">smtpd_helo_restrictions</a>=
         -o <a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a>=
         -o <a href="postconf.5.html#smtpd_sender_restrictions">smtpd_sender_restrictions</a>=
@@ -710,19 +710,27 @@ requests no content filtering for mail from the content filter.
 This is required or else mail will stay in the content filtering
 loop. </p>
 
-<li> <p> The "-o <a href="postconf.5.html#receive_override_options">receive_override_options</a>" overrides <a href="postconf.5.html">main.cf</a>
-settings. It is complementary to the options that are specified in
-<a href="postconf.5.html">main.cf</a>: </p>
+<li> <p> The "-o <a href="postconf.5.html#receive_override_options">receive_override_options</a>" overrides <a href="postconf.5.html">main.cf</a> settings
+to avoid duplicating work that was already done before the content
+filter. These options are complementary to the options that are
+specified in <a href="postconf.5.html">main.cf</a>: </p>
 
 <ul>
 
-    <li> <p> Disable attempts to find out if a recipient is unknown,
-    and disable header/body checks. This work was already done
-    before the content filter and repeating it would be wasteful.
-    </p>
+    <li> <p> We specify "<a href="postconf.5.html#no_unknown_recipient_checks">no_unknown_recipient_checks</a>" to disable
+    attempts to find out if a recipient is unknown. </p>
 
-    <li> <p> Enable virtual alias expansion, canonical mappings,
-    address masquerading, and other address mappings. </p>
+    <li> <p> We specify "<a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>" to disable header/body
+    checks.  </p>
+
+    <li> <p> We specify "<a href="postconf.5.html#no_milters">no_milters</a>" to disable Milter applications
+    (this option is available only in Postfix 2.3 and later).  </p>
+
+    <li> <p> We don't specify "no_address_mapping" here.  This
+    enables virtual alias expansion, canonical mappings, address
+    masquerading, and other address mappings after the content
+    filter. The <a href="postconf.5.html">main.cf</a> setting of "<a href="postconf.5.html#receive_override_options">receive_override_options</a>"
+    disables these mappings before the content filter.  </p>
 
 </ul>
 
diff --git a/postfix/html/MILTER_README.html b/postfix/html/MILTER_README.html
new file mode 100644 (file)
index 0000000..926bc41
--- /dev/null
@@ -0,0 +1,765 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix before-queue Milter support </title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix before-queue Milter support </h1>
+
+<hr>
+
+<h2>Introduction</h2>
+
+<p> Postfix version 2.3 introduces support for the Sendmail version
+8 Milter (mail filter) protocol. This protocol is used by applications
+that run outside the MTA to inspect SMTP events (CONNECT, DISCONNECT),
+SMTP commands (HELO, MAIL FROM, etc.) as well as mail content.  All
+this happens before mail is queued.  </p>
+
+<p> The reason for adding Milter support to Postfix is that there
+exists a large collection of applications, not only to block unwanted
+mail, but also to verify authenticity (examples: <a
+href="http://sourceforge.net/projects/sid-milter/">SenderID+SPF</a> and
+<a href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>)
+or to digitally sign mail (example: <a
+href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>).
+Having yet another MTA-specific version of all that software is a
+poor use of human and system resources. </p>
+
+<p> Postfix 2.3 implements all the requests of Sendmail version 8
+Milter protocols up to version 4, except one: message body replacement.
+See, however, the <a href="#limitations">limitations</a> section
+at the end of this document. </p>
+
+<p> This document provides information on the following topics: </p>
+
+<ul>
+
+<li><a href="#plumbing">How Milter applications plug into Postfix </a>
+
+<li><a href="#building">Building Milter applications</a>
+
+<li><a href="#running">Running Milter applications</a>
+
+<li><a href="#config">Configuring Postfix</a>
+
+<li><a href="#workarounds">Workarounds</a>
+
+<li><a href="#limitations">Limitations</a>
+
+</ul>
+
+<h2><a name="plumbing">How Milter applications plug into Postfix </a> </h2>
+
+<p> The Postfix Milter implementation uses two different lists of
+mail filters: one list of filters that are used for SMTP mail only,
+and one list of filters that are used for non-SMTP mail. The two
+lists have different capabilities, which is unfortunate.  Avoiding
+this would require major restructuring of Postfix. </p>
+
+<ul>
+
+<li> <p> The SMTP-only filters handle mail that arrives via the
+Postfix <a href="smtpd.8.html">smtpd(8)</a> server. They are typically used to filter unwanted
+mail and to sign mail from authorized SMTP clients. You specify
+SMTP-only Milter applications with the <a href="postconf.5.html#smtpd_milters">smtpd_milters</a> parameter as
+described in a later section.  Mail that arrives via the Postfix
+<a href="smtpd.8.html">smtpd(8)</a> server is not filtered by the non-SMTP filters that are
+described next. </p>
+
+<li> <p> The non-SMTP filters handle mail that arrives via the
+Postfix <a href="sendmail.1.html">sendmail(1)</a> command-line or via the Postfix <a href="qmqpd.8.html">qmqpd(8)</a> server.
+They are typically used to digitally sign mail only. Although
+non-SMTP filters can be used to filter unwanted mail, they have
+limitations compared to the SMTP-only filters. You specify non-SMTP
+Milter applications with the <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a> parameter as described
+in a later section.  </p>
+
+</ul>
+
+<p> For those who are familiar with the Postfix architecture, the
+figure below shows how Milter applications plug into Postfix.  Names
+followed by a number are Postfix commands or server programs, while
+unnumbered names inside shaded areas represent Postfix queues.  To
+avoid clutter, the path for local submission is simplified (the
+<a href="OVERVIEW.html">OVERVIEW</a> document has a more complete description).  </p>
+
+<blockquote>
+
+<table>
+
+<tr> 
+
+<td colspan="2"> </td> 
+
+<td align="center"> SMTP-only <br> filters </td> 
+
+<td> </td> 
+
+<td align="center"> non-SMTP <br> filters </td> 
+
+</tr>
+
+<tr> 
+
+<td colspan="2"> </td> 
+
+<td align="center"> <table> <tr> <td align="center">
+^<br> <tt> | </tt> </td> <td align="center"> <tt> |<br> v </tt>
+</td> </tr> </table> </td>
+
+<td rowspan="2"> </td>
+
+<td rowspan="3" align="center"> <table> <tr> <td align="center">
+^<br> <tt> |<br> |<br> | </tt> </td> <td align="center"> <tt> |<br>
+|<br> |<br> v </tt> </td> </tr> </table> </td>
+
+</tr>
+
+<tr>
+
+<td> Network </td> <td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="smtpd.8.html">smtpd(8)</a>
+</td>
+
+</tr>
+
+<tr>
+
+<td colspan="3"> </td> <td> <tt> \ </tt> </td>
+
+</tr>
+
+<tr>
+
+<td> Network </td> <td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="qmqpd.8.html">qmqpd(8)</a>
+</td>
+
+<td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="cleanup.8.html">cleanup(8)</a>
+</td>
+
+<td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a
+href="QSHAPE_README.html#incoming_queue"> incoming </a> </td>
+
+</tr>
+
+<tr>
+
+<td colspan="3"> </td> <td> <tt> / </tt> </td>
+
+</tr>
+
+<tr>
+
+<td colspan="2"> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="pickup.8.html">pickup(8)</a>
+</td>
+
+</tr>
+
+<tr> <td colspan="2"> </td> <td align="center"> : </td> </tr>
+
+<tr>
+
+<td> Local </td> <td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="sendmail.1.html">sendmail(1)</a>
+</td>
+
+</tr>
+
+</table>
+
+</blockquote>
+
+<h2><a name="building">Building Milter applications</a></h2>
+
+<p> Milter applications have been written in C, JAVA and Perl, but
+this document deals with C applications only.   For these, you need
+an object library that implements the Sendmail 8 Milter protocol.
+Postfix currently does not provide such a library, but Sendmail
+does. </p>
+
+<p> On some Linux and *BSD distributions, the Sendmail libmilter
+library is installed by default. With this, applications such as
+<a href="http://sourceforge.net/projects/dk-milter/">dk-milter</a>
+and <a href="http://sourceforge.net/projects/sid-milter/">sid-milter</a>
+build out of the box without requiring any tinkering:</p>
+
+<blockquote>
+<pre>
+$ <b>gzcat dk-milter-<i>x.y.z</i>.tar.gz | tar xf -</b>
+$ <b>cd dk-milter-<i>x.y.z</i></b>
+$ <b>make</b>
+[...<i>lots of output omitted</i>...]
+</pre>
+</blockquote>
+
+<p> On other platforms you have two options: </p>
+
+<ul>
+
+<li> <p>Install the Sendmail libmilter object library and include
+files.  On Linux systems, libmilter may be provided by the
+sendmail-devel package.  After installing libmilter, build the
+Milter applications as described in the preceding paragraph.  </p>
+
+<li> <p>Don't install the Sendmail libmilter library, but build the
+library from Sendmail source code instead: </p>
+
+<blockquote>
+<pre>
+$ <b>gzcat sendmail-<i>x.y.z</i>.tar.gz | tar xf -</b>
+$ <b>cd sendmail-<i>x.y.z</i></b>
+$ <b>make</b>
+[...<i>lots of output omitted</i>...]
+</pre>
+</blockquote>
+
+<p> After building your own libmilter library, follow the installation
+instructions in the Milter application source distribution to specify
+the location of the libmilter include files and object library.
+Typically, these settings are configured in a file named
+<tt>sid-filter/Makefile.m4</tt> or similar:
+
+<blockquote>
+<pre>
+APPENDDEF(`confINCDIRS', `-I/some/where/sendmail-x.y.z/include')
+APPENDDEF(`confLIBDIRS', `-L/some/where/sendmail-x.y.z/obj.<i>systemtype</i>/libmilter')
+</pre>
+</blockquote>
+
+<p>Then build the Milter application. </p>
+
+</ul>
+
+<h2><a name="running">Running Milter applications</a></h2>
+
+<p> To run a Milter application, see the documentation of the filter
+for options.  A typical command looks like this:</p>
+
+<blockquote>
+<pre>
+$ <b>/some/where/dk-filter -p inet:<i>portnumber</i>@localhost ...<i>other options</i>...</b>
+</pre>
+</blockquote>
+
+<h2><a name="config">Configuring Postfix</a></h2>
+
+<p> Like Sendmail, Postfix has a lot of configuration options that
+control how it talks to Milter applications. With the initial Postfix
+Milter protocol implementation, many options are global, that is,
+they apply to all Milter applications. Future Postfix versions may
+support per-Milter timeouts, per-Milter error handling, etc. </p>
+
+<p> Information in this section: </p>
+
+<ul>
+
+<li><a href="#smtp-only-milters">SMTP-Only Milter applications </a>
+
+<li><a href="#non-smtp-milters">Non-SMTP Milter applications </a>
+
+<li><a href="#errors">Milter error handling </a>
+
+<li><a href="#version">Milter protocol version</a>
+
+<li><a href="#timeouts">Milter protocol timeouts</a>
+
+<li><a href="#macros">Sendmail macro emulation</a>
+
+</ul>
+
+<h3><a name="smtp-only-milters">SMTP-Only Milter applications</a></h3>
+
+<p>  The SMTP-only Milter applications handle mail that arrives via
+the Postfix <a href="smtpd.8.html">smtpd(8)</a> server. They are typically used to filter
+unwanted mail, and to sign mail from authorized SMTP clients.  Mail
+that arrives via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server is not filtered by the
+non-SMTP filters that are described in the next section. </p>
+
+<p> You specify SMTP-only Milter applications (there can be more
+than one) with the <a href="postconf.5.html#smtpd_milters">smtpd_milters</a> parameter.  Each Milter application
+is identified by the name of its listening socket; other Milter
+configuration options will be discussed in later sections.  Milter
+applications are applied in the order as specified, and the first
+Milter application that rejects a command will override the responses
+from other Milter applications.  </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    # Milters for mail that arrives via the <a href="smtpd.8.html">smtpd(8)</a> server.
+    # See below for socket address syntax.
+    <a href="postconf.5.html#smtpd_milters">smtpd_milters</a> = inet:localhost:<i>portnumber</i> ...<i>other filters</i>...
+</pre>
+</blockquote>
+
+<p> The general syntax for listening sockets is as follows: </p>
+
+<blockquote>
+
+<dl>
+
+<dt> <b>unix:</b><i>pathname</i> </dt> <dd><p>Connect to the local
+UNIX-domain server that is bound to the specified pathname. If the
+<a href="smtpd.8.html">smtpd(8)</a> or <a href="cleanup.8.html">cleanup(8)</a> process runs chrooted, an absolute pathname
+is interpreted relative to the Postfix queue directory.</p> </dd>
+
+<dt> <b> inet:</b><i>host</i><b>:</b><i>port</i> </dt> <dd> <p>
+Connect to the specified TCP port on the specified local or remote
+host.  The host and port can be specified in numeric or symbolic
+form.</p>
+
+<p> Note: Postfix syntax differs from Milter syntax which has the
+form <b>inet:</b><i>port</i><b>@</b><i>host</i>. </p>  </dd>
+
+</dl>
+
+</blockquote>
+
+<h3> <a name="non-smtp-milters">Non-SMTP Milter applications </a> </h3>
+
+<p> The non-SMTP Milter applications handle mail that arrives via
+the Postfix <a href="sendmail.1.html">sendmail(1)</a> command-line or via the Postfix <a href="qmqpd.8.html">qmqpd(8)</a>
+server. They are typically used to digitally sign mail. Although
+non-SMTP filters can be used to filter unwanted mail, there are
+limitations as discussed later in this section.  Mail that arrives
+via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server is not filtered by the non-SMTP
+filters.  </p>
+
+<p> You specify non-SMTP Milter applications with the <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>
+parameter. This parameter uses the same syntax as the <a href="postconf.5.html#smtpd_milters">smtpd_milters</a>
+parameter in the previous section. As with the SMTP-only filters,
+you can specify more than one Milter application; they are applied
+in the order as specified, and the first Milter application that
+rejects a command will override the responses from the other
+applications.  </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    # Milters for non-SMTP mail.
+    # See below for socket address syntax.
+    <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a> = inet:localhost:<i>portnumber</i> ...<i>other filters</i>...
+</pre>
+</blockquote>
+
+<p> There's one small complication when using Milter applications
+for non-SMTP mail: there is no SMTP session.  To keep Milter
+applications happy, the Postfix <a href="cleanup.8.html">cleanup(8)</a> server actually has to
+simulate the SMTP client CONNECT and DISCONNECT events, and the
+SMTP client EHLO, MAIL FROM, RCPT TO and DATA commands. </p>
+
+<ul>
+
+<li> <p> When new mail arrives via the <a href="sendmail.1.html">sendmail(1)</a> command line,
+the Postfix <a href="cleanup.8.html">cleanup(8)</a> server pretends that the mail arrives with
+ESMTP from "localhost" with IP address "127.0.0.1". The result is
+very similar to what happens with command line submissions in
+Sendmail version 8.12 and later, although Sendmail uses a different
+mechanism to achieve this result.  </p>
+
+<li> <p> When new mail arrives via the <a href="qmqpd.8.html">qmqpd(8)</a> server, the Postfix
+<a href="cleanup.8.html">cleanup(8)</a> server pretends that the mail arrives with ESMTP, and
+uses the QMQPD client hostname and IP address. </p>
+
+<li> <p> When old mail is re-injected into the queue with "postsuper
+-r", the Postfix <a href="cleanup.8.html">cleanup(8)</a> server uses the same client information
+that was used when the mail arrived as new mail. </p>
+
+</ul>
+
+<p> This generally works as expected, with only one exception:
+non-SMTP filters must not REJECT or TEMPFAIL simulated RCPT TO
+commands.  When a <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a> application REJECTs or TEMPFAILs
+a recipient, Postfix will report a configuration error, and mail
+will stay in the queue.  </p>
+
+<p> None of this is a problem for mail filters that digitally sign
+mail. </p>
+
+<h3><a name="errors">Milter error handling</a></h3>
+
+<p> The <a href="postconf.5.html#milter_default_action">milter_default_action</a> parameter specifies how Postfix handles
+Milter application errors. The default action is to respond with a
+temporary error status, so that the client will try again later.
+Specify "accept" if you want to receive mail as if the filter does
+not exist, and "reject" to reject mail with a permanent status.
+</p>
+
+<blockquote>
+<pre>
+    # What to do in case of errors? Specify accept, reject, or tempfail.
+    <a href="postconf.5.html#milter_default_action">milter_default_action</a> = tempfail
+</pre>
+</blockquote>
+
+<h3><a name="version">Milter protocol version</a></h3>
+
+<p> As Postfix is not built with the Sendmail libmilter library,
+you may need to configure the Milter protocol version that Postfix
+should use.  The default version is 2. </p>
+
+<blockquote>
+<pre>
+<a href="postconf.5.html#milter_protocol">milter_protocol</a> = 2
+</pre>
+</blockquote>
+
+<p> If the Postfix <a href="postconf.5.html#milter_protocol">milter_protocol</a> setting specifies a too low
+version, the libmilter library will log an error message like this:
+</p>
+
+<blockquote>
+<pre>
+<i>application name</i>: st_optionneg[<i>xxxxx</i>]: 0x<i>yy</i> does not fulfill action requirements 0x<i>zz</i>
+</pre>
+</blockquote>
+
+<p> The remedy is to increase the Postfix <a href="postconf.5.html#milter_protocol">milter_protocol</a> version
+number.  See, however, the <a href="#limitations">limitations</a>
+section below for features that aren't supported by Postfix. </p>
+
+<p> If the Postfix <a href="postconf.5.html#milter_protocol">milter_protocol</a> setting specifies a too high
+version, the libmilter library simply hangs up without logging a
+warning, and you see a Postfix warning message like one of the
+following: </p>
+
+<blockquote>
+<pre>
+postfix/smtpd[21045]: warning: milter inet:<i>host</i>:<i>port</i>: can't read packet header: Unknown error : 0
+postfix/cleanup[15190]: warning: milter inet:<i>host</i>:<i>port</i>: can't read packet header: Success
+</pre>
+</blockquote>
+
+<p> The remedy is to lower the Postfix <a href="postconf.5.html#milter_protocol">milter_protocol</a> version
+number. </p>
+
+<h3><a name="timeouts">Milter protocol timeouts</a></h3>
+
+<p> Postfix uses different time limits at different Milter protocol
+stages. The table shows wich timeouts are used and when
+(EOH = end of headers; EOM = end of message).  </p>
+
+<blockquote>
+
+<table border="1">
+
+<tr> <th> Parameter </th> <th> Time limit </th> <th> Protocol
+stage</th> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_connect_timeout">milter_connect_timeout</a> </td> <td> 30s </td> <td> CONNECT
+</td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_command_timeout">milter_command_timeout</a> </td> <td> 30s </td> <td> HELO,
+MAIL, RCPT, DATA, UNKNOWN </td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_content_timeout">milter_content_timeout</a> </td> <td> 300s </td> <td> HEADER,
+EOH, BODY, EOM </td> </tr>
+
+</table>
+
+</blockquote>
+
+<p> Beware: 30s is not a lot for applications that do a lot of DNS
+lookups.  However, if you increase the above timeouts too much,
+remote SMTP clients may hang up and mail may be delivered multiple
+times. This is an inherent problem with before-queue filtering. </p>
+
+<h3><a name="macros">Sendmail macro emulation</a></h3>
+
+<p> Postfix emulates a limited number of Sendmail macros, as shown
+in the table. Different macros are available at different SMTP
+protocol stages (EOM = end-of-message); their availability is not
+always the same as in Sendmail. See the <a
+href="#workarounds">workarounds</a> section below for solutions.
+</p>
+
+<blockquote>
+
+<table border="1">
+
+<tr> <th> Name </th> <th> Availability </th> <th> Description </th>
+</tr>
+
+<tr> <td> i </td> <td> DATA, EOM </td> <td> Queue ID </td> </tr>
+
+<tr> <td> j </td> <td> Always </td> <td> value of <a href="postconf.5.html#myhostname">myhostname</a> </td>
+</tr>
+
+<tr> <td> {auth_authen} </td> <td> MAIL, DATA, EOM </td> <td> SASL
+login name </td> </tr>
+
+<tr> <td> {auth_author} </td> <td> MAIL, DATA, EOM </td> <td> SASL
+sender </td> </tr>
+
+<tr> <td> {auth_type} </td> <td> MAIL, DATA, EOM </td> <td> SASL
+login method </td> </tr>
+
+<tr> <td> {client_addr} </td> <td> Always </td> <td> Client IP
+address </td> </tr>
+
+<tr> <td> {client_connections} </td> <td> CONNECT </td> <td>
+Connection concurrency for this client </td> </tr>
+
+<tr> <td> {client_name} </td> <td> Always </td> <td> Client hostname,
+"unknown" when lookup or verification fails </td> </tr>
+
+<tr> <td> {client_ptr} </td> <td> CONNECT, HELO, MAIL, DATA </td>
+<td> Client name from reverse lookup, "unknown" when lookup fails
+</td> </tr>
+
+<tr> <td> {cert_issuer} </td> <td> HELO, MAIL, DATA, EOM </td> <td>
+TLS client certificate issuer </td> </tr>
+
+<tr> <td> {cert_subject} </td> <td> HELO, MAIL, DATA, EOM </td>
+<td> TLS client certificate subject </td> </tr>
+
+<tr> <td> {cipher_bits} </td> <td> HELO, MAIL, DATA, EOM </td> <td>
+TLS session key size </td> </tr>
+
+<tr> <td> {cipher} </td> <td> HELO, MAIL, DATA, EOM </td> <td> TLS
+cipher </td> </tr>
+
+<tr> <td> {daemon_name} </td> <td> Always </td> <td> value of
+<a href="postconf.5.html#milter_macro_daemon_name">milter_macro_daemon_name</a> </td> </tr>
+
+<tr> <td> {mail_addr} </td> <td> MAIL </td> <td> Sender address
+</td> </tr>
+
+<tr> <td> {rcpt_addr} </td> <td> RCPT </td> <td> Recipient address
+</td> </tr>
+
+<tr> <td> {tls_version} </td> <td> HELO, MAIL, DATA, EOM </td> <td>
+TLS protocol version </td> </tr>
+
+<tr> <td> v </td> <td> Always </td> <td> value of <a href="postconf.5.html#milter_macro_v">milter_macro_v</a>
+</td> </tr>
+
+</table>
+
+</blockquote>
+
+<p> Postfix sends specific sets of macros at different SMTP protocol
+stages.  The sets are configured with the parameters as described
+in the table (EOM = end of message). </p>
+
+<blockquote>
+
+<table border="1">
+
+<tr> <th> Parameter name </th> <th> Protocol version </th> <th>
+Protocol stage </th> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_connect_macros">milter_connect_macros</a> </td> <td> 2 or higher </td> <td>
+CONNECT </td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_helo_macros">milter_helo_macros</a> </td> <td> 2 or higher </td> <td>
+HELO/EHLO </td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_mail_macros">milter_mail_macros</a> </td> <td> 2 or higher </td> <td> MAIL
+FROM </td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_rcpt_macros">milter_rcpt_macros</a> </td> <td> 2 or higher </td> <td> RCPT
+TO </td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_data_macros">milter_data_macros</a> </td> <td> 4 or higher </td> <td> DATA
+</td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_end_of_data_macros">milter_end_of_data_macros</a> </td> <td> 2 or higher </td>
+<td> EOM </td> </tr>
+
+<tr> <td> <a href="postconf.5.html#milter_unknown_command_macros">milter_unknown_command_macros</a> </td> <td> 3 or higher </td>
+<td> unknown command </td> </tr>
+
+</table>
+
+</blockquote>
+
+<h2><a name="workarounds">Workarounds</a></h2>
+
+<p> Sendmail Milter applications were originally developed for the
+Sendmail version 8 MTA, which has a different architecture than
+Postfix.  The result is that some Milter applications make assumptions
+that aren't true in a Postfix environment. </p>
+
+<ul>
+
+<li> <p> Some Milter applications log a warning that looks like
+this: </p>
+
+<blockquote> <pre>
+sid-filter[36540]: WARNING: sendmail symbol 'i' not available
+</pre>
+</blockquote>
+
+<p> And they may insert a message header with "unknown-msgid" like
+this: </p>
+
+<blockquote>
+<pre>
+X-SenderID: Sendmail Sender-ID Filter vx.y.z host.example.com &lt;unknown-msgid&gt;
+</pre>
+</blockquote>
+
+<p> This happens because the Milter application expects that the
+queue ID is known <i>before</i> the MTA accepts the MAIL FROM
+(sender) command.  Postfix, on the other hand, does not create a
+queue file until <i>after</i> Postfix accepts the first valid RCPT
+TO (recipient) command. This queue file name must be globally unique
+across multiple queue directories, so it cannot be chosen until the
+file is actually created. </p>
+
+<p> To work around the ugly message header from Milter applications,
+we add a little code to the Milter source to look up the queue ID
+after Postfix receives the end of the message. </p>
+
+<ul>
+
+<li> <p> Edit the filter source file (typically named
+<tt>dk-filter/dk-filter.c</tt> or similar). </p>
+
+<li> <p> Look up the <tt>mlfi_eom()</tt> function and add code near
+the top shown as <b>bold</b> text below: </p>
+
+</ul>
+
+<blockquote>
+<pre>
+sic = (Context) smfi_getpriv(ctx);
+assert(sic != NULL);
+<b>
+/*
+**  Determine the job ID for logging.
+*/
+if (sic->ctx_jobid == 0 || strcmp(sic->ctx_jobid, MSGIDUNKNOWN) == 0) {
+        char *jobid = smfi_getsymval(ctx, "i");
+        if (jobid != 0)
+                sic->ctx_jobid = jobid;
+}</b>
+</pre>
+</blockquote>
+
+<p> This does not remove the WARNING message, however. </p>
+
+<p> With some Milter applications we can fix both the WARNING and
+the "unknown-msgid" by postponing the call of <tt>mlfi_eoh()</tt>
+(or whatever routine logs the WARNING) until the end of the message.
+</p>
+
+<ul>
+
+<li> <p> Edit the filter source file (typically named
+<tt>sid-filter/sid-filter.c</tt> or similar). </p>
+
+<li> <p> Look up the <tt>smfilter</tt> table and replace
+<tt>mlfi_eoh</tt> (or whatever routine logs the WARNING) by NULL.
+</p>
+
+<li> <p> Look up the <tt>mlfi_eom()</tt> function and add code near
+the top that calls <tt>mlfi_eoh()</tt> as shown by the <b>bold</b>
+text below: </p>
+
+</ul>
+
+<blockquote>
+<pre>
+        assert(ctx != NULL);
+#endif /* !DEBUG */
+<b>
+        ret = mlfi_eoh(ctx);
+        if (ret != SMFIS_CONTINUE)
+                return ret;</b>
+</pre>
+</blockquote>
+
+<p> This works with sid-milter-0.2.10. Other Milter applications
+will dump core when you do this. </p>
+
+</ul>
+
+<h2><a name="limitations">Limitations</a></h2>
+
+<p> This section lists limitations of the Postfix Milter implementation.
+Some limitations will be removed disappear as support is extended
+over time. Of course the usual limitations of before-queue filtering
+will always apply. See the <a href="CONTENT_INSPECTION_README.html">CONTENT_INSPECTION_README</a> document for
+a discussion. </p>
+
+<ul>
+
+<li> <p> Postfix currently supports only applications that speak
+the Sendmail 8 Milter protocol versions 2..4. Support for other
+protocol types or protocol versions may be added later. </p>
+
+<li> <p> For applications that are written in C, you need to use
+the Sendmail libmilter library. A Postfix replacement may be 
+provided in the future. </p>
+
+<li> <p> There are TWO sets of mail filters: filters that are used
+for SMTP mail only (specified with the <a href="postconf.5.html#smtpd_milters">smtpd_milters</a> parameter),
+and filters for non-SMTP mail (specified with the <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>
+parameter).  The non-SMTP filters are primarily for local submissions.
+</p>
+
+<li> <p> When mail is filtered by non-SMTP filters, the Postfix
+<a href="cleanup.8.html">cleanup(8)</a> server has to simulate the SMTP client CONNECT and
+DISCONNECT events, and the SMTP client EHLO, MAIL FROM, RCPT TO and
+DATA commands.  This works as expected, with only one exception:
+non-SMTP filters must not REJECT or TEMPFAIL simulated RCPT TO
+commands.  When a non-SMTP filter REJECTs or TEMPFAILs a recipient,
+Postfix will report a configuration error, and mail will stay in
+the queue.  </p>
+
+<li> <p> Postfix currently does not apply content filters to mail
+that is forwarded or aliased internally, or to mail that is generated
+internally such as bounces or Postmaster notifications. This may
+be a problem when you want to apply a signing Milter to such mail.
+</p>
+
+<li> <p> When you use the before-queue content filter for incoming
+SMTP mail (see <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a>), Milter applications have access
+only to the SMTP command information; they have no access to the
+message header or body, and cannot make modifications to the message
+or to the envelope. </p>
+
+<li> <p> Postfix 2.3 does not support Milter requests to replace
+the message body. Milter applications that request this unsupported
+operation will log a warning like this: </p>
+
+<blockquote>
+<pre>
+<i>application name</i>: st_optionneg[134563840]: 0x3d does not fulfill action requirements 0x1e
+</pre>
+</blockquote>
+
+<p> The solution is (to wait for) a Postfix version that supports
+the missing functionality.
+
+<li> <p> Most Milter configuration options are global. Future Postfix
+versions may support per-Milter timeouts, per-Milter error handling,
+etc. </p>
+
+</ul>
+
+</body>
+
+</html>
index 748dc3dc933abebde6e576449bdbbbf830f3b305..05e39eb5f2974cb1cbd57723e99c1fc2cbacc1b1 100644 (file)
@@ -174,7 +174,7 @@ are in directory <tt>/usr/local/lib</tt>:  </p>
 </ul>
 
 <p> If you need to apply other customizations (such as Berkeley DB
-databases, MySQL, PosgreSQL, LDAP or SASL), see the respective
+databases, MySQL, PostgreSQL, LDAP or SASL), see the respective
 Postfix README documents, and combine their "<tt>make makefiles</tt>"
 instructions with the instructions above:  </p>
 
@@ -224,11 +224,32 @@ key configuration </a>
 <h3><a name="server_cert_key">Server-side certificate and private
 key configuration </a> </h3>
 
-<p> In order to use TLS, the Postfix SMTP server needs a certificate
-and a private key. Both must be in "pem" format. The private key
-must not be encrypted, meaning:  the key must be accessible without
-password.  Both certificate and private key may be in the same
-file.  </p>
+<p> In order to use TLS, the Postfix SMTP server generally needs
+a certificate and a private key. Both must be in "PEM" format. The
+private key must not be encrypted, meaning:  the key must be accessible
+without password.  Both certificate and private key may be in the same
+file, in which case the certificate file should be owned by "root" and
+not be readable by any other user. If the key is stored separately,
+this applies to the key file only, and the certificate file may be
+"world-readable". </p>
+
+<p> Public Internet MX hosts without certificates signed by a "reputable"
+CA must generate, and be prepared to present to most clients, a
+self-signed or private-CA signed certificate. The client will not be
+able to authenticate the server, but unless it is running Postfix 2.3 or
+similar software, it will still insist on a server certificate. </p>
+
+<p> For servers that are <b>not</b> public Internet MX hosts, Postfix
+2.3 supports configurations with no certificates. This entails the
+use of just the anonymous TLS ciphers, which are not supported by
+typical SMTP clients. Since such clients will not, as a rule, fall
+back to plain text after a TLS handshake failure, the server will
+be unable to receive email from most TLS enabled clients. To avoid
+accidental configurations with no certificates, Postfix 2.3 enables
+certificate-less operation only when the administrator explicitly sets
+"<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>&nbsp;=&nbsp;none". This ensures that new Postfix
+configurations with just "<a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a>&nbsp;=&nbsp;yes" added, will
+not accidentally run with no certificates. </p>
 
 <p> Both RSA and DSA certificates are supported. Typically you will
 only have RSA certificates issued by a commercial CA. In addition,
@@ -240,9 +261,9 @@ preferred. </p>
 
 <p> In order for remote SMTP clients to check the Postfix SMTP
 server certificates, the CA certificate (in case of a certificate
-chain, all CA certificates) must be available.  You should add
-these certificates to the server certificate, the server certificate
-first, then the issuing CA(s).  </p>
+chain, all CA certificates) must be available.  You should add any
+intermediate CA certificates to the server certificate: the server
+certificate first, then the intermediate CA(s).  </p>
 
 <p> Example: the certificate for "server.dom.ain" was issued by
 "intermediate CA" which itself has a certificate issued by "root
@@ -295,9 +316,19 @@ is correctly configured to supply its intermediate CA certificate). </p>
 </pre>  
 </blockquote>
 
+<p> Postfix 2.3 and later, TLS without certificates for servers serving
+exclusively anonymous-cipher capable clients: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> = none
+</pre>  
+</blockquote>
+
 <p> To verify a remote SMTP client certificate, the Postfix SMTP
 server needs to trust the certificates of the issuing certification
-authorities. These certificates in "pem" format can be stored in a
+authorities. These certificates in "PEM" format can be stored in a
 single $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> or in multiple files, one CA per file in
 the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> directory. If you use a directory, don't forget
 to create the necessary "hash" links with: </p>
@@ -319,16 +350,16 @@ privileges) from the files in the directory when the information
 is needed. Thus, the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> directory needs to be
 accessible inside the optional chroot jail. </p>
 
-<p> When you configure Postfix to request client certificates (by
-setting $<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes), any certificates in
-$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> are sent to the client, in order to allow it to
+<p> When you configure Postfix to request <a
+href="#server_vrfy_client">client certificates</a>, any CA certificates
+in $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> are sent to the client, in order to allow it to
 choose an identity signed by a CA you trust. If no $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>
-is specified, no preferred CA list is sent, and the client is free
-to choose an identity signed by any CA. Many clients use a fixed
-identity regardless of the preferred CA list and you may be able
-to reduce TLS negotiation overhead by installing client CA certificates
-mostly or only in $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a>. In the latter case you need
-not specify a $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>. </p>
+is specified, no preferred CA list is sent, and the client is free to
+choose an identity signed by any CA. Many clients use a fixed identity
+regardless of the preferred CA list and you may be able to reduce TLS
+negotiation overhead by installing client CA certificates mostly or
+only in $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a>. In the latter case you need not specify a
+$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>. </p>
 
 <p> Note, that unless client certificates are used to allow greater
 access to TLS authenticated clients, it is best to not ask for
@@ -348,7 +379,7 @@ the TLS handshake when client certificates are requested. </p>
 <h3><a name="server_logging"> Server-side TLS activity logging </a> </h3>
 
 <p> To get additional information about Postfix SMTP server TLS
-activity you can increase the loglevel from 0..4. Each logging
+activity you can increase the log level from 0..4. Each logging
 level also includes the information that is logged at a lower
 logging level. </p>
 
@@ -374,7 +405,7 @@ transmission after STARTTLS </td> </tr>
 
 </blockquote>
 
-<p> Use loglevel 3 only in case of problems. Use of loglevel 4 is
+<p> Use log level 3 only in case of problems. Use of log level 4 is
 strongly discouraged. </p>
 
 <p> Example: </p>
@@ -406,7 +437,7 @@ since the headers may be changed by intermediate servers. </p>
 
 <p> By default, TLS is disabled in the Postfix SMTP server, so no
 difference to plain Postfix is visible.  Explicitly switch it on
-using "<a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> = yes". </p>
+using "<a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a>&nbsp;=&nbsp;yes". </p>
 
 <p> Example: </p>
  
@@ -425,11 +456,12 @@ SMTP clients, but does not require that clients use TLS encryption.
 is never offered due to insufficient privileges to access the server
 private key. This is intended behavior. </p>
 
-<p> You can ENFORCE the use of TLS, so that the Postfix SMTP server
-announces STARTTLS and accepts no mail without TLS encryption, by
-setting "<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> = yes". According to <a href="http://www.faqs.org/rfcs/rfc2487.html">RFC 2487</a> this MUST
-NOT be applied in case of a publicly-referenced Postfix SMTP server.
-This option is off by default and should only seldom be used. </p>
+<p> <a name="server_enforce">You can ENFORCE the use of TLS</a>, so that
+the Postfix SMTP server announces STARTTLS and accepts no mail without
+TLS encryption, by setting "<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>&nbsp;=&nbsp;yes". According
+to <a href="http://www.faqs.org/rfcs/rfc2487.html">RFC 2487</a> this MUST NOT be applied in case of a publicly-referenced
+Postfix SMTP server.  This option is off by default and should only
+seldom be used. </p>
 
 <p> Example: </p>
  
@@ -449,7 +481,7 @@ and OE (5.01 Mac on all ports). </p>
 
 <p> It is strictly discouraged to use this mode from <a href="postconf.5.html">main.cf</a>. If
 you want to support this service, enable a special port in <a href="master.5.html">master.cf</a>
-and specify "-o <a href="postconf.5.html#smtpd_tls_wrappermode">smtpd_tls_wrappermode</a> = yes" as an <a href="smtpd.8.html">smtpd(8)</a> command
+and specify "-o <a href="postconf.5.html#smtpd_tls_wrappermode">smtpd_tls_wrappermode</a>&nbsp;=&nbsp;yes" as an <a href="smtpd.8.html">smtpd(8)</a> command
 line option.  Port 465 (smtps) was once chosen for this feature.
 </p>
 
@@ -467,46 +499,43 @@ line option.  Port 465 (smtps) was once chosen for this feature.
 
 <p> To receive a remote SMTP client certificate, the Postfix SMTP
 server must explicitly ask for one (any contents of $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>
-are also sent to the client as a hint for choosing a certificate
-from a suitable CA). Unfortunately, Netscape clients will either
-complain if no matching client certificate is available or will
-offer the user client a list of certificates to choose from.
-Additionally some MTAs (notably some versions of qmail) are unable
-to complete TLS negotiation when client certificates are requested,
-and abort the SMTP session. So this option is "off" by default.
-You will however need the certificate if you want to use certificate
-based relaying with, for example, the <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a>
-feature.  </p>
+are also sent to the client as a hint for choosing a certificate from
+a suitable CA). Unfortunately, Netscape clients will either complain
+if no matching client certificate is available or will offer the user
+client a list of certificates to choose from. Additionally some MTAs
+(notably some versions of qmail) are unable to complete TLS negotiation
+when client certificates are requested, and abort the SMTP session. So
+this option is "off" by default. You will however need the certificate
+if you want to use certificate based relaying with, for example, the
+<a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> feature. A server that wants client certificates
+must first present its own certificate. While Postfix 2.3 by default
+offers anonymous ciphers to clients, these are automatically suppressed
+when the server is configured to ask for client certificates. </p>
 
 <p> Example: </p>
  
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = no
+    <a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> = yes
+    <a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes
 </pre>
 </blockquote>
 
-<p> You may also decide to REQUIRE a remote SMTP client certificate
-before allowing TLS connections.  This feature is included for
-completeness, and implies "<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes".  </p>
-
-<p> Please be aware, that this will inhibit TLS connections without
-a proper client certificate and that it makes sense only when
-non-TLS submission is disabled (<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> = yes). Otherwise,
-clients could bypass the restriction by simply not using STARTTLS
-at all. </p>
-
-<p> When TLS is not enforced, the connection will be handled as
-if only "<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes" is specified, and a warning is
+<p> When TLS is <a href="#server_enforce">enforced</a> you may also decide
+to REQUIRE a remote SMTP client certificate for all TLS connections,
+by setting "<a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>&nbsp;=&nbsp;yes". This feature implies
+"<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a>&nbsp;=&nbsp;yes". When TLS is not enforced,
+"<a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>&nbsp;=&nbsp;yes" is ignored and a warning is
 logged. </p>
 
 <p> Example: </p>
+
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a> = no
+    <a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> = yes
+    <a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a> = yes
 </pre>
 </blockquote>
 
@@ -527,15 +556,15 @@ CA issues special CA which then issues the actual certificate...)
 
 <h3><a name="server_tls_auth">Supporting AUTH over TLS only</a></h3>
 
-<p> Sending AUTH data over an unencrypted channel poses a security
-risk. When TLS layer encryption is required (<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> =
-yes), the Postfix SMTP server will announce and accept AUTH only
+<p> Sending AUTH data over an unencrypted channel poses a security risk.
+When TLS layer encryption is required (<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>&nbsp;=&nbsp;yes),
+the Postfix SMTP server will announce and accept AUTH only
 after the TLS layer has been activated with STARTTLS. When TLS
-layer encryption is optional (<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> = no), it may
+layer encryption is optional (<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>&nbsp;=&nbsp;no), it may
 however still be useful to only offer AUTH when TLS is active. To
 maintain compatibility with non-TLS clients, the default is to
 accept AUTH without encryption. In order to change this behavior,
-set "<a href="postconf.5.html#smtpd_tls_auth_only">smtpd_tls_auth_only</a> = yes". </p>
+set "<a href="postconf.5.html#smtpd_tls_auth_only">smtpd_tls_auth_only</a>&nbsp;=&nbsp;yes". </p>
 
 <p> Example: </p>
  
@@ -658,30 +687,50 @@ the user or host.</p>
 
 <h3><a name="server_cipher">Server-side cipher controls</a> </h3>
 
-<p> To influence the Postfix SMTP server cipher selection scheme,
-you can give cipherlist string.  A detailed description would go
-to far here; please refer to the OpenSSL documentation.  If you
-don't know what to do with it, simply don't touch it and leave the
-(openssl-)compiled in default! </p>
+<p> The description below is for Postfix 2.3; for Postfix &lt; 2.3 the
+smtpd_tls_cipherlist parameter specifies the acceptable ciphers as an
+explicit OpenSSL cipherlist. </p>
 
-<p> DO NOT USE " to enclose the string, specify just the string!!! </p>
+<p> The Postfix SMTP server supports 5 distinct cipher security levels
+as specified by the <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a> configuration parameter. The
+default value is "export" which is the only one appropriate for public
+MX hosts.  On private MX hosts or MSAs one can further restrict the
+OpenSSL cipherlist selection. </p>
+
+<p> By default anonymous ciphers are allowed, and automatically disabled
+when client certificates are requested. If clients are expected to always
+verify the server certificate you may want to exclude anonymous ciphers
+by setting "<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a>&nbsp;=&nbsp;aNULL".  One can't
+force a client to check the server certificate, so excluding anonymous
+ciphers is generally unnecessary. </p>
+
+<p> For a server that is not a public Internet MX host, Postfix 2.3
+supports configurations with no <a href="#server_cert_key">server
+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>&nbsp;=&nbsp;none"
+and not specifying an <a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a>. </p>
+
+<p> Example: (MSA that requires  TLS with reasonably secure ciphers) </p>
 
-<p> Example: </p>
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtpd_tls_cipherlist">smtpd_tls_cipherlist</a> = DEFAULT
+    <a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> = yes
+    <a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> = yes
+    <a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> = /etc/postfix/cert.pem
+    <a href="postconf.5.html#smtpd_tls_key_file">smtpd_tls_key_file</a> = /etc/postfix/key.pem
+    <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a> = medium
+    <a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = aNULL, MD5
 </pre>
 </blockquote>
 
 <p> If you want to take advantage of ciphers with EDH, DH parameters
 are needed.  Instead of using the built-in DH parameters for both
-1024bit and 512bit, it is better to generate "own" parameters,
+1024bit and 512bit, it is better to generate your own parameters,
 since otherwise it would "pay" for a possible attacker to start a
 brute force attack against parameters that are used by everybody.
-For this reason, the parameters chosen are already different from
-those distributed with other TLS packages. </p>
+For this reason, the default parameters chosen by OpenSSL are already
+different from those distributed with other TLS packages. </p>
 
 <p> To generate your own set of DH parameters, use: </p>
 
@@ -723,6 +772,8 @@ handshake procedures.  </p>
 
 <ul>
 
+<li><a href="#client_lmtp_tls"> TLS support in the LMTP delivery agent </a>
+
 <li><a href="#client_cert_key">Client-side certificate and private
 key configuration </a>
 
@@ -731,19 +782,25 @@ key configuration </a>
 
 <li><a href="#client_tls_cache">Client-side TLS session cache</a>
 
-<li><a href="#client_tls_enable"> Enabling TLS in the Postfix SMTP client </a>
+<li><a href="#client_tls_limits"> Client TLS limitations </a>
 
-<li><a href="#client_tls_require"> Requiring TLS encryption </a>
+<li><a href="#client_tls_levels"> Client TLS security levels </a>
 
-<li><a href="#client_tls_nopeer"> Disabling server certificate verification </a>
+<li><a href="#client_tls_none"> Disabling TLS in the SMTP/LMTP client</a>
 
-<li><a href="#client_tls_per_site"> Per-site TLS policies </a>
+<li><a href="#client_tls_may"> Enabling TLS in the SMTP/LMTP client </a>
+
+<li><a href="#client_tls_encrypt"> Mandating TLS encryption </a>
+
+<li><a href="#client_tls_verify"> Mandating server certificate verification </a>
+
+<li><a href="#client_tls_secure"> Secure server certificate verification </a>
+
+<li><a href="#client_tls_policy"> Per-destination TLS policy </a>
 
-<!--
 <li><a href="#client_tls_obs"> Obsolete per-site TLS policy support </a>
--->
 
-<li><a href="#client_tls_harden"> Closing a DNS loophole with <!-- legacy --> per-site TLS policies </a>
+<li><a href="#client_tls_harden"> Closing a DNS loophole with obsolete per-site TLS policies </a>
 
 <li><a href="#client_tls_discover"> Discovering servers that support TLS </a>
 
@@ -755,9 +812,47 @@ key configuration </a>
 
 </ul>
 
+<h3><a name="client_lmtp_tls"> TLS support in the LMTP delivery agent </a>
+</h3>
+
+<p> In Postfix 2.3, the <a href="smtp.8.html">smtp(8)</a> and <a href="lmtp.8.html">lmtp(8)</a> delivery agents have been
+merged into a single dual-purpose program. As a result the <a href="lmtp.8.html">lmtp(8)</a>
+delivery agent is no longer the poor cousin of the more extensively used
+<a href="smtp.8.html">smtp(8)</a>. Specifically, as of Postfix 2.3, all the TLS features described
+below apply equally to SMTP and LMTP, after replacing the "smtp_"
+prefix of the each parameter name with "lmtp_".
+
+<p> The LMTP delivery agent can communicate with LMTP servers listening
+on UNIX-domain sockets. When server certificate verification is enabled
+and the server is listening on a UNIX-domain socket, the $<a href="postconf.5.html#myhostname">myhostname</a>
+parameter is used to set the TLS verification <i>nexthop</i> and
+<i>hostname</i>. Note, opportunistic encryption of LMTP traffic over
+UNIX-domain sockets is futile. TLS is only useful in this context when
+it is mandatory, typically to allow at least one of the server or the
+client to authenticate the other. The "null" cipher grade may be
+appropriate in this context, when available on both client and server.
+The "null" ciphers provide authentication without encryption. </p>
+
 <h3><a name="client_cert_key">Client-side certificate and private
 key configuration </a> </h3>
 
+<p> Do not configure client certificates unless you <b>must</b> present
+client TLS certificates to one or more servers. Client certificates are
+not usually needed, and can cause problems in configurations that work
+well without them. The recommended setting is to let the defaults stand: </p>
+
+<blockquote>
+<pre>
+    <a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a> =
+    <a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a> =
+    <a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a> =
+    <a href="postconf.5.html#smtp_tls_dkey_file">smtp_tls_dkey_file</a> =
+</pre>
+</blockquote>
+
+<p> The best way to use the default settings is to comment out the above
+parameters in <a href="postconf.5.html">main.cf</a> if present. </p>
+
 <p> During TLS startup negotiation the Postfix SMTP client may present
 a certificate to the remote SMTP server.  The Netscape client is
 rather clever here and lets the user select between only those
@@ -773,7 +868,7 @@ certificate is presented.  </p>
 
 <p> It is possible for the Postfix SMTP client to use the same
 key/certificate pair as the Postfix SMTP server.  If a certificate
-is to be presented, it must be in "pem" format. The private key
+is to be presented, it must be in "PEM" format. The private key
 must not be encrypted, meaning: it must be accessible without
 password. Both parts (certificate and private key) may be in the
 same file. </p>
@@ -953,119 +1048,727 @@ recommends a maximum of 24 hours.  </p>
 </pre>
 </blockquote>
 
-<h3><a name="client_tls_enable"> Enabling TLS in the Postfix SMTP
-client </a> </h3>
+<h3><a name="client_tls_limits"> Client TLS limitations </a>
+</h3>
+
+<p> The security properties of TLS communication channels are
+application specific. While the TLS protocol can provide a confidential,
+tamper-resistant, mutually authenticated channel between client
+and server, not all of these security features are applicable to every
+communication. </p>
+
+<p> For example, while mutual TLS authentication between browsers and web
+servers is possible, it is not practical, or even useful, for web-servers
+that serve the public to verify the identity of every potential user. In
+practice, most HTTPS transactions are asymmetric: the browser verifies
+the HTTPS server's identity, but the user remains anonymous. Much of
+the security policy is up to the client. If the client chooses to not
+verify the server's name, the server is not aware of this. There are many
+interesting browser security topics, but we shall not dwell
+on them here. Rather, our goal is to understand the security features
+of TLS in conjunction with SMTP. </p>
+
+<p> An important SMTP-specific observation is that a public MX host is
+even more at the mercy of the SMTP client than is an HTTPS server. Not only
+can it not enforce due care in the client's use of TLS, but it cannot even
+enforce the use of TLS, because TLS support in SMTP clients is still the
+exception rather than the rule. One cannot, in practice, limit access to
+one's MX hosts to just TLS-enabled clients. Such a policy would result
+in a vast reduction in one's ability to communicate by email with the
+world at large. </p>
+
+<p> One may be tempted to try enforcing TLS for mail from specific
+sending organizations, but this, too, runs into obstacles. One such
+obstacle is that we don't know who is (allegedly) sending mail until
+we see the "MAIL FROM:" SMTP command, and at that point, if TLS
+is not already in use, a potentially sensitive sender address (and
+with SMTP PIPELINING one or more of the recipients) has (have) already been
+leaked in the clear. Another obstacle is that mail from the sender to
+the recipient may be forwarded, and the forwarding organization may not
+have any security arrangements with the final destination. Bounces also
+need to be protected. These can only be identified by the IP address and
+HELO name of the connecting client, and it is difficult to keep track
+of all the potential IP addresses or HELO names of the outbound email
+servers of the sending organization. </p>
+
+<p> Consequently, TLS security for mail delivery to public MX hosts is
+almost entirely the client's responsibility. The server is largely a
+passive enabler of TLS security, the rest is up to the client. While the
+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
+attack. </p>
+
+<p> If clients instead attempted to verify the recipient domain name,
+an SMTP server for multiple domains would need to
+list all its email domain names in its certificate, and generate a
+new certificate each time a new domain were added. At least some CAs set
+fairly low limits (20 for one prominent CA) on the number of names that
+server certificates can contain. This approach is not consistent with
+current practice and does not scale. </p>
+
+<p> It is regrettably the case that TLS <i>secure-channels</i>
+(fully authenticated and immune to man-in-the-middle attacks) impose
+constraints on the sending and receiving sites that preclude ubiquitous
+deployment. One needs to manually configure this type of security for
+each destination domain, and in many cases implement non-default TLS
+<a href="#client_tls_policy">policy table</a> entries for additional
+domains hosted at a common secured destination. With Postfix 2.3, we
+make secure-channel configurations substantially easier to configure,
+but they will never be the norm. For the generic domain with which you
+have made no specific security arrangements, this security level is not
+a good fit. </p>
+
+<p> Given that strong authentication is not generally possible, and that
+verifiable certificates cost time and money, many servers that implement
+TLS use self-signed certificates or private CAs. This further limits
+the applicability of verified TLS on the public Internet. </p>
+
+<p> Historical note: while the documentation of these issues and many of the
+related features are new with Postfix 2.3, the issue was well
+understood before Postfix 1.0, when Lutz J&auml;nicke was designing
+the first unofficial Postfix TLS patch. See his original post <a
+href="http://www.imc.org/ietf-apps-tls/mail-archive/msg00304.html">http://www.imc.org/ietf-apps-tls/mail-archive/msg00304.html</a>
+and the first response <a
+href="http://www.imc.org/ietf-apps-tls/mail-archive/msg00305.html">http://www.imc.org/ietf-apps-tls/mail-archive/msg00305.html</a>.
+The problem is not even unique to SMTP or even TLS, similar issues exist
+for secure connections via aliases for HTTPS and Kerberos. SMTP merely
+uses indirect naming (via MX records) more frequently. </p>
+
+<h3><a name="client_tls_levels"> Client TLS security levels </a>
+</h3>
+
+<p> The TLS security levels listed below are described in more detail
+in the sections that follow.</p>
+
+<dl>
+<dt><b>none</b></dt>
+<dd><a href="#client_tls_none">No TLS.</a></dd>
+<dt><b>may</b></dt>
+<dd><a href="#client_tls_may">Opportunistic TLS.</a></dd>
+<dt><b>encrypt</b></dt>
+<dd><a href="#client_tls_encrypt">Mandatory TLS encryption.</a>
+<dt><b>verify</b></dt>
+<dd><a href="#client_tls_verify">Mandatory server certificate verification.</a>
+<dt><b>secure</b></dt>
+<dd><a href="#client_tls_secure">Secure-channel TLS.</a>
+</dl>
+
+<h3><a name="client_tls_none"> Disabling TLS in the SMTP/LMTP client </a>
+</h3>
 
-<p> By default, TLS is disabled in the Postfix SMTP client, so no
-difference to plain Postfix is visible.  If you enable TLS, the
-Postfix SMTP client will send STARTTLS when TLS support is announced
-by the remote SMTP server. </p>
+<p> At the "none" TLS security level, TLS encryption is
+disabled. This is the default security level. With Postfix 2.3 and later,
+it can be configured explicitly by setting "<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>&nbsp;=&nbsp;none". </p>
 
-<p> When the server accepts the STARTTLS command, but the subsequent
-TLS handshake fails, and no other server is available, the Postfix SMTP
-client defers the delivery attempt, and the mail stays in the queue. After
-a handshake failure, the communications channel is in an indeterminate
-state and cannot be used for non-TLS deliveries. </p>
+<p> With Postfix 2.2 and earlier, or when <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> is set to
+its default (backwards compatible) empty value, the appropriate configuration
+settings are "<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>&nbsp;=&nbsp;no" and "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;no".
+With either approach, TLS is not used even if supported by the server.
+For LMTP, use the corresponding "lmtp_" parameters. </p>
+
+<p> Per destination settings may override this default setting, in which case
+TLS is used selectively, only with destinations explicitly configured
+for TLS. </p>
+
+<p> You can disable TLS for a subset of destinations, while leaving
+it enabled for the rest. With the Postfix 2.3+ TLS <a
+href="#client_tls_policy">policy table</a>, specify the "none"
+security level. With the obsolete <a href="#client_tls_obs">per-site</a>
+table, specify the "NONE" keyword. </p>
+
+<h3><a name="client_tls_may"> Opportunistic TLS </a>
+</h3>
+
+<p> At the "may" TLS security level, TLS encryption is <i>opportunistic</i>.
+The SMTP transaction is encrypted if the STARTTLS ESMTP feature
+is supported by the server. Otherwise, messages are sent in the clear.
+With Postfix 2.3 and later, opportunistic TLS can be configured by
+setting "<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>&nbsp;=&nbsp;may".
+
+<p> Since sending in the clear is acceptable, demanding stronger
+than default TLS security merely reduces inter-operability.  For
+this reason, Postfix 2.3 and later ignore the <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a>
+and <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameters at the "may"
+security level: all protocols are allowed, and "export" grade or
+better ciphers are used. </p>
+
+<p> With Postfix 2.2 and earlier, or when <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> is
+set to its default (backwards compatible) empty value, the appropriate
+configuration settings are "<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>&nbsp;=&nbsp;yes" and
+"<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;no".
+For LMTP use the corresponding "lmtp" parameters. </p>
+
+<p> With opportunistic TLS, mail delivery continues even if the
+server certificate is untrusted or bears the wrong name.  Starting
+with Postfix 2.3, when the TLS handshake fails for an opportunistic
+TLS session, rather than give up on mail delivery, the transaction
+is retried with TLS disabled. Trying an unencrypted connection makes
+it possible to deliver mail to sites with non-interoperable server
+TLS implementations. </p>
+
+<p> Opportunistic encryption is never used for LMTP over UNIX-domain
+sockets. The communications channel is already confidential without
+TLS, so the only potential benefit of TLS is authentication. Do not
+configure opportunistic TLS for LMTP deliveries over UNIX-domain sockets.
+Only configure TLS for LMTP over UNIX-domain sockets at the
+<a href="#client_tls_encrypt">encrypt</a> security level or higher.
+Attempts to configure opportunistic encryption of LMTP sessions will
+be ignored with a warning written to the mail logs. </p>
+
+<p> You can enable opportunistic TLS just for selected destinations. With
+the Postfix 2.3+ TLS <a href="#client_tls_policy">policy table</a>,
+specify the "may" security level. With the obsolete <a
+href="#client_tls_obs">per-site</a> table, specify the "MAY" keyword.</p>
+
+<p> This is the most common security level for TLS protected SMTP
+sessions, stronger security is not generally available and, if needed,
+is typically only configured on a per-destination basis. See the section
+on TLS <a href="#client_tls_limits">limitations</a> above. </p>
 
 <p> Example: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax: </p>
+
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
     <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> = yes
+    <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = no
 </pre>
 </blockquote>
 
-<h3><a name="client_tls_require"> Requiring TLS encryption </a>
+<h3><a name="client_tls_encrypt"> Mandatory TLS encryption </a>
 </h3>
 
-<p> You can ENFORCE the use of TLS, so that the Postfix SMTP client
-will not deliver mail over unencrypted connections.  In this mode,
-the remote SMTP server hostname must match the information in the
-remote server certificate, and the server certificate must be issued
-by a CA that is trusted by the Postfix SMTP client.  If the remote
-server certificate doesn't verify or the remote SMTP server hostname
-doesn't match, and no other server is available, the delivery
-attempt is deferred and the mail stays in the queue.  </p>
-
-<p> The remote SMTP server hostname is verified against all names
-provided as dNSNames
-in the SubjectAlternativeName. If no dNSNames are specified, the
-CommonName is checked.  Verification may be turned off with the
-<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> option which is discussed below. </p>
-
-<p> Enforcing the use of TLS is useful if you know that you will
-only
-connect to servers that support <a href="http://www.faqs.org/rfcs/rfc2487.html">RFC 2487</a> _and_ that present server
-certificates that meet the above requirements.  An example would
-be a client only sends email to one specific mailhub that offers
-the necessary STARTTLS support.  </p>
+<p> At the "encrypt" TLS security level, messages are sent only
+over TLS encrypted sessions. The SMTP transaction is aborted unless
+the STARTTLS ESMTP feature is supported by the server. If no
+suitable servers are found, the message will be deferred. With Postfix
+2.3 and later, mandatory TLS encryption can be configured by setting
+"<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>&nbsp;=&nbsp;encrypt". Even though TLS encryption
+is always used, mail delivery continues if the server certificate is
+untrusted or bears the wrong name. </p>
+
+<p> At this security level and higher, the <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>
+and <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> configuration parameters determine
+the list of sufficiently secure SSL protocol versions and the minimum
+cipher strength. If the protocol or cipher requirements are not
+met, the mail transaction is aborted.  The documentation for these
+parameters includes useful interoperability and security guidelines.
+</p>
+
+<p> With Postfix 2.2 and earlier, or when <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>
+is set to its default (backwards compatible) empty value, the
+appropriate configuration settings are "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes"
+and "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;no". For LMTP use the corresponding
+<i>lmtp_</i> parameters. </p>
+
+<p> Despite the potential for eliminating passive eavesdropping attacks,
+mandatory TLS encryption is not viable as a default security level for
+mail delivery to the public Internet. Most MX hosts do not support TLS at
+all, and some of those that do have broken implementations. On a host
+that delivers mail to the Internet, you should not configure mandatory
+TLS encryption as the default security level. </p>
+
+<p> You can enable mandatory TLS encryption just for specific destinations.
+With the Postfix 2.3+ TLS <a href="#client_tls_policy">policy
+table</a>, specify the "encrypt" security level. With the
+obsolete <a href="#client_tls_obs">per-site</a> table, specify the
+"MUST_NOPEERMATCH" keyword. While the obsolete approach still works
+with Postfix 2.3, it is strongly discouraged: users of Postfix 2.3+
+should use the new TLS policy settings. </p>
+
+<p> Examples: </p>
+
+<p> In the example below, traffic to <i>example.com</i> and its sub-domains
+via the corresponding MX hosts always uses TLS. The protocol version will be
+"SSLv3" or "TLSv1" (the default setting of <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>
+excludes "SSLv2"). Only high or medium strength (i.e. 128 bit or
+better) ciphers will be used by default for all "encrypt" security
+level sessions. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = hash:/etc/postfix/tls_policy
+
+/etc/postfix/tls_policy:
+    example.com       encrypt
+    .example.com      encrypt
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax (no support for sub-domains without resorting to
+regexp tables). With Postfix 2.3+, do not use the obsolete <a
+href="#client_tls_obs">per-site</a> table. </p>
 
-<p> Example: </p>
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes
+    <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> = hash:/etc/postfix/tls_per_site
+
+/etc/postfix/tls_per_site:
+    example.com       MUST_NOPEERMATCH
 </pre>
 </blockquote>
 
-<h3> <a name="client_tls_nopeer"> Disabling server certificate
-verification </a> </h3>
-
-<p> As of <a href="http://www.faqs.org/rfcs/rfc2487.html">RFC 2487</a> the requirements for hostname checking for MTA
-clients are not set. When TLS is required (<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes),
-the option <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> can be set to "no" to disable
-strict remote SMTP server hostname checking. In this case, the mail
-delivery will proceed regardless of the CommonName etc. listed in
-the certificate. </p>
-
-<p>  Despite the potential for eliminating "man-in-the-middle" and
-other attacks, mandatory certificate/peername verification is not
-viable as a default Internet mail delivery policy at this time.  A
-significant fraction of TLS enabled MTAs uses self-signed certificates,
-or certificates that are signed by a private certificate authority.
-On a machine that delivers mail to the Internet, if you set
-<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes, you should probably also set
-<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = no.  You can use the per-site TLS
-policies (see below) to enable full peer verification for specific
-destinations that are known to have verifiable TLS server certificates.
+<p> In the next example, secure message submission is configured
+via the MSA "<tt>[example.net]:587</tt>". TLS sessions are encrypted
+without authentication, because this MSA does not possess an acceptable
+certificate. This MSA is known to be capable of "TLSv1" and "high" grade
+ciphers, so these are selected via the <a href="#client_tls_policy">policy
+table</a>. </p>
+
+<p><b>Note:</b> the policy table lookup key is the verbatim next-hop
+specification from the recipient domain, <a href="transport.5.html">transport(5)</a> table or <a href="postconf.5.html#relayhost">relayhost</a>
+parameter, with any enclosing square brackets and optional port. Take
+care to be consistent: the suffixes ":smtp" or ":25" or no port suffix
+result in different policy table lookup keys, even though they are
+functionally equivalent nexthop specifications. Use at most one of these
+forms for all destinations. Below, the policy table has multiple keys,
+just in case the transport table entries are not specified consistently. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = hash:/etc/postfix/tls_policy
+
+/etc/services:
+    submission      587/tcp         msa             # mail message submission
+
+/etc/postfix/tls_policy:
+    [example.net]:587 encrypt protocols=TLSv1 ciphers=high
+    [example.net]:msa encrypt protocols=TLSv1 ciphers=high
+    [example.net]:submission encrypt protocols=TLSv1 ciphers=high
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax: </p>
+
+<p> <b>Note:</b> Avoid policy lookups with the bare hostname (for
+example, "example.net").  Instead,
+use the destination (for example, "[example.net]:587"), as the <a
+href="#client_tls_obs">per-site</a> table lookup key (a recipient domain
+or MX-enabled transport nexthop with no port suffix may look like a bare
+hostname, but is still a suitable <i>destination</i>). With Postfix 2.3+,
+do not use the obsolete <a href="#client_tls_obs">per-site</a> table;
+use the new <a href="#client_tls_policy">policy table</a> instead. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> = hash:/etc/postfix/tls_per_site
+
+/etc/postfix/tls_per_site:
+    [example.net]:587   MUST_NOPEERMATCH
+</pre>
+</blockquote>
+
+<h3><a name="client_tls_verify"> Mandatory server certificate verification </a>
+</h3>
+
+<p> At the "verify" TLS security level, messages are sent only
+over TLS encrypted sessions for which server certificate verification
+succeeds. If no suitable servers are found, the message will be
+deferred. With Postfix 2.3 and later, mandatory server certificate
+verification can be configured by setting
+"<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>&nbsp;=&nbsp;verify", the
+<a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter can override the default
+"hostname" certificate match strategy. Fine-tuning the matching
+strategy is generally only appropriate for <a
+href="#client_tls_secure">secure-channel</a> destinations. </p>
+
+<p> With Postfix 2.2 and earlier, or when <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>
+is set to its default (backwards compatible) empty value, the
+appropriate configuration settings are "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes" and
+"<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;yes". For LMTP use the corresponding
+<i>lmtp_</i> parameters. </p>
+
+<p> If the server certificate chain is trusted (see <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>), any DNS names in the SubjectAlternativeName
+certificate extension are used to verify the server name.  If no
+DNS names are specified, the certificate CommonName is checked.
+If you want mandatory encryption without server certificate
+verification, see <a href="#client_tls_encrypt">above</a>. </p>
+
+<p> Despite the potential for eliminating "man-in-the-middle" and other
+attacks, mandatory certificate trust chain and subject name verification
+is not viable as a default Internet mail delivery policy.  Most MX hosts
+do not support TLS at all, and a significant portion of TLS enabled
+MTAs use self-signed certificates, or certificates that are signed by
+a private certificate authority. On a machine that delivers mail to
+the Internet, you should not configure mandatory server certificate
+verification as a default policy. </p>
+
+<p> Mandatory server certificate verification as a default security
+level may be appropriate if you know that you will only connect to
+servers that support <a href="http://www.faqs.org/rfcs/rfc2487.html">RFC 2487</a> <i>and</i> that present verifiable
+server certificates. An example would be a client that sends all
+email to a central mailhub that offers the necessary STARTTLS
+support. In such cases, you can often use a <a
+href="#client_tls_secure">secure-channel</a> configuration instead.
 </p>
 
+<p> You can enable mandatory server certificate verification just
+for specific destinations.  With the Postfix 2.3+ TLS <a
+href="#client_tls_policy">policy table</a>, specify the "verify"
+security level. With the obsolete <a href="#client_tls_obs">per-site</a>
+table, specify the "MUST" keyword.  While the obsolete approach
+still works with Postfix 2.3, it is strongly discouraged: users of
+Postfix 2.3+ should use the new TLS policy settings. </p>
+
 <p> Example: </p>
+
+<p> In this example, the client encrypts all traffic to the
+<i>example.com</i> domain. The peer hostname is verified, but
+verification is vulnerable to DNS response forgery. Mail transmission
+to <i>example.com</i> recipients uses "high" grade ciphers. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    indexed = ${<a href="postconf.5.html#default_database_type">default_database_type</a>}:${<a href="postconf.5.html#config_directory">config_directory</a>}/
+    <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = ${<a href="postconf.5.html#config_directory">config_directory</a>}/CAfile.pem
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ${indexed}tls_policy
+
+/etc/postfix/tls_policy:
+    example.com       verify ciphers=high
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax: </p>
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    indexed = ${<a href="postconf.5.html#default_database_type">default_database_type</a>}:${<a href="postconf.5.html#config_directory">config_directory</a>}/
+    <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = ${<a href="postconf.5.html#config_directory">config_directory</a>}/CAfile.pem
+    <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> = ${indexed}tls_per_site
+
+/etc/postfix/tls_per_site:
+    example.com         MUST
+</pre>
+</blockquote>
+
+<h3><a name="client_tls_secure"> Secure server certificate verification </a>
+</h3>
+
+<p> At the <i>secure</i> TLS security level, messages are sent only over
+<i>secure-channel</i> TLS sessions where DNS forgery resistant server
+certificate verification succeeds. If no suitable servers are found, the
+message will be deferred. With Postfix 2.3 and later, secure-channels
+can be configured by setting "<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>&nbsp;=&nbsp;secure".
+The <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter can override the default
+"nexthop, dot-nexthop" certificate match strategy. </p>
+
+<p> With Postfix 2.2 and earlier, or when <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>
+is set to its default (backwards compatible) empty value, the
+appropriate configuration settings are "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes"
+and "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;yes" with additional settings to
+<a href="#client_tls_harden">harden</a> peer certificate verification
+against forged DNS data. For LMTP, use the corresponding <i>lmtp_</i>
+parameters. </p>
+
+<p> If the server certificate chain is trusted (see <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>), any DNS names in the SubjectAlternativeName certificate
+extension are used to verify the server name. If no DNS names are
+specified, the CommonName is checked. If you want mandatory encryption
+without server certificate verification, see <a
+href="#client_tls_encrypt">above</a>. </p>
+
+<p> Despite the potential for eliminating "man-in-the-middle" and other
+attacks, mandatory secure server certificate verification is not
+viable as a default Internet mail delivery policy.  Most MX hosts
+do not support TLS at all, and a significant portion of TLS enabled
+MTAs use self-signed certificates, or certificates that are signed
+by a private certificate authority. On a machine that delivers mail
+to the Internet, you should not configure secure TLS verification
+as a default policy. </p>
+
+<p> Mandatory secure server certificate verification as a default
+security level may be appropriate if you know that you will only
+connect to servers that support <a href="http://www.faqs.org/rfcs/rfc2487.html">RFC 2487</a> <i>and</i> that present
+verifiable server certificates. An example would be a client that
+sends all email to a central mailhub that offers the necessary
+STARTTLS support. </p>
+
+<p> You can enable secure TLS verification just for specific destinations.
+With the Postfix 2.3+ TLS <a href="#client_tls_policy">policy table</a>,
+specify the "secure" security level. With the obsolete
+<a href="#client_tls_obs">per-site</a> table, specify the "MUST"
+keyword and <a href="#client_tls_harden">harden</a> the certificate
+verification against DNS forgery. While the obsolete approach still
+works with Postfix 2.3, it is strongly discouraged: users of Postfix 2.3+
+should use the new TLS policy settings. </p>
+
+<p> Examples: </p>
+
+<p> Secure-channel TLS without <a href="transport.5.html">transport(5)</a> table overrides: </p>
+
+<p> The client will encrypt all traffic and verify the destination name
+immune from forged DNS responses. MX lookups are still used to find
+the SMTP servers for <i>example.com</i>, but these are not used when
+checking the names in the server certificate(s). Rather, the requirement
+is that the MX hosts for <i>example.com</i> have trusted certificates
+with a subject name of <i>example.com</i> or a sub-domain, see the
+documentation for the <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter. </p>
+
+<p> The related domains <i>example.co.uk</i> and <i>example.co.jp</i> are
+hosted on the same MX hosts as the primary <i>example.com</i> domain, and
+traffic to these is secured by verifying the primary <i>example.com</i>
+domain in the server certificates. This frees the server administrator
+from needing the CA to sign certificates that list all the secondary
+domains. The downside is that clients that want secure channels to the
+secondary domains need explicit TLS <a href="#client_tls_policy">policy
+table</a> entries. </p>
+
+<p> Note, there are two ways to handle related domains.  The first is to
+use the default routing for each domain, but add policy table entries
+to override the expected certificate subject name.  The second is to
+override the next-hop in the transport table, and use a single policy
+table entry for the common nexthop.  We choose the first approach,
+because it works better when domain ownership changes. With the second
+approach we securely deliver mail to the wrong destination, with the
+first approach, authentication fails and mail stays in the local queue,
+the first approach is more appropriate in most cases. <p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = /etc/postfix/CAfile.pem
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = hash:/etc/postfix/tls_policy
+
+/etc/postfix/transport:
+
+/etc/postfix/tls_policy:
+    example.com     secure
+    example.co.uk   secure match=example.com:.example.com
+    example.co.jp   secure match=example.com:.example.com
+</pre>
+</blockquote>
+
+<p> Secure-channel TLS with <a href="transport.5.html">transport(5)</a> table overrides: <p>
+
+<p> In this case traffic to <i>example.com</i> and its related domains
+is sent to a single logical gateway (to avoid a single point of failure,
+its name may resolve to one or more load-balancer addresses, or to the
+combined addresses of multiple physical hosts). All the physical hosts
+reachable via the gateway's IP addresses have the logical gateway name
+listed in their certificates. This secure-channel configuration can also
+be implemented via a <a href="#client_tls_harden">hardened</a> variant of
+the MUST policy in the obsolete <a href="#client_tls_obs">per-site</a>
+table. As stated above, this approach has the potential to mis-deliver
+email if the related domains change hands. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = /etc/postfix/CAfile.pem
+    <a href="postconf.5.html#transport_maps">transport_maps</a> = hash:/etc/postfix/transport
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = hash:/etc/postfix/tls_policy
+
+/etc/postfix/transport:
+    example.com     <a href="smtp.8.html">smtp</a>:[tls.example.com]
+    example.co.uk   <a href="smtp.8.html">smtp</a>:[tls.example.com]
+    example.co.jp   <a href="smtp.8.html">smtp</a>:[tls.example.com]
+
+/etc/postfix/tls_policy:
+    [tls.example.com] secure match=tls.example.com
+</pre>
+</blockquote>
+
+<p> Postfix 2.2.9+ syntax: </p>
+
+<p> <b>Note:</b> Avoid policy lookups with the bare hostname (for
+example, "tls.example.com").  Instead, use the destination (for
+example, "[tls.example.com]") as the <a
+href="#client_tls_obs">per-site</a> table lookup key (a recipient domain
+or MX-enabled transport nexthop with no port suffix may look like a bare
+hostname, but is still a suitable <i>destination</i>). With Postfix 2.3+,
+do not use the obsolete <a href="#client_tls_obs">per-site</a> table;
+use the new <a href="#client_tls_policy">policy table</a> instead. </p>
+
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes
-    <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = no
+    <a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a> = no
+    <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = /etc/postfix/CAfile.pem
+    <a href="postconf.5.html#transport_maps">transport_maps</a> = hash:/etc/postfix/transport
+    <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> = hash:/etc/postfix/tls_per_site
+
+/etc/postfix/transport:
+    example.com     <a href="smtp.8.html">smtp</a>:[tls.example.com]
+    example.co.uk   <a href="smtp.8.html">smtp</a>:[tls.example.com]
+    example.co.jp   <a href="smtp.8.html">smtp</a>:[tls.example.com]
+
+/etc/postfix/tls_per_site:
+    [tls.example.com]       MUST
 </pre>
 </blockquote>
 
-<h3> <a name="client_tls_per_site"> Per-site TLS policies </a> </h3>
+<h3> <a name="client_tls_policy"> TLS policy table </a>
+</h3>
+
+<p> Postfix 2.3 introduces a new more flexible TLS policy table. For
+earlier releases, read the description of the obsolete Postfix 2.2 <a
+href="#client_tls_obs">per-site</a> table. </p>
 
 <p> A small fraction of servers offer STARTTLS but the negotiation
-consistently fails, leading to mail aging out of the queue and
-bouncing back to the sender. In such cases, you can use the per-site
-policies to disable TLS for the problem sites.  Alternatively, you
-can enable TLS for just a few specific sites and not enable it for
-all sites. </p>
+consistently fails. With Postfix 2.3, so long as encryption is not
+enforced, the delivery is immediately retried with TLS disabled.  You no
+longer need to explicitly disable TLS for the problem destinations.
+As soon as their TLS software or configuration is repaired, encryption
+will be used. </p>
+
+<p> The new policy table is specified via the <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>
+parameter. This lists optional lookup tables with the Postfix SMTP client
+TLS security policy by next-hop destination. It supersedes the obsolete
+<a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> parameter. When $<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> is not empty,
+the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> parameter is ignored (a warning is written to the
+logs if it is also non-empty). </p>
+
+<p> The TLS policy table is indexed by the full next-hop destination,
+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
+<a href="#client_lmtp_tls">LMTP</a> 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
+sockets, is used as the nexthop name for certificate verification. The
+port and any enclosing square brackets are used in the table lookup key,
+but are not used for server name verification. </p>
+
+<p> When the lookup key is a domain name without enclosing square brackets
+or any <i>:port</i> suffix (typically the recipient domain), and the full
+domain is not found in the table, just as with the <a href="transport.5.html">transport(5)</a> table,
+the parent domain starting with a leading "." is matched recursively. This
+allows one to specify a security policy for a recipient domain and all
+its sub-domains. </p>
+
+<p> The lookup result is a security level, followed by an optional
+list of whitespace and/or comma separated name=value attributes
+that override related <a href="postconf.5.html">main.cf</a> settings.  The TLS security <a
+href="#client_tls_levels">levels</a> are described above. Below, we
+describe the corresponding table syntax: </p>
+
+<dl>
+
+<dt><b>none</b></dt>         
+<dd>No TLS. No additional attributes are supported at this level. </dd>
+
+<dt><b>may</b></dt>
+<dd>Opportunistic TLS. No additional attributes are supported at this
+level. </dd>
+
+<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. At this level and
+higher the optional "ciphers" attribute overrides the <a href="postconf.5.html">main.cf</a>
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> parameter and the optional "protocols"
+keyword overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameter.
+In the policy table, multiple protocols must be separated by colons,
+as attribute values may not contain whitespace or commas.</dd>
+
+<dt><b>verify</b></dt>
+<dd>Mandatory server certificate verification. The optional "match"
+attribute overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter.
+In the policy table, multiple match patterns and strategies must
+be separated by colons. </dd>
+
+<dt><b>secure</b></dt> <dd>Secure-channel TLS. The optional "match"
+attribute overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter. In
+the policy table, multiple match patterns and strategies must be separated
+by colons. The match attribute is useful when additional domains are
+supported by common server, the policy entries for the additional domains
+specify matching rules for the primary domain certificate. While transport
+table overrides routing secondary domains to the primary nexthop also
+allow secure verification, they risk delivery to the wrong destination
+when domains change hands or are re-assigned to new gateways. With the
+"match" attribute approach, routing is not perturbed, and mail is deferred
+if verification of a new MX host fails. </dd>
+
+</dl>
+
+<p>
+Example:
+</p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = hash:/etc/postfix/tls_policy
+/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.net             secure
+    .example.net            secure match=.example.net:example.net
+    [mail.example.org]:587  secure match=nexthop
+</pre>
+</blockquote>
 
-<!-- insert new-style TLS policy mechanism here
+<p> <b>Note:</b> The "hostname" strategy if listed in a non-default setting
+of <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> or in the "match" attribute in the policy
+table can render the "secure" level vulnerable to DNS forgery. Do not use
+the "hostname" strategy for <a href="#client_tls_secure">secure-channel</a>
+configurations in environments where DNS security is not assured. </p>
 
 <h3> <a name="client_tls_obs"> Obsolete per-site TLS policy support 
 </a> </h3>
 
 <p> This section describes an obsolete per-site TLS policy mechanism.
-Unlike the newer mechanism it supports TLS policy lookup by server
-hostname, and lacks control over what names can appear in server
-certificates.  Because of this, the obsolete mechanism is vulnerable
-to false DNS hostname information in MX or CNAME records.  These
-attacks can be eliminated only with great difficulty.  </p>
-
--->
-
-<p> The <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> table is searched for a policy that matches
+Unlike the Postfix 2.3 <a href="#client_tls_policy">policy table</a>
+mechanism, this uses as a policy lookup key a potentially untrusted
+server hostname, and lacks control over what names can appear in
+server certificates.  Because of this, the obsolete mechanism is
+typically vulnerable to false DNS hostname information in MX or
+CNAME records.  These attacks can be eliminated only with great
+difficulty. The new <a href="#client_tls_policy">policy table</a>
+makes <a href="#client_tls_secure">secure-channel</a> configurations
+easier and provides more control over the cipher and protocol selection
+for sessions with mandatory encryption. </p>
+
+<p> Avoid policy lookups with the bare hostname.  Instead, use the
+full destination nexthop (enclosed in [] with a possible ":port"
+suffix) as the per-site table lookup key (a recipient domain or
+MX-enabled transport nexthop with no port suffix may look like a bare
+hostname, but is still a suitable <i>destination</i>).  With Postfix 2.3+,
+use of the obsolete approach documented here is strongly discouraged:
+use the new <a href="#client_tls_policy">policy table</a> instead. </p>
+
+<p> Starting with Postfix 2.3, the underlying TLS enforcement levels are
+common to the obsolete per-site table and the new policy table. The
+<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>
+parameters control the TLS ciphers and protocols for mandatory
+encryption regardless of which table is used. The
+<a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter determines the match strategy
+for the obsolete "MUST" keyword in the same way as for the "verify"
+level in the new policy. </p>
+
+<p> With Postfix &lt; 2.3, the obsolete smtp_tls_cipherlist parameter
+is also applied for opportunistic TLS sessions, and should be used with
+care, or not at all. Setting cipherlist restrictions that are incompatible
+with a remote SMTP server render that server unreachable, TLS handshakes
+are always attempted and always fail. </p>
+
+<p> When <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> is empty (default) and <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a>
+is not empty, the per-site table is searched for a policy that matches
 the following information:  </p>
 
 <blockquote>
@@ -1073,17 +1776,19 @@ the following information:  </p>
 <dl>
 
 <dt> remote SMTP server hostname </dt> <dd> This is simply the DNS
-name of the server that the Postfix SMTP client connects to; this
-name may be obtained from other DNS lookups, such as MX lookups or
-CNAME lookups. </dd>
-
-<dt> next-hop destination </dt> <dd> This is normally the domain
-portion of the recipient address, but it may be overruled by
-information from the <a href="transport.5.html">transport(5)</a> table, from the <a href="postconf.5.html#relayhost">relayhost</a> parameter
-setting, or from the <a href="postconf.5.html#relay_transport">relay_transport</a> setting. When it's not the
-recipient domain, the next-hop destination can have the Postfix-specific
-form "<tt>[name]</tt>", <tt>[name]:port</tt>", "<tt>name</tt>" or
-"<tt>name:port</tt>".  </dd>
+name of the server that the Postfix SMTP client connects to; this name
+may be obtained from other DNS lookups, such as MX lookups or CNAME
+lookups. Use of the hostname lookup key is discouraged; always use the
+next-hop destination instead. </dd>
+
+<dt> next-hop destination </dt> <dd> This is normally the domain portion
+of the recipient address, but it may be overridden by information from
+the <a href="transport.5.html">transport(5)</a> table, from the <a href="postconf.5.html#relayhost">relayhost</a> parameter setting, or from
+the <a href="postconf.5.html#relay_transport">relay_transport</a> setting. When it is not the recipient domain, the
+next-hop destination can have the Postfix-specific form "<tt>[name]</tt>",
+"<tt>[name]:port</tt>", "<tt>name</tt>" or "<tt>name:port</tt>".  This is
+the recommended lookup key for per-site policy lookups (and incidentally
+for <a href="SASL_README.html#client_sasl">SASL password</a> lookups). </dd>
 
 </dl>
 
@@ -1103,34 +1808,27 @@ side specify one of the following keywords:  </p>
 
 <dl>
 
-<dt> NONE </dt> <dd> Don't use TLS at all. This overrides a less
-specific <b>MAY</b> lookup result from the alternate host or next-hop
-lookup key, and overrides the global <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>,
-and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> settings. </dd>
-
-<dt> MAY </dt> <dd> Try to use TLS if the server announces support,
-otherwise use the unencrypted connection. This has less precedence
-than a more specific result (including <b>NONE</b>) from the alternate
-host or next-hop lookup key, and has less precedence than the more
-specific global "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes" or "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>
-= yes".  </dd>
-
-<dt> MUST_NOPEERMATCH </dt> <dd> Require TLS encryption, but do not
-require that the remote SMTP server hostname matches the information
-in the remote SMTP server certificate, or that the server certificate
-was issued by a trusted CA. This overrides a less secure <b>NONE</b>
-or a less specific <b>MAY</b> lookup result from the alternate host
-or next-hop lookup key, and overrides the global <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>,
-<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> settings.  </dd>
-
-<dt> MUST </dt> <dd> Require TLS encryption, require that the remote
-SMTP server hostname matches the information in the remote SMTP
-server certificate, and require that the remote SMTP server certificate
-was issued by a trusted CA. This overrides a less secure <b>NONE</b>
-and <b>MUST_NOPEERMATCH</b> or a less specific <b>MAY</b> lookup
+<dt> NONE </dt> <dd> No TLS. This overrides a less specific "MAY" lookup
 result from the alternate host or next-hop lookup key, and overrides
-the global <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>
-settings.  </dd>
+the global <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>, and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>
+settings. </dd>
+
+<dt> MAY </dt> <dd> Opportunistic TLS. This has less precedence than
+a more specific result (including "NONE") from the alternate host or
+next-hop lookup key, and has less precedence than the more specific global
+"<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes" or "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;yes".  </dd>
+
+<dt> MUST_NOPEERMATCH </dt> <dd> Mandatory TLS encryption. This
+overrides a less secure "NONE" or a less specific "MAY" lookup result
+from the alternate host or next-hop lookup key, and overrides the global
+<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> settings.
+</dd>
+
+<dt> MUST </dt> <dd> Mandatory server certificate verification.
+This overrides a less secure "NONE" and "MUST_NOPEERMATCH" or a
+less specific "MAY" lookup result from the alternate host or next-hop
+lookup key, and overrides the global <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>
+and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> settings.  </dd>
 
 </dl>
 
@@ -1144,8 +1842,8 @@ policies can be summarized as follows: </p>
 <li> <p> When neither the remote SMTP server hostname nor the
 next-hop destination are found in the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> table, the
 policy is based on <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> and
-<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>. Note: "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes" and
-"<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = yes" imply "<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> = yes". </p>
+<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>. Note: "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes" and
+"<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;yes" imply "<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>&nbsp;=&nbsp;yes". </p>
 
 <li> <p> When both hostname and next-hop destination lookups produce
 a result, the more specific per-site policy (NONE, MUST, etc)
@@ -1154,60 +1852,83 @@ policy (MUST, etc) overrides the less secure one (NONE).  </p>
 
 <li> <p> After the per-site policy lookups are combined, the result
 generally overrides the global policy. The exception is the less
-specific <b>MAY</b> per-site policy, which is overruled by the more
-specific global "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes" with server certificate
+specific "MAY" per-site policy, which is overruled by the more
+specific global "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes" with server certificate
 verification as specified with the <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>
 parameter.  </p>
 
 </ul>
 
 <h3> <a name="client_tls_harden"> Closing a DNS loophole with 
-<!-- legacy --> per-site TLS policies </a> </h3>
+obsolete per-site TLS policies </a> </h3>
+
+<p> For a general discussion of TLS security for SMTP see <a
+href="#client_tls_limits">TLS limitations</a> above. What follows applies
+only to Postfix 2.2.9 and subsequent Postfix 2.2 patch levels. Do
+not use this approach with Postfix 2.3+; instead see the instructions under <a
+href="#client_tls_secure">secure</a> server certificate verification. </p>
 
 <p> As long as no secure DNS lookup mechanism is available, false
-hostnames in MX or CNAME responses can change the server hostname
-that Postfix uses for TLS policy lookup and server certificate
-verification. Even with a perfect match between the server hostname
-and the server certificate, there is no guarantee that Postfix is
-connected to the right server.  To avoid this loophole take the
-following steps: </p>
+hostnames in MX or CNAME responses can change Postfix's notion of the
+server hostname that is used for TLS policy lookup and server certificate
+verification. Even with a perfect match between the server hostname and
+the server certificate, there is no guarantee that Postfix is connected
+to the right server.  To avoid this loophole, take all of the following
+steps: </p>
 
-<ul>
+<ol>
+
+<li> <p> Use a dedicated transport for all secure-channel deliveries. </p>
 
 <li> <p> Eliminate MX lookups. Specify local <a href="transport.5.html">transport(5)</a> table
 entries for sensitive domains with explicit <a href="smtp.8.html">smtp</a>:[<i>mailhost</i>]
 or <a href="smtp.8.html">smtp</a>:[<i>mailhost</i>]:<i>port</i> destinations (you can assure
-security of this table unlike DNS); in the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> table
-specify the value <b>MUST</b> for the key [<i>mailhost</i>] or
+security of this table unlike DNS); in the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a>
+table, specify the value "MUST" for the key [<i>mailhost</i>] or
 <a href="smtp.8.html">smtp</a>:[<i>mailhost</i>]:<i>port</i>. This prevents false hostname
-information in DNS MX records from changing the server hostname
-that Postfix uses for TLS policy lookup and server certificate
+information in DNS MX records from changing Postfix's notion of the
+server hostname that is used for TLS policy lookup and server certificate
 verification. </p>
 
-<li> <p> Disallow CNAME hostname overrides. In <a href="postconf.5.html">main.cf</a> specify
-"<a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a> = no". This prevents false hostname
+<li> <p> Disallow CNAME hostname overrides. In <a href="postconf.5.html">main.cf</a>, specify
+"<a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a>&nbsp;=&nbsp;no". This prevents false hostname
 information in DNS CNAME records from changing the server hostname
 that Postfix uses for TLS policy lookup and server certificate
-verification. This feature requires Postfix 2.2.9 or later.  </p>
+verification. This feature requires Postfix 2.2.9 or later.  The
+default value is "no" starting with Postfix 2.3. </p>
 
-</ul>
+</ol>
 
 <p> Example: </p>
 
-<blockquote> <pre>
-/etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> = hash:/etc/postfix/tls_per_site
-    <a href="postconf.5.html#relayhost">relayhost</a> = [msa.example.net]:587
+<p> We give the <a href="postconf.5.html#default_transport">non-default</a>
+"securetls" transport an explicit <a href="master.5.html">master.cf</a> process limit, so that we
+don't raise its process limit when raising $<a href="postconf.5.html#default_process_limit">default_process_limit</a>. The
+total process limit for *all* transports should stay somewhat under 1024
+(the typical select() file descriptor limit); otherwise transports may
+be throttled under steady high load, compounding congestion.  It is not
+uncommon at high volume sites to set the default process limit to 500
+or more. </p>
 
-/etc/postfix/tls_per_site:
-    # <a href="postconf.5.html#relayhost">relayhost</a> exact nexthop match
-    [msa.example.net]:587       MUST
+<p> We also default the "securetls" transport TLS security level to
+<a href="#client_tls_verify">MUST</a>, obviating the need for <a
+href="#client_tls_obs">per-site</a> table entries for secure-channel
+destinations. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#transport_maps">transport_maps</a> = hash:/etc/postfix/transport
 
-    # TLS should not be used with the <i>example.org</i> MX hosts.
-    example.org                 NONE
+/etc/postfix/transport:
+    example.com         securetls:[tls.example.com]
 
-    # TLS should not be used with the host <i>smtp.example.com</i>.
-    smtp.example.com             NONE
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+    securetls unix  -       -       n       -       100     smtp
+        -o <a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a>=no
+        -o <a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a>=
+        -o <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>=yes
+        -o <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>=yes
 </pre>
 </blockquote>
 
@@ -1229,7 +1950,7 @@ postfix/smtp[pid]: Host offered STARTTLS: [hostname.example.com]
 </blockquote>
 
 <p> Example: </p>
+
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
@@ -1242,8 +1963,8 @@ postfix/smtp[pid]: Host offered STARTTLS: [hostname.example.com]
 <p> When verifying a remote SMTP server certificate, a verification
 depth of 1 is sufficient if the certificate is directly issued by
 a CA specified with <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> or <a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a>.  The default
-value of 5 should also suffice for longer chains (root CA issues
-special CA which then issues the actual certificate...) </p>
+value of 5 should also suffice for longer chains (where the root CA issues
+a special CA certificate which then issues the actual certificate). </p>
 
 <p> Example: </p>
  
@@ -1256,20 +1977,33 @@ special CA which then issues the actual certificate...) </p>
 
 <h3> <a name="client_cipher">Client-side cipher controls </a> </h3>
 
-<p> To influence the Postfix SMTP client cipher selection scheme,
-you can give cipherlist string.  A detailed description would go
-to far here; please refer to the OpenSSL documentation.  If you
-don't know what to do with it, simply don't touch it and leave the
-(openssl-)compiled in default! </p>
-
-<p> DO NOT USE " to enclose the string, specify just the string!!! </p>
+<p> The Postfix SMTP client supports 5 distinct cipher security levels
+as specified by the <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> configuration
+parameter. This setting controls the minimum acceptable SMTP client
+TLS cipher grade for use with mandatory TLS encryption. The default
+value "medium" is suitable for most destinations with which you may
+want to enforce TLS, and is beyond the reach of today's crypt-analytic
+methods. See <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> for information on how to configure
+ciphers on a per-destination basis. </p>
+
+<p> By default anonymous ciphers are allowed, and automatically
+disabled when server certificates are verified. If you
+want to disable even at the "encrypt" security level, set
+"<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a>&nbsp;=&nbsp;aNULL",
+to disable anonymous ciphers even with opportunistic TLS, set
+"<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a>&nbsp;=&nbsp;aNULL". There is generally no
+need to take these measures. Anonymous ciphers save bandwidth and TLS
+session cache space, if certificates are ignored, there is little point
+in requesting them. </p>
 
 <p> Example: </p>
  
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> = DEFAULT
+    <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> = medium
+    <a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> = RC4, MD5
+    <a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = aNULL
 </pre>
 </blockquote>
 
@@ -1460,7 +2194,7 @@ steps ago. </p>
 <blockquote>
 <pre>
 % <b>openssl ca -out FOO-cert.pem -infiles FOO-req.pem</b>
-Uing configuration from /etc/ssl/openssl.cnf
+Using configuration from /etc/ssl/openssl.cnf
 Enter PEM pass phrase:<b>whatever</b>
 Check that the request matches the signature
 Signature ok
@@ -1494,20 +2228,26 @@ super-user privileges. </p>
 </blockquote>
 
 <li> <p> Configure Postfix, by adding the following to
-<tt>/etc/postfix/<a href="postconf.5.html">main.cf</a> </tt>. </p>
+<tt>/etc/postfix/<a href="postconf.5.html">main.cf</a> </tt>. It is generally best to not configure
+client certificates, unless there are servers which authenticate your mail
+submission via client certificates. Often servers that perform TLS client
+authentication will issue the required certificates signed by their own
+CA. If you configure the client certificate and key incorrectly, you
+will be unable to send mail to sites that request client certificate,
+but don't require them from all clients. </p>
 
 <blockquote>
 <pre>
 <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = /etc/postfix/cacert.pem
-<a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a> = /etc/postfix/FOO-cert.pem
-<a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a> = /etc/postfix/FOO-key.pem
-<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> = btree:/var/run/smtp_tls_session_cache
+<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> =
+    btree:/var/spool/postfix/smtp_tls_session_cache
 <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> = yes
 <a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> = /etc/postfix/cacert.pem
 <a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> = /etc/postfix/FOO-cert.pem
 <a href="postconf.5.html#smtpd_tls_key_file">smtpd_tls_key_file</a> = /etc/postfix/FOO-key.pem
 <a href="postconf.5.html#smtpd_tls_received_header">smtpd_tls_received_header</a> = yes
-<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> = btree:/var/run/smtpd_tls_session_cache
+<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> =
+    btree:/var/spool/postfix/smtpd_tls_session_cache
 <a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> = yes
 <a href="postconf.5.html#tls_random_source">tls_random_source</a> = dev:/dev/urandom
 </pre>
@@ -1542,7 +2282,7 @@ lines of code) is not included with Postfix. </p>
 of several kbytes or more, and that implements the sequence operation.
 In most cases, btree databases should be adequate.  </p>
 
-<p> NOTE:  You cannot use dbm databases. TLS session objects
+<p> NOTE:  You cannot use DBM databases. TLS session objects
 are too large. </p>
 
 <li> <p> <a href="master.5.html">master.cf</a>: Specify "unix" instead of "fifo" as
@@ -1554,14 +2294,14 @@ generation (PRNG) pool, and in order to access the TLS session
 cache databases. Such a protocol cannot be run across fifos. </p>
 
 <li> <p> <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a>: the MUST_NOPEERMATCH per-site policy
-cannot override the global "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = yes" setting.
+cannot override the global "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;yes" setting.
 </p>
 
 <li> <p> <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a>: a combined (NONE + MAY) lookup result
 for (hostname and next-hop destination) produces counter-intuitive
 results for different <a href="postconf.5.html">main.cf</a> settings.  TLS is enabled with
-"<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = no", but it is disabled when both
-"<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = yes" and "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = yes".
+"<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;no", but it is disabled when both
+"<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>&nbsp;=&nbsp;yes" and "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>&nbsp;=&nbsp;yes".
 </p>
 
 </ul>
index 586508aae65b485fbc75a3d9909f2b5b92e35518..a55106809a56b4852920f58d77764b786cabfbee 100644 (file)
@@ -180,11 +180,11 @@ ACCESS(5)                                                            ACCESS(5)
 
 <b>REJECT ACTIONS</b>
        Postfix  version  2.3  and  later  support enhanced status
-       codes.  When no code is specified at the beginning of  the
-       <i>text</i> below, Postfix inserts a default enhanced status code
-       of "5.7.1" in the case of reject actions, and  "4.7.1"  in
-       the  case  of  defer  actions. See "ENHANCED STATUS CODES"
-       below.
+       codes as defined in <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a>.  When no code  is  specified
+       at  the  beginning  of  the  <i>text</i> below, Postfix inserts a
+       default enhanced status code of "5.7.1"  in  the  case  of
+       reject  actions, and "4.7.1" in the case of defer actions.
+       See "ENHANCED STATUS CODES" below.
 
        <b>4</b><i>NN text</i>
 
@@ -194,14 +194,17 @@ ACCESS(5)                                                            ACCESS(5)
               text. <b>4</b><i>NN</i> means "try again later", while <b>5</b><i>NN</i>  means
               "do not try again".
 
+              The  reply  code "421" causes Postfix to disconnect
+              immediately (Postfix version 2.3 and later).
+
        <b>REJECT</b> <i>optional text...</i>
-              Reject  the  address etc. that matches the pattern.
-              Reply with <i>$reject</i><b>_</b><i>code optional text...</i>  when  the
-              optional  text is specified, otherwise reply with a
+              Reject the address etc. that matches  the  pattern.
+              Reply  with  <i>$reject</i><b>_</b><i>code optional text...</i> when the
+              optional text is specified, otherwise reply with  a
               generic error response message.
 
        <b>DEFER_IF_REJECT</b> <i>optional text...</i>
-              Defer the request if some later  restriction  would
+              Defer  the  request if some later restriction would
               result in a REJECT action. Reply with "<b>450</b> <i>optional</i>
               <i>text...</i> when the optional text is specified, other-
               wise reply with a generic error response message.
@@ -209,10 +212,10 @@ ACCESS(5)                                                            ACCESS(5)
               This feature is available in Postfix 2.1 and later.
 
        <b>DEFER_IF_PERMIT</b> <i>optional text...</i>
-              Defer the request if some later  restriction  would
-              result  in a an explicit or implicit PERMIT action.
-              Reply with "<b>450</b> <i>optional text...</i> when the  optional
-              text  is  specified, otherwise reply with a generic
+              Defer  the  request if some later restriction would
+              result in a an explicit or implicit PERMIT  action.
+              Reply  with "<b>450</b> <i>optional text...</i> when the optional
+              text is specified, otherwise reply with  a  generic
               error response message.
 
               This feature is available in Postfix 2.1 and later.
@@ -223,158 +226,160 @@ ACCESS(5)                                                            ACCESS(5)
               <b><a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a></b>, and so on).
 
        <b>DISCARD</b> <i>optional text...</i>
-              Claim successful delivery and silently discard  the
-              message.   Log the optional text if specified, oth-
+              Claim  successful delivery and silently discard the
+              message.  Log the optional text if specified,  oth-
               erwise log a generic message.
 
-              Note: this action currently affects all  recipients
-              of  the  message.   To  discard  only one recipient
-              without discarding  the  entire  message,  use  the
+              Note:  this action currently affects all recipients
+              of the message.   To  discard  only  one  recipient
+              without  discarding  the  entire  message,  use the
               <a href="transport.5.html">transport(5)</a> table to direct mail to the <a href="discard.8.html">discard(8)</a>
               service.
 
               This feature is available in Postfix 2.0 and later.
 
-       <b>DUNNO</b>  Pretend  that  the  lookup  key was not found. This
-              prevents Postfix  from  trying  substrings  of  the
-              lookup  key (such as a subdomain name, or a network
+       <b>DUNNO</b>  Pretend that the lookup key  was  not  found.  This
+              prevents  Postfix  from  trying  substrings  of the
+              lookup key (such as a subdomain name, or a  network
               address subnetwork).
 
               This feature is available in Postfix 2.0 and later.
 
        <b>FILTER</b> <i>transport:destination</i>
-              After  the  message is queued, send the entire mes-
+              After the message is queued, send the  entire  mes-
               sage through the specified external content filter.
-              The  <i>transport:destination</i>  syntax  is described in
-              the <a href="transport.5.html"><b>transport</b>(5)</a>  manual  page.   More  information
-              about  external  content  filters is in the Postfix
+              The <i>transport:destination</i> syntax  is  described  in
+              the  <a href="transport.5.html"><b>transport</b>(5)</a>  manual  page.   More information
+              about external content filters is  in  the  Postfix
               <a href="FILTER_README.html">FILTER_README</a> file.
 
-              Note:  this  action  overrides  the  <a href="postconf.5.html"><b>main.cf</a>   <a href="postconf.5.html#content_filter">con</a>-</b>
+              Note:   this  action  overrides  the  <a href="postconf.5.html"><b>main.cf</a>  <a href="postconf.5.html#content_filter">con</a>-</b>
               <b><a href="postconf.5.html#content_filter">tent_filter</a></b>  setting,  and  currently  affects  all
               recipients of the message.
 
               This feature is available in Postfix 2.0 and later.
 
        <b>HOLD</b> <i>optional text...</i>
-              Place  the message on the <b>hold</b> queue, where it will
-              sit until someone either deletes it or releases  it
-              for  delivery.  Log the optional text if specified,
+              Place the message on the <b>hold</b> queue, where it  will
+              sit  until someone either deletes it or releases it
+              for delivery.  Log the optional text if  specified,
               otherwise log a generic message.
 
-              Mail that is placed on hold can  be  examined  with
-              the  <a href="postcat.1.html"><b>postcat</b>(1)</a>  command,  and  can be destroyed or
+              Mail  that  is  placed on hold can be examined with
+              the <a href="postcat.1.html"><b>postcat</b>(1)</a> command, and  can  be  destroyed  or
               released with the <a href="postsuper.1.html"><b>postsuper</b>(1)</a> command.
 
-              Note: use "<b>postsuper -r</b>" to release mail  that  was
-              kept  on  hold for a significant fraction of <b>$<a href="postconf.5.html#maximal_queue_lifetime">maxi</a>-</b>
+              Note:  use  "<b>postsuper -r</b>" to release mail that was
+              kept on hold for a significant fraction  of  <b>$<a href="postconf.5.html#maximal_queue_lifetime">maxi</a>-</b>
               <b><a href="postconf.5.html#maximal_queue_lifetime">mal_queue_lifetime</a></b>  or  <b>$<a href="postconf.5.html#bounce_queue_lifetime">bounce_queue_lifetime</a></b>,  or
               longer.
 
-              Note:  this action currently affects all recipients
+              Note: this action currently affects all  recipients
               of the message.
 
               This feature is available in Postfix 2.0 and later.
 
        <b>PREPEND</b> <i>headername: headervalue</i>
-              Prepend  the  specified  message header to the mes-
+              Prepend the specified message header  to  the  mes-
               sage.  When this action is used multiple times, the
-              first  prepended  header  appears before the second
+              first prepended header appears  before  the  second
               etc. prepended header.
 
-              Note: this action does not support multi-line  mes-
+              Note:  this action does not support multi-line mes-
               sage headers.
 
-              Note:  this  action must be used before the message
-              content  is  received;  it  cannot   be   used   in
+              Note: this action must be used before  the  message
+              content   is   received;   it  cannot  be  used  in
               <b><a href="postconf.5.html#smtpd_end_of_data_restrictions">smtpd_end_of_data_restrictions</a></b>.
 
               This feature is available in Postfix 2.1 and later.
 
        <b>REDIRECT</b> <i>user@domain</i>
-              After the message is queued, send  the  message  to
+              After  the  message  is queued, send the message to
               the  specified  address  instead  of  the  intended
               recipient(s).
 
-              Note: this action overrides the FILTER action,  and
+              Note:  this action overrides the FILTER action, and
               currently affects all recipients of the message.
 
               This feature is available in Postfix 2.1 and later.
 
        <b>WARN</b> <i>optional text...</i>
               Log a warning with the optional text, together with
-              client  information  and  if  available, with helo,
+              client information and  if  available,  with  helo,
               sender, recipient and protocol information.
 
               This feature is available in Postfix 2.1 and later.
 
 <b>ENHANCED STATUS CODES</b>
-       When an enhanced status code is specified in an access ta-
-       ble, it is subject to modification. The  following  trans-
-       formations  are  needed when the same access table is used
-       for client, helo, sender,  or  recipient  access  restric-
-       tions;  they  happen regardless of whether Postfix replies
-       to a MAIL FROM, RCPT TO or other SMTP command.
-
-       <b>o</b>      When a sender address matches a REJECT action,  the
-              Postfix  SMTP server will transform a recipient DSN
-              status (e.g., 4.1.1-4.1.6) into  the  corresponding
+       Postfix version 2.3  and  later  support  enhanced  status
+       codes  as  defined  in  <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a>.  When an enhanced status
+       code is specified in an access table,  it  is  subject  to
+       modification.  The  following  transformations  are needed
+       when the same access  table  is  used  for  client,  helo,
+       sender,  or  recipient  access  restrictions;  they happen
+       regardless of whether Postfix replies to a MAIL FROM, RCPT
+       TO or other SMTP command.
+
+       <b>o</b>      When  a sender address matches a REJECT action, the
+              Postfix SMTP server will transform a recipient  DSN
+              status  (e.g.,  4.1.1-4.1.6) into the corresponding
               sender DSN status, and vice versa.
 
-       <b>o</b>      When   non-address  information  matches  a  REJECT
-              action (such as the HELO command  argument  or  the
-              client  hostname/address),  the Postfix SMTP server
-              will transform a sender  or  recipient  DSN  status
-              into   a  generic  non-address  DSN  status  (e.g.,
+       <b>o</b>      When  non-address  information  matches  a   REJECT
+              action  (such  as  the HELO command argument or the
+              client hostname/address), the Postfix  SMTP  server
+              will  transform  a  sender  or recipient DSN status
+              into  a  generic  non-address  DSN  status   (e.g.,
               4.0.0).
 
 <b>REGULAR EXPRESSION TABLES</b>
-       This section describes how the table lookups  change  when
+       This  section  describes how the table lookups change when
        the table is given in the form of regular expressions. For
-       a description of regular expression lookup  table  syntax,
+       a  description  of regular expression lookup table syntax,
        see <a href="regexp_table.5.html"><b>regexp_table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>.
 
-       Each  pattern  is  a regular expression that is applied to
+       Each pattern is a regular expression that  is  applied  to
        the entire string being looked up. Depending on the appli-
-       cation,  that  string  is  an  entire  client hostname, an
+       cation, that string  is  an  entire  client  hostname,  an
        entire client IP address, or an entire mail address. Thus,
        no  parent  domain  or  parent  network  search  is  done,
-       <i>user@domain</i> mail addresses are not broken  up  into  their
+       <i>user@domain</i>  mail  addresses  are not broken up into their
        <i>user@</i> and <i>domain</i> constituent parts, nor is <i>user+foo</i> broken
        up into <i>user</i> and <i>foo</i>.
 
-       Patterns are applied in the order as specified in the  ta-
-       ble,  until  a  pattern  is  found that matches the search
+       Patterns  are applied in the order as specified in the ta-
+       ble, until a pattern is  found  that  matches  the  search
        string.
 
-       Actions are the same as with indexed  file  lookups,  with
-       the  additional feature that parenthesized substrings from
+       Actions  are  the  same as with indexed file lookups, with
+       the additional feature that parenthesized substrings  from
        the pattern can be interpolated as <b>$1</b>, <b>$2</b> and so on.
 
 <b>TCP-BASED TABLES</b>
-       This section describes how the table lookups  change  when
+       This  section  describes how the table lookups change when
        lookups are directed to a TCP-based server. For a descrip-
        tion of the TCP client/server lookup protocol, see <a href="tcp_table.5.html"><b>tcp_ta-</b></a>
        <a href="tcp_table.5.html"><b>ble</b>(5)</a>.  This feature is not available up to and including
        Postfix version 2.2.
 
-       Each lookup operation uses the entire query  string  once.
-       Depending  on  the  application,  that string is an entire
+       Each  lookup  operation uses the entire query string once.
+       Depending on the application, that  string  is  an  entire
        client hostname, an entire client IP address, or an entire
-       mail  address.   Thus,  no parent domain or parent network
-       search is done, <i>user@domain</i> mail addresses are not  broken
-       up  into  their <i>user@</i> and <i>domain</i> constituent parts, nor is
+       mail address.  Thus, no parent domain  or  parent  network
+       search  is done, <i>user@domain</i> mail addresses are not broken
+       up into their <i>user@</i> and <i>domain</i> constituent parts,  nor  is
        <i>user+foo</i> broken up into <i>user</i> and <i>foo</i>.
 
        Actions are the same as with indexed file lookups.
 
 <b>EXAMPLE</b>
-       The following example uses an indexed file,  so  that  the
-       order  of  table entries does not matter. The example per-
-       mits access by the client at address 1.2.3.4  but  rejects
-       all  other  clients  in 1.2.3.0/24. Instead of <b>hash</b> lookup
-       tables, some systems use <b>dbm</b>.  Use the  command  "<b>postconf</b>
-       <b>-m</b>"  to  find  out  what lookup tables Postfix supports on
+       The  following  example  uses an indexed file, so that the
+       order of table entries does not matter. The  example  per-
+       mits  access  by the client at address 1.2.3.4 but rejects
+       all other clients in 1.2.3.0/24. Instead  of  <b>hash</b>  lookup
+       tables,  some  systems use <b>dbm</b>.  Use the command "<b>postconf</b>
+       <b>-m</b>" to find out what lookup  tables  Postfix  supports  on
        your system.
 
        /etc/postfix/<a href="postconf.5.html">main.cf</a>:
@@ -389,7 +394,7 @@ ACCESS(5)                                                            ACCESS(5)
        editing the file.
 
 <b>BUGS</b>
-       The table format does not understand quoting  conventions.
+       The  table format does not understand quoting conventions.
 
 <b>SEE ALSO</b>
        <a href="postmap.1.html">postmap(1)</a>, Postfix lookup table manager
@@ -402,7 +407,7 @@ ACCESS(5)                                                            ACCESS(5)
        <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>
index 6d744f593d500679e6eb318a225ce39b2aa73c1e..d207e47038f4ddafcad02b31d6cd1956c136a897 100644 (file)
@@ -136,6 +136,81 @@ CLEANUP(8)                                                          CLEANUP(8)
               The set of characters that Postfix will remove from
               message content.
 
+<b>BEFORE QUEUE MILTER CONTROLS</b>
+       As of version 2.3, Postfix supports the Sendmail version 8
+       Milter  (mail  filter) protocol. When mail is not received
+       via the <a href="smtpd.8.html">smtpd(8)</a> server, the <a href="cleanup.8.html">cleanup(8)</a> server will  simu-
+       late  SMTP events to the extent that this is possible. For
+       details see the <a href="MILTER_README.html">MILTER_README</a> document.
+
+       <b><a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a> (empty)</b>
+              A list of Milter (mail filter) applications for new
+              mail  that does not arrive via the Postfix <a href="smtpd.8.html"><b>smtpd</b>(8)</a>
+              server.
+
+       <b><a href="postconf.5.html#milter_protocol">milter_protocol</a> (2)</b>
+              The mail filter protocol version and optional  pro-
+              tocol  extensions  for  communication with a Milter
+              (mail filter) application.
+
+       <b><a href="postconf.5.html#milter_default_action">milter_default_action</a> (tempfail)</b>
+              The default action  when  a  Milter  (mail  filter)
+              application is unavailable or mis-configured.
+
+       <b><a href="postconf.5.html#milter_macro_daemon_name">milter_macro_daemon_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
+              The {daemon_name} macro value for Milter (mail fil-
+              ter) applications.
+
+       <b><a href="postconf.5.html#milter_macro_v">milter_macro_v</a> ($<a href="postconf.5.html#mail_name">mail_name</a> $<a href="postconf.5.html#mail_version">mail_version</a>)</b>
+              The {v} macro value for Milter (mail filter) appli-
+              cations.
+
+       <b><a href="postconf.5.html#milter_connect_timeout">milter_connect_timeout</a> (30s)</b>
+              The  time  limit  for  connecting to a Milter (mail
+              filter) application, and for  negotiating  protocol
+              options.
+
+       <b><a href="postconf.5.html#milter_command_timeout">milter_command_timeout</a> (30s)</b>
+              The  time  limit  for  sending an SMTP command to a
+              Milter (mail filter) application, and for receiving
+              the response.
+
+       <b><a href="postconf.5.html#milter_content_timeout">milter_content_timeout</a> (300s)</b>
+              The  time  limit  for  sending message content to a
+              Milter (mail filter) application, and for receiving
+              the response.
+
+       <b><a href="postconf.5.html#milter_connect_macros">milter_connect_macros</a> (see postconf -n output)</b>
+              The  macros  that  are sent to Milter (mail filter)
+              applications after completion of  an  SMTP  connec-
+              tion.
+
+       <b><a href="postconf.5.html#milter_helo_macros">milter_helo_macros</a> (see postconf -n output)</b>
+              The  macros  that  are sent to Milter (mail filter)
+              applications after the SMTP HELO or EHLO command.
+
+       <b><a href="postconf.5.html#milter_mail_macros">milter_mail_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  Milter  (mail  filter)
+              applications after the SMTP MAIL FROM command.
+
+       <b><a href="postconf.5.html#milter_rcpt_macros">milter_rcpt_macros</a> (see postconf -n output)</b>
+              The  macros  that  are sent to Milter (mail filter)
+              applications after the SMTP RCPT TO command.
+
+       <b><a href="postconf.5.html#milter_data_macros">milter_data_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  version  4  or  higher
+              Milter  (mail  filter)  applications after the SMTP
+              DATA command.
+
+       <b><a href="postconf.5.html#milter_unknown_command_macros">milter_unknown_command_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  version  3  or  higher
+              Milter  (mail filter) applications after an unknown
+              SMTP command.
+
+       <b><a href="postconf.5.html#milter_end_of_data_macros">milter_end_of_data_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  Milter  (mail  filter)
+              applications after the message end-of-data.
+
 <b>MIME PROCESSING CONTROLS</b>
        Available in Postfix version 2.0 and later:
 
@@ -151,19 +226,19 @@ CLEANUP(8)                                                          CLEANUP(8)
               will handle.
 
        <b><a href="postconf.5.html#strict_8bitmime">strict_8bitmime</a> (no)</b>
-              Enable both  <a href="postconf.5.html#strict_7bit_headers">strict_7bit_headers</a>  and  strict_8bit-
+              Enable  both  <a href="postconf.5.html#strict_7bit_headers">strict_7bit_headers</a>  and strict_8bit-
               mime_body.
 
        <b><a href="postconf.5.html#strict_7bit_headers">strict_7bit_headers</a> (no)</b>
               Reject mail with 8-bit text in message headers.
 
        <b><a href="postconf.5.html#strict_8bitmime_body">strict_8bitmime_body</a> (no)</b>
-              Reject  8-bit  message body text without 8-bit MIME
+              Reject 8-bit message body text without  8-bit  MIME
               content encoding information.
 
        <b><a href="postconf.5.html#strict_mime_encoding_domain">strict_mime_encoding_domain</a> (no)</b>
               Reject mail with invalid Content-Transfer-Encoding:
-              information  for  the message/* or multipart/* MIME
+              information for the message/* or  multipart/*  MIME
               content types.
 
 <b>AUTOMATIC BCC RECIPIENT CONTROLS</b>
@@ -171,31 +246,31 @@ CLEANUP(8)                                                          CLEANUP(8)
        mail enters the mail system:
 
        <b><a href="postconf.5.html#always_bcc">always_bcc</a> (empty)</b>
-              Optional  address  that  receives  a  "blind carbon
+              Optional address  that  receives  a  "blind  carbon
               copy" of each message that is received by the Post-
               fix mail system.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#sender_bcc_maps">sender_bcc_maps</a> (empty)</b>
-              Optional  BCC  (blind  carbon-copy)  address lookup
+              Optional BCC  (blind  carbon-copy)  address  lookup
               tables, indexed by sender address.
 
        <b><a href="postconf.5.html#recipient_bcc_maps">recipient_bcc_maps</a> (empty)</b>
-              Optional BCC  (blind  carbon-copy)  address  lookup
+              Optional  BCC  (blind  carbon-copy)  address lookup
               tables, indexed by recipient address.
 
 <b>ADDRESS TRANSFORMATION CONTROLS</b>
-       Address  rewriting  is delegated to the <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a>
-       daemon.  The <a href="cleanup.8.html"><b>cleanup</b>(8)</a>  server  implements  table  driven
+       Address rewriting is delegated to  the  <a href="trivial-rewrite.8.html"><b>trivial-rewrite</b>(8)</a>
+       daemon.   The  <a href="cleanup.8.html"><b>cleanup</b>(8)</a>  server  implements table driven
        address mapping.
 
        <b><a href="postconf.5.html#empty_address_recipient">empty_address_recipient</a> (MAILER-DAEMON)</b>
-              The   recipient  of  mail  addressed  to  the  null
+              The  recipient  of  mail  addressed  to  the   null
               address.
 
        <b><a href="postconf.5.html#canonical_maps">canonical_maps</a> (empty)</b>
-              Optional address mapping lookup tables for  message
+              Optional  address mapping lookup tables for message
               headers and envelopes.
 
        <b><a href="postconf.5.html#recipient_canonical_maps">recipient_canonical_maps</a> (empty)</b>
@@ -206,49 +281,49 @@ CLEANUP(8)                                                          CLEANUP(8)
               Optional address mapping lookup tables for envelope
               and header sender addresses.
 
-       <b><a href="postconf.5.html#masquerade_classes">masquerade_classes</a>     (envelope_sender,    header_sender,</b>
+       <b><a href="postconf.5.html#masquerade_classes">masquerade_classes</a>    (envelope_sender,     header_sender,</b>
        <b>header_recipient)</b>
               What addresses are subject to address masquerading.
 
        <b><a href="postconf.5.html#masquerade_domains">masquerade_domains</a> (empty)</b>
-              Optional list of domains whose subdomain  structure
+              Optional  list of domains whose subdomain structure
               will be stripped off in email addresses.
 
        <b><a href="postconf.5.html#masquerade_exceptions">masquerade_exceptions</a> (empty)</b>
-              Optional  list of user names that are not subjected
-              to address masquerading, even  when  their  address
+              Optional list of user names that are not  subjected
+              to  address  masquerading,  even when their address
               matches $<a href="postconf.5.html#masquerade_domains">masquerade_domains</a>.
 
        <b><a href="postconf.5.html#propagate_unmatched_extensions">propagate_unmatched_extensions</a> (canonical, virtual)</b>
-              What  address  lookup tables copy an address exten-
+              What address lookup tables copy an  address  exten-
               sion from the lookup key to the lookup result.
 
        Available before Postfix version 2.0:
 
        <b><a href="postconf.5.html#virtual_maps">virtual_maps</a> (empty)</b>
               Optional lookup tables with a) names of domains for
-              which  all  addresses  are  aliased to addresses in
-              other local or remote  domains,  and  b)  addresses
-              that  are  aliased  to  addresses in other local or
+              which all addresses are  aliased  to  addresses  in
+              other  local  or  remote  domains, and b) addresses
+              that are aliased to addresses  in  other  local  or
               remote domains.
 
        Available in Postfix version 2.0 and later:
 
        <b><a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> ($<a href="postconf.5.html#virtual_maps">virtual_maps</a>)</b>
-              Optional lookup tables  that  alias  specific  mail
-              addresses  or  domains  to  other  local  or remote
+              Optional  lookup  tables  that  alias specific mail
+              addresses or  domains  to  other  local  or  remote
               address.
 
        Available in Postfix version 2.2 and later:
 
-       <b><a href="postconf.5.html#canonical_classes">canonical_classes</a>  (envelope_sender,   envelope_recipient,</b>
+       <b><a href="postconf.5.html#canonical_classes">canonical_classes</a>   (envelope_sender,  envelope_recipient,</b>
        <b>header_sender, header_recipient)</b>
-              What  addresses  are  subject   to   <a href="postconf.5.html#canonical_maps">canonical_maps</a>
+              What   addresses   are  subject  to  <a href="postconf.5.html#canonical_maps">canonical_maps</a>
               address mapping.
 
        <b><a href="postconf.5.html#recipient_canonical_classes">recipient_canonical_classes</a>           (envelope_recipient,</b>
        <b>header_recipient)</b>
-              What  addresses  are  subject  to <a href="postconf.5.html#recipient_canonical_maps">recipient_canoni</a>-
+              What addresses  are  subject  to  <a href="postconf.5.html#recipient_canonical_maps">recipient_canoni</a>-
               <a href="postconf.5.html#recipient_canonical_maps">cal_maps</a> address mapping.
 
        <b><a href="postconf.5.html#sender_canonical_classes">sender_canonical_classes</a> (envelope_sender, header_sender)</b>
@@ -256,15 +331,15 @@ CLEANUP(8)                                                          CLEANUP(8)
               address mapping.
 
        <b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> (empty)</b>
-              Don't  rewrite  message headers from remote clients
+              Don't rewrite message headers from  remote  clients
               at all when this parameter is empty; otherwise, re-
-              write  message  headers  and  append  the specified
+              write message  headers  and  append  the  specified
               domain name to incomplete addresses.
 
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#duplicate_filter_limit">duplicate_filter_limit</a> (1000)</b>
-              The maximal number of addresses remembered  by  the
-              address  duplicate  filter  for  <a href="aliases.5.html"><b>aliases</b>(5)</a> or <a href="virtual.5.html"><b>vir-</b></a>
+              The  maximal  number of addresses remembered by the
+              address duplicate filter  for  <a href="aliases.5.html"><b>aliases</b>(5)</a>  or  <a href="virtual.5.html"><b>vir-</b></a>
               <a href="virtual.5.html"><b>tual</b>(5)</a> alias expansion, or for <a href="showq.8.html"><b>showq</b>(8)</a> queue dis-
               plays.
 
@@ -273,16 +348,16 @@ CLEANUP(8)                                                          CLEANUP(8)
               message header.
 
        <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a> (50)</b>
-              The maximal number of  Received:   message  headers
+              The  maximal  number  of Received:  message headers
               that is allowed in the primary message headers.
 
        <b><a href="postconf.5.html#in_flow_delay">in_flow_delay</a> (1s)</b>
-              Time  to pause before accepting a new message, when
+              Time to pause before accepting a new message,  when
               the message arrival rate exceeds the message deliv-
               ery rate.
 
        <b><a href="postconf.5.html#message_size_limit">message_size_limit</a> (10240000)</b>
-              The  maximal  size in bytes of a message, including
+              The maximal size in bytes of a  message,  including
               envelope information.
 
        Available in Postfix version 2.0 and later:
@@ -300,35 +375,35 @@ CLEANUP(8)                                                          CLEANUP(8)
               will handle.
 
        <b><a href="postconf.5.html#queue_file_attribute_count_limit">queue_file_attribute_count_limit</a> (100)</b>
-              The  maximal number of (name=value) attributes that
+              The maximal number of (name=value) attributes  that
               may be stored in a Postfix queue file.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#virtual_alias_expansion_limit">virtual_alias_expansion_limit</a> (1000)</b>
-              The maximal number of addresses that virtual  alias
+              The  maximal number of addresses that virtual alias
               expansion produces from each original recipient.
 
        <b><a href="postconf.5.html#virtual_alias_recursion_limit">virtual_alias_recursion_limit</a> (1000)</b>
-              The  maximal  nesting depth of virtual alias expan-
+              The maximal nesting depth of virtual  alias  expan-
               sion.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <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#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The  maximal  number  of  digits  after the decimal
+              The maximal number  of  digits  after  the  decimal
               point when logging sub-second delay values.
 
        <b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
-              The time after which the sender receives  the  mes-
+              The  time  after which the sender receives the mes-
               sage headers of mail that is still queued.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -336,12 +411,12 @@ CLEANUP(8)                                                          CLEANUP(8)
               over an internal communication channel.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time  that  an  idle  Postfix
-              daemon  process  waits for the next service request
+              The  maximum  amount  of  time that an idle Postfix
+              daemon process waits for the next  service  request
               before exiting.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
-              The maximal number of connection requests before  a
+              The  maximal number of connection requests before a
               Postfix daemon process terminates.
 
        <b><a href="postconf.5.html#myhostname">myhostname</a> (see 'postconf -d' output)</b>
@@ -349,19 +424,19 @@ CLEANUP(8)                                                          CLEANUP(8)
 
        <b><a href="postconf.5.html#myorigin">myorigin</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
               The domain name that locally-posted mail appears to
-              come from, and that locally posted mail  is  deliv-
+              come  from,  and that locally posted mail is deliv-
               ered to.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The  process  ID  of  a  Postfix  command or daemon
+              The process ID  of  a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The process name of a  Postfix  command  or  daemon
+              The  process  name  of  a Postfix command or daemon
               process.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The  location of the Postfix top-level queue direc-
+              The location of the Postfix top-level queue  direc-
               tory.
 
        <b><a href="postconf.5.html#soft_bounce">soft_bounce</a> (no)</b>
@@ -372,14 +447,14 @@ CLEANUP(8)                                                          CLEANUP(8)
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The  mail  system  name  that  is  prepended to the
-              process name in syslog  records,  so  that  "smtpd"
+              The mail system  name  that  is  prepended  to  the
+              process  name  in  syslog  records, so that "smtpd"
               becomes, for example, "postfix/smtpd".
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#enable_original_recipient">enable_original_recipient</a> (yes)</b>
-              Enable   support   for  the  X-Original-To  message
+              Enable  support  for  the   X-Original-To   message
               header.
 
 <b>FILES</b>
@@ -400,9 +475,10 @@ CLEANUP(8)                                                          CLEANUP(8)
 
 <b>README FILES</b>
        <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> Postfix address manipulation
+       <a href="CONTENT_INSPECTION_README.html">CONTENT_INSPECTION_README</a> content inspection
 
 <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>
index 44322686814d9cf78017b28ecf6ce5380db1bd52..1372fd5395f3196421c4be3aad1fcee5c643bd0e 100644 (file)
@@ -75,7 +75,10 @@ overview </a>
 <li> <a href="FILTER_README.html"> After-queue content filter </a>
 
 <li> <a href="SMTPD_PROXY_README.html"> Before-queue content
-Filter  </a>
+filter  </a>
+
+<li> <a href="MILTER_README.html"> Before-queue Milter applications
+</a>
 
 </ul>
 
index 4703069dff168b40c9fbdf0e077d87832d82ce3b..5f3cfb01c00d99443b54949123df620181827c49 100644 (file)
@@ -3716,6 +3716,61 @@ The default TCP port that the Postfix LMTP client connects to.
 </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_tls_CAfile">lmtp_tls_CAfile</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_CApath">lmtp_tls_CApath</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_cert_file">lmtp_tls_cert_file</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_dcert_file">lmtp_tls_dcert_file</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_dkey_file">lmtp_tls_dkey_file</a>
+(default: $<a href="postconf.5.html#lmtp_tls_dcert_file">lmtp_tls_dcert_file</a>)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_dkey_file">smtp_tls_dkey_file</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_tls_enforce_peername">lmtp_tls_enforce_peername</a>
@@ -3727,6 +3782,72 @@ configuration parameter.  See there for details. </p>
 <p> This feature is available in Postfix 2.3 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_tls_exclude_ciphers">lmtp_tls_exclude_ciphers</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_key_file">lmtp_tls_key_file</a>
+(default: $<a href="postconf.5.html#lmtp_tls_cert_file">lmtp_tls_cert_file</a>)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_loglevel">lmtp_tls_loglevel</a>
+(default: 0)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_mandatory_ciphers">lmtp_tls_mandatory_ciphers</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_mandatory_exclude_ciphers">lmtp_tls_mandatory_exclude_ciphers</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_mandatory_protocols">lmtp_tls_mandatory_protocols</a>
+(default: SSLv3, TLSv1)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_tls_note_starttls_offer">lmtp_tls_note_starttls_offer</a>
@@ -3749,6 +3870,17 @@ parameter.  See there for details. </p>
 <p> This feature is available in Postfix 2.3 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_tls_policy_maps">lmtp_tls_policy_maps</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_tls_scert_verifydepth">lmtp_tls_scert_verifydepth</a>
@@ -3760,6 +3892,50 @@ configuration parameter.  See there for details. </p>
 <p> This feature is available in Postfix 2.3 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_tls_secure_cert_match">lmtp_tls_secure_cert_match</a>
+(default: nexthop)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_session_cache_database">lmtp_tls_session_cache_database</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_session_cache_timeout">lmtp_tls_session_cache_timeout</a>
+(default: 3600s)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_session_cache_timeout">smtp_tls_session_cache_timeout</a>
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_verify_cert_match">lmtp_tls_verify_cert_match</a>
+(default: hostname)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_use_tls">lmtp_use_tls</a>
@@ -4718,6 +4894,224 @@ content.  The usual C-like escape sequences are recognized: <tt>\a
 <p> This feature is available in Postfix 2.3 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="milter_command_timeout">milter_command_timeout</a>
+(default: 30s)</b></DT><DD>
+
+<p> The time limit for sending an SMTP command to a Milter (mail
+filter) application, and for receiving the response.  </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit). </p>
+
+<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_connect_macros">milter_connect_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to Milter (mail filter) applications
+after completion of an SMTP connection. See <a href="MILTER_README.html">MILTER_README</a>
+for a list of available macro names and their meanings. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_connect_timeout">milter_connect_timeout</a>
+(default: 30s)</b></DT><DD>
+
+<p> The time limit for connecting to a Milter (mail filter)
+application, and for negotiating protocol options. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit). </p>
+
+<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_content_timeout">milter_content_timeout</a>
+(default: 300s)</b></DT><DD>
+
+<p> The time limit for sending message content to a Milter (mail
+filter) application, and for receiving the response.  </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit). </p>
+
+<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_data_macros">milter_data_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to version 4 or higher Milter (mail
+filter) applications after the SMTP DATA command. See <a href="MILTER_README.html">MILTER_README</a>
+for a list of available macro names and their meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_default_action">milter_default_action</a>
+(default: tempfail)</b></DT><DD>
+
+<p> The default action when a Milter (mail filter) application is
+unavailable or mis-configured. Specify one of the following: </p>
+
+<dl compact>
+
+<dt>accept</dt> <dd>Proceed as if the mail filter was not present.
+</dd>
+
+<dt>reject</dt> <dd>Reject all further commands in this session
+with a permanent status code.</dd>
+
+<dt>tempfail</dt> <dd>Reject all further commands in this session
+with a temporary status code. </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_end_of_data_macros">milter_end_of_data_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the message end-of-data. See <a href="MILTER_README.html">MILTER_README</a> for a list of
+available macro names and their meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_helo_macros">milter_helo_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the SMTP HELO or EHLO command. See
+<a href="MILTER_README.html">MILTER_README</a> for a list of available macro names and their meanings.
+</p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_macro_daemon_name">milter_macro_daemon_name</a>
+(default: $<a href="postconf.5.html#myhostname">myhostname</a>)</b></DT><DD>
+
+<p> The {daemon_name} macro value for Milter (mail filter) applications.
+See <a href="MILTER_README.html">MILTER_README</a> for a list of available macro names and their
+meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_macro_v">milter_macro_v</a>
+(default: $<a href="postconf.5.html#mail_name">mail_name</a> $<a href="postconf.5.html#mail_version">mail_version</a>)</b></DT><DD>
+
+<p> The {v} macro value for Milter (mail filter) applications.
+See <a href="MILTER_README.html">MILTER_README</a> for a list of available macro names and their
+meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_mail_macros">milter_mail_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the SMTP MAIL FROM command. See <a href="MILTER_README.html">MILTER_README</a>
+for a list of available macro names and their meanings. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_protocol">milter_protocol</a>
+(default: 2)</b></DT><DD>
+
+<p> The mail filter protocol version and optional protocol extensions
+for communication with a Milter (mail filter) application. This
+information should match the protocol that is expected by the actual
+mail filter application.  </p>
+
+<p>Protocol versions: </p>
+
+<dl compact>
+
+<dt>2</dt> <dd>Use Sendmail 8 mail filter protocol version 2.</dd>
+
+<dt>3</dt> <dd>Use Sendmail 8 mail filter protocol version 3.</dd>
+
+<dt>4</dt> <dd>Use Sendmail 8 mail filter protocol version 4.</dd>
+
+</dl>
+
+<p>Protocol extensions: </p>
+
+<dl compact>
+
+<dt>no_header_reply</dt> <dd> Specify this when the Milter application
+will not reply for each individual message header.</dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_rcpt_macros">milter_rcpt_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the SMTP RCPT TO command. See <a href="MILTER_README.html">MILTER_README</a>
+for a list of available macro names and their meanings. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="milter_unknown_command_macros">milter_unknown_command_macros</a>
+(default: see postconf -n output)</b></DT><DD>
+
+<p> The macros that are sent to version 3 or higher Milter (mail
+filter) applications after an unknown SMTP command.  See <a href="MILTER_README.html">MILTER_README</a>
+for a list of available macro names and their meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="mime_boundary_length_limit">mime_boundary_length_limit</a>
@@ -5059,6 +5453,21 @@ or <a href="postconf.5.html#reject_non_fqdn_recipient">reject_non_fqdn_recipient
 </p>
 
 
+</DD>
+
+<DT><b><a name="non_smtpd_milters">non_smtpd_milters</a>
+(default: empty)</b></DT><DD>
+
+<p> A list of Milter (mail filter) applications for new mail that
+does not arrive via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server. This includes local
+submission via the <a href="sendmail.1.html">sendmail(1)</a> command line, new mail that arrives
+via the Postfix <a href="qmqpd.8.html">qmqpd(8)</a> server, and old mail that is re-injected
+into the queue with "postsuper -r".  See the <a href="MILTER_README.html">MILTER_README</a> document
+for details.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="notify_classes">notify_classes</a>
@@ -5639,6 +6048,11 @@ filter. </dd>
 <dd>Disable header/body_checks. This is typically specified AFTER
 an external content filter. </dd>
 
+<dt><b><a name="no_milters">no_milters</a></b></dt>
+
+<dd>Disable Milter (mail filter) applications. This is typically
+specified AFTER an external content filter. </dd>
+
 </dl>
 
 <p>
@@ -6786,6 +7200,9 @@ will only connect to servers that support <a href="http://www.faqs.org/rfcs/rfc2
 provide valid server certificates.  Typical use is for clients that
 send all their email to a dedicated mailhub.  </p>
 
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> instead. </p>
+
 
 </DD>
 
@@ -7254,6 +7671,8 @@ Example:
 <p> The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7262,9 +7681,10 @@ client uses for TLS encrypted SMTP sessions. </p>
 
 <p> The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions with a verified server
-certificate. </p>
+certificate. This feature is still under construction. It will not be
+included in the Postfix 2.3 release. </p>
 
-<p> This feature is available in Postfix 2.3 and later. </p>
+<p> This feature should be available in Postfix 2.4 and later. </p>
 
 
 </DD>
@@ -7373,6 +7793,8 @@ Do not wait for the response to the SMTP QUIT command.
 <p> Time limit for Postfix SMTP client write and read operations
 during TLS startup and shutdown handshake procedures. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7390,6 +7812,8 @@ client certificate file.  </p>
 <a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = /etc/postfix/CAcert.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7411,6 +7835,8 @@ must be inside the chroot jail. </p>
 <a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a> = /etc/postfix/certs
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7421,6 +7847,23 @@ must be inside the chroot jail. </p>
 This file may also contain the client private key, and these may
 be the same as the server certificate and key file. </p>
 
+<p> Do not configure client certificates unless you <b>must</b> present
+client TLS certificates to one or more servers. Client certificates are
+not usually needed, and can cause problems in configurations that work
+well without them. The recommended setting is to let the defaults stand: </p>
+
+<blockquote>
+<pre>
+    <a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a> =
+    <a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a> =
+    <a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a> =
+    <a href="postconf.5.html#smtp_tls_dkey_file">smtp_tls_dkey_file</a> =
+</pre>
+</blockquote>
+
+<p> The best way to use the default settings is to comment out the above
+parameters in <a href="postconf.5.html">main.cf</a> if present. </p>
+
 <p> In order to verify certificates, the CA certificate (in case
 of a certificate chain, all CA certificates) must be available.
 You should add these certificates to the server certificate, the
@@ -7445,15 +7888,27 @@ hence pass the "openssl verify -purpose sslclient ..." test. </p>
 <a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a> = /etc/postfix/client.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
 <DT><b><a name="smtp_tls_cipherlist">smtp_tls_cipherlist</a>
 (default: empty)</b></DT><DD>
 
-<p> Controls the Postfix SMTP client TLS cipher selection scheme.
-For details, see the OpenSSL documentation. Note: do not use ""
-quotes around the parameter value. </p>
+<p> Obsolete Postfix &lt; 2.3 control for the Postfix SMTP client TLS
+cipher list. As this feature applies to all security levels, it is easy
+to create inter-operability problems by choosing a non-default cipher
+list. Do not use a non-default TLS cipher list on hosts that deliver email
+to the public Internet: you will be unable to send email to servers that
+only support the ciphers you exclude. Using a restricted cipher list
+may be more appropriate for an internal MTA, where one can exert some
+control over the TLS software and settings of the peer servers. </p>
+
+<p> <b>Note:</b> do not use "" quotes around the parameter value. </p>
+
+<p> This feature is available in Postfix version 2.2. It is not used with
+Postfix 2.3 and later; use <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> instead. </p>
 
 
 </DD>
@@ -7473,6 +7928,8 @@ This file may also contain the server private key. </p>
 <a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a> = /etc/postfix/client-dsa.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7486,6 +7943,8 @@ be accessible without password. </p>
 <p> This file may be combined with the server certificate file
 specified with $<a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a>. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7506,6 +7965,40 @@ environment where special CAs are created.  If not used carefully,
 this option opens the danger of a "man-in-the-middle" attack (the
 CommonName of this attacker will be logged). </p>
 
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> instead. </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a>
+(default: empty)</b></DT><DD>
+
+<p> List of ciphers or cipher types to exclude from the SMTP client cipher
+list at all security levels. This is not an OpenSSL cipherlist, it is
+a simple list separated by whitespace and/or commas. The elements are a
+single cipher, or one or more "+" separated cipher properties, in which
+case only ciphers matching <b>all</b> the properties are excluded. </p>
+
+<p> Examples (some of these will cause problems): </p>
+
+<pre>
+<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = aNULL
+<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = MD5, DES
+<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = DES+MD5
+<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = AES256-SHA, DES-CBC3-MD5
+<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = kEDH+aRSA
+</pre>
+
+<p> The first setting, disables anonymous ciphers. The next setting
+disables ciphers that use the MD5 digest algorithm or the (single) DES
+encryption algorithm. The next setting disables ciphers that use MD5 and
+DES together.  The next setting disables the two ciphers "AES256-SHA"
+and "DES-CBC3-MD5". The last setting disables ciphers that use "EDH"
+key exchange with RSA authentication. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
 
 </DD>
 
@@ -7525,6 +8018,8 @@ must be accessible without password. </p>
 <a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a> = $<a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a>
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7554,6 +8049,121 @@ transmission after STARTTLS. </dd>
 <p> Use "<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> = 3" only in case of problems. Use of
 loglevel 4 is strongly discouraged. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a>
+(default: medium)</b></DT><DD>
+
+<p> The minimum SMTP client TLS cipher grade that is strong enough to
+be used with the "encrypt" security level and higher.  The default
+value "medium" is suitable for most destinations with which you may
+want to enforce TLS, and is beyond the reach of today's crypt-analytic
+methods. See <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> for information on how to configure
+ciphers on a per-destination basis. </p>
+
+<p> The following cipher grades are supported: </p>
+
+<dl>
+<dt><b>export</b></dt>
+<dd> Enable the mainstream "EXPORT" grade or better OpenSSL
+ciphers.  This is always used for opportunistic encryption. It is
+not recommended for mandatory encryption unless you must enforce TLS
+with "crippled" peers. The underlying cipherlist is specified via the
+<a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a> configuration parameter, which you are strongly
+encouraged to not change. The default value of <a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a>
+includes anonymous ciphers, but these are automatically filtered out if
+the client is configured to verify server certificates. If you must
+exclude anonymous ciphers also at the "encrypt" security level, set
+"<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>low</b></dt>
+<dd> Enable the mainstream "LOW" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the <a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> configuration
+parameter, which you are strongly encouraged to not change. The default
+value of <a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> includes anonymous ciphers, but these are
+automatically filtered out if the client is configured to verify server
+certificates. If you must exclude anonymous ciphers also at the "encrypt"
+security level, set "<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>medium</b></dt>
+<dd> Enable the mainstream "MEDIUM" grade or better OpenSSL ciphers.
+The underlying cipherlist is specified via the <a href="postconf.5.html#tls_medium_cipherlist">tls_medium_cipherlist</a>
+configuration parameter, which you are strongly encouraged to not change.
+The default value of <a href="postconf.5.html#tls_medium_cipherlist">tls_medium_cipherlist</a> includes anonymous ciphers,
+but these are automatically filtered out if the client is configured to
+verify server certificates. If you must exclude anonymous ciphers also
+at the "encrypt" security level, set "<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a>
+= aNULL". </dd>
+
+<dt><b>high</b></dt>
+<dd> Enable only the mainstream "HIGH" grade OpenSSL ciphers.  This
+setting is appropriate when all mandatory TLS destinations support
+some of "HIGH" grade ciphers, this is not uncommon. The underlying
+cipherlist is specified via the <a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a> configuration
+parameter, which you are strongly encouraged to not change. The default
+value of <a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a> includes anonymous ciphers, but these are
+automatically filtered out if the client is configured to verify server
+certificates. If you must exclude anonymous ciphers also at the "encrypt"
+security level, set "<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>null</b></dt>
+<dd> Enable only the "NULL" OpenSSL ciphers, these provide authentication
+without encryption.  This setting is only appropriate in the rare case
+that all servers are prepared to use NULL ciphers (not normally enabled
+in TLS servers). A plausible use-case is an LMTP server listening on a
+UNIX-domain socket that is configured to support "NULL" ciphers. The
+underlying cipherlist is specified via the <a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a>
+configuration parameter, which you are strongly encouraged to not
+change. The default value of <a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> excludes anonymous
+ciphers (OpenSSL 0.9.8 has NULL ciphers that offer data integrity without
+encryption or authentication). </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a>
+(default: empty)</b></DT><DD>
+
+<p> List of ciphers or cipher types to exclude from the SMTP client
+cipher list at the mandatory TLS security levels: "encrypt", "verify"
+and "secure". See <a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> for syntax details. When
+both "exclude" parameters are defined, the combined list of ciphers is
+excluded (provided the TLS security level is "encrypt" or higher). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>
+(default: SSLv3, TLSv1)</b></DT><DD>
+
+<p> List of TLS protocol versions that are secure enough to be used
+with the "encrypt" security level and higher. In <a href="postconf.5.html">main.cf</a> the values
+are separated by whitespace, commas or colons. In the policy table
+(see <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>) the only valid 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> Since SSL version 2 has known protocol weaknesses and
+is now deprecated, the default setting only lists "SSLv3" and
+"TLSv1". This means that by default, SSL version 2 will not be used
+at the "encrypt" security level and higher. </p>
+
+<p> See the documentation of the <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> parameter and
+<a href="TLS_README.html">TLS_README</a> for more information about security levels. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
 
 </DD>
 
@@ -7569,6 +8179,8 @@ when TLS is not already enabled for that server. </p>
 postfix/smtp[pid]:  Host offered STARTTLS: [name.of.host]
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7578,9 +8190,16 @@ postfix/smtp[pid]:  Host offered STARTTLS: [name.of.host]
 <p> Optional lookup tables with the Postfix SMTP client TLS usage
 policy by next-hop destination and by remote SMTP server hostname.
 When both lookups succeed, the more specific per-site policy (NONE,
-MUST, etc) overrides the less specific one (MAY), and the more
-secure per-site policy (MUST, etc) overrides the less secure one
-(NONE).  </p>
+MUST, etc) overrides the less specific one (MAY), and the more secure
+per-site policy (MUST, etc) overrides the less secure one (NONE).
+With Postfix 2.3 and later <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> is strongly discouraged:
+use <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> instead. </p>
+
+<p> Use of the bare hostname as the per-site table lookup key is
+discouraged. Always use the full destination nexthop (enclosed in
+[] with a possible ":port" suffix). A recipient domain or MX-enabled
+transport next-hop with no port suffix may look like a bare hostname,
+but is still a suitable <i>destination</i>. </p>
 
 <p> Specify a next-hop destination or server hostname on the left-hand
 side; no wildcards are allowed. The next-hop destination is either
@@ -7621,34 +8240,139 @@ settings.  </dd>
 
 </dl>
 
+<p> The above keywords correspond to the "none", "may", "encrypt" and
+"verify" security levels for the new <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> parameter
+introduced in Postfix 2.3. Starting with Postfix 2.3, and independently
+of how the policy is specified, the <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameters only apply when TLS encryption
+is mandatory. Connections for which encryption is optional enable
+all "export" grade and better ciphers. </p>
+
 <p> As long as no secure DNS lookup mechanism is available, false
 hostnames in MX or CNAME responses can change the server hostname
 that Postfix uses for TLS policy lookup and server certificate
-verification. Even with a perfect match between the server hostname
-and the server certificate, there is no guarantee that Postfix is
-connected to the right server.  To avoid this loophole take the
-following steps: </p>
+verification. Even with a perfect match between the server hostname and
+the server certificate, there is no guarantee that Postfix is connected
+to the right server.  See <a href="TLS_README.html">TLS_README</a> (Closing a DNS loophole with obsolete
+per-site TLS policies) for a possible work-around. </p>
 
-<ul>
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> instead. </p>
 
-<li> Disallow CNAME hostname overrides. In <a href="postconf.5.html">main.cf</a> specify
-"<a href="postconf.5.html#smtp_cname_overrides_servername">smtp_cname_overrides_servername</a> = no". This prevents false hostname
-information in DNS CNAME records from changing the server hostname
-that Postfix uses for TLS policy lookup and server certificate
-verification. This feature requires Postfix 2.2.9 or later.
 
-<li> Eliminate MX lookups. Specify local <a href="transport.5.html">transport(5)</a> table entries
-for sensitive domains with explicit <a href="smtp.8.html">smtp</a>:[mailhost] or <a href="smtp.8.html">smtp</a>:[mailhost]:port
-destinations.  This prevents false hostname information in DNS MX
-records from changing the server hostname that Postfix uses for TLS
-policy lookup and server certificate verification.
+</DD>
+
+<DT><b><a name="smtp_tls_policy_maps">smtp_tls_policy_maps</a>
+(default: empty)</b></DT><DD>
 
-<li> Specify MUST for these mail hosts (including [ ] and port) in
-the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> table.
+<p> Optional lookup tables with the Postfix SMTP client TLS security
+policy by next-hop destination; when a non-empty value is specified,
+this overrides the obsolete <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> parameter.  See
+<a href="TLS_README.html">TLS_README</a> for a more detailed discussion of TLS security levels.
+</p>
+
+<p> The TLS policy table is indexed by the full next-hop destination,
+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 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
+sockets, is used as the nexthop name for certificate verification. The
+port and any enclosing square brackets are used in the table lookup key,
+but are not used for server name verification. </p>
+
+<p> When the lookup key is a domain name without enclosing square brackets
+or any <i>:port</i> suffix (typically the recipient domain), and the full
+domain is not found in the table, just as with the <a href="transport.5.html">transport(5)</a> table,
+the parent domain starting with a leading "." is matched recursively. This
+allows one to specify a security policy for a recipient domain and all
+its sub-domains. </p>
+
+<p> The lookup result is a security level, followed by an optional list
+of whitespace and/or comma separated name=value attributes that override
+related <a href="postconf.5.html">main.cf</a> settings. The TLS security levels in order of increasing
+security are: </p>
 
-</ul>
+<dl>
+
+<dt><b>none</b></dt>
+<dd>No TLS. No additional attributes are supported at this level. </dd>
+
+<dt><b>may</b></dt>
+<dd>Opportunistic TLS. No additional attributes are supported at this
+level. Since sending in the clear is acceptable, demanding stronger
+than default TLS security parameters merely reduces inter-operability.
+Postfix 2.3 and later ignore the <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameters at this security level; all
+protocols are allowed and "export" grade or better ciphers are used.
+When TLS handshakes fail, the connection is retried with TLS disabled.
+This allows mail delivery to sites with non-interoperable TLS
+implementations.</dd>
+
+<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. At this level
+and higher the optional "ciphers" attribute overrides the <a href="postconf.5.html">main.cf</a>
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> parameter and the optional "protocols"
+keyword overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameter.
+In the policy table, multiple protocols must be separated by colons,
+as attribute values may not contain whitespace or commas. </p>
+
+<dt><b>verify</b></dt> <dd>Mandatory TLS verification.  At this security
+level, DNS MX lookups are trusted to be secure enough, and the name
+verified in the server certificate is usually obtained indirectly via
+unauthenticated DNS MX lookups.  The optional "match" attribute overrides
+the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter. In the policy table,
+multiple match patterns and strategies must be separated by colons.
+In practice explicit control over matching is more common with the
+"secure" policy, described below. </dd>
+
+<dt><b>secure</b></dt> <dd>Secure-channel TLS. At this security level, DNS
+MX lookups, though potentially used to determine the candidate next-hop
+gateway IP addresses, are <b>not</b> trusted to be secure enough for TLS
+peername verification. Instead, the default name verified in the server
+certificate is obtained directly from the next-hop, or is explicitly
+specified via the optional <b>match</b> attribute which overrides the
+<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter. In the policy table,
+multiple match patterns and strategies must be separated by colons.
+The match attribute is most useful when multiple domains are supported by
+common server, the policy entries for additional domains specify matching
+rules for the primary domain certificate. While transport table overrides
+routing the secondary domains to the primary nexthop also allow secure
+verification, they risk delivery to the wrong destination when domains
+change hands or are re-assigned to new gateways. With the "match"
+attribute approach, routing is not perturbed, and mail is deferred if
+verification of a new MX host fails. </dd>
+
+</dl>
+
+<p>
+Example:
+</p>
+
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = hash:/etc/postfix/tls_policy
+</pre>
+<pre>
+tls_policy:
+    example.edu                 none
+    example.mil                 may
+    example.gov                 encrypt protocols=TLSv1
+    example.com                 verify ciphers=high
+    example.net                 secure
+    .example.net                secure match=.example.net:example.net
+    [mail.example.org]:587      secure match=nexthop
+</pre>
+
+<p> <b>Note:</b> The <b>hostname</b> strategy if listed in a non-default
+setting of <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> or in the <b>match</b> attribute
+in the policy table can render the <b>secure</b> level vulnerable to
+DNS forgery. Do not use the <b>hostname</b> strategy for secure-channel
+configurations in environments where DNS security is not assured. </p>
 
-<p> </p>
+<p> This feature is available in Postfix 2.3 and later. </p>
 
 
 </DD>
@@ -7662,6 +8386,148 @@ a CA listed in the CA files.  The default value (5) should suffice
 for longer chains (the root CA issues special CA which then issues
 the actual certificate...). </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a>
+(default: nexthop, dot-nexthop)</b></DT><DD>
+
+<p> The server certificate peername verification method for the
+"secure" TLS security level. In a "secure" TLS policy table
+($<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>) entry the optional "match" attribute
+overrides this <a href="postconf.5.html">main.cf</a> setting. </p>
+
+<p> This parameter specifies one or more patterns or strategies separated
+by commas, whitespace or colons.  In the policy table the only valid
+separator is the colon character. </p>
+
+<p> For a description of the pattern and strategy syntax see the
+<a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter. The "hostname" strategy should
+be avoided in this context, as in the absence of a secure global DNS, using
+the results of MX lookups in certificate verification is not immune to active
+(man-in-the-middle) attacks on DNS. </p>
+
+<p>
+Sample <a href="postconf.5.html">main.cf</a> setting:
+</p>
+
+<pre>
+<a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> = nexthop
+</pre>
+
+<p>
+Sample policy table override:
+</p>
+
+<pre>
+example.net     secure match=example.com:.example.com
+.example.net    secure match=example.com:.example.com
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_security_level">smtp_tls_security_level</a>
+(default: empty)</b></DT><DD>
+
+<p> The default SMTP TLS security level for all destinations; when
+a non-empty value is specified, this overrides the obsolete parameters
+<a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>, <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>, and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>.  </p>
+
+<p> Specify one of the following security levels: </p>
+
+<dl>
+
+<dt><b>none</b></dt> <dd> TLS will not be used unless enabled for specific
+destinations via <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>. </dd>
+
+<dt><b>may</b></dt>
+<dd> Opportunistic TLS. TLS will be used if supported by the server. Since
+sending in the clear is acceptable, demanding stronger than default TLS
+security parameters merely reduces inter-operability. Postfix 2.3 and
+later ignore the <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameters at this security level; all
+protocols are allowed and "export" grade or better ciphers are used.
+When TLS handshakes fail, the connection is retried with TLS disabled.
+This allows mail delivery to sites with non-interoperable TLS
+implementations. </dd>
+
+<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. Since a minimum
+level of security is intended, it reasonable to be specific about
+sufficiently secure protocol versions and ciphers. At this security level
+and higher, the <a href="postconf.5.html">main.cf</a> parameters <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> and
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> specify the TLS protocols and minimum
+cipher grade which the administrator considers secure enough for
+mandatory encrypted sessions. This security level is not an appropriate
+default for systems delivering mail to the Internet. </dd>
+
+<dt><b>verify</b></dt> <dd>Mandatory TLS verification. At this security
+level, DNS MX lookups are trusted to be secure enough, and the name
+verified in the server certificate is usually obtained indirectly
+via unauthenticated DNS MX lookups. The <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
+parameter controls how the server name is verified. In practice explicit
+control over matching is more common at the "secure" level, described
+below. This security level is not an appropriate default for systems
+delivering mail to the Internet. </dd>
+
+<dt><b>secure</b></dt> <dd>Secure-channel TLS.  At this security level,
+DNS MX lookups, though potentially used to determine the candidate
+next-hop gateway IP addresses, are <b>not</b> trusted to be secure enough
+for TLS peername verification. Instead, the default name verified in
+the server certificate is obtained from the next-hop domain as specified
+in the <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> configuration parameter. The default
+matching rule is that a server certificate matches when its name is equal
+to or is a sub-domain of the nexthop domain. This security level is not
+an appropriate default for systems delivering mail to the Internet. </dd>
+
+</dl>
+
+<p>
+Examples:
+</p>
+
+<p>No TLS, old-style: <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>=no and <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>=no.</p>
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = none
+</pre>
+
+<p>Opportunistic TLS:</p>
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+</pre>
+
+<p>Mandatory (high-grade) TLS encryption:</p>
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = encrypt
+    <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> = high
+</pre>
+
+<p>Mandatory TLS verification, of hostname or nexthop domain:</p>
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = verify
+    <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> = high
+    <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> = hostname, nexthop, dot-nexthop
+</pre>
+
+<p>Secure channel TLS with exact nexthop name matching:</p>
+<pre>
+<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = secure
+    <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = TLSv1
+    <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> = high
+    <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> = nexthop
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
 
 </DD>
 
@@ -7671,7 +8537,15 @@ the actual certificate...). </p>
 <p> Name of the file containing the optional Postfix SMTP client
 TLS session cache. Specify a database type that supports enumeration,
 such as <b>btree</b> or <b>sdbm</b>; there is no need to support
-concurrent access.  The file is created if it does not exist.  </p>
+concurrent access.  The file is created if it does not exist. The <a href="smtp.8.html">smtp(8)</a>
+daemon does not use this parameter directly, rather the cache is
+implemented indirectly in the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon. This means that
+per-smtp-instance <a href="master.5.html">master.cf</a> overrides of this parameter are not effective.
+Note, that each of the cache databases supported by <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon:
+$<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a>, $<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a>
+(and with Postfix 2.3 and later $lmtp_session_cache_database), needs to
+be stored separately, it is not at this time possible to store multiple
+caches in a single database. </p>
 
 <p> Note: <b>dbm</b> databases are not suitable. TLS
 session objects are too large. </p>
@@ -7679,9 +8553,11 @@ session objects are too large. </p>
 <p> Example: </p>
 
 <pre>
-<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> = btree:/var/postfix/smtp_scache
+<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> = btree:/var/spool/postfix/smtp_scache
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -7689,8 +8565,93 @@ session objects are too large. </p>
 (default: 3600s)</b></DT><DD>
 
 <p> The expiration time of Postfix SMTP client TLS session cache
-information.  A cache cleanup is performed periodically every
-$<a href="postconf.5.html#smtp_tls_session_cache_timeout">smtp_tls_session_cache_timeout</a> seconds.  </p>
+information.  A cache cleanup is performed periodically
+every $<a href="postconf.5.html#smtp_tls_session_cache_timeout">smtp_tls_session_cache_timeout</a> seconds. As with
+$<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a>, this parameter is implemented in the
+<a href="tlsmgr.8.html">tlsmgr(8)</a> daemon and therefore per-smtp-instance <a href="master.5.html">master.cf</a> overrides
+are not possible. </p>
+
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
+(default: hostname)</b></DT><DD>
+
+<p> The server certificate peername verification method for the
+"verify" TLS security level. In a "verify" TLS policy table
+($<a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>) entry the optional "match" attribute
+overrides this <a href="postconf.5.html">main.cf</a> setting. </p>
+
+<p> This parameter specifies one or more patterns or strategies separated
+by commas, whitespace or colons.  In the policy table the only valid
+separator is the colon character. </p>
+
+<p> Patterns specify domain names, or domain name suffixes: </p>
+
+<dl>
+
+<dt><i>example.com</i></dt> <dd> Match the <i>example.com</i> domain,
+i.e. one of the names the server certificate must be <i>example.com</i>,
+upper and lower case distinctions are ignored. </dd>
+
+<dt><i>.example.com</i></dt>
+<dd> Match subdomains of the <i>example.com</i> domain, i.e. match
+a name in the server certificate that consists of a non-zero number of
+labels followed by a <i>.example.com</i> suffix. Case distinctions are
+ignored.</dd>
+
+</dl>
+
+<p> Strategies specify a transformation from the next-hop domain
+to the expected name in the server certificate: </p>
+
+<dl>
+
+<dt>nexthop</dt>
+<dd> Match against the next-hop domain, which is either the recipient
+domain, or the transport next-hop configured for the domain stripped of
+any optional socket type prefix, enclosing square brackets and trailing
+port. When MX lookups are not suppressed, this is the original nexthop
+domain prior to the MX lookup, not the result of the MX lookup. For
+LMTP delivery via UNIX-domain sockets, the verified next-hop name is
+$<a href="postconf.5.html#myhostname">myhostname</a>.  This strategy is suitable for use with the "secure"
+policy. Case is ignored.</dd>
+
+<dt>dot-nexthop</dt>
+<dd> As above, but match server certificate names that are subdomains
+of the next-hop domain. Case is ignored.</dd>
+
+<dt>hostname</dt> <dd> Match against the hostname of the server, often
+obtained via an unauthenticated DNS MX lookup. For LMTP delivery via
+UNIX-domain sockets, the verified name is $<a href="postconf.5.html#myhostname">myhostname</a>. This matches
+the verification strategy of the "MUST" keyword in the obsolete
+<a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> table, and is suitable for use with the "verify"
+security level. When the next-hop name is enclosed in square brackets
+to suppress MX lookups, the "hostname" strategy is the same as the
+"nexthop" strategy. Case is ignored.</dd>
+
+</dl>
+
+<p>
+Sample <a href="postconf.5.html">main.cf</a> setting:
+</p>
+
+<pre>
+<a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> = hostname, nexthop, dot-nexthop
+</pre>
+
+<p>
+Sample policy table override:
+</p>
+
+<pre>
+example.com     verify  match=hostname:nexthop
+.example.com    verify  match=example.com:.example.com:hostname
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
 
 
 </DD>
@@ -7700,10 +8661,13 @@ $<a href="postconf.5.html#smtp_tls_session_cache_timeout">smtp_tls_session_cache
 
 <p> Opportunistic mode: use TLS when a remote SMTP server announces
 STARTTLS support, otherwise send the mail in the clear. Beware:
-some SMTP servers offer STARTTLS even if it is not configured.  If
-the TLS handshake fails, and no other server is available, delivery
-is deferred and mail stays in the queue.  If this is a concern for
-you, use the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> feature instead.  </p>
+some SMTP servers offer STARTTLS even if it is not configured.  With
+Postfix &lt; 2.3, if the TLS handshake fails, and no other server is
+available, delivery is deferred and mail stays in the queue. If this
+is a concern for you, use the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> feature instead.  </p>
+
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> instead. </p>
 
 
 </DD>
@@ -8482,6 +9446,8 @@ dedicated servers. </p>
 STARTTLS due to insufficient privileges to access the server private
 key. This is intended behavior. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -8757,6 +9723,18 @@ parameters.
 </p>
 
 
+</DD>
+
+<DT><b><a name="smtpd_milters">smtpd_milters</a>
+(default: empty)</b></DT><DD>
+
+<p> A list of Milter (mail filter) applications for new mail that
+arrives via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server.  See the <a href="MILTER_README.html">MILTER_README</a>
+document for details.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtpd_noop_commands">smtpd_noop_commands</a>
@@ -9436,6 +10414,8 @@ Example:
 <p> The SASL authentication security options that the Postfix SMTP
 server uses for TLS encrypted SMTP sessions. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9671,6 +10651,8 @@ server delays all responses by (number of errors) seconds. </p>
 <p> The time limit for Postfix SMTP server write and read operations
 during TLS startup and shutdown handshake procedures. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9711,6 +10693,8 @@ list of trusted CAs if you want to use chroot-mode. </p>
 <a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> = /etc/postfix/CAcert.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9735,6 +10719,8 @@ feature is therefore not recommended. </p>
 <a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> = /etc/postfix/certs
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9746,10 +10732,12 @@ information is needed for certificate based mail relaying with,
 for example, the <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> feature. </p>
 
 <p> Some clients such as Netscape will either complain if no
-certificate is available (for the list of CAs in /etc/postfix/certs)
+certificate is available (for the list of CAs in $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>)
 or will offer multiple client certificates to choose from. This
 may be annoying, so this option is "off" by default. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9760,6 +10748,8 @@ may be annoying, so this option is "off" by default. </p>
 not announce or accept SASL authentication over unencrypted
 connections. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9772,6 +10762,8 @@ file.  The default value should also suffice for longer chains (the
 root CA issues special CA which then issues the actual certificate...).
 </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9781,6 +10773,23 @@ root CA issues special CA which then issues the actual certificate...).
 <p> File with the Postfix SMTP server RSA certificate in PEM format.
 This file may also contain the server private key. </p>
 
+<p> Public Internet MX hosts without certificates signed by a "reputable"
+CA must generate, and be prepared to present to most clients, a
+self-signed or private-CA signed certificate. The client will not be
+able to authenticate the server, but unless it is running Postfix 2.3 or
+similar software, it will still insist on a server certificate. </p>
+
+<p> For servers that are <b>not</b> public Internet MX hosts, Postfix
+2.3 supports configurations with no certificates. This entails the use
+of just the anonymous TLS ciphers, which are not supported by typical
+SMTP clients. Since such clients will not, as a rule, fall back to plain
+text after a TLS handshake failure, the server will be unable to receive
+email from TLS enabled clients. To avoid accidental configurations with
+no certificates, Postfix 2.3 enables certificate-less operation only
+when the administrator explicitly sets "<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> = none". This
+ensures that new Postfix configurations with just "<a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> = yes"
+added, will not accidentally run with no certificates. </p>
+
 <p> Both RSA and DSA certificates are supported.  When both types
 are present, the cipher used determines which certificate will be
 presented to the client.  For Netscape and OpenSSL clients without
@@ -9811,15 +10820,102 @@ certificate and hence pass the "openssl verify -purpose sslserver
 <a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> = /etc/postfix/server.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
 <DT><b><a name="smtpd_tls_cipherlist">smtpd_tls_cipherlist</a>
 (default: empty)</b></DT><DD>
 
-<p> Controls the Postfix SMTP server TLS cipher selection scheme.
-For details, see the OpenSSL documentation. Note: do not use ""
-quotes around the parameter value. </p>
+<p> Obsolete Postfix &lt; 2.3 control for the Postfix SMTP server TLS
+cipher list. It is easy to create inter-operability problems by choosing
+a non-default cipher list. Do not use a non-default TLS cipherlist for
+MX hosts on the public Internet. Clients that begin the TLS handshake,
+but are unable to agree on a common cipher, may not be able to send any
+email to the SMTP server. Using a restricted cipher list may be more
+appropriate for a dedicated MSA or an internal mailhub, where one can
+exert some control over the TLS software and settings of the connecting
+clients. </p>
+
+<p> <b>Note:</b> do not use "" quotes around the parameter value. </p>
+
+<p>This feature is available with Postfix version 2.2. It is not used with
+Postfix 2.3 and later; use <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a> instead. </p>
+
+
+</DD>
+
+<DT><b><a name="smtpd_tls_ciphers">smtpd_tls_ciphers</a>
+(default: export)</b></DT><DD>
+
+<p> The minimum acceptable SMTP server TLS cipher grade. It is easy to
+create inter-operability problems by choosing a non-default cipher grade.
+Do not use a stronger than default minimum cipher grade for MX hosts on
+the public Internet. Clients that begin the TLS handshake, but are unable
+to agree on a common cipher, may not be able to send any email to the
+SMTP server. Using a restricted cipher list may be more appropriate for a
+dedicated MSA or an internal mailhub, where one can exert some control over
+the TLS software and settings of the connecting clients. Configurations
+with no certificates are also not likely to inter-operate with most
+clients, see the notes for "<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>". </p>
+
+<p> The following cipher grades are supported: </p>
+
+<dl>
+<dt><b>export</b></dt>
+<dd> Enable the mainstream "EXPORT" grade or better OpenSSL ciphers.
+This is the most appropriate setting for public MX hosts. The underlying
+cipherlist is specified via the <a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a> configuration
+parameter, which you are strongly encouraged to not change. The default
+value of <a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a> includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>low</b></dt>
+<dd> Enable the mainstream "LOW" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the <a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> configuration
+parameter, which you are strongly encouraged to not change. The default
+value of <a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>medium</b></dt>
+<dd> Enable the mainstream "MEDIUM" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the <a href="postconf.5.html#tls_medium_cipherlist">tls_medium_cipherlist</a> configuration
+parameter, which you are strongly encouraged to not change. The default
+value of <a href="postconf.5.html#tls_medium_cipherlist">tls_medium_cipherlist</a> includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>high</b></dt>
+<dd> Enable only the mainstream "HIGH" grade OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the <a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a> configuration
+parameter, which you are strongly encouraged to not change. The default
+value of <a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a> includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers, set
+"<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = aNULL". </dd>
+
+<dt><b>null</b></dt>
+<dd> Enable only the "NULL" OpenSSL ciphers, these provide authentication
+without encryption.  This setting is only appropriate in the rare
+case that all clients are prepared to use NULL ciphers (not normally
+enabled in TLS clients). The underlying cipherlist is specified via the
+<a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> configuration parameter, which you are strongly
+encouraged to not change. The default value of <a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a>
+excludes anonymous ciphers (OpenSSL 0.9.8 has NULL ciphers that offer
+data integrity without encryption or authentication). </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
 
 
 </DD>
@@ -9839,6 +10935,8 @@ This file may also contain the server private key. <p>
 <a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a> = /etc/postfix/server-dsa.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9867,6 +10965,8 @@ Gathering Daemon EGD", available at <a href="http://www.lothar.com/tech/crypto/"
 <a href="postconf.5.html#smtpd_tls_dh1024_param_file">smtpd_tls_dh1024_param_file</a> = /etc/postfix/dh_1024.pem
 </pre>
 
+<p>This feature is available with Postfix version 2.2.</p>
+
 
 </DD>
 
@@ -9885,6 +10985,8 @@ configuration parameter.  </p>
 <a href="postconf.5.html#smtpd_tls_dh512_param_file">smtpd_tls_dh512_param_file</a> = /etc/postfix/dh_512.pem
 </pre>
 
+<p>This feature is available with Postfix version 2.2.</p>
+
 
 </DD>
 
@@ -9898,6 +11000,39 @@ with $<a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a>. <
 <p> The private key must not be encrypted. In other words, the key
 must be accessible without password. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a>
+(default: empty)</b></DT><DD>
+
+<p> List of ciphers or cipher types to exclude from the SMTP server
+cipher list. This is not an OpenSSL cipherlist; it is a simple list
+separated by whitespace and/or commas. The elements are a single
+cipher, or one or more "+" separated cipher properties, in which
+case only ciphers matching <b>all</b> the properties are excluded. </p>
+
+<p> Examples (some of these will cause problems): </p>
+
+<pre>
+<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = aNULL
+<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = MD5, DES
+<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = DES+MD5
+<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = AES256-SHA, DES-CBC3-MD5
+<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = kEDH+aRSA
+</pre>
+
+<p> The first setting disables anonymous ciphers. The next setting
+disables ciphers that use the MD5 digest algorithm or the (single) DES
+encryption algorithm. The next setting disables ciphers that use MD5 and
+DES together.  The next setting disables the two ciphers "AES256-SHA"
+and "DES-CBC3-MD5". The last setting disables ciphers that use "EDH"
+key exchange with RSA authentication. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
 
 </DD>
 
@@ -9940,6 +11075,34 @@ transmission after STARTTLS. </dd>
 <p> Use "<a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> = 3" only in case of problems. Use of
 loglevel 4 is strongly discouraged. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="smtpd_tls_protocols">smtpd_tls_protocols</a>
+(default: empty)</b></DT><DD>
+
+<p> The list of TLS protocols supported by the server. If empty the
+default list of protocols is used (i.e. all TLS protocol versions are
+supported). Any non-empty value is interpreted as 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> DO NOT set this to a non-default value on an MX-host,
+as some clients may not support any of the narrower set of protocols,
+and may be unable to fallback to plaintext sessions. If you restrict
+the protocol list on an MX host, you may lose mail. </p>
+
+<p> Example: </p>
+
+<pre>
+<a href="postconf.5.html#smtpd_tls_protocols">smtpd_tls_protocols</a> = SSLv3, TLSv1
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
 
 </DD>
 
@@ -9953,6 +11116,8 @@ CommonName.  This is disabled by default, as the information may
 be modified in transit through other mail servers.  Only information
 that was recorded by the final destination can be trusted. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9963,10 +11128,10 @@ that was recorded by the final destination can be trusted. </p>
 certificate in order to allow TLS connections to proceed.  This
 option implies "<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes". </p>
 
-<p> When TLS encryption is optional, remote SMTP clients can bypass
-the restriction by simply not using STARTTLS at all. For this reason
-a TLS connection will be handled as if only "<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a>
-= yes" is specified.  </p>
+<p> When TLS encryption is optional, this setting is ignored with
+a warning written to the mail log. </p>
+
+<p> This feature is available in Postfix 2.2 and later.  </p>
 
 
 </DD>
@@ -9977,7 +11142,15 @@ a TLS connection will be handled as if only "<a href="postconf.5.html#smtpd_tls_
 <p> Name of the file containing the optional Postfix SMTP server
 TLS session cache. Specify a database type that supports enumeration,
 such as <b>btree</b> or <b>sdbm</b>; there is no need to support
-concurrent access.  The file is created if it does not exist.  </p>
+concurrent access.  The file is created if it does not exist. The <a href="smtpd.8.html">smtpd(8)</a>
+daemon does not use this parameter directly, rather the cache is
+implemented indirectly in the <a href="tlsmgr.8.html">tlsmgr(8)</a> daemon. This means that
+per-smtpd-instance <a href="master.5.html">master.cf</a> overrides of this parameter are not
+effective. Note, that each of the cache databases supported by <a href="tlsmgr.8.html">tlsmgr(8)</a>
+daemon: $<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a>, $<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a>
+(and with Postfix 2.3 and later $lmtp_session_cache_database), needs to be
+stored separately, it is not at this time possible to store multiple
+caches in a single database. </p>
 
 <p> Note: <b>dbm</b> databases are not suitable. TLS
 session objects are too large. </p>
@@ -9985,9 +11158,11 @@ session objects are too large. </p>
 <p> Example: </p>
 
 <pre>
-<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> = btree:/var/postfix/smtpd_scache
+<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> = btree:/var/spool/postfix/smtpd_scache
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -9995,8 +11170,13 @@ session objects are too large. </p>
 (default: 3600s)</b></DT><DD>
 
 <p> The expiration time of Postfix SMTP server TLS session cache
-information.  A cache cleanup is performed periodically every
-$<a href="postconf.5.html#smtpd_tls_session_cache_timeout">smtpd_tls_session_cache_timeout</a> seconds.  </p>
+information. A cache cleanup is performed periodically
+every $<a href="postconf.5.html#smtpd_tls_session_cache_timeout">smtpd_tls_session_cache_timeout</a> seconds. As with
+$<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a>, this parameter is implemented in the
+<a href="tlsmgr.8.html">tlsmgr(8)</a> daemon and therefore per-smtpd-instance <a href="master.5.html">master.cf</a> overrides
+are not possible. </p>
+
+<p> This feature is available in Postfix 2.2 and later.  </p>
 
 
 </DD>
@@ -10012,6 +11192,8 @@ instead of using the STARTTLS command. </p>
 server's command line. Port 465 (smtps) was once chosen for this
 purpose. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -10025,6 +11207,8 @@ but do not require that clients use TLS encryption. </p>
 STARTTLS due to insufficient privileges to access the server private
 key. This is intended behavior. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -10267,6 +11451,79 @@ internal pseudo random number generator (PRNG).  The default of 32
 bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 (or 168bit) session key. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
+
+</DD>
+
+<DT><b><a name="tls_export_cipherlist">tls_export_cipherlist</a>
+(default: ALL:+RC4:@STRENGTH)</b></DT><DD>
+
+<p> The OpenSSL cipherlist for "EXPORT" or higher grade ciphers. This
+defines the meaning of the "export" setting in <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a>,
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and <a href="postconf.5.html#lmtp_tls_mandatory_ciphers">lmtp_tls_mandatory_ciphers</a>. This is
+the cipherlist for the opportunistic ("may") TLS client security
+level and is the default cipherlist for the SMTP server. You are
+strongly encouraged to not change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="tls_high_cipherlist">tls_high_cipherlist</a>
+(default: !EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)</b></DT><DD>
+
+<p> The OpenSSL cipherlist for "HIGH" grade ciphers. This defines
+the meaning of the "high" setting in <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a>,
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and <a href="postconf.5.html#lmtp_tls_mandatory_ciphers">lmtp_tls_mandatory_ciphers</a>. You are
+strongly encouraged to not change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="tls_low_cipherlist">tls_low_cipherlist</a>
+(default: !EXPORT:ALL:+RC4:@STRENGTH)</b></DT><DD>
+
+<p> The OpenSSL cipherlist for "LOW" or higher grade ciphers. This defines
+the meaning of the "low" setting in <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a>,
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and <a href="postconf.5.html#lmtp_tls_mandatory_ciphers">lmtp_tls_mandatory_ciphers</a>. You are
+strongly encouraged to not change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="tls_medium_cipherlist">tls_medium_cipherlist</a>
+(default: !EXPORT:!LOW:ALL:+RC4:@STRENGTH)</b></DT><DD>
+
+<p> The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers. This
+defines the meaning of the "medium" setting in <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a>,
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and <a href="postconf.5.html#lmtp_tls_mandatory_ciphers">lmtp_tls_mandatory_ciphers</a>. This is
+the default cipherlist for mandatory TLS encryption in the TLS
+client (with anonymous ciphers disabled when verifying server
+certificates). You are strongly encouraged to not change this
+setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="tls_null_cipherlist">tls_null_cipherlist</a>
+(default: !aNULL:eNULL+kRSA)</b></DT><DD>
+
+<p> The OpenSSL cipherlist for "NULL" grade ciphers that provide
+authentication without encryption. This defines the meaning of the "null"
+setting in <a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a>, <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> and
+<a href="postconf.5.html#lmtp_tls_mandatory_ciphers">lmtp_tls_mandatory_ciphers</a>.  You are strongly encouraged to not
+change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
 
 </DD>
 
@@ -10279,6 +11536,8 @@ pool. The default of 32 bytes (256 bits) is good enough for 128bit
 symmetric keys.  If using EGD or a device file, a maximum of 255
 bytes is read. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -10293,6 +11552,8 @@ not exist, and its length is fixed at 1024 bytes.  </p>
 kept in the /var file system, instead of under $<a href="postconf.5.html#config_directory">config_directory</a>.
 The location should not be inside the chroot jail. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -10303,6 +11564,8 @@ The location should not be inside the chroot jail. </p>
 the pseudo random number generator (PRNG) to the file specified
 with $<a href="postconf.5.html#tls_random_exchange_name">tls_random_exchange_name</a>.  </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -10314,6 +11577,8 @@ in-memory pseudo random number generator (PRNG) pool from external
 sources.  The actual time between re-seeding attempts is calculated
 using the PRNG, and is between 0 and the time specified.  </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
@@ -10330,6 +11595,8 @@ device file.  </p>
 <p> Note: on OpenBSD systems specify /dev/arandom when /dev/urandom
 gives timeout errors.  </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 
 </DD>
 
index 4663d9dc612f6765ac300a0a638fbee4091f8cc1..a60f09dcec41723d0d25795535a59b34eae5f06b 100644 (file)
@@ -20,7 +20,7 @@ QSHAPE(1)                                                            QSHAPE(1)
        Postfix queue message distribution in time and  by  sender
        domain  or recipient domain. The program needs read access
        to the queue directories and queue files, so it  must  run
-       as  the  superuser  or the <b><a href="postconf.5.html#mail_owner">mail_owner</a></b> specified in <i>main.cf</i>
+       as  the  superuser  or the <b><a href="postconf.5.html#mail_owner">mail_owner</a></b> specified in <i<a href="postconf.5.html">>main.cf</</a>i>
        (typically <b>postfix</b>).
 
        Options:
@@ -78,7 +78,7 @@ QSHAPE(1)                                                            QSHAPE(1)
               ters, the terminal_width limit is violated.
 
        <b>-c</b> <i>config</i><b>_</b><i>directory</i>
-              The <b>main.cf</b> 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.
 
@@ -90,8 +90,8 @@ QSHAPE(1)                                                            QSHAPE(1)
               a different set of queues, just list  their  direc-
               tory names on the command line.  Absolute paths are
               used as is, other paths are taken relative  to  the
-              <i>main.cf</i>  <b><a href="postconf.5.html#queue_directory">queue_directory</a></b>  parameter setting.  While
-              <i>main.cf</i> supports the use of <i>$variable</i> expansion  in
+              <i<a href="postconf.5.html">>main.cf</</a>i>  <b><a href="postconf.5.html#queue_directory">queue_directory</a></b>  parameter setting.  While
+              <i<a href="postconf.5.html">>main.cf</</a>i> supports the use of <i>$variable</i> expansion  in
               the  definition  of  the <b><a href="postconf.5.html#queue_directory">queue_directory</a></b> parameter,
               the <b>qshape</b> program does not. If you must use  vari-
               able expansions in the <b><a href="postconf.5.html#queue_directory">queue_directory</a></b> setting, you
@@ -104,7 +104,7 @@ QSHAPE(1)                                                            QSHAPE(1)
        <a href="QSHAPE_README.html">QSHAPE_README</a> Examples and background material.
 
 <b>FILES</b>
-       $<a href="postconf.5.html#config_directory">config_directory</a>/main.cf, Postfix installation parameters.
+       $<a href="postconf.5.html#config_directory">config_directory</a>/<a href="postconf.5.html">main.cf</a>, Postfix installation parameters.
        $<a href="postconf.5.html#queue_directory">queue_directory</a>/maildrop/, local submission directory.
        $<a href="postconf.5.html#queue_directory">queue_directory</a>/incoming/, new message queue.
        $<a href="postconf.5.html#queue_directory">queue_directory</a>/hold/, messages waiting for tech support.
index 7149c1885fb3bd1a998f7a6d22d9b1b2077fc6cf..f4ce549151c275efe6f00569c9fef4ca8d2d9d53 100644 (file)
@@ -313,95 +313,130 @@ SMTP(8)                                                                SMTP(8)
        Detailed  information  about STARTTLS configuration may be
        found in the <a href="TLS_README.html">TLS_README</a> document.
 
-       <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic mode: use  TLS  when  a  remote  SMTP
-              server  announces  STARTTLS support, otherwise send
-              the mail in the clear.
-
-       <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement mode: require that remote SMTP  servers
-              use  TLS  encryption,  and  never  send mail in the
-              clear.
+       <b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> (empty)</b>
+              The default SMTP TLS security level for all  desti-
+              nations;  when a non-empty value is specified, this
+              overrides  the  obsolete  parameters  <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a>,
+              <a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a>, and <a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a>.
 
        <b><a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a>           ($<a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_secu</a>-</b>
        <b><a href="postconf.5.html#smtp_sasl_security_options">rity_options</a>)</b>
-              The SASL authentication security options  that  the
-              Postfix  SMTP  client  uses  for TLS encrypted SMTP
+              The  SASL  authentication security options that the
+              Postfix SMTP client uses  for  TLS  encrypted  SMTP
               sessions.
 
        <b><a href="postconf.5.html#smtp_starttls_timeout">smtp_starttls_timeout</a> (300s)</b>
-              Time limit for Postfix SMTP client write  and  read
-              operations  during  TLS  startup and shutdown hand-
+              Time  limit  for Postfix SMTP client write and read
+              operations during TLS startup  and  shutdown  hand-
               shake procedures.
 
        <b><a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> (empty)</b>
-              The file with the certificate of the  certification
-              authority  (CA) that issued the Postfix SMTP client
+              The  file with the certificate of the certification
+              authority (CA) that issued the Postfix SMTP  client
               certificate.
 
        <b><a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a> (empty)</b>
-              Directory with  PEM  format  certificate  authority
-              certificates  that  the Postfix SMTP client uses to
+              Directory  with  PEM  format  certificate authority
+              certificates that the Postfix SMTP client  uses  to
               verify a remote SMTP server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a> (empty)</b>
-              File with the Postfix SMTP client  RSA  certificate
+              File  with  the Postfix SMTP client RSA certificate
               in PEM format.
 
-       <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
-              Controls  the Postfix SMTP client TLS cipher selec-
-              tion scheme.
+       <b><a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> (medium)</b>
+              The minimum SMTP client TLS cipher  grade  that  is
+              strong  enough  to be used with the "encrypt" secu-
+              rity level and higher.
+
+       <b><a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> (empty)</b>
+              List of ciphers or cipher types to exclude from the
+              SMTP client cipher list at all security levels.
+
+       <b><a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> (empty)</b>
+              List of ciphers or cipher types to exclude from the
+              SMTP client cipher list at the mandatory TLS  secu-
+              rity levels: "encrypt", "verify" and "secure".
 
        <b><a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a> (empty)</b>
-              File with the Postfix SMTP client  DSA  certificate
+              File  with  the Postfix SMTP client DSA certificate
               in PEM format.
 
        <b><a href="postconf.5.html#smtp_tls_dkey_file">smtp_tls_dkey_file</a> ($<a href="postconf.5.html#smtp_tls_dcert_file">smtp_tls_dcert_file</a>)</b>
-              File  with  the Postfix SMTP client DSA private key
+              File with the Postfix SMTP client DSA  private  key
               in PEM format.
 
-       <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              When TLS encryption is enforced, require  that  the
-              remote SMTP server hostname matches the information
-              in the remote SMTP server certificate.
-
        <b><a href="postconf.5.html#smtp_tls_key_file">smtp_tls_key_file</a> ($<a href="postconf.5.html#smtp_tls_cert_file">smtp_tls_cert_file</a>)</b>
-              File with the Postfix SMTP client RSA  private  key
+              File  with  the Postfix SMTP client RSA private key
               in PEM format.
 
        <b><a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> (0)</b>
-              Enable  additional  Postfix  SMTP client logging of
+              Enable additional Postfix SMTP  client  logging  of
               TLS activity.
 
        <b><a href="postconf.5.html#smtp_tls_note_starttls_offer">smtp_tls_note_starttls_offer</a> (no)</b>
-              Log the hostname  of  a  remote  SMTP  server  that
-              offers  STARTTLS,  when  TLS is not already enabled
+              Log  the  hostname  of  a  remote  SMTP server that
+              offers STARTTLS, when TLS is  not  already  enabled
               for that server.
 
-       <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
+       <b><a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> (empty)</b>
               Optional lookup tables with the Postfix SMTP client
-              TLS  usage  policy  by  next-hop destination and by
-              remote SMTP server hostname.
+              TLS security policy by next-hop destination; when a
+              non-empty  value  is  specified, this overrides the
+              obsolete <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> parameter.
+
+       <b><a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> (SSLv3, TLSv1)</b>
+              List of  TLS  protocol  versions  that  are  secure
+              enough to be used with the "encrypt" security level
+              and higher.
 
        <b><a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> (5)</b>
               The verification depth for remote SMTP server  cer-
               tificates.
 
+       <b><a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> (nexthop, dot-nexthop)</b>
+              The server certificate peername verification method
+              for the "secure" TLS security level.
+
        <b><a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> (empty)</b>
-              Name  of  the  file containing the optional Postfix
+              Name of the file containing  the  optional  Postfix
               SMTP client TLS session cache.
 
        <b><a href="postconf.5.html#smtp_tls_session_cache_timeout">smtp_tls_session_cache_timeout</a> (3600s)</b>
               The expiration time of Postfix SMTP client TLS ses-
               sion cache information.
 
+       <b><a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> (hostname)</b>
+              The server certificate peername verification method
+              for the "verify" TLS security level.
+
        <b><a href="postconf.5.html#tls_daemon_random_bytes">tls_daemon_random_bytes</a> (32)</b>
               The  number  of pseudo-random bytes that an <a href="smtp.8.html"><b>smtp</b>(8)</a>
               or <a href="smtpd.8.html"><b>smtpd</b>(8)</a> process  requests  from  the  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>
               server  in order to seed its internal pseudo random
               number generator (PRNG).
 
-       Available in Postfix version 2.3 and later:
+       <b><a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a></b>
+       <b>(!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "HIGH" grade ciphers.
+
+       <b><a href="postconf.5.html#tls_medium_cipherlist">tls_medium_cipherlist</a> (!EXPORT:!LOW:ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "MEDIUM" or higher grade
+              ciphers.
+
+       <b><a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> (!EXPORT:ALL:+RC4:@STRENGTH)</b>
+              The  OpenSSL  cipherlist  for "LOW" or higher grade
+              ciphers.
+
+       <b><a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a> (ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "EXPORT" or higher grade
+              ciphers.
+
+       <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (!aNULL:eNULL+kRSA)</b>
+              The  OpenSSL  cipherlist  for  "NULL" grade ciphers
+              that provide authentication without encryption.
+
+       Available in Postfix version 2.4 and later:
 
        <b><a href="postconf.5.html#smtp_sasl_tls_verified_security_options">smtp_sasl_tls_verified_security_options</a></b>
        <b>($<a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a>)</b>
@@ -409,6 +444,31 @@ SMTP(8)                                                                SMTP(8)
               Postfix  SMTP  client  uses  for TLS encrypted SMTP
               sessions with a verified server certificate.
 
+<b>OBSOLETE STARTTLS CONTROLS</b>
+       The following configuration parameters exist for  compati-
+       bility with Postfix versions before 2.3. Support for these
+       will be removed in a future release.
+
+       <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
+              Opportunistic mode: use  TLS  when  a  remote  SMTP
+              server  announces  STARTTLS support, otherwise send
+              the mail in the clear.
+
+       <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
+              Enforcement mode: require that remote SMTP  servers
+              use  TLS  encryption,  and  never  send mail in the
+              clear.
+
+       <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
+              When TLS encryption is enforced, require  that  the
+              remote SMTP server hostname matches the information
+              in the remote SMTP server certificate.
+
+       <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
+              Optional lookup tables with the Postfix SMTP client
+              TLS  usage  policy  by  next-hop destination and by
+              remote SMTP server hostname.
+
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a>      ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
index 0f2ebcd47a56a3e31115df4cb9ffa39a24075a62..70e407e6e8f0c58a5633df1dcac8ec70a5f0181a 100644 (file)
@@ -180,6 +180,81 @@ SMTPD(8)                                                              SMTPD(8)
               The time limit for connecting to a proxy filter and
               for sending or receiving information.
 
+<b>BEFORE QUEUE MILTER CONTROLS</b>
+       As of version 2.3, Postfix supports the Sendmail version 8
+       Milter  (mail  filter) protocol. These content filters run
+       outside Postfix. They can inspect the SMTP command  stream
+       and  the  message  content,  and can request modifications
+       before mail is queued. For details see  the  <a href="MILTER_README.html">MILTER_README</a>
+       document.
+
+       <b><a href="postconf.5.html#smtpd_milters">smtpd_milters</a> (empty)</b>
+              A list of Milter (mail filter) applications for new
+              mail that arrives via the Postfix <a href="smtpd.8.html"><b>smtpd</b>(8)</a>  server.
+
+       <b><a href="postconf.5.html#milter_protocol">milter_protocol</a> (2)</b>
+              The  mail filter protocol version and optional pro-
+              tocol extensions for communication  with  a  Milter
+              (mail filter) application.
+
+       <b><a href="postconf.5.html#milter_default_action">milter_default_action</a> (tempfail)</b>
+              The  default  action  when  a  Milter (mail filter)
+              application is unavailable or mis-configured.
+
+       <b><a href="postconf.5.html#milter_macro_daemon_name">milter_macro_daemon_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
+              The {daemon_name} macro value for Milter (mail fil-
+              ter) applications.
+
+       <b><a href="postconf.5.html#milter_macro_v">milter_macro_v</a> ($<a href="postconf.5.html#mail_name">mail_name</a> $<a href="postconf.5.html#mail_version">mail_version</a>)</b>
+              The {v} macro value for Milter (mail filter) appli-
+              cations.
+
+       <b><a href="postconf.5.html#milter_connect_timeout">milter_connect_timeout</a> (30s)</b>
+              The time limit for connecting  to  a  Milter  (mail
+              filter)  application,  and for negotiating protocol
+              options.
+
+       <b><a href="postconf.5.html#milter_command_timeout">milter_command_timeout</a> (30s)</b>
+              The time limit for sending an  SMTP  command  to  a
+              Milter (mail filter) application, and for receiving
+              the response.
+
+       <b><a href="postconf.5.html#milter_content_timeout">milter_content_timeout</a> (300s)</b>
+              The time limit for sending  message  content  to  a
+              Milter (mail filter) application, and for receiving
+              the response.
+
+       <b><a href="postconf.5.html#milter_connect_macros">milter_connect_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  Milter  (mail  filter)
+              applications  after  completion  of an SMTP connec-
+              tion.
+
+       <b><a href="postconf.5.html#milter_helo_macros">milter_helo_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  Milter  (mail  filter)
+              applications after the SMTP HELO or EHLO command.
+
+       <b><a href="postconf.5.html#milter_mail_macros">milter_mail_macros</a> (see postconf -n output)</b>
+              The  macros  that  are sent to Milter (mail filter)
+              applications after the SMTP MAIL FROM command.
+
+       <b><a href="postconf.5.html#milter_rcpt_macros">milter_rcpt_macros</a> (see postconf -n output)</b>
+              The macros that are sent to  Milter  (mail  filter)
+              applications after the SMTP RCPT TO command.
+
+       <b><a href="postconf.5.html#milter_data_macros">milter_data_macros</a> (see postconf -n output)</b>
+              The  macros  that  are  sent to version 4 or higher
+              Milter (mail filter) applications  after  the  SMTP
+              DATA command.
+
+       <b><a href="postconf.5.html#milter_unknown_command_macros">milter_unknown_command_macros</a> (see postconf -n output)</b>
+              The  macros  that  are  sent to version 3 or higher
+              Milter (mail filter) applications after an  unknown
+              SMTP command.
+
+       <b><a href="postconf.5.html#milter_end_of_data_macros">milter_end_of_data_macros</a> (see postconf -n output)</b>
+              The  macros  that  are sent to Milter (mail filter)
+              applications after the message end-of-data.
+
 <b>GENERAL CONTENT INSPECTION CONTROLS</b>
        The following parameters are applicable for both  built-in
        and external content filters.
@@ -300,34 +375,41 @@ SMTPD(8)                                                              SMTPD(8)
               File with the Postfix SMTP server  RSA  certificate
               in PEM format.
 
-       <b><a href="postconf.5.html#smtpd_tls_cipherlist">smtpd_tls_cipherlist</a> (empty)</b>
-              Controls  the Postfix SMTP server TLS cipher selec-
-              tion scheme.
+       <b><a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a> (export)</b>
+              The  minimum  acceptable  SMTP  server  TLS  cipher
+              grade.
+
+       <b><a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> (empty)</b>
+              List of ciphers or cipher types to exclude from the
+              SMTP server cipher list.
 
        <b><a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a> (empty)</b>
-              File with the Postfix SMTP server  DSA  certificate
+              File  with  the Postfix SMTP server DSA certificate
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_dh1024_param_file">smtpd_tls_dh1024_param_file</a> (empty)</b>
-              File  with  DH  parameters  that  the  Postfix SMTP
+              File with  DH  parameters  that  the  Postfix  SMTP
               server should use with EDH ciphers.
 
        <b><a href="postconf.5.html#smtpd_tls_dh512_param_file">smtpd_tls_dh512_param_file</a> (empty)</b>
-              File with  DH  parameters  that  the  Postfix  SMTP
+              File  with  DH  parameters  that  the  Postfix SMTP
               server should use with EDH ciphers.
 
        <b><a href="postconf.5.html#smtpd_tls_dkey_file">smtpd_tls_dkey_file</a> ($<a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a>)</b>
-              File  with  the Postfix SMTP server DSA private key
+              File with the Postfix SMTP server DSA  private  key
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_key_file">smtpd_tls_key_file</a> ($<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>)</b>
-              File with the Postfix SMTP server RSA  private  key
+              File  with  the Postfix SMTP server RSA private key
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> (0)</b>
-              Enable  additional  Postfix  SMTP server logging of
+              Enable additional Postfix SMTP  server  logging  of
               TLS activity.
 
+       <b><a href="postconf.5.html#smtpd_tls_protocols">smtpd_tls_protocols</a> (empty)</b>
+              The  list of TLS protocols supported by the server.
+
        <b><a href="postconf.5.html#smtpd_tls_received_header">smtpd_tls_received_header</a> (no)</b>
               Request  that  the  Postfix  SMTP  server  produces
               Received:  message headers that include information
@@ -359,6 +441,26 @@ SMTPD(8)                                                              SMTPD(8)
               server in order to seed its internal pseudo  random
               number generator (PRNG).
 
+       <b><a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a></b>
+       <b>(!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "HIGH" grade ciphers.
+
+       <b><a href="postconf.5.html#tls_medium_cipherlist">tls_medium_cipherlist</a> (!EXPORT:!LOW:ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "MEDIUM" or higher grade
+              ciphers.
+
+       <b><a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> (!EXPORT:ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "LOW"  or  higher  grade
+              ciphers.
+
+       <b><a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a> (ALL:+RC4:@STRENGTH)</b>
+              The OpenSSL cipherlist for "EXPORT" or higher grade
+              ciphers.
+
+       <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (!aNULL:eNULL+kRSA)</b>
+              The OpenSSL cipherlist  for  "NULL"  grade  ciphers
+              that provide authentication without encryption.
+
 <b>VERP SUPPORT CONTROLS</b>
        With  VERP  style  delivery,  each  recipient of a message
        receives a customized copy of the message with his/her own
@@ -977,6 +1079,7 @@ SMTPD(8)                                                              SMTPD(8)
        <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> Postfix address manipulation
        <a href="FILTER_README.html">FILTER_README</a>, external after-queue content filter
        <a href="LOCAL_RECIPIENT_README.html">LOCAL_RECIPIENT_README</a>, blocking unknown local recipients
+       <a href="MILTER_README.html">MILTER_README</a>, before-queue mail filter applications
        <a href="SMTPD_ACCESS_README.html">SMTPD_ACCESS_README</a>, built-in access policies
        <a href="SMTPD_POLICY_README.html">SMTPD_POLICY_README</a>, external policy server
        <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a>, external before-queue content filter
index 6c0f680f2359dcec017ba8a324573551f0219445..e26c8a80a5d3ac9e2a0696184a741b862c21174f 100644 (file)
@@ -15,7 +15,7 @@ SPAWN(8)                                                              SPAWN(8)
 <b>DESCRIPTION</b>
        The  <a href="spawn.8.html"><b>spawn</b>(8)</a>  daemon  provides  the Postfix equivalent of
        <b>inetd</b>.  It listens on a port as specified in  the  Postfix
-       <b>master.cf</b>  file  and spawns an external command whenever a
+       <a href="master.5.html"><b>master.cf</b></a>  file  and spawns an external command whenever a
        connection is established.  The  connection  can  be  made
        over  local IPC (such as UNIX-domain sockets) or over non-
        local IPC (such as TCP sockets).  The  command's  standard
@@ -26,7 +26,7 @@ SPAWN(8)                                                              SPAWN(8)
        manager.
 
 <b>COMMAND ATTRIBUTE SYNTAX</b>
-       The external command attributes are given in the <b>master.cf</b>
+       The external command attributes are given in the <a href="master.5.html"><b>master.cf</b></a>
        file at the end of a service definition.  The syntax is as
        follows:
 
@@ -66,7 +66,7 @@ SPAWN(8)                                                              SPAWN(8)
        data-driven attacks.
 
 <b>CONFIGURATION PARAMETERS</b>
-       Changes to <b>main.cf</b> are picked up automatically as <a href="spawn.8.html"><b>spawn</b>(8)</a>
+       Changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up automatically as <a href="spawn.8.html"><b>spawn</b>(8)</a>
        processes run for only a limited amount of time.  Use  the
        command "<b>postfix reload</b>" to speed up a change.
 
@@ -74,7 +74,7 @@ SPAWN(8)                                                              SPAWN(8)
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
        In the text below, <i>transport</i> is the  first  field  of  the
-       entry in the <b>master.cf</b> file.
+       entry in the <a href="master.5.html"><b>master.cf</b></a> file.
 
 <b>RESOURCE AND RATE CONTROL</b>
        <i>transport</i><b>_time_limit ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b>
@@ -83,8 +83,8 @@ SPAWN(8)                                                              SPAWN(8)
 
 <b>MISCELLANEOUS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  main.cf  and
-              master.cf configuration files.
+              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#daemon_timeout">daemon_timeout</a> (18000s)</b>
               How  much time a Postfix daemon process may take to
index e2b15e73e526e1369a773ff59d13604d0714cf5b..a60980d8a3f6bedaef31b2b1dccdabf0e7a11eca 100644 (file)
@@ -64,13 +64,21 @@ TLSMGR(8)                                                            TLSMGR(8)
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
 <b>TLS SESSION CACHE</b>
-       <b><a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> (empty)</b>
-              Name of the file containing  the  optional  Postfix
-              SMTP server TLS session cache.
+       <b><a href="postconf.5.html#lmtp_tls_loglevel">lmtp_tls_loglevel</a> (0)</b>
+              The LMTP-specific version of the  <a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a>
+              configuration parameter.
 
-       <b><a href="postconf.5.html#smtpd_tls_session_cache_timeout">smtpd_tls_session_cache_timeout</a> (3600s)</b>
-              The expiration time of Postfix SMTP server TLS ses-
-              sion cache information.
+       <b><a href="postconf.5.html#lmtp_tls_session_cache_database">lmtp_tls_session_cache_database</a> (empty)</b>
+              The  LMTP-specific  version  of  the  smtp_tls_ses-
+              sion_cache_database configuration parameter.
+
+       <b><a href="postconf.5.html#lmtp_tls_session_cache_timeout">lmtp_tls_session_cache_timeout</a> (3600s)</b>
+              The  LMTP-specific  version  of  the  smtp_tls_ses-
+              sion_cache_timeout configuration parameter.
+
+       <b><a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> (0)</b>
+              Enable  additional  Postfix  SMTP client logging of
+              TLS activity.
 
        <b><a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> (empty)</b>
               Name of the file containing  the  optional  Postfix
@@ -80,56 +88,68 @@ TLSMGR(8)                                                            TLSMGR(8)
               The expiration time of Postfix SMTP client TLS ses-
               sion cache information.
 
+       <b><a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> (0)</b>
+              Enable additional Postfix SMTP  server  logging  of
+              TLS activity.
+
+       <b><a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> (empty)</b>
+              Name  of  the  file containing the optional Postfix
+              SMTP server TLS session cache.
+
+       <b><a href="postconf.5.html#smtpd_tls_session_cache_timeout">smtpd_tls_session_cache_timeout</a> (3600s)</b>
+              The expiration time of Postfix SMTP server TLS ses-
+              sion cache information.
+
 <b>PSEUDO RANDOM NUMBER GENERATOR</b>
        <b><a href="postconf.5.html#tls_random_source">tls_random_source</a> (see 'postconf -d' output)</b>
               The  external  entropy  source  for  the  in-memory
-              <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>  pseudo  random  number  generator (PRNG)
+              <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> pseudo  random  number  generator  (PRNG)
               pool.
 
        <b><a href="postconf.5.html#tls_random_bytes">tls_random_bytes</a> (32)</b>
-              The number  of  bytes  that  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>  reads  from
-              $<a href="postconf.5.html#tls_random_source">tls_random_source</a>  when  (re)seeding the in-memory
+              The  number  of  bytes  that  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>  reads from
+              $<a href="postconf.5.html#tls_random_source">tls_random_source</a> when (re)seeding  the  in-memory
               pseudo random number generator (PRNG) pool.
 
        <b><a href="postconf.5.html#tls_random_exchange_name">tls_random_exchange_name</a> (${<a href="postconf.5.html#config_directory">config_directory</a>}/prng_exch)</b>
-              Name of the pseudo random number  generator  (PRNG)
+              Name  of  the pseudo random number generator (PRNG)
               state file that is maintained by <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>.
 
        <b><a href="postconf.5.html#tls_random_prng_update_period">tls_random_prng_update_period</a> (3600s)</b>
-              The  time between attempts by <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> to save the
-              state of the pseudo random number generator  (PRNG)
+              The time between attempts by <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> to save  the
+              state  of the pseudo random number generator (PRNG)
               to    the    file    specified    with    $<a href="postconf.5.html#tls_random_exchange_name">tls_ran</a>-
               <a href="postconf.5.html#tls_random_exchange_name">dom_exchange_name</a>.
 
        <b><a href="postconf.5.html#tls_random_reseed_period">tls_random_reseed_period</a> (3600s)</b>
-              The maximal time between attempts by  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>  to
-              re-seed  the in-memory pseudo random number genera-
+              The  maximal  time between attempts by <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> to
+              re-seed the in-memory pseudo random number  genera-
               tor (PRNG) pool from external sources.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <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#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The  process  ID  of  a  Postfix  command or daemon
+              The process ID  of  a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The process name of a  Postfix  command  or  daemon
+              The  process  name  of  a Postfix command or daemon
               process.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The  mail  system  name  that  is  prepended to the
-              process name in syslog  records,  so  that  "smtpd"
+              The mail system  name  that  is  prepended  to  the
+              process  name  in  syslog  records, so that "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>SEE ALSO</b>
@@ -144,7 +164,7 @@ TLSMGR(8)                                                            TLSMGR(8)
        <a href="TLS_README.html">TLS_README</a>, Postfix TLS configuration and operation
 
 <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>
index f48f3f884d6c0c488e4b342e84ead8ae10586727..668d0c2e3fa5c48e5945c75a2be59c2253d142a2 100644 (file)
@@ -175,7 +175,8 @@ such as pop-before-smtp.
 .nf
 .ad
 .fi
-Postfix version 2.3 and later support enhanced status codes.
+Postfix version 2.3 and later support enhanced status codes
+as defined in RFC 3463.
 When no code is specified at the beginning of the \fItext\fR
 below, Postfix inserts a default enhanced status code of "5.7.1"
 in the case of reject actions, and "4.7.1" in the case of
@@ -185,6 +186,9 @@ defer actions. See "ENHANCED STATUS CODES" below.
 Reject the address etc. that matches the pattern, and respond with
 the numerical three-digit code and text. \fB4\fINN\fR means "try
 again later", while \fB5\fINN\fR means "do not try again".
+.IP
+The reply code "421" causes Postfix to disconnect immediately
+(Postfix version 2.3 and later).
 .IP "\fBREJECT \fIoptional text...\fR
 Reject the address etc. that matches the pattern. Reply with
 \fI$reject_code optional text...\fR when the optional text is
@@ -285,6 +289,8 @@ This feature is available in Postfix 2.1 and later.
 .nf
 .ad
 .fi
+Postfix version 2.3 and later support enhanced status codes
+as defined in RFC 3463.
 When an enhanced status code is specified in an access
 table, it is subject to modification. The following
 transformations are needed when the same access table is
index e4471ffe4efd370c64dad27cee7f78862af7ae82..4a364a45dc5cf019f8af0e43097c7bb649acf2ad 100644 (file)
@@ -1973,11 +1973,66 @@ parameter.  See there for details.
 This feature is available in Postfix 2.3 and later.
 .SH lmtp_tcp_port (default: 24)
 The default TCP port that the Postfix LMTP client connects to.
+.SH lmtp_tls_CAfile (default: empty)
+The LMTP-specific version of the smtp_tls_CAfile
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_CApath (default: empty)
+The LMTP-specific version of the smtp_tls_CApath
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_cert_file (default: empty)
+The LMTP-specific version of the smtp_tls_cert_file
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_dcert_file (default: empty)
+The LMTP-specific version of the smtp_tls_dcert_file
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_dkey_file (default: $lmtp_tls_dcert_file)
+The LMTP-specific version of the smtp_tls_dkey_file
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH lmtp_tls_enforce_peername (default: yes)
 The LMTP-specific version of the smtp_tls_enforce_peername
 configuration parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_exclude_ciphers (default: empty)
+The LMTP-specific version of the smtp_tls_exclude_ciphers
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_key_file (default: $lmtp_tls_cert_file)
+The LMTP-specific version of the smtp_tls_key_file
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_loglevel (default: 0)
+The LMTP-specific version of the smtp_tls_loglevel
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_mandatory_ciphers (default: empty)
+The LMTP-specific version of the smtp_tls_mandatory_ciphers
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_mandatory_exclude_ciphers (default: empty)
+The LMTP-specific version of the smtp_tls_mandatory_exclude_ciphers
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_mandatory_protocols (default: SSLv3, TLSv1)
+The LMTP-specific version of the smtp_tls_mandatory_protocols
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH lmtp_tls_note_starttls_offer (default: no)
 The LMTP-specific version of the smtp_tls_note_starttls_offer
 configuration parameter.  See there for details.
@@ -1988,11 +2043,36 @@ The LMTP-specific version of the smtp_tls_per_site configuration
 parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_policy_maps (default: empty)
+The LMTP-specific version of the smtp_tls_policy_maps
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH lmtp_tls_scert_verifydepth (default: 5)
 The LMTP-specific version of the smtp_tls_scert_verifydepth
 configuration parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_secure_cert_match (default: nexthop)
+The LMTP-specific version of the smtp_tls_secure_cert_match
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_session_cache_database (default: empty)
+The LMTP-specific version of the smtp_tls_session_cache_database
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_session_cache_timeout (default: 3600s)
+The LMTP-specific version of the smtp_tls_session_cache_timeout
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_verify_cert_match (default: hostname)
+The LMTP-specific version of the smtp_tls_verify_cert_match
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH lmtp_use_tls (default: no)
 The LMTP-specific version of the smtp_use_tls configuration
 parameter.  See there for details.
@@ -2579,6 +2659,126 @@ message_strip_characters = \e0
 .ft R
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH milter_command_timeout (default: 30s)
+The time limit for sending an SMTP command to a Milter (mail
+filter) application, and for receiving the response.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).
+.PP
+Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds).
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_connect_macros (default: see postconf -n output)
+The macros that are sent to Milter (mail filter) applications
+after completion of an SMTP connection. See MILTER_README
+for a list of available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_connect_timeout (default: 30s)
+The time limit for connecting to a Milter (mail filter)
+application, and for negotiating protocol options.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).
+.PP
+Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds).
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_content_timeout (default: 300s)
+The time limit for sending message content to a Milter (mail
+filter) application, and for receiving the response.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).
+.PP
+Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds).
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_data_macros (default: see postconf -n output)
+The macros that are sent to version 4 or higher Milter (mail
+filter) applications after the SMTP DATA command. See MILTER_README
+for a list of available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_default_action (default: tempfail)
+The default action when a Milter (mail filter) application is
+unavailable or mis-configured. Specify one of the following:
+.IP "accept"
+Proceed as if the mail filter was not present.
+.IP "reject"
+Reject all further commands in this session
+with a permanent status code.
+.IP "tempfail"
+Reject all further commands in this session
+with a temporary status code.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_end_of_data_macros (default: see postconf -n output)
+The macros that are sent to Milter (mail filter) applications
+after the message end-of-data. See MILTER_README for a list of
+available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_helo_macros (default: see postconf -n output)
+The macros that are sent to Milter (mail filter) applications
+after the SMTP HELO or EHLO command. See
+MILTER_README for a list of available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_macro_daemon_name (default: $myhostname)
+The {daemon_name} macro value for Milter (mail filter) applications.
+See MILTER_README for a list of available macro names and their
+meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_macro_v (default: $mail_name $mail_version)
+The {v} macro value for Milter (mail filter) applications.
+See MILTER_README for a list of available macro names and their
+meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_mail_macros (default: see postconf -n output)
+The macros that are sent to Milter (mail filter) applications
+after the SMTP MAIL FROM command. See MILTER_README
+for a list of available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_protocol (default: 2)
+The mail filter protocol version and optional protocol extensions
+for communication with a Milter (mail filter) application. This
+information should match the protocol that is expected by the actual
+mail filter application.
+.PP
+Protocol versions:
+.IP "2"
+Use Sendmail 8 mail filter protocol version 2.
+.IP "3"
+Use Sendmail 8 mail filter protocol version 3.
+.IP "4"
+Use Sendmail 8 mail filter protocol version 4.
+.PP
+Protocol extensions:
+.IP "no_header_reply"
+Specify this when the Milter application
+will not reply for each individual message header.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_rcpt_macros (default: see postconf -n output)
+The macros that are sent to Milter (mail filter) applications
+after the SMTP RCPT TO command. See MILTER_README
+for a list of available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH milter_unknown_command_macros (default: see postconf -n output)
+The macros that are sent to version 3 or higher Milter (mail
+filter) applications after an unknown SMTP command.  See MILTER_README
+for a list of available macro names and their meanings.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH mime_boundary_length_limit (default: 2048)
 The maximal length of MIME multipart boundary strings. The MIME
 processor is unable to distinguish between boundary strings that
@@ -2786,6 +2986,15 @@ Sendmail compatibility feature that specifies the location of the
 The numerical Postfix SMTP server reply code when a client request
 is rejected by the reject_non_fqdn_helo_hostname, reject_non_fqdn_sender
 or reject_non_fqdn_recipient restriction.
+.SH non_smtpd_milters (default: empty)
+A list of Milter (mail filter) applications for new mail that
+does not arrive via the Postfix \fBsmtpd\fR(8) server. This includes local
+submission via the \fBsendmail\fR(1) command line, new mail that arrives
+via the Postfix \fBqmqpd\fR(8) server, and old mail that is re-injected
+into the queue with "postsuper -r".  See the MILTER_README document
+for details.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH notify_classes (default: resource, software)
 The list of error classes that are reported to the postmaster. The
 default is to report only the most serious problems. The paranoid
@@ -3078,6 +3287,9 @@ filter.
 .IP "\fBno_header_body_checks\fR"
 Disable header/body_checks. This is typically specified AFTER
 an external content filter.
+.IP "\fBno_milters\fR"
+Disable Milter (mail filter) applications. This is typically
+specified AFTER an external content filter.
 .PP
 Note: when the "BEFORE content filter" receive_override_options
 setting is specified in the main.cf file, specify the "AFTER content
@@ -3804,6 +4016,9 @@ This option is useful only if you are definitely sure that you
 will only connect to servers that support RFC 2487 _and_ that
 provide valid server certificates.  Typical use is for clients that
 send all their email to a dedicated mailhub.
+.PP
+This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_security_level instead.
 .SH smtp_fallback_relay (default: $fallback_relay)
 Optional list of relay hosts for SMTP destinations that can't be
 found or that are unreachable. With Postfix 2.2 and earlier this
@@ -4053,12 +4268,15 @@ smtp_sasl_security_options = noplaintext
 .SH smtp_sasl_tls_security_options (default: $smtp_sasl_security_options)
 The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_sasl_tls_verified_security_options (default: $smtp_sasl_tls_security_options)
 The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions with a verified server
-certificate.
+certificate. This feature is still under construction. It will not be
+included in the Postfix 2.3 release.
 .PP
-This feature is available in Postfix 2.3 and later.
+This feature should be available in Postfix 2.4 and later.
 .SH smtp_sasl_type (default: cyrus)
 The SASL plug-in type that the Postfix SMTP client should use
 for authentication.  The available types are listed with the
@@ -4107,6 +4325,8 @@ Do not wait for the response to the SMTP QUIT command.
 .SH smtp_starttls_timeout (default: 300s)
 Time limit for Postfix SMTP client write and read operations
 during TLS startup and shutdown handshake procedures.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_CAfile (default: empty)
 The file with the certificate of the certification authority
 (CA) that issued the Postfix SMTP client certificate.  This is
@@ -4122,6 +4342,8 @@ smtp_tls_CAfile = /etc/postfix/CAcert.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_CApath (default: empty)
 Directory with PEM format certificate authority certificates
 that the Postfix SMTP client uses to verify a remote SMTP server
@@ -4140,11 +4362,37 @@ smtp_tls_CApath = /etc/postfix/certs
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_cert_file (default: empty)
 File with the Postfix SMTP client RSA certificate in PEM format.
 This file may also contain the client private key, and these may
 be the same as the server certificate and key file.
 .PP
+Do not configure client certificates unless you \fBmust\fR present
+client TLS certificates to one or more servers. Client certificates are
+not usually needed, and can cause problems in configurations that work
+well without them. The recommended setting is to let the defaults stand:
+.na
+.nf
+.in +4
+.nf
+.na
+.ft C
+    smtp_tls_cert_file =
+    smtp_tls_dcert_file =
+    smtp_tls_key_file =
+    smtp_tls_dkey_file =
+.fi
+.ad
+.ft R
+.in -4
+.fi
+.ad
+.PP
+The best way to use the default settings is to comment out the above
+parameters in main.cf if present.
+.PP
 In order to verify certificates, the CA certificate (in case
 of a certificate chain, all CA certificates) must be available.
 You should add these certificates to the server certificate, the
@@ -4172,10 +4420,22 @@ smtp_tls_cert_file = /etc/postfix/client.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_cipherlist (default: empty)
-Controls the Postfix SMTP client TLS cipher selection scheme.
-For details, see the OpenSSL documentation. Note: do not use ""
-quotes around the parameter value.
+Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
+cipher list. As this feature applies to all security levels, it is easy
+to create inter-operability problems by choosing a non-default cipher
+list. Do not use a non-default TLS cipher list on hosts that deliver email
+to the public Internet: you will be unable to send email to servers that
+only support the ciphers you exclude. Using a restricted cipher list
+may be more appropriate for an internal MTA, where one can exert some
+control over the TLS software and settings of the peer servers.
+.PP
+\fBNote:\fR do not use "" quotes around the parameter value.
+.PP
+This feature is available in Postfix version 2.2. It is not used with
+Postfix 2.3 and later; use smtp_tls_mandatory_ciphers instead.
 .SH smtp_tls_dcert_file (default: empty)
 File with the Postfix SMTP client DSA certificate in PEM format.
 This file may also contain the server private key.
@@ -4191,6 +4451,8 @@ smtp_tls_dcert_file = /etc/postfix/client-dsa.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_dkey_file (default: $smtp_tls_dcert_file)
 File with the Postfix SMTP client DSA private key in PEM format.
 The private key must not be encrypted. In other words, the key must
@@ -4198,6 +4460,8 @@ be accessible without password.
 .PP
 This file may be combined with the server certificate file
 specified with $smtp_tls_cert_file.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_enforce_peername (default: yes)
 When TLS encryption is enforced, require that the remote SMTP
 server hostname matches the information in the remote SMTP server
@@ -4212,6 +4476,38 @@ Disabling the hostname verification can make sense in closed
 environment where special CAs are created.  If not used carefully,
 this option opens the danger of a "man-in-the-middle" attack (the
 CommonName of this attacker will be logged).
+.PP
+This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_security_level instead.
+.SH smtp_tls_exclude_ciphers (default: empty)
+List of ciphers or cipher types to exclude from the SMTP client cipher
+list at all security levels. This is not an OpenSSL cipherlist, it is
+a simple list separated by whitespace and/or commas. The elements are a
+single cipher, or one or more "+" separated cipher properties, in which
+case only ciphers matching \fBall\fR the properties are excluded.
+.PP
+Examples (some of these will cause problems):
+.PP
+.nf
+.na
+.ft C
+smtp_tls_exclude_ciphers = aNULL
+smtp_tls_exclude_ciphers = MD5, DES
+smtp_tls_exclude_ciphers = DES+MD5
+smtp_tls_exclude_ciphers = AES256-SHA, DES-CBC3-MD5
+smtp_tls_exclude_ciphers = kEDH+aRSA
+.fi
+.ad
+.ft R
+.PP
+The first setting, disables anonymous ciphers. The next setting
+disables ciphers that use the MD5 digest algorithm or the (single) DES
+encryption algorithm. The next setting disables ciphers that use MD5 and
+DES together.  The next setting disables the two ciphers "AES256-SHA"
+and "DES-CBC3-MD5". The last setting disables ciphers that use "EDH"
+key exchange with RSA authentication.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtp_tls_key_file (default: $smtp_tls_cert_file)
 File with the Postfix SMTP client RSA private key in PEM format.
 This file may be combined with the client certificate file specified
@@ -4229,6 +4525,8 @@ smtp_tls_key_file = $smtp_tls_cert_file
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_loglevel (default: 0)
 Enable additional Postfix SMTP client logging of TLS activity.
 Each logging level also includes the information that is logged at
@@ -4248,6 +4546,95 @@ transmission after STARTTLS.
 .PP
 Use "smtp_tls_loglevel = 3" only in case of problems. Use of
 loglevel 4 is strongly discouraged.
+.PP
+This feature is available in Postfix 2.2 and later.
+.SH smtp_tls_mandatory_ciphers (default: medium)
+The minimum SMTP client TLS cipher grade that is strong enough to
+be used with the "encrypt" security level and higher.  The default
+value "medium" is suitable for most destinations with which you may
+want to enforce TLS, and is beyond the reach of today's crypt-analytic
+methods. See smtp_tls_policy_maps for information on how to configure
+ciphers on a per-destination basis.
+.PP
+The following cipher grades are supported:
+.IP "\fBexport\fR"
+Enable the mainstream "EXPORT" grade or better OpenSSL
+ciphers.  This is always used for opportunistic encryption. It is
+not recommended for mandatory encryption unless you must enforce TLS
+with "crippled" peers. The underlying cipherlist is specified via the
+tls_export_cipherlist configuration parameter, which you are strongly
+encouraged to not change. The default value of tls_export_cipherlist
+includes anonymous ciphers, but these are automatically filtered out if
+the client is configured to verify server certificates. If you must
+exclude anonymous ciphers also at the "encrypt" security level, set
+"smtp_tls_mandatory_exclude_ciphers = aNULL".
+.IP "\fBlow\fR"
+Enable the mainstream "LOW" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_low_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_low_cipherlist includes anonymous ciphers, but these are
+automatically filtered out if the client is configured to verify server
+certificates. If you must exclude anonymous ciphers also at the "encrypt"
+security level, set "smtp_tls_mandatory_exclude_ciphers = aNULL".
+.IP "\fBmedium\fR"
+Enable the mainstream "MEDIUM" grade or better OpenSSL ciphers.
+The underlying cipherlist is specified via the tls_medium_cipherlist
+configuration parameter, which you are strongly encouraged to not change.
+The default value of tls_medium_cipherlist includes anonymous ciphers,
+but these are automatically filtered out if the client is configured to
+verify server certificates. If you must exclude anonymous ciphers also
+at the "encrypt" security level, set "smtp_tls_mandatory_exclude_ciphers
+= aNULL".
+.IP "\fBhigh\fR"
+Enable only the mainstream "HIGH" grade OpenSSL ciphers.  This
+setting is appropriate when all mandatory TLS destinations support
+some of "HIGH" grade ciphers, this is not uncommon. The underlying
+cipherlist is specified via the tls_high_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_high_cipherlist includes anonymous ciphers, but these are
+automatically filtered out if the client is configured to verify server
+certificates. If you must exclude anonymous ciphers also at the "encrypt"
+security level, set "smtp_tls_mandatory_exclude_ciphers = aNULL".
+.IP "\fBnull\fR"
+Enable only the "NULL" OpenSSL ciphers, these provide authentication
+without encryption.  This setting is only appropriate in the rare case
+that all servers are prepared to use NULL ciphers (not normally enabled
+in TLS servers). A plausible use-case is an LMTP server listening on a
+UNIX-domain socket that is configured to support "NULL" ciphers. The
+underlying cipherlist is specified via the tls_null_cipherlist
+configuration parameter, which you are strongly encouraged to not
+change. The default value of tls_null_cipherlist excludes anonymous
+ciphers (OpenSSL 0.9.8 has NULL ciphers that offer data integrity without
+encryption or authentication).
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH smtp_tls_mandatory_exclude_ciphers (default: empty)
+List of ciphers or cipher types to exclude from the SMTP client
+cipher list at the mandatory TLS security levels: "encrypt", "verify"
+and "secure". See smtp_tls_exclude_ciphers for syntax details. When
+both "exclude" parameters are defined, the combined list of ciphers is
+excluded (provided the TLS security level is "encrypt" or higher).
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH smtp_tls_mandatory_protocols (default: SSLv3, TLSv1)
+List of TLS protocol versions that are secure enough to be used
+with the "encrypt" security level and higher. In main.cf the values
+are separated by whitespace, commas or colons. In the policy table
+(see smtp_tls_policy_maps) the only valid 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
+Since SSL version 2 has known protocol weaknesses and
+is now deprecated, the default setting only lists "SSLv3" and
+"TLSv1". This means that by default, SSL version 2 will not be used
+at the "encrypt" security level and higher.
+.PP
+See the documentation of the smtp_tls_policy_maps parameter and
+TLS_README for more information about security levels.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtp_tls_note_starttls_offer (default: no)
 Log the hostname of a remote SMTP server that offers STARTTLS,
 when TLS is not already enabled for that server.
@@ -4261,13 +4648,22 @@ postfix/smtp[pid]:  Host offered STARTTLS: [name.of.host]
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_per_site (default: empty)
 Optional lookup tables with the Postfix SMTP client TLS usage
 policy by next-hop destination and by remote SMTP server hostname.
 When both lookups succeed, the more specific per-site policy (NONE,
-MUST, etc) overrides the less specific one (MAY), and the more
-secure per-site policy (MUST, etc) overrides the less secure one
-(NONE).
+MUST, etc) overrides the less specific one (MAY), and the more secure
+per-site policy (MUST, etc) overrides the less secure one (NONE).
+With Postfix 2.3 and later smtp_tls_per_site is strongly discouraged:
+use smtp_tls_policy_maps instead.
+.PP
+Use of the bare hostname as the per-site table lookup key is
+discouraged. Always use the full destination nexthop (enclosed in
+[] with a possible ":port" suffix). A recipient domain or MX-enabled
+transport next-hop with no port suffix may look like a bare hostname,
+but is still a suitable \fIdestination\fR.
 .PP
 Specify a next-hop destination or server hostname on the left-hand
 side; no wildcards are allowed. The next-hop destination is either
@@ -4304,40 +4700,298 @@ result from the alternate host or next-hop lookup key, and overrides
 the global smtp_use_tls, smtp_enforce_tls and smtp_tls_enforce_peername
 settings.
 .PP
+The above keywords correspond to the "none", "may", "encrypt" and
+"verify" security levels for the new smtp_tls_security_level parameter
+introduced in Postfix 2.3. Starting with Postfix 2.3, and independently
+of how the policy is specified, the smtp_tls_mandatory_ciphers and
+smtp_tls_mandatory_protocols parameters only apply when TLS encryption
+is mandatory. Connections for which encryption is optional enable
+all "export" grade and better ciphers.
+.PP
 As long as no secure DNS lookup mechanism is available, false
 hostnames in MX or CNAME responses can change the server hostname
 that Postfix uses for TLS policy lookup and server certificate
-verification. Even with a perfect match between the server hostname
-and the server certificate, there is no guarantee that Postfix is
-connected to the right server.  To avoid this loophole take the
-following steps:
-.IP \(bu
-Disallow CNAME hostname overrides. In main.cf specify
-"smtp_cname_overrides_servername = no". This prevents false hostname
-information in DNS CNAME records from changing the server hostname
-that Postfix uses for TLS policy lookup and server certificate
-verification. This feature requires Postfix 2.2.9 or later.
-.IP \(bu
-Eliminate MX lookups. Specify local \fBtransport\fR(5) table entries
-for sensitive domains with explicit smtp:[mailhost] or smtp:[mailhost]:port
-destinations.  This prevents false hostname information in DNS MX
-records from changing the server hostname that Postfix uses for TLS
-policy lookup and server certificate verification.
-.IP \(bu
-Specify MUST for these mail hosts (including [ ] and port) in
-the smtp_tls_per_site table.
+verification. Even with a perfect match between the server hostname and
+the server certificate, there is no guarantee that Postfix is connected
+to the right server.  See TLS_README (Closing a DNS loophole with obsolete
+per-site TLS policies) for a possible work-around.
+.PP
+This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_policy_maps instead.
+.SH smtp_tls_policy_maps (default: empty)
+Optional lookup tables with the Postfix SMTP client TLS security
+policy by next-hop destination; when a non-empty value is specified,
+this overrides the obsolete smtp_tls_per_site parameter.  See
+TLS_README for a more detailed discussion of TLS security levels.
+.PP
+The TLS policy table is indexed by the full next-hop destination,
+which is either the recipient domain, or the verbatim next-hop
+specified in the transport table, $local_transport, $virtual_transport,
+$relay_transport or $default_transport. This includes any enclosing
+square brackets and any non-default destination server port suffix. The
+LMTP socket type prefix (inet: or unix:) is not included in the lookup
+key.
+.PP
+Only the next-hop domain, or $myhostname with LMTP over UNIX-domain
+sockets, is used as the nexthop name for certificate verification. The
+port and any enclosing square brackets are used in the table lookup key,
+but are not used for server name verification.
+.PP
+When the lookup key is a domain name without enclosing square brackets
+or any \fI:port\fR suffix (typically the recipient domain), and the full
+domain is not found in the table, just as with the \fBtransport\fR(5) table,
+the parent domain starting with a leading "." is matched recursively. This
+allows one to specify a security policy for a recipient domain and all
+its sub-domains.
+.PP
+The lookup result is a security level, followed by an optional list
+of whitespace and/or comma separated name=value attributes that override
+related main.cf settings. The TLS security levels in order of increasing
+security are:
+.IP "\fBnone\fR"
+No TLS. No additional attributes are supported at this level.
+.IP "\fBmay\fR"
+Opportunistic TLS. No additional attributes are supported at this
+level. Since sending in the clear is acceptable, demanding stronger
+than default TLS security parameters merely reduces inter-operability.
+Postfix 2.3 and later ignore the smtp_tls_mandatory_ciphers and
+smtp_tls_mandatory_protocols parameters at this security level; all
+protocols are allowed and "export" grade or better ciphers are used.
+When TLS handshakes fail, the connection is retried with TLS disabled.
+This allows mail delivery to sites with non-interoperable TLS
+implementations.
+.IP "\fBencrypt\fR"
+Mandatory TLS encryption. At this level
+and higher the optional "ciphers" attribute overrides the main.cf
+smtp_tls_mandatory_ciphers parameter and the optional "protocols"
+keyword overrides the main.cf smtp_tls_mandatory_protocols parameter.
+In the policy table, multiple protocols must be separated by colons,
+as attribute values may not contain whitespace or commas.
+.IP "\fBverify\fR"
+Mandatory TLS verification.  At this security
+level, DNS MX lookups are trusted to be secure enough, and the name
+verified in the server certificate is usually obtained indirectly via
+unauthenticated DNS MX lookups.  The optional "match" attribute overrides
+the main.cf smtp_tls_verify_cert_match parameter. In the policy table,
+multiple match patterns and strategies must be separated by colons.
+In practice explicit control over matching is more common with the
+"secure" policy, described below.
+.IP "\fBsecure\fR"
+Secure-channel TLS. At this security level, DNS
+MX lookups, though potentially used to determine the candidate next-hop
+gateway IP addresses, are \fBnot\fR trusted to be secure enough for TLS
+peername verification. Instead, the default name verified in the server
+certificate is obtained directly from the next-hop, or is explicitly
+specified via the optional \fBmatch\fR attribute which overrides the
+main.cf smtp_tls_secure_cert_match parameter. In the policy table,
+multiple match patterns and strategies must be separated by colons.
+The match attribute is most useful when multiple domains are supported by
+common server, the policy entries for additional domains specify matching
+rules for the primary domain certificate. While transport table overrides
+routing the secondary domains to the primary nexthop also allow secure
+verification, they risk delivery to the wrong destination when domains
+change hands or are re-assigned to new gateways. With the "match"
+attribute approach, routing is not perturbed, and mail is deferred if
+verification of a new MX host fails.
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+main.cf:
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+.fi
+.ad
+.ft R
+.nf
+.na
+.ft C
+tls_policy:
+    example.edu                 none
+    example.mil                 may
+    example.gov                 encrypt protocols=TLSv1
+    example.com                 verify ciphers=high
+    example.net                 secure
+    .example.net                secure match=.example.net:example.net
+    [mail.example.org]:587      secure match=nexthop
+.fi
+.ad
+.ft R
 .PP
+\fBNote:\fR The \fBhostname\fR strategy if listed in a non-default
+setting of smtp_tls_secure_cert_match or in the \fBmatch\fR attribute
+in the policy table can render the \fBsecure\fR level vulnerable to
+DNS forgery. Do not use the \fBhostname\fR strategy for secure-channel
+configurations in environments where DNS security is not assured.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtp_tls_scert_verifydepth (default: 5)
 The verification depth for remote SMTP server certificates. A
 depth of 1 is sufficient, if the certificate is directly issued by
 a CA listed in the CA files.  The default value (5) should suffice
 for longer chains (the root CA issues special CA which then issues
 the actual certificate...).
+.PP
+This feature is available in Postfix 2.2 and later.
+.SH smtp_tls_secure_cert_match (default: nexthop, dot-nexthop)
+The server certificate peername verification method for the
+"secure" TLS security level. In a "secure" TLS policy table
+($smtp_tls_policy_maps) entry the optional "match" attribute
+overrides this main.cf setting.
+.PP
+This parameter specifies one or more patterns or strategies separated
+by commas, whitespace or colons.  In the policy table the only valid
+separator is the colon character.
+.PP
+For a description of the pattern and strategy syntax see the
+smtp_tls_verify_cert_match parameter. The "hostname" strategy should
+be avoided in this context, as in the absence of a secure global DNS, using
+the results of MX lookups in certificate verification is not immune to active
+(man-in-the-middle) attacks on DNS.
+.PP
+Sample main.cf setting:
+.PP
+.nf
+.na
+.ft C
+smtp_tls_secure_cert_match = nexthop
+.fi
+.ad
+.ft R
+.PP
+Sample policy table override:
+.PP
+.nf
+.na
+.ft C
+example.net     secure match=example.com:.example.com
+\e&.example.net    secure match=example.com:.example.com
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH smtp_tls_security_level (default: empty)
+The default SMTP TLS security level for all destinations; when
+a non-empty value is specified, this overrides the obsolete parameters
+smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
+.PP
+Specify one of the following security levels:
+.IP "\fBnone\fR"
+TLS will not be used unless enabled for specific
+destinations via smtp_tls_policy_maps.
+.IP "\fBmay\fR"
+Opportunistic TLS. TLS will be used if supported by the server. Since
+sending in the clear is acceptable, demanding stronger than default TLS
+security parameters merely reduces inter-operability. Postfix 2.3 and
+later ignore the smtp_tls_mandatory_ciphers and
+smtp_tls_mandatory_protocols parameters at this security level; all
+protocols are allowed and "export" grade or better ciphers are used.
+When TLS handshakes fail, the connection is retried with TLS disabled.
+This allows mail delivery to sites with non-interoperable TLS
+implementations.
+.IP "\fBencrypt\fR"
+Mandatory TLS encryption. Since a minimum
+level of security is intended, it reasonable to be specific about
+sufficiently secure protocol versions and ciphers. At this security level
+and higher, the main.cf parameters smtp_tls_mandatory_protocols and
+smtp_tls_mandatory_ciphers specify the TLS protocols and minimum
+cipher grade which the administrator considers secure enough for
+mandatory encrypted sessions. This security level is not an appropriate
+default for systems delivering mail to the Internet.
+.IP "\fBverify\fR"
+Mandatory TLS verification. At this security
+level, DNS MX lookups are trusted to be secure enough, and the name
+verified in the server certificate is usually obtained indirectly
+via unauthenticated DNS MX lookups. The smtp_tls_verify_cert_match
+parameter controls how the server name is verified. In practice explicit
+control over matching is more common at the "secure" level, described
+below. This security level is not an appropriate default for systems
+delivering mail to the Internet.
+.IP "\fBsecure\fR"
+Secure-channel TLS.  At this security level,
+DNS MX lookups, though potentially used to determine the candidate
+next-hop gateway IP addresses, are \fBnot\fR trusted to be secure enough
+for TLS peername verification. Instead, the default name verified in
+the server certificate is obtained from the next-hop domain as specified
+in the smtp_tls_secure_cert_match configuration parameter. The default
+matching rule is that a server certificate matches when its name is equal
+to or is a sub-domain of the nexthop domain. This security level is not
+an appropriate default for systems delivering mail to the Internet.
+.PP
+Examples:
+.PP
+No TLS, old-style: smtp_use_tls=no and smtp_enforce_tls=no.
+.nf
+.na
+.ft C
+main.cf:
+    smtp_tls_security_level = none
+.fi
+.ad
+.ft R
+.PP
+Opportunistic TLS:
+.nf
+.na
+.ft C
+main.cf:
+    smtp_tls_security_level = may
+.fi
+.ad
+.ft R
+.PP
+Mandatory (high-grade) TLS encryption:
+.nf
+.na
+.ft C
+main.cf:
+    smtp_tls_security_level = encrypt
+    smtp_tls_mandatory_ciphers = high
+.fi
+.ad
+.ft R
+.PP
+Mandatory TLS verification, of hostname or nexthop domain:
+.nf
+.na
+.ft C
+main.cf:
+    smtp_tls_security_level = verify
+    smtp_tls_mandatory_ciphers = high
+    smtp_tls_verify_cert_match = hostname, nexthop, dot-nexthop
+.fi
+.ad
+.ft R
+.PP
+Secure channel TLS with exact nexthop name matching:
+.nf
+.na
+.ft C
+main.cf:
+    smtp_tls_security_level = secure
+    smtp_tls_mandatory_protocols = TLSv1
+    smtp_tls_mandatory_ciphers = high
+    smtp_tls_secure_cert_match = nexthop
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtp_tls_session_cache_database (default: empty)
 Name of the file containing the optional Postfix SMTP client
 TLS session cache. Specify a database type that supports enumeration,
 such as \fBbtree\fR or \fBsdbm\fR; there is no need to support
-concurrent access.  The file is created if it does not exist.
+concurrent access.  The file is created if it does not exist. The \fBsmtp\fR(8)
+daemon does not use this parameter directly, rather the cache is
+implemented indirectly in the \fBtlsmgr\fR(8) daemon. This means that
+per-smtp-instance master.cf overrides of this parameter are not effective.
+Note, that each of the cache databases supported by \fBtlsmgr\fR(8) daemon:
+$smtpd_tls_session_cache_database, $smtp_tls_session_cache_database
+(and with Postfix 2.3 and later $lmtp_session_cache_database), needs to
+be stored separately, it is not at this time possible to store multiple
+caches in a single database.
 .PP
 Note: \fBdbm\fR databases are not suitable. TLS
 session objects are too large.
@@ -4347,21 +5001,98 @@ Example:
 .nf
 .na
 .ft C
-smtp_tls_session_cache_database = btree:/var/postfix/smtp_scache
+smtp_tls_session_cache_database = btree:/var/spool/postfix/smtp_scache
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtp_tls_session_cache_timeout (default: 3600s)
 The expiration time of Postfix SMTP client TLS session cache
-information.  A cache cleanup is performed periodically every
-$smtp_tls_session_cache_timeout seconds.
+information.  A cache cleanup is performed periodically
+every $smtp_tls_session_cache_timeout seconds. As with
+$smtp_tls_session_cache_database, this parameter is implemented in the
+\fBtlsmgr\fR(8) daemon and therefore per-smtp-instance master.cf overrides
+are not possible.
+.PP
+This feature is available in Postfix 2.2 and later.
+.SH smtp_tls_verify_cert_match (default: hostname)
+The server certificate peername verification method for the
+"verify" TLS security level. In a "verify" TLS policy table
+($smtp_tls_policy_maps) entry the optional "match" attribute
+overrides this main.cf setting.
+.PP
+This parameter specifies one or more patterns or strategies separated
+by commas, whitespace or colons.  In the policy table the only valid
+separator is the colon character.
+.PP
+Patterns specify domain names, or domain name suffixes:
+.IP "\fIexample.com\fR"
+Match the \fIexample.com\fR domain,
+i.e. one of the names the server certificate must be \fIexample.com\fR,
+upper and lower case distinctions are ignored.
+.IP "\fI.example.com\fR"
+Match subdomains of the \fIexample.com\fR domain, i.e. match
+a name in the server certificate that consists of a non-zero number of
+labels followed by a \fI.example.com\fR suffix. Case distinctions are
+ignored.
+.PP
+Strategies specify a transformation from the next-hop domain
+to the expected name in the server certificate:
+.IP "nexthop"
+Match against the next-hop domain, which is either the recipient
+domain, or the transport next-hop configured for the domain stripped of
+any optional socket type prefix, enclosing square brackets and trailing
+port. When MX lookups are not suppressed, this is the original nexthop
+domain prior to the MX lookup, not the result of the MX lookup. For
+LMTP delivery via UNIX-domain sockets, the verified next-hop name is
+$myhostname.  This strategy is suitable for use with the "secure"
+policy. Case is ignored.
+.IP "dot-nexthop"
+As above, but match server certificate names that are subdomains
+of the next-hop domain. Case is ignored.
+.IP "hostname"
+Match against the hostname of the server, often
+obtained via an unauthenticated DNS MX lookup. For LMTP delivery via
+UNIX-domain sockets, the verified name is $myhostname. This matches
+the verification strategy of the "MUST" keyword in the obsolete
+smtp_tls_per_site table, and is suitable for use with the "verify"
+security level. When the next-hop name is enclosed in square brackets
+to suppress MX lookups, the "hostname" strategy is the same as the
+"nexthop" strategy. Case is ignored.
+.PP
+Sample main.cf setting:
+.PP
+.nf
+.na
+.ft C
+smtp_tls_verify_cert_match = hostname, nexthop, dot-nexthop
+.fi
+.ad
+.ft R
+.PP
+Sample policy table override:
+.PP
+.nf
+.na
+.ft C
+example.com     verify  match=hostname:nexthop
+\e&.example.com    verify  match=example.com:.example.com:hostname
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtp_use_tls (default: no)
 Opportunistic mode: use TLS when a remote SMTP server announces
 STARTTLS support, otherwise send the mail in the clear. Beware:
-some SMTP servers offer STARTTLS even if it is not configured.  If
-the TLS handshake fails, and no other server is available, delivery
-is deferred and mail stays in the queue.  If this is a concern for
-you, use the smtp_tls_per_site feature instead.
+some SMTP servers offer STARTTLS even if it is not configured.  With
+Postfix < 2.3, if the TLS handshake fails, and no other server is
+available, delivery is deferred and mail stays in the queue. If this
+is a concern for you, use the smtp_tls_per_site feature instead.
+.PP
+This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_security_level instead.
 .SH smtp_xforward_timeout (default: 300s)
 The SMTP client time limit for sending the XFORWARD command, and
 for receiving the server response.
@@ -4890,6 +5621,8 @@ Note 1: this mode implies "smtpd_tls_auth_only = yes".
 Note 2: when invoked via "\fBsendmail -bs\fR", Postfix will never offer
 STARTTLS due to insufficient privileges to access the server private
 key. This is intended behavior.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_error_sleep_time (default: 1s)
 With Postfix version 2.1 and later: the SMTP server response delay after
 a client has made more than $smtpd_soft_error_limit errors, and
@@ -5051,6 +5784,12 @@ increment the error counter with each junk command.  The junk
 command count is reset after mail is delivered.  See also the
 smtpd_error_sleep_time and smtpd_soft_error_limit configuration
 parameters.
+.SH smtpd_milters (default: empty)
+A list of Milter (mail filter) applications for new mail that
+arrives via the Postfix \fBsmtpd\fR(8) server.  See the MILTER_README
+document for details.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtpd_noop_commands (default: empty)
 List of commands that the Postfix SMTP server replies to with "250
 Ok", without doing any syntax checks and without changing state.
@@ -5473,6 +6212,8 @@ smtpd_sasl_security_options = noanonymous, noplaintext
 .SH smtpd_sasl_tls_security_options (default: $smtpd_sasl_security_options)
 The SASL authentication security options that the Postfix SMTP
 server uses for TLS encrypted SMTP sessions.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_sasl_type (default: cyrus)
 The SASL plug-in type that the Postfix SMTP server should use
 for authentication. The available types are listed with the
@@ -5627,6 +6368,8 @@ server delays all responses by (number of errors) seconds.
 .SH smtpd_starttls_timeout (default: 300s)
 The time limit for Postfix SMTP server write and read operations
 during TLS startup and shutdown handshake procedures.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_timeout (default: 300s)
 The time limit for sending a Postfix SMTP server response and for
 receiving a remote SMTP client request.
@@ -5653,6 +6396,8 @@ smtpd_tls_CAfile = /etc/postfix/CAcert.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_CApath (default: empty)
 Directory with PEM format certificate authority certificates
 that the Postfix SMTP server offers to remote SMTP clients for the
@@ -5675,28 +6420,53 @@ smtpd_tls_CApath = /etc/postfix/certs
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_ask_ccert (default: no)
 Ask a remote SMTP client for a client certificate. This
 information is needed for certificate based mail relaying with,
 for example, the permit_tls_clientcerts feature.
 .PP
 Some clients such as Netscape will either complain if no
-certificate is available (for the list of CAs in /etc/postfix/certs)
+certificate is available (for the list of CAs in $smtpd_tls_CAfile)
 or will offer multiple client certificates to choose from. This
 may be annoying, so this option is "off" by default.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_auth_only (default: no)
 When TLS encryption is optional in the Postfix SMTP server, do
 not announce or accept SASL authentication over unencrypted
 connections.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_ccert_verifydepth (default: 5)
 The verification depth for remote SMTP client certificates. A
 depth of 1 is sufficient if the issuing CA is listed in a local CA
 file.  The default value should also suffice for longer chains (the
 root CA issues special CA which then issues the actual certificate...).
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_cert_file (default: empty)
 File with the Postfix SMTP server RSA certificate in PEM format.
 This file may also contain the server private key.
 .PP
+Public Internet MX hosts without certificates signed by a "reputable"
+CA must generate, and be prepared to present to most clients, a
+self-signed or private-CA signed certificate. The client will not be
+able to authenticate the server, but unless it is running Postfix 2.3 or
+similar software, it will still insist on a server certificate.
+.PP
+For servers that are \fBnot\fR public Internet MX hosts, Postfix
+2.3 supports configurations with no certificates. This entails the use
+of just the anonymous TLS ciphers, which are not supported by typical
+SMTP clients. Since such clients will not, as a rule, fall back to plain
+text after a TLS handshake failure, the server will be unable to receive
+email from TLS enabled clients. To avoid accidental configurations with
+no certificates, Postfix 2.3 enables certificate-less operation only
+when the administrator explicitly sets "smtpd_tls_cert_file = none". This
+ensures that new Postfix configurations with just "smtpd_use_tls = yes"
+added, will not accidentally run with no certificates.
+.PP
 Both RSA and DSA certificates are supported.  When both types
 are present, the cipher used determines which certificate will be
 presented to the client.  For Netscape and OpenSSL clients without
@@ -5730,10 +6500,83 @@ smtpd_tls_cert_file = /etc/postfix/server.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_cipherlist (default: empty)
-Controls the Postfix SMTP server TLS cipher selection scheme.
-For details, see the OpenSSL documentation. Note: do not use ""
-quotes around the parameter value.
+Obsolete Postfix < 2.3 control for the Postfix SMTP server TLS
+cipher list. It is easy to create inter-operability problems by choosing
+a non-default cipher list. Do not use a non-default TLS cipherlist for
+MX hosts on the public Internet. Clients that begin the TLS handshake,
+but are unable to agree on a common cipher, may not be able to send any
+email to the SMTP server. Using a restricted cipher list may be more
+appropriate for a dedicated MSA or an internal mailhub, where one can
+exert some control over the TLS software and settings of the connecting
+clients.
+.PP
+\fBNote:\fR do not use "" quotes around the parameter value.
+.PP
+This feature is available with Postfix version 2.2. It is not used with
+Postfix 2.3 and later; use smtpd_tls_ciphers instead.
+.SH smtpd_tls_ciphers (default: export)
+The minimum acceptable SMTP server TLS cipher grade. It is easy to
+create inter-operability problems by choosing a non-default cipher grade.
+Do not use a stronger than default minimum cipher grade for MX hosts on
+the public Internet. Clients that begin the TLS handshake, but are unable
+to agree on a common cipher, may not be able to send any email to the
+SMTP server. Using a restricted cipher list may be more appropriate for a
+dedicated MSA or an internal mailhub, where one can exert some control over
+the TLS software and settings of the connecting clients. Configurations
+with no certificates are also not likely to inter-operate with most
+clients, see the notes for "smtpd_tls_cert_file".
+.PP
+The following cipher grades are supported:
+.IP "\fBexport\fR"
+Enable the mainstream "EXPORT" grade or better OpenSSL ciphers.
+This is the most appropriate setting for public MX hosts. The underlying
+cipherlist is specified via the tls_export_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_export_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "smtpd_tls_exclude_ciphers = aNULL".
+.IP "\fBlow\fR"
+Enable the mainstream "LOW" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_low_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_low_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "smtpd_tls_exclude_ciphers = aNULL".
+.IP "\fBmedium\fR"
+Enable the mainstream "MEDIUM" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_medium_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_medium_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "smtpd_tls_exclude_ciphers = aNULL".
+.IP "\fBhigh\fR"
+Enable only the mainstream "HIGH" grade OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_high_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_high_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers, set
+"smtpd_tls_exclude_ciphers = aNULL".
+.IP "\fBnull\fR"
+Enable only the "NULL" OpenSSL ciphers, these provide authentication
+without encryption.  This setting is only appropriate in the rare
+case that all clients are prepared to use NULL ciphers (not normally
+enabled in TLS clients). The underlying cipherlist is specified via the
+tls_null_cipherlist configuration parameter, which you are strongly
+encouraged to not change. The default value of tls_null_cipherlist
+excludes anonymous ciphers (OpenSSL 0.9.8 has NULL ciphers that offer
+data integrity without encryption or authentication).
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtpd_tls_dcert_file (default: empty)
 File with the Postfix SMTP server DSA certificate in PEM format.
 This file may also contain the server private key.
@@ -5749,6 +6592,8 @@ smtpd_tls_dcert_file = /etc/postfix/server-dsa.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_dh1024_param_file (default: empty)
 File with DH parameters that the Postfix SMTP server should
 use with EDH ciphers.
@@ -5778,6 +6623,8 @@ smtpd_tls_dh1024_param_file = /etc/postfix/dh_1024.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available with Postfix version 2.2.
 .SH smtpd_tls_dh512_param_file (default: empty)
 File with DH parameters that the Postfix SMTP server should
 use with EDH ciphers.
@@ -5794,6 +6641,8 @@ smtpd_tls_dh512_param_file = /etc/postfix/dh_512.pem
 .fi
 .ad
 .ft R
+.PP
+This feature is available with Postfix version 2.2.
 .SH smtpd_tls_dkey_file (default: $smtpd_tls_dcert_file)
 File with the Postfix SMTP server DSA private key in PEM format.
 This file may be combined with the server certificate file specified
@@ -5801,6 +6650,37 @@ with $smtpd_tls_dcert_file.
 .PP
 The private key must not be encrypted. In other words, the key
 must be accessible without password.
+.PP
+This feature is available in Postfix 2.2 and later.
+.SH smtpd_tls_exclude_ciphers (default: empty)
+List of ciphers or cipher types to exclude from the SMTP server
+cipher list. This is not an OpenSSL cipherlist; it is a simple list
+separated by whitespace and/or commas. The elements are a single
+cipher, or one or more "+" separated cipher properties, in which
+case only ciphers matching \fBall\fR the properties are excluded.
+.PP
+Examples (some of these will cause problems):
+.PP
+.nf
+.na
+.ft C
+smtpd_tls_exclude_ciphers = aNULL
+smtpd_tls_exclude_ciphers = MD5, DES
+smtpd_tls_exclude_ciphers = DES+MD5
+smtpd_tls_exclude_ciphers = AES256-SHA, DES-CBC3-MD5
+smtpd_tls_exclude_ciphers = kEDH+aRSA
+.fi
+.ad
+.ft R
+.PP
+The first setting disables anonymous ciphers. The next setting
+disables ciphers that use the MD5 digest algorithm or the (single) DES
+encryption algorithm. The next setting disables ciphers that use MD5 and
+DES together.  The next setting disables the two ciphers "AES256-SHA"
+and "DES-CBC3-MD5". The last setting disables ciphers that use "EDH"
+key exchange with RSA authentication.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtpd_tls_key_file (default: $smtpd_tls_cert_file)
 File with the Postfix SMTP server RSA private key in PEM format.
 This file may be combined with the server certificate file specified
@@ -5827,6 +6707,32 @@ transmission after STARTTLS.
 .PP
 Use "smtpd_tls_loglevel = 3" only in case of problems. Use of
 loglevel 4 is strongly discouraged.
+.PP
+This feature is available in Postfix 2.2 and later.
+.SH smtpd_tls_protocols (default: empty)
+The list of TLS protocols supported by the server. If empty the
+default list of protocols is used (i.e. all TLS protocol versions are
+supported). Any non-empty value is interpreted as 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
+DO NOT set this to a non-default value on an MX-host,
+as some clients may not support any of the narrower set of protocols,
+and may be unable to fallback to plaintext sessions. If you restrict
+the protocol list on an MX host, you may lose mail.
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+smtpd_tls_protocols = SSLv3, TLSv1
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH smtpd_tls_received_header (default: no)
 Request that the Postfix SMTP server produces Received:  message
 headers that include information about the protocol and cipher used,
@@ -5834,20 +6740,30 @@ as well as the client CommonName and client certificate issuer
 CommonName.  This is disabled by default, as the information may
 be modified in transit through other mail servers.  Only information
 that was recorded by the final destination can be trusted.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_req_ccert (default: no)
 When TLS encryption is enforced, require a remote SMTP client
 certificate in order to allow TLS connections to proceed.  This
 option implies "smtpd_tls_ask_ccert = yes".
 .PP
-When TLS encryption is optional, remote SMTP clients can bypass
-the restriction by simply not using STARTTLS at all. For this reason
-a TLS connection will be handled as if only "smtpd_tls_ask_ccert
-= yes" is specified.
+When TLS encryption is optional, this setting is ignored with
+a warning written to the mail log.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_session_cache_database (default: empty)
 Name of the file containing the optional Postfix SMTP server
 TLS session cache. Specify a database type that supports enumeration,
 such as \fBbtree\fR or \fBsdbm\fR; there is no need to support
-concurrent access.  The file is created if it does not exist.
+concurrent access.  The file is created if it does not exist. The \fBsmtpd\fR(8)
+daemon does not use this parameter directly, rather the cache is
+implemented indirectly in the \fBtlsmgr\fR(8) daemon. This means that
+per-smtpd-instance master.cf overrides of this parameter are not
+effective. Note, that each of the cache databases supported by \fBtlsmgr\fR(8)
+daemon: $smtpd_tls_session_cache_database, $smtp_tls_session_cache_database
+(and with Postfix 2.3 and later $lmtp_session_cache_database), needs to be
+stored separately, it is not at this time possible to store multiple
+caches in a single database.
 .PP
 Note: \fBdbm\fR databases are not suitable. TLS
 session objects are too large.
@@ -5857,14 +6773,21 @@ Example:
 .nf
 .na
 .ft C
-smtpd_tls_session_cache_database = btree:/var/postfix/smtpd_scache
+smtpd_tls_session_cache_database = btree:/var/spool/postfix/smtpd_scache
 .fi
 .ad
 .ft R
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_session_cache_timeout (default: 3600s)
 The expiration time of Postfix SMTP server TLS session cache
-information.  A cache cleanup is performed periodically every
-$smtpd_tls_session_cache_timeout seconds.
+information. A cache cleanup is performed periodically
+every $smtpd_tls_session_cache_timeout seconds. As with
+$smtpd_tls_session_cache_database, this parameter is implemented in the
+\fBtlsmgr\fR(8) daemon and therefore per-smtpd-instance master.cf overrides
+are not possible.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_tls_wrappermode (default: no)
 Run the Postfix SMTP server in the non-standard "wrapper" mode,
 instead of using the STARTTLS command.
@@ -5873,6 +6796,8 @@ If you want to support this service, enable a special port in
 master.cf, and specify "-o smtpd_tls_wrappermode=yes" on the SMTP
 server's command line. Port 465 (smtps) was once chosen for this
 purpose.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH smtpd_use_tls (default: no)
 Opportunistic mode: announce STARTTLS support to SMTP clients,
 but do not require that clients use TLS encryption.
@@ -5880,6 +6805,8 @@ but do not require that clients use TLS encryption.
 Note: when invoked via "\fBsendmail -bs\fR", Postfix will never offer
 STARTTLS due to insufficient privileges to access the server private
 key. This is intended behavior.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH soft_bounce (default: no)
 Safety net to keep mail queued that would otherwise be returned to
 the sender.  This parameter disables locally-generated bounces,
@@ -6002,12 +6929,57 @@ process requests from the \fBtlsmgr\fR(8) server in order to seed its
 internal pseudo random number generator (PRNG).  The default of 32
 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_export_cipherlist (default: ALL:+RC4:@STRENGTH)
+The OpenSSL cipherlist for "EXPORT" or higher grade ciphers. This
+defines the meaning of the "export" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. This is
+the cipherlist for the opportunistic ("may") TLS client security
+level and is the default cipherlist for the SMTP server. You are
+strongly encouraged to not change this setting.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH tls_high_cipherlist (default: !EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)
+The OpenSSL cipherlist for "HIGH" grade ciphers. This defines
+the meaning of the "high" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. You are
+strongly encouraged to not change this setting.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH tls_low_cipherlist (default: !EXPORT:ALL:+RC4:@STRENGTH)
+The OpenSSL cipherlist for "LOW" or higher grade ciphers. This defines
+the meaning of the "low" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. You are
+strongly encouraged to not change this setting.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH tls_medium_cipherlist (default: !EXPORT:!LOW:ALL:+RC4:@STRENGTH)
+The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers. This
+defines the meaning of the "medium" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. This is
+the default cipherlist for mandatory TLS encryption in the TLS
+client (with anonymous ciphers disabled when verifying server
+certificates). You are strongly encouraged to not change this
+setting.
+.PP
+This feature is available in Postfix 2.3 and later.
+.SH tls_null_cipherlist (default: !aNULL:eNULL+kRSA)
+The OpenSSL cipherlist for "NULL" grade ciphers that provide
+authentication without encryption. This defines the meaning of the "null"
+setting in smtpd_tls_ciphers, smtp_tls_mandatory_ciphers and
+lmtp_tls_mandatory_ciphers.  You are strongly encouraged to not
+change this setting.
+.PP
+This feature is available in Postfix 2.3 and later.
 .SH tls_random_bytes (default: 32)
 The number of bytes that \fBtlsmgr\fR(8) reads from $tls_random_source
 when (re)seeding the in-memory pseudo random number generator (PRNG)
 pool. The default of 32 bytes (256 bits) is good enough for 128bit
 symmetric keys.  If using EGD or a device file, a maximum of 255
 bytes is read.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH tls_random_exchange_name (default: ${config_directory}/prng_exch)
 Name of the pseudo random number generator (PRNG) state file
 that is maintained by \fBtlsmgr\fR(8). The file is created when it does
@@ -6016,15 +6988,21 @@ not exist, and its length is fixed at 1024 bytes.
 Since this file is modified by Postfix, it should probably be
 kept in the /var file system, instead of under $config_directory.
 The location should not be inside the chroot jail.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH tls_random_prng_update_period (default: 3600s)
 The time between attempts by \fBtlsmgr\fR(8) to save the state of
 the pseudo random number generator (PRNG) to the file specified
 with $tls_random_exchange_name.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH tls_random_reseed_period (default: 3600s)
 The maximal time between attempts by \fBtlsmgr\fR(8) to re-seed the
 in-memory pseudo random number generator (PRNG) pool from external
 sources.  The actual time between re-seeding attempts is calculated
 using the PRNG, and is between 0 and the time specified.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH tls_random_source (default: see "postconf -d" output)
 The external entropy source for the in-memory \fBtlsmgr\fR(8) pseudo
 random number generator (PRNG) pool. Be sure to specify a non-blocking
@@ -6035,6 +7013,8 @@ device file.
 .PP
 Note: on OpenBSD systems specify /dev/arandom when /dev/urandom
 gives timeout errors.
+.PP
+This feature is available in Postfix 2.2 and later.
 .SH trace_service_name (default: trace)
 The name of the trace service. This service is implemented by the
 \fBbounce\fR(8) daemon and maintains a record
index 558a6b7d5a0c395d95b2dd98c5628b00ee3fd6ee..bf83d73c0dc27fc79e0ca0046790a6d594d605e0 100644 (file)
@@ -126,6 +126,59 @@ content.
 .IP "\fBmessage_strip_characters (empty)\fR"
 The set of characters that Postfix will remove from message
 content.
+.SH "BEFORE QUEUE MILTER CONTROLS"
+.na
+.nf
+.ad
+.fi
+As of version 2.3, Postfix supports the Sendmail version 8
+Milter (mail filter) protocol. When mail is not received via
+the smtpd(8) server, the cleanup(8) server will simulate
+SMTP events to the extent that this is possible. For details
+see the MILTER_README document.
+.IP "\fBnon_smtpd_milters (empty)\fR"
+A list of Milter (mail filter) applications for new mail that
+does not arrive via the Postfix \fBsmtpd\fR(8) server.
+.IP "\fBmilter_protocol (2)\fR"
+The mail filter protocol version and optional protocol extensions
+for communication with a Milter (mail filter) application.
+.IP "\fBmilter_default_action (tempfail)\fR"
+The default action when a Milter (mail filter) application is
+unavailable or mis-configured.
+.IP "\fBmilter_macro_daemon_name ($myhostname)\fR"
+The {daemon_name} macro value for Milter (mail filter) applications.
+.IP "\fBmilter_macro_v ($mail_name $mail_version)\fR"
+The {v} macro value for Milter (mail filter) applications.
+.IP "\fBmilter_connect_timeout (30s)\fR"
+The time limit for connecting to a Milter (mail filter)
+application, and for negotiating protocol options.
+.IP "\fBmilter_command_timeout (30s)\fR"
+The time limit for sending an SMTP command to a Milter (mail
+filter) application, and for receiving the response.
+.IP "\fBmilter_content_timeout (300s)\fR"
+The time limit for sending message content to a Milter (mail
+filter) application, and for receiving the response.
+.IP "\fBmilter_connect_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after completion of an SMTP connection.
+.IP "\fBmilter_helo_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the SMTP HELO or EHLO command.
+.IP "\fBmilter_mail_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the SMTP MAIL FROM command.
+.IP "\fBmilter_rcpt_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the SMTP RCPT TO command.
+.IP "\fBmilter_data_macros (see postconf -n output)\fR"
+The macros that are sent to version 4 or higher Milter (mail
+filter) applications after the SMTP DATA command.
+.IP "\fBmilter_unknown_command_macros (see postconf -n output)\fR"
+The macros that are sent to version 3 or higher Milter (mail
+filter) applications after an unknown SMTP command.
+.IP "\fBmilter_end_of_data_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the message end-of-data.
 .SH "MIME PROCESSING CONTROLS"
 .na
 .nf
@@ -336,6 +389,7 @@ Use "\fBpostconf readme_directory\fR" or
 .na
 .nf
 ADDRESS_REWRITING_README Postfix address manipulation
+CONTENT_INSPECTION_README content inspection
 .SH "LICENSE"
 .na
 .nf
index 5877a72b2ee259221a74236cc712b6aa35388597..d7ae6465592751eb232a97460fdf2c07661653ad 100644 (file)
@@ -281,12 +281,10 @@ for authentication.
 .fi
 Detailed information about STARTTLS configuration may be found
 in the TLS_README document.
-.IP "\fBsmtp_use_tls (no)\fR"
-Opportunistic mode: use TLS when a remote SMTP server announces
-STARTTLS support, otherwise send the mail in the clear.
-.IP "\fBsmtp_enforce_tls (no)\fR"
-Enforcement mode: require that remote SMTP servers use TLS
-encryption, and never send mail in the clear.
+.IP "\fBsmtp_tls_security_level (empty)\fR"
+The default SMTP TLS security level for all destinations; when
+a non-empty value is specified, this overrides the obsolete parameters
+smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
 .IP "\fBsmtp_sasl_tls_security_options ($smtp_sasl_security_options)\fR"
 The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions.
@@ -302,16 +300,20 @@ that the Postfix SMTP client uses to verify a remote SMTP server
 certificate.
 .IP "\fBsmtp_tls_cert_file (empty)\fR"
 File with the Postfix SMTP client RSA certificate in PEM format.
-.IP "\fBsmtp_tls_cipherlist (empty)\fR"
-Controls the Postfix SMTP client TLS cipher selection scheme.
+.IP "\fBsmtp_tls_mandatory_ciphers (medium)\fR"
+The minimum SMTP client TLS cipher grade that is strong enough to
+be used with the "encrypt" security level and higher.
+.IP "\fBsmtp_tls_exclude_ciphers (empty)\fR"
+List of ciphers or cipher types to exclude from the SMTP client cipher
+list at all security levels.
+.IP "\fBsmtp_tls_mandatory_exclude_ciphers (empty)\fR"
+List of ciphers or cipher types to exclude from the SMTP client
+cipher list at the mandatory TLS security levels: "encrypt", "verify"
+and "secure".
 .IP "\fBsmtp_tls_dcert_file (empty)\fR"
 File with the Postfix SMTP client DSA certificate in PEM format.
 .IP "\fBsmtp_tls_dkey_file ($smtp_tls_dcert_file)\fR"
 File with the Postfix SMTP client DSA private key in PEM format.
-.IP "\fBsmtp_tls_enforce_peername (yes)\fR"
-When TLS encryption is enforced, require that the remote SMTP
-server hostname matches the information in the remote SMTP server
-certificate.
 .IP "\fBsmtp_tls_key_file ($smtp_tls_cert_file)\fR"
 File with the Postfix SMTP client RSA private key in PEM format.
 .IP "\fBsmtp_tls_loglevel (0)\fR"
@@ -319,27 +321,69 @@ Enable additional Postfix SMTP client logging of TLS activity.
 .IP "\fBsmtp_tls_note_starttls_offer (no)\fR"
 Log the hostname of a remote SMTP server that offers STARTTLS,
 when TLS is not already enabled for that server.
-.IP "\fBsmtp_tls_per_site (empty)\fR"
-Optional lookup tables with the Postfix SMTP client TLS usage
-policy by next-hop destination and by remote SMTP server hostname.
+.IP "\fBsmtp_tls_policy_maps (empty)\fR"
+Optional lookup tables with the Postfix SMTP client TLS security
+policy by next-hop destination; when a non-empty value is specified,
+this overrides the obsolete smtp_tls_per_site parameter.
+.IP "\fBsmtp_tls_mandatory_protocols (SSLv3, TLSv1)\fR"
+List of TLS protocol versions that are secure enough to be used
+with the "encrypt" security level and higher.
 .IP "\fBsmtp_tls_scert_verifydepth (5)\fR"
 The verification depth for remote SMTP server certificates.
+.IP "\fBsmtp_tls_secure_cert_match (nexthop, dot-nexthop)\fR"
+The server certificate peername verification method for the
+"secure" TLS security level.
 .IP "\fBsmtp_tls_session_cache_database (empty)\fR"
 Name of the file containing the optional Postfix SMTP client
 TLS session cache.
 .IP "\fBsmtp_tls_session_cache_timeout (3600s)\fR"
 The expiration time of Postfix SMTP client TLS session cache
 information.
+.IP "\fBsmtp_tls_verify_cert_match (hostname)\fR"
+The server certificate peername verification method for the
+"verify" TLS security level.
 .IP "\fBtls_daemon_random_bytes (32)\fR"
 The number of pseudo-random bytes that an \fBsmtp\fR(8) or \fBsmtpd\fR(8)
 process requests from the \fBtlsmgr\fR(8) server in order to seed its
 internal pseudo random number generator (PRNG).
+.IP "\fBtls_high_cipherlist (!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "HIGH" grade ciphers.
+.IP "\fBtls_medium_cipherlist (!EXPORT:!LOW:ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers.
+.IP "\fBtls_low_cipherlist (!EXPORT:ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "LOW" or higher grade ciphers.
+.IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
+.IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+The OpenSSL cipherlist for "NULL" grade ciphers that provide
+authentication without encryption.
 .PP
-Available in Postfix version 2.3 and later:
+Available in Postfix version 2.4 and later:
 .IP "\fBsmtp_sasl_tls_verified_security_options ($smtp_sasl_tls_security_options)\fR"
 The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions with a verified server
 certificate.
+.SH "OBSOLETE STARTTLS CONTROLS"
+.na
+.nf
+.ad
+.fi
+The following configuration parameters exist for compatibility
+with Postfix versions before 2.3. Support for these will
+be removed in a future release.
+.IP "\fBsmtp_use_tls (no)\fR"
+Opportunistic mode: use TLS when a remote SMTP server announces
+STARTTLS support, otherwise send the mail in the clear.
+.IP "\fBsmtp_enforce_tls (no)\fR"
+Enforcement mode: require that remote SMTP servers use TLS
+encryption, and never send mail in the clear.
+.IP "\fBsmtp_tls_enforce_peername (yes)\fR"
+When TLS encryption is enforced, require that the remote SMTP
+server hostname matches the information in the remote SMTP server
+certificate.
+.IP "\fBsmtp_tls_per_site (empty)\fR"
+Optional lookup tables with the Postfix SMTP client TLS usage
+policy by next-hop destination and by remote SMTP server hostname.
 .SH "RESOURCE AND RATE CONTROLS"
 .na
 .nf
index 0d2f17f3c4c82f688a334acba383b7b5eeb784d3..4a2d1fe377c159307eed803942e9de05b496af42 100644 (file)
@@ -172,6 +172,59 @@ How the Postfix SMTP server announces itself to the proxy filter.
 .IP "\fBsmtpd_proxy_timeout (100s)\fR"
 The time limit for connecting to a proxy filter and for sending or
 receiving information.
+.SH "BEFORE QUEUE MILTER CONTROLS"
+.na
+.nf
+.ad
+.fi
+As of version 2.3, Postfix supports the Sendmail version 8
+Milter (mail filter) protocol. These content filters run
+outside Postfix. They can inspect the SMTP command stream
+and the message content, and can request modifications before
+mail is queued. For details see the MILTER_README document.
+.IP "\fBsmtpd_milters (empty)\fR"
+A list of Milter (mail filter) applications for new mail that
+arrives via the Postfix \fBsmtpd\fR(8) server.
+.IP "\fBmilter_protocol (2)\fR"
+The mail filter protocol version and optional protocol extensions
+for communication with a Milter (mail filter) application.
+.IP "\fBmilter_default_action (tempfail)\fR"
+The default action when a Milter (mail filter) application is
+unavailable or mis-configured.
+.IP "\fBmilter_macro_daemon_name ($myhostname)\fR"
+The {daemon_name} macro value for Milter (mail filter) applications.
+.IP "\fBmilter_macro_v ($mail_name $mail_version)\fR"
+The {v} macro value for Milter (mail filter) applications.
+.IP "\fBmilter_connect_timeout (30s)\fR"
+The time limit for connecting to a Milter (mail filter)
+application, and for negotiating protocol options.
+.IP "\fBmilter_command_timeout (30s)\fR"
+The time limit for sending an SMTP command to a Milter (mail
+filter) application, and for receiving the response.
+.IP "\fBmilter_content_timeout (300s)\fR"
+The time limit for sending message content to a Milter (mail
+filter) application, and for receiving the response.
+.IP "\fBmilter_connect_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after completion of an SMTP connection.
+.IP "\fBmilter_helo_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the SMTP HELO or EHLO command.
+.IP "\fBmilter_mail_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the SMTP MAIL FROM command.
+.IP "\fBmilter_rcpt_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the SMTP RCPT TO command.
+.IP "\fBmilter_data_macros (see postconf -n output)\fR"
+The macros that are sent to version 4 or higher Milter (mail
+filter) applications after the SMTP DATA command.
+.IP "\fBmilter_unknown_command_macros (see postconf -n output)\fR"
+The macros that are sent to version 3 or higher Milter (mail
+filter) applications after an unknown SMTP command.
+.IP "\fBmilter_end_of_data_macros (see postconf -n output)\fR"
+The macros that are sent to Milter (mail filter) applications
+after the message end-of-data.
 .SH "GENERAL CONTENT INSPECTION CONTROLS"
 .na
 .nf
@@ -269,8 +322,11 @@ connections.
 The verification depth for remote SMTP client certificates.
 .IP "\fBsmtpd_tls_cert_file (empty)\fR"
 File with the Postfix SMTP server RSA certificate in PEM format.
-.IP "\fBsmtpd_tls_cipherlist (empty)\fR"
-Controls the Postfix SMTP server TLS cipher selection scheme.
+.IP "\fBsmtpd_tls_ciphers (export)\fR"
+The minimum acceptable SMTP server TLS cipher grade.
+.IP "\fBsmtpd_tls_exclude_ciphers (empty)\fR"
+List of ciphers or cipher types to exclude from the SMTP server
+cipher list.
 .IP "\fBsmtpd_tls_dcert_file (empty)\fR"
 File with the Postfix SMTP server DSA certificate in PEM format.
 .IP "\fBsmtpd_tls_dh1024_param_file (empty)\fR"
@@ -285,6 +341,8 @@ File with the Postfix SMTP server DSA private key in PEM format.
 File with the Postfix SMTP server RSA private key in PEM format.
 .IP "\fBsmtpd_tls_loglevel (0)\fR"
 Enable additional Postfix SMTP server logging of TLS activity.
+.IP "\fBsmtpd_tls_protocols (empty)\fR"
+The list of TLS protocols supported by the server.
 .IP "\fBsmtpd_tls_received_header (no)\fR"
 Request that the Postfix SMTP server produces Received:  message
 headers that include information about the protocol and cipher used,
@@ -306,6 +364,17 @@ instead of using the STARTTLS command.
 The number of pseudo-random bytes that an \fBsmtp\fR(8) or \fBsmtpd\fR(8)
 process requests from the \fBtlsmgr\fR(8) server in order to seed its
 internal pseudo random number generator (PRNG).
+.IP "\fBtls_high_cipherlist (!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "HIGH" grade ciphers.
+.IP "\fBtls_medium_cipherlist (!EXPORT:!LOW:ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers.
+.IP "\fBtls_low_cipherlist (!EXPORT:ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "LOW" or higher grade ciphers.
+.IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
+The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
+.IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+The OpenSSL cipherlist for "NULL" grade ciphers that provide
+authentication without encryption.
 .SH "VERP SUPPORT CONTROLS"
 .na
 .nf
@@ -784,6 +853,7 @@ ADDRESS_CLASS_README, blocking unknown hosted or relay recipients
 ADDRESS_REWRITING_README Postfix address manipulation
 FILTER_README, external after-queue content filter
 LOCAL_RECIPIENT_README, blocking unknown local recipients
+MILTER_README, before-queue mail filter applications
 SMTPD_ACCESS_README, built-in access policies
 SMTPD_POLICY_README, external policy server
 SMTPD_PROXY_README, external before-queue content filter
index 368d4397f623ff3439755f5fb2d69ca922791d18..6bba143a529af7d1fdea4104865f47278abd5824 100644 (file)
@@ -73,18 +73,31 @@ The text below provides only a parameter summary. See
 .nf
 .ad
 .fi
-.IP "\fBsmtpd_tls_session_cache_database (empty)\fR"
-Name of the file containing the optional Postfix SMTP server
-TLS session cache.
-.IP "\fBsmtpd_tls_session_cache_timeout (3600s)\fR"
-The expiration time of Postfix SMTP server TLS session cache
-information.
+.IP "\fBlmtp_tls_loglevel (0)\fR"
+The LMTP-specific version of the smtp_tls_loglevel
+configuration parameter.
+.IP "\fBlmtp_tls_session_cache_database (empty)\fR"
+The LMTP-specific version of the smtp_tls_session_cache_database
+configuration parameter.
+.IP "\fBlmtp_tls_session_cache_timeout (3600s)\fR"
+The LMTP-specific version of the smtp_tls_session_cache_timeout
+configuration parameter.
+.IP "\fBsmtp_tls_loglevel (0)\fR"
+Enable additional Postfix SMTP client logging of TLS activity.
 .IP "\fBsmtp_tls_session_cache_database (empty)\fR"
 Name of the file containing the optional Postfix SMTP client
 TLS session cache.
 .IP "\fBsmtp_tls_session_cache_timeout (3600s)\fR"
 The expiration time of Postfix SMTP client TLS session cache
 information.
+.IP "\fBsmtpd_tls_loglevel (0)\fR"
+Enable additional Postfix SMTP server logging of TLS activity.
+.IP "\fBsmtpd_tls_session_cache_database (empty)\fR"
+Name of the file containing the optional Postfix SMTP server
+TLS session cache.
+.IP "\fBsmtpd_tls_session_cache_timeout (3600s)\fR"
+The expiration time of Postfix SMTP server TLS session cache
+information.
 .SH "PSEUDO RANDOM NUMBER GENERATOR"
 .na
 .nf
index b2c9e70bad520c54261f412f28ae0109a56dc21e..f5b803bbdf0c106433dd70b1cab30cfbeb73b757 100755 (executable)
@@ -109,6 +109,7 @@ while (<>) {
     s;\bbroken_sasl_auth_clients\b;<a href="postconf.5.html#broken_sasl_auth_clients">$&</a>;g;
     s;\bcanonical_classes\b;<a href="postconf.5.html#canonical_classes">$&</a>;g;
     s;\bcanonical_maps\b;<a href="postconf.5.html#canonical_maps">$&</a>;g;
+    s;\bnon_smtpd_milters\b;<a href="postconf.5.html#non_smtpd_milters">$&</a>;g;
     s;\bcleanup_service_name\b;<a href="postconf.5.html#cleanup_service_name">$&</a>;g;
     s;\bcommand_execu[-</bB>]*\n* *[<bB>]*tion_direc[-</bB>]*\n* *[<bB>]*tory\b;<a href="postconf.5.html#command_execution_directory">$&</a>;g;
     s;\bexecu[-</bB>]*\n* *[<bB>]*tion_directory_expansion_filter\b;<a href="postconf.5.html#execution_directory_expansion_filter">$&</a>;g;
@@ -202,7 +203,24 @@ while (<>) {
     s;\blmtp_host_lookup\b;<a href="postconf.5.html#lmtp_host_lookup">$&</a>;g;
     s;\blmtp_connection_cache_destinations\b;<a href="postconf.5.html#lmtp_connection_cache_destinations">$&</a>;g;
     s;\blmtp_connection_cache_time_limit\b;<a href="postconf.5.html#lmtp_connection_cache_time_limit">$&</a>;g;
+    s;\blmtp_tls_mandatory_protocols\b;<a href="postconf.5.html#lmtp_tls_mandatory_protocols">$&</a>;g;
+    s;\blmtp_tls_policy_maps\b;<a href="postconf.5.html#lmtp_tls_policy_maps">$&</a>;g;
+    s;\blmtp_tls_secure_cert_match\b;<a href="postconf.5.html#lmtp_tls_secure_cert_match">$&</a>;g;
+    s;\blmtp_tls_security_level\b;<a href="postconf.5.html#lmtp_tls_security_level">$&</a>;g;
+    s;\blmtp_tls_verify_cert_match\b;<a href="postconf.5.html#lmtp_tls_verify_cert_match">$&</a>;g;
     s;\blmtp_tls_per_site\b;<a href="postconf.5.html#lmtp_tls_per_site">$&</a>;g;
+    s;\blmtp_tls_cert_file\b;<a href="postconf.5.html#lmtp_tls_cert_file">$&</a>;g;
+    s;\blmtp_tls_key_file\b;<a href="postconf.5.html#lmtp_tls_key_file">$&</a>;g;
+    s;\blmtp_tls_dcert_file\b;<a href="postconf.5.html#lmtp_tls_dcert_file">$&</a>;g;
+    s;\blmtp_tls_dkey_file\b;<a href="postconf.5.html#lmtp_tls_dkey_file">$&</a>;g;
+    s;\blmtp_tls_CAfile\b;<a href="postconf.5.html#lmtp_tls_CAfile">$&</a>;g;
+    s;\blmtp_tls_CApath\b;<a href="postconf.5.html#lmtp_tls_CApath">$&</a>;g;
+    s;\blmtp_tls_mandatory_ciphers\b;<a href="postconf.5.html#lmtp_tls_mandatory_ciphers">$&</a>;g;
+    s;\blmtp_tls_exclude_ciphers\b;<a href="postconf.5.html#lmtp_tls_exclude_ciphers">$&</a>;g;
+    s;\blmtp_tls_mandatory_exclude_ciphers\b;<a href="postconf.5.html#lmtp_tls_mandatory_exclude_ciphers">$&</a>;g;
+    s;\blmtp_tls_loglevel\b;<a href="postconf.5.html#lmtp_tls_loglevel">$&</a>;g;
+    s;\blmtp_tls_session_cache_database\b;<a href="postconf.5.html#lmtp_tls_session_cache_database">$&</a>;g;
+    s;\blmtp_tls_session_cache_timeout\b;<a href="postconf.5.html#lmtp_tls_session_cache_timeout">$&</a>;g;
     s;\blmtp_generic_maps\b;<a href="postconf.5.html#lmtp_generic_maps">$&</a>;g;
     s;\blmtp_pix_workaround_threshold_time\b;<a href="postconf.5.html#lmtp_pix_workaround_threshold_time">$&</a>;g;
     s;\blmtp_pix_workaround_delay_time\b;<a href="postconf.5.html#lmtp_pix_workaround_delay_time">$&</a>;g;
@@ -320,6 +338,7 @@ while (<>) {
     s;\bno_unknown_recip[-</bB>]*\n* *[<bB>]*ient_checks\b;<a href="postconf.5.html#no_unknown_recipient_checks">$&</a>;g;
     s;\bno_address_mappings\b;<a href="postconf.5.html#no_address_mappings">$&</a>;g;
     s;\bno_header_body_checks\b;<a href="postconf.5.html#no_header_body_checks">$&</a>;g;
+    s;\bno_milters\b;<a href="postconf.5.html#no_milters">$&</a>;g;
     s;\brecip[-</bB>]*\n* *[<bB>]*ient_bcc_maps\b;<a href="postconf.5.html#recipient_bcc_maps">$&</a>;g;
     s;\brecip[-</bB>]*\n* *[<bB>]*ient_canoni[-</bB>]*\n* *[<bB>]*cal_classes\b;<a href="postconf.5.html#recipient_canonical_classes">$&</a>;g;
     s;\brecip[-</bB>]*\n* *[<bB>]*ient_canoni[-</bB>]*\n* *[<bB>]*cal_maps\b;<a href="postconf.5.html#recipient_canonical_maps">$&</a>;g;
@@ -433,6 +452,7 @@ while (<>) {
     s;\bsmtpd_helo_restrictions\b;<a href="postconf.5.html#smtpd_helo_restrictions">$&</a>;g;
     s;\bsmtpd_history_flush_threshold\b;<a href="postconf.5.html#smtpd_history_flush_threshold">$&</a>;g;
     s;\bsmtpd_junk_command_limit\b;<a href="postconf.5.html#smtpd_junk_command_limit">$&</a>;g;
+    s;\bsmtpd_milters\b;<a href="postconf.5.html#smtpd_milters">$&</a>;g;
     s;\bsmtpd_noop_commands\b;<a href="postconf.5.html#smtpd_noop_commands">$&</a>;g;
     s;\bsmtpd_null_access_lookup_key\b;<a href="postconf.5.html#smtpd_null_access_lookup_key">$&</a>;g;
     s;\bsmtpd_recipient_overshoot_limit\b;<a href="postconf.5.html#smtpd_recipient_overshoot_limit">$&</a>;g;
@@ -508,7 +528,9 @@ while (<>) {
     s;\bsmtp_tls_CAfile\b;<a href="postconf.5.html#smtp_tls_CAfile">$&</a>;g;
     s;\bsmtp_tls_CApath\b;<a href="postconf.5.html#smtp_tls_CApath">$&</a>;g;
     s;\bsmtp_tls_cert_file\b;<a href="postconf.5.html#smtp_tls_cert_file">$&</a>;g;
-    s;\bsmtp_tls_cipherlist\b;<a href="postconf.5.html#smtp_tls_cipherlist">$&</a>;g;
+    s;\bsmtp_tls_mandatory_ciphers\b;<a href="postconf.5.html#smtp_tls_mandatory_ciphers">$&</a>;g;
+    s;\bsmtp_tls_exclude_ciphers\b;<a href="postconf.5.html#smtp_tls_exclude_ciphers">$&</a>;g;
+    s;\bsmtp_tls_mandatory_exclude_ciphers\b;<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">$&</a>;g;
     s;\bsmtp_tls_dcert_file\b;<a href="postconf.5.html#smtp_tls_dcert_file">$&</a>;g;
     s;\bsmtp_tls_dkey_file\b;<a href="postconf.5.html#smtp_tls_dkey_file">$&</a>;g;
     s;\bsmtp_tls_enforce_peername\b;<a href="postconf.5.html#smtp_tls_enforce_peername">$&</a>;g;
@@ -516,7 +538,12 @@ while (<>) {
     s;\bsmtp_tls_loglevel\b;<a href="postconf.5.html#smtp_tls_loglevel">$&</a>;g;
     s;\bsmtp_tls_note_starttls_offer\b;<a href="postconf.5.html#smtp_tls_note_starttls_offer">$&</a>;g;
     s;\bsmtp_tls_per_site\b;<a href="postconf.5.html#smtp_tls_per_site">$&</a>;g;
+    s;\bsmtp_tls_policy_maps\b;<a href="postconf.5.html#smtp_tls_policy_maps">$&</a>;g;
+    s;\bsmtp_tls_mandatory_protocols\b;<a href="postconf.5.html#smtp_tls_mandatory_protocols">$&</a>;g;
+    s;\bsmtp_tls_verify_cert_match\b;<a href="postconf.5.html#smtp_tls_verify_cert_match">$&</a>;g;
+    s;\bsmtp_tls_secure_cert_match\b;<a href="postconf.5.html#smtp_tls_secure_cert_match">$&</a>;g;
     s;\bsmtp_tls_scert_verifydepth\b;<a href="postconf.5.html#smtp_tls_scert_verifydepth">$&</a>;g;
+    s;\bsmtp_tls_security_level\b;<a href="postconf.5.html#smtp_tls_security_level">$&</a>;g;
     s;\bsmtp_tls_session_cache_database\b;<a href="postconf.5.html#smtp_tls_session_cache_database">$&</a>;g;
     s;\bsmtp_tls_session_cache_timeout\b;<a href="postconf.5.html#smtp_tls_session_cache_timeout">$&</a>;g;
     s;\bsmtp_use_tls\b;<a href="postconf.5.html#smtp_use_tls">$&</a>;g;
@@ -530,13 +557,15 @@ while (<>) {
     s;\bsmtpd_tls_auth_only\b;<a href="postconf.5.html#smtpd_tls_auth_only">$&</a>;g;
     s;\bsmtpd_tls_ccert_verifydepth\b;<a href="postconf.5.html#smtpd_tls_ccert_verifydepth">$&</a>;g;
     s;\bsmtpd_tls_cert_file\b;<a href="postconf.5.html#smtpd_tls_cert_file">$&</a>;g;
-    s;\bsmtpd_tls_cipherlist\b;<a href="postconf.5.html#smtpd_tls_cipherlist">$&</a>;g;
+    s;\bsmtpd_tls_ciphers\b;<a href="postconf.5.html#smtpd_tls_ciphers">$&</a>;g;
+    s;\bsmtpd_tls_exclude_ciphers\b;<a href="postconf.5.html#smtpd_tls_exclude_ciphers">$&</a>;g;
     s;\bsmtpd_tls_dcert_file\b;<a href="postconf.5.html#smtpd_tls_dcert_file">$&</a>;g;
     s;\bsmtpd_tls_dh1024_param_file\b;<a href="postconf.5.html#smtpd_tls_dh1024_param_file">$&</a>;g;
     s;\bsmtpd_tls_dh512_param_file\b;<a href="postconf.5.html#smtpd_tls_dh512_param_file">$&</a>;g;
     s;\bsmtpd_tls_dkey_file\b;<a href="postconf.5.html#smtpd_tls_dkey_file">$&</a>;g;
     s;\bsmtpd_tls_key_file\b;<a href="postconf.5.html#smtpd_tls_key_file">$&</a>;g;
     s;\bsmtpd_tls_loglevel\b;<a href="postconf.5.html#smtpd_tls_loglevel">$&</a>;g;
+    s;\bsmtpd_tls_protocols\b;<a href="postconf.5.html#smtpd_tls_protocols">$&</a>;g;
     s;\bsmtpd_tls_received_header\b;<a href="postconf.5.html#smtpd_tls_received_header">$&</a>;g;
     s;\bsmtpd_tls_req_ccert\b;<a href="postconf.5.html#smtpd_tls_req_ccert">$&</a>;g;
     s;\bsmtpd_tls_session_cache_database\b;<a href="postconf.5.html#smtpd_tls_session_cache_database">$&</a>;g;
@@ -550,6 +579,11 @@ while (<>) {
     s;\btls_ran[-</Bb>]*\n* *[<Bb>]*dom_prng_update_period\b;<a href="postconf.5.html#tls_random_prng_update_period">$&</a>;g;
     s;\btls_ran[-</Bb>]*\n* *[<Bb>]*dom_reseed_period\b;<a href="postconf.5.html#tls_random_reseed_period">$&</a>;g;
     s;\btls_ran[-</Bb>]*\n* *[<Bb>]*dom_source\b;<a href="postconf.5.html#tls_random_source">$&</a>;g;
+    s;\btls_high_cipherlist\b;<a href="postconf.5.html#tls_high_cipherlist">$&</a>;g;
+    s;\btls_medium_cipherlist\b;<a href="postconf.5.html#tls_medium_cipherlist">$&</a>;g;
+    s;\btls_low_cipherlist\b;<a href="postconf.5.html#tls_low_cipherlist">$&</a>;g;
+    s;\btls_export_cipherlist\b;<a href="postconf.5.html#tls_export_cipherlist">$&</a>;g;
+    s;\btls_null_cipherlist\b;<a href="postconf.5.html#tls_null_cipherlist">$&</a>;g;
  
     s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;
 
@@ -711,6 +745,23 @@ while (<>) {
 
     s;\bcheck_etrn_access\b;<a href="postconf.5.html#check_etrn_access">$&</a>;g;
 
+    # Milters.
+
+    s;\bmilter_macro_daemon_name\b;<a href="postconf.5.html#milter_macro_daemon_name">$&</a>;g;
+    s;\bmilter_macro_v\b;<a href="postconf.5.html#milter_macro_v">$&</a>;g;
+    s;\bmilter_connect_timeout\b;<a href="postconf.5.html#milter_connect_timeout">$&</a>;g;
+    s;\bmilter_command_timeout\b;<a href="postconf.5.html#milter_command_timeout">$&</a>;g;
+    s;\bmilter_content_timeout\b;<a href="postconf.5.html#milter_content_timeout">$&</a>;g;
+    s;\bmilter_protocol\b;<a href="postconf.5.html#milter_protocol">$&</a>;g;
+    s;\bmilter_default_action\b;<a href="postconf.5.html#milter_default_action">$&</a>;g;
+    s;\bmilter_connect_macros\b;<a href="postconf.5.html#milter_connect_macros">$&</a>;g;
+    s;\bmilter_helo_macros\b;<a href="postconf.5.html#milter_helo_macros">$&</a>;g;
+    s;\bmilter_mail_macros\b;<a href="postconf.5.html#milter_mail_macros">$&</a>;g;
+    s;\bmilter_rcpt_macros\b;<a href="postconf.5.html#milter_rcpt_macros">$&</a>;g;
+    s;\bmilter_data_macros\b;<a href="postconf.5.html#milter_data_macros">$&</a>;g;
+    s;\bmilter_unknown_command_macros\b;<a href="postconf.5.html#milter_unknown_command_macros">$&</a>;g;
+    s;\bmilter_end_of_data_macros\b;<a href="postconf.5.html#milter_end_of_data_macros">$&</a>;g;
+
     # Split *README, parameter or restriction hyperlinks across line breaks
 
     s/(<a href="[^"]*">)([-A-Za-z0-9_]*)\b([-<\/bB>]*\n *[<bB>]*)\b([-A-Za-z0-9_]*)(<\/a>)/$1$2$5$3$1$4$5/;
index cf70acf0248b8e5948512ed808c4b828a591cc9c..6c8873f1716ec863b742f3561e53b75090898e3e 100755 (executable)
@@ -60,6 +60,13 @@ sendmail_path
 smtpd_expansion_filter
 tls_random_source
 virtual_mailbox_lock
+milter_connect_macros
+milter_helo_macros
+milter_mail_macros
+milter_rcpt_macros
+milter_data_macros
+milter_unknown_command_macros
+milter_end_of_data_macros
 EOF
 
 for $name (split(/\s+/, $censored)) {
index 7a275bb954b22490760be001de580d0b20364af0..98a529d32add22b1ef0843253861ef21a745d4bf 100644 (file)
@@ -25,7 +25,7 @@ mail is queued. Each approach serves a different purpose.  </p>
 
 <dl>
 
-<dt> <b> built-in, light-weight, real-time </b> </dt>
+<dt> <b> before queue, built-in, light-weight</b> </dt>
 
 <dd> <p> This method inspects mail BEFORE it is stored in the queue,
 and uses Postfix's built-in message header and message body
@@ -38,7 +38,7 @@ the content inspection methods described below. Details are described
 in the BUILTIN_FILTER_README and BACKSCATTER_README documents.
 </p>
 
-<dt> <b> external, heavy-weight, not real time </b> </dt>
+<dt> <b> after queue, external, heavy-weight</b> </dt>
 
 <dd> <p> This method inspects mail AFTER it is stored in the queue,
 and uses standard protocols such as SMTP or "pipe to command and
@@ -48,21 +48,33 @@ while receiving mail, and without running out of memory resources
 under a peak load. Details of this approach are in the FILTER_README
 document. </p>
 
-<dt> <b> external, medium-weight, real-time </b> </dt>
+<dt> <b> before queue, external, medium-weight</b> </dt>
 
-<dd> <p> This method inspects mail BEFORE it is stored in the queue,
-and uses the SMTP protocol. Although this approach appears to be
-the more attractive one, it really combines the worst of the other
-two.  Because mail is inspected before it is queued, content
-inspection software must finish in a limited amount of time, and
-must run in a limited amount of memory.  If content inspection
-needs too much time then incoming mail deliveries will time out,
-and if content inspection needs too much memory then software will
-crash under a peak load. Before-queue inspection limits the peak
-load that your system can handle, and limits the sophistication of
-the content filter that you can use.  Details are in the
-SMTPD_PROXY_README document.  This approach is available only with
-Postfix version 2.1 and later.  </p>
+<dd> <p> The following two methods inspect mail BEFORE it is stored in the
+queue.  </p>
+
+<ul>
+
+<li> <p> The first method uses the SMTP protocol, and is described
+in the SMTPD_PROXY_README document.  This approach is available
+with Postfix version 2.1 and later.  </p>
+
+<li> <p> The second method uses the Sendmail 8 Milter protocol, and
+is described in the MILTER_README document.  This approach is
+available with Postfix version 2.3 and later.  </p>
+
+</ul>
+
+<p> Although these approaches appear to be attractive, they have
+some serious limitations that you need to be aware of.  First,
+content inspection software must finish in a limited amount of time;
+if content inspection needs too much time then incoming mail
+deliveries will time out.  Second, content inspection software must
+run in a limited amount of memory; if content inspection needs too
+much memory then software will crash under a peak load.  Before-queue
+inspection limits the peak load that your system can handle, and
+limits the sophistication of the content filter that you can use.
+</p>
 
 </dl>
 
index 0405e2838f57d6b1b0ace269af1480be3c68bd1a..7161a05f6106fe05d038cce95bd83c1493f32bba 100644 (file)
@@ -688,7 +688,7 @@ that injects mail back into Postfix. </p>
     # ===================================================================
     localhost:10026 inet  n       -       n       -       10      smtpd
         -o content_filter= 
-        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
+        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
         -o smtpd_helo_restrictions=
         -o smtpd_client_restrictions=
         -o smtpd_sender_restrictions=
@@ -710,19 +710,27 @@ requests no content filtering for mail from the content filter.
 This is required or else mail will stay in the content filtering
 loop. </p>
 
-<li> <p> The "-o receive_override_options" overrides main.cf
-settings. It is complementary to the options that are specified in
-main.cf: </p>
+<li> <p> The "-o receive_override_options" overrides main.cf settings
+to avoid duplicating work that was already done before the content
+filter. These options are complementary to the options that are
+specified in main.cf: </p>
 
 <ul>
 
-    <li> <p> Disable attempts to find out if a recipient is unknown,
-    and disable header/body checks. This work was already done
-    before the content filter and repeating it would be wasteful.
-    </p>
+    <li> <p> We specify "no_unknown_recipient_checks" to disable
+    attempts to find out if a recipient is unknown. </p>
 
-    <li> <p> Enable virtual alias expansion, canonical mappings,
-    address masquerading, and other address mappings. </p>
+    <li> <p> We specify "no_header_body_checks" to disable header/body
+    checks.  </p>
+
+    <li> <p> We specify "no_milters" to disable Milter applications
+    (this option is available only in Postfix 2.3 and later).  </p>
+
+    <li> <p> We don't specify "no_address_mapping" here.  This
+    enables virtual alias expansion, canonical mappings, address
+    masquerading, and other address mappings after the content
+    filter. The main.cf setting of "receive_override_options"
+    disables these mappings before the content filter.  </p>
 
 </ul>
 
diff --git a/postfix/proto/MILTER_README.html b/postfix/proto/MILTER_README.html
new file mode 100644 (file)
index 0000000..8446ea6
--- /dev/null
@@ -0,0 +1,765 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix before-queue Milter support </title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix before-queue Milter support </h1>
+
+<hr>
+
+<h2>Introduction</h2>
+
+<p> Postfix version 2.3 introduces support for the Sendmail version
+8 Milter (mail filter) protocol. This protocol is used by applications
+that run outside the MTA to inspect SMTP events (CONNECT, DISCONNECT),
+SMTP commands (HELO, MAIL FROM, etc.) as well as mail content.  All
+this happens before mail is queued.  </p>
+
+<p> The reason for adding Milter support to Postfix is that there
+exists a large collection of applications, not only to block unwanted
+mail, but also to verify authenticity (examples: <a
+href="http://sourceforge.net/projects/sid-milter/">SenderID+SPF</a> and
+<a href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>)
+or to digitally sign mail (example: <a
+href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>).
+Having yet another MTA-specific version of all that software is a
+poor use of human and system resources. </p>
+
+<p> Postfix 2.3 implements all the requests of Sendmail version 8
+Milter protocols up to version 4, except one: message body replacement.
+See, however, the <a href="#limitations">limitations</a> section
+at the end of this document. </p>
+
+<p> This document provides information on the following topics: </p>
+
+<ul>
+
+<li><a href="#plumbing">How Milter applications plug into Postfix </a>
+
+<li><a href="#building">Building Milter applications</a>
+
+<li><a href="#running">Running Milter applications</a>
+
+<li><a href="#config">Configuring Postfix</a>
+
+<li><a href="#workarounds">Workarounds</a>
+
+<li><a href="#limitations">Limitations</a>
+
+</ul>
+
+<h2><a name="plumbing">How Milter applications plug into Postfix </a> </h2>
+
+<p> The Postfix Milter implementation uses two different lists of
+mail filters: one list of filters that are used for SMTP mail only,
+and one list of filters that are used for non-SMTP mail. The two
+lists have different capabilities, which is unfortunate.  Avoiding
+this would require major restructuring of Postfix. </p>
+
+<ul>
+
+<li> <p> The SMTP-only filters handle mail that arrives via the
+Postfix smtpd(8) server. They are typically used to filter unwanted
+mail and to sign mail from authorized SMTP clients. You specify
+SMTP-only Milter applications with the smtpd_milters parameter as
+described in a later section.  Mail that arrives via the Postfix
+smtpd(8) server is not filtered by the non-SMTP filters that are
+described next. </p>
+
+<li> <p> The non-SMTP filters handle mail that arrives via the
+Postfix sendmail(1) command-line or via the Postfix qmqpd(8) server.
+They are typically used to digitally sign mail only. Although
+non-SMTP filters can be used to filter unwanted mail, they have
+limitations compared to the SMTP-only filters. You specify non-SMTP
+Milter applications with the non_smtpd_milters parameter as described
+in a later section.  </p>
+
+</ul>
+
+<p> For those who are familiar with the Postfix architecture, the
+figure below shows how Milter applications plug into Postfix.  Names
+followed by a number are Postfix commands or server programs, while
+unnumbered names inside shaded areas represent Postfix queues.  To
+avoid clutter, the path for local submission is simplified (the
+OVERVIEW document has a more complete description).  </p>
+
+<blockquote>
+
+<table>
+
+<tr> 
+
+<td colspan="2"> </td> 
+
+<td align="center"> SMTP-only <br> filters </td> 
+
+<td> </td> 
+
+<td align="center"> non-SMTP <br> filters </td> 
+
+</tr>
+
+<tr> 
+
+<td colspan="2"> </td> 
+
+<td align="center"> <table> <tr> <td align="center">
+^<br> <tt> | </tt> </td> <td align="center"> <tt> |<br> v </tt>
+</td> </tr> </table> </td>
+
+<td rowspan="2"> </td>
+
+<td rowspan="3" align="center"> <table> <tr> <td align="center">
+^<br> <tt> |<br> |<br> | </tt> </td> <td align="center"> <tt> |<br>
+|<br> |<br> v </tt> </td> </tr> </table> </td>
+
+</tr>
+
+<tr>
+
+<td> Network </td> <td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> smtpd(8)
+</td>
+
+</tr>
+
+<tr>
+
+<td colspan="3"> </td> <td> <tt> \ </tt> </td>
+
+</tr>
+
+<tr>
+
+<td> Network </td> <td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> qmqpd(8)
+</td>
+
+<td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> cleanup(8)
+</td>
+
+<td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a
+href="QSHAPE_README.html#incoming_queue"> incoming </a> </td>
+
+</tr>
+
+<tr>
+
+<td colspan="3"> </td> <td> <tt> / </tt> </td>
+
+</tr>
+
+<tr>
+
+<td colspan="2"> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> pickup(8)
+</td>
+
+</tr>
+
+<tr> <td colspan="2"> </td> <td align="center"> : </td> </tr>
+
+<tr>
+
+<td> Local </td> <td> <tt> -&gt; </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> sendmail(1)
+</td>
+
+</tr>
+
+</table>
+
+</blockquote>
+
+<h2><a name="building">Building Milter applications</a></h2>
+
+<p> Milter applications have been written in C, JAVA and Perl, but
+this document deals with C applications only.   For these, you need
+an object library that implements the Sendmail 8 Milter protocol.
+Postfix currently does not provide such a library, but Sendmail
+does. </p>
+
+<p> On some Linux and *BSD distributions, the Sendmail libmilter
+library is installed by default. With this, applications such as
+<a href="http://sourceforge.net/projects/dk-milter/">dk-milter</a>
+and <a href="http://sourceforge.net/projects/sid-milter/">sid-milter</a>
+build out of the box without requiring any tinkering:</p>
+
+<blockquote>
+<pre>
+$ <b>gzcat dk-milter-<i>x.y.z</i>.tar.gz | tar xf -</b>
+$ <b>cd dk-milter-<i>x.y.z</i></b>
+$ <b>make</b>
+[...<i>lots of output omitted</i>...]
+</pre>
+</blockquote>
+
+<p> On other platforms you have two options: </p>
+
+<ul>
+
+<li> <p>Install the Sendmail libmilter object library and include
+files.  On Linux systems, libmilter may be provided by the
+sendmail-devel package.  After installing libmilter, build the
+Milter applications as described in the preceding paragraph.  </p>
+
+<li> <p>Don't install the Sendmail libmilter library, but build the
+library from Sendmail source code instead: </p>
+
+<blockquote>
+<pre>
+$ <b>gzcat sendmail-<i>x.y.z</i>.tar.gz | tar xf -</b>
+$ <b>cd sendmail-<i>x.y.z</i></b>
+$ <b>make</b>
+[...<i>lots of output omitted</i>...]
+</pre>
+</blockquote>
+
+<p> After building your own libmilter library, follow the installation
+instructions in the Milter application source distribution to specify
+the location of the libmilter include files and object library.
+Typically, these settings are configured in a file named
+<tt>sid-filter/Makefile.m4</tt> or similar:
+
+<blockquote>
+<pre>
+APPENDDEF(`confINCDIRS', `-I/some/where/sendmail-x.y.z/include')
+APPENDDEF(`confLIBDIRS', `-L/some/where/sendmail-x.y.z/obj.<i>systemtype</i>/libmilter')
+</pre>
+</blockquote>
+
+<p>Then build the Milter application. </p>
+
+</ul>
+
+<h2><a name="running">Running Milter applications</a></h2>
+
+<p> To run a Milter application, see the documentation of the filter
+for options.  A typical command looks like this:</p>
+
+<blockquote>
+<pre>
+$ <b>/some/where/dk-filter -p inet:<i>portnumber</i>@localhost ...<i>other options</i>...</b>
+</pre>
+</blockquote>
+
+<h2><a name="config">Configuring Postfix</a></h2>
+
+<p> Like Sendmail, Postfix has a lot of configuration options that
+control how it talks to Milter applications. With the initial Postfix
+Milter protocol implementation, many options are global, that is,
+they apply to all Milter applications. Future Postfix versions may
+support per-Milter timeouts, per-Milter error handling, etc. </p>
+
+<p> Information in this section: </p>
+
+<ul>
+
+<li><a href="#smtp-only-milters">SMTP-Only Milter applications </a>
+
+<li><a href="#non-smtp-milters">Non-SMTP Milter applications </a>
+
+<li><a href="#errors">Milter error handling </a>
+
+<li><a href="#version">Milter protocol version</a>
+
+<li><a href="#timeouts">Milter protocol timeouts</a>
+
+<li><a href="#macros">Sendmail macro emulation</a>
+
+</ul>
+
+<h3><a name="smtp-only-milters">SMTP-Only Milter applications</a></h3>
+
+<p>  The SMTP-only Milter applications handle mail that arrives via
+the Postfix smtpd(8) server. They are typically used to filter
+unwanted mail, and to sign mail from authorized SMTP clients.  Mail
+that arrives via the Postfix smtpd(8) server is not filtered by the
+non-SMTP filters that are described in the next section. </p>
+
+<p> You specify SMTP-only Milter applications (there can be more
+than one) with the smtpd_milters parameter.  Each Milter application
+is identified by the name of its listening socket; other Milter
+configuration options will be discussed in later sections.  Milter
+applications are applied in the order as specified, and the first
+Milter application that rejects a command will override the responses
+from other Milter applications.  </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    # Milters for mail that arrives via the smtpd(8) server.
+    # See below for socket address syntax.
+    smtpd_milters = inet:localhost:<i>portnumber</i> ...<i>other filters</i>...
+</pre>
+</blockquote>
+
+<p> The general syntax for listening sockets is as follows: </p>
+
+<blockquote>
+
+<dl>
+
+<dt> <b>unix:</b><i>pathname</i> </dt> <dd><p>Connect to the local
+UNIX-domain server that is bound to the specified pathname. If the
+smtpd(8) or cleanup(8) process runs chrooted, an absolute pathname
+is interpreted relative to the Postfix queue directory.</p> </dd>
+
+<dt> <b> inet:</b><i>host</i><b>:</b><i>port</i> </dt> <dd> <p>
+Connect to the specified TCP port on the specified local or remote
+host.  The host and port can be specified in numeric or symbolic
+form.</p>
+
+<p> Note: Postfix syntax differs from Milter syntax which has the
+form <b>inet:</b><i>port</i><b>@</b><i>host</i>. </p>  </dd>
+
+</dl>
+
+</blockquote>
+
+<h3> <a name="non-smtp-milters">Non-SMTP Milter applications </a> </h3>
+
+<p> The non-SMTP Milter applications handle mail that arrives via
+the Postfix sendmail(1) command-line or via the Postfix qmqpd(8)
+server. They are typically used to digitally sign mail. Although
+non-SMTP filters can be used to filter unwanted mail, there are
+limitations as discussed later in this section.  Mail that arrives
+via the Postfix smtpd(8) server is not filtered by the non-SMTP
+filters.  </p>
+
+<p> You specify non-SMTP Milter applications with the non_smtpd_milters
+parameter. This parameter uses the same syntax as the smtpd_milters
+parameter in the previous section. As with the SMTP-only filters,
+you can specify more than one Milter application; they are applied
+in the order as specified, and the first Milter application that
+rejects a command will override the responses from the other
+applications.  </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    # Milters for non-SMTP mail.
+    # See below for socket address syntax.
+    non_smtpd_milters = inet:localhost:<i>portnumber</i> ...<i>other filters</i>...
+</pre>
+</blockquote>
+
+<p> There's one small complication when using Milter applications
+for non-SMTP mail: there is no SMTP session.  To keep Milter
+applications happy, the Postfix cleanup(8) server actually has to
+simulate the SMTP client CONNECT and DISCONNECT events, and the
+SMTP client EHLO, MAIL FROM, RCPT TO and DATA commands. </p>
+
+<ul>
+
+<li> <p> When new mail arrives via the sendmail(1) command line,
+the Postfix cleanup(8) server pretends that the mail arrives with
+ESMTP from "localhost" with IP address "127.0.0.1". The result is
+very similar to what happens with command line submissions in
+Sendmail version 8.12 and later, although Sendmail uses a different
+mechanism to achieve this result.  </p>
+
+<li> <p> When new mail arrives via the qmqpd(8) server, the Postfix
+cleanup(8) server pretends that the mail arrives with ESMTP, and
+uses the QMQPD client hostname and IP address. </p>
+
+<li> <p> When old mail is re-injected into the queue with "postsuper
+-r", the Postfix cleanup(8) server uses the same client information
+that was used when the mail arrived as new mail. </p>
+
+</ul>
+
+<p> This generally works as expected, with only one exception:
+non-SMTP filters must not REJECT or TEMPFAIL simulated RCPT TO
+commands.  When a non_smtpd_milters application REJECTs or TEMPFAILs
+a recipient, Postfix will report a configuration error, and mail
+will stay in the queue.  </p>
+
+<p> None of this is a problem for mail filters that digitally sign
+mail. </p>
+
+<h3><a name="errors">Milter error handling</a></h3>
+
+<p> The milter_default_action parameter specifies how Postfix handles
+Milter application errors. The default action is to respond with a
+temporary error status, so that the client will try again later.
+Specify "accept" if you want to receive mail as if the filter does
+not exist, and "reject" to reject mail with a permanent status.
+</p>
+
+<blockquote>
+<pre>
+    # What to do in case of errors? Specify accept, reject, or tempfail.
+    milter_default_action = tempfail
+</pre>
+</blockquote>
+
+<h3><a name="version">Milter protocol version</a></h3>
+
+<p> As Postfix is not built with the Sendmail libmilter library,
+you may need to configure the Milter protocol version that Postfix
+should use.  The default version is 2. </p>
+
+<blockquote>
+<pre>
+milter_protocol = 2
+</pre>
+</blockquote>
+
+<p> If the Postfix milter_protocol setting specifies a too low
+version, the libmilter library will log an error message like this:
+</p>
+
+<blockquote>
+<pre>
+<i>application name</i>: st_optionneg[<i>xxxxx</i>]: 0x<i>yy</i> does not fulfill action requirements 0x<i>zz</i>
+</pre>
+</blockquote>
+
+<p> The remedy is to increase the Postfix milter_protocol version
+number.  See, however, the <a href="#limitations">limitations</a>
+section below for features that aren't supported by Postfix. </p>
+
+<p> If the Postfix milter_protocol setting specifies a too high
+version, the libmilter library simply hangs up without logging a
+warning, and you see a Postfix warning message like one of the
+following: </p>
+
+<blockquote>
+<pre>
+postfix/smtpd[21045]: warning: milter inet:<i>host</i>:<i>port</i>: can't read packet header: Unknown error : 0
+postfix/cleanup[15190]: warning: milter inet:<i>host</i>:<i>port</i>: can't read packet header: Success
+</pre>
+</blockquote>
+
+<p> The remedy is to lower the Postfix milter_protocol version
+number. </p>
+
+<h3><a name="timeouts">Milter protocol timeouts</a></h3>
+
+<p> Postfix uses different time limits at different Milter protocol
+stages. The table shows wich timeouts are used and when
+(EOH = end of headers; EOM = end of message).  </p>
+
+<blockquote>
+
+<table border="1">
+
+<tr> <th> Parameter </th> <th> Time limit </th> <th> Protocol
+stage</th> </tr>
+
+<tr> <td> milter_connect_timeout </td> <td> 30s </td> <td> CONNECT
+</td> </tr>
+
+<tr> <td> milter_command_timeout </td> <td> 30s </td> <td> HELO,
+MAIL, RCPT, DATA, UNKNOWN </td> </tr>
+
+<tr> <td> milter_content_timeout </td> <td> 300s </td> <td> HEADER,
+EOH, BODY, EOM </td> </tr>
+
+</table>
+
+</blockquote>
+
+<p> Beware: 30s is not a lot for applications that do a lot of DNS
+lookups.  However, if you increase the above timeouts too much,
+remote SMTP clients may hang up and mail may be delivered multiple
+times. This is an inherent problem with before-queue filtering. </p>
+
+<h3><a name="macros">Sendmail macro emulation</a></h3>
+
+<p> Postfix emulates a limited number of Sendmail macros, as shown
+in the table. Different macros are available at different SMTP
+protocol stages (EOM = end-of-message); their availability is not
+always the same as in Sendmail. See the <a
+href="#workarounds">workarounds</a> section below for solutions.
+</p>
+
+<blockquote>
+
+<table border="1">
+
+<tr> <th> Name </th> <th> Availability </th> <th> Description </th>
+</tr>
+
+<tr> <td> i </td> <td> DATA, EOM </td> <td> Queue ID </td> </tr>
+
+<tr> <td> j </td> <td> Always </td> <td> value of myhostname </td>
+</tr>
+
+<tr> <td> {auth_authen} </td> <td> MAIL, DATA, EOM </td> <td> SASL
+login name </td> </tr>
+
+<tr> <td> {auth_author} </td> <td> MAIL, DATA, EOM </td> <td> SASL
+sender </td> </tr>
+
+<tr> <td> {auth_type} </td> <td> MAIL, DATA, EOM </td> <td> SASL
+login method </td> </tr>
+
+<tr> <td> {client_addr} </td> <td> Always </td> <td> Client IP
+address </td> </tr>
+
+<tr> <td> {client_connections} </td> <td> CONNECT </td> <td>
+Connection concurrency for this client </td> </tr>
+
+<tr> <td> {client_name} </td> <td> Always </td> <td> Client hostname,
+"unknown" when lookup or verification fails </td> </tr>
+
+<tr> <td> {client_ptr} </td> <td> CONNECT, HELO, MAIL, DATA </td>
+<td> Client name from reverse lookup, "unknown" when lookup fails
+</td> </tr>
+
+<tr> <td> {cert_issuer} </td> <td> HELO, MAIL, DATA, EOM </td> <td>
+TLS client certificate issuer </td> </tr>
+
+<tr> <td> {cert_subject} </td> <td> HELO, MAIL, DATA, EOM </td>
+<td> TLS client certificate subject </td> </tr>
+
+<tr> <td> {cipher_bits} </td> <td> HELO, MAIL, DATA, EOM </td> <td>
+TLS session key size </td> </tr>
+
+<tr> <td> {cipher} </td> <td> HELO, MAIL, DATA, EOM </td> <td> TLS
+cipher </td> </tr>
+
+<tr> <td> {daemon_name} </td> <td> Always </td> <td> value of
+milter_macro_daemon_name </td> </tr>
+
+<tr> <td> {mail_addr} </td> <td> MAIL </td> <td> Sender address
+</td> </tr>
+
+<tr> <td> {rcpt_addr} </td> <td> RCPT </td> <td> Recipient address
+</td> </tr>
+
+<tr> <td> {tls_version} </td> <td> HELO, MAIL, DATA, EOM </td> <td>
+TLS protocol version </td> </tr>
+
+<tr> <td> v </td> <td> Always </td> <td> value of milter_macro_v
+</td> </tr>
+
+</table>
+
+</blockquote>
+
+<p> Postfix sends specific sets of macros at different SMTP protocol
+stages.  The sets are configured with the parameters as described
+in the table (EOM = end of message). </p>
+
+<blockquote>
+
+<table border="1">
+
+<tr> <th> Parameter name </th> <th> Protocol version </th> <th>
+Protocol stage </th> </tr>
+
+<tr> <td> milter_connect_macros </td> <td> 2 or higher </td> <td>
+CONNECT </td> </tr>
+
+<tr> <td> milter_helo_macros </td> <td> 2 or higher </td> <td>
+HELO/EHLO </td> </tr>
+
+<tr> <td> milter_mail_macros </td> <td> 2 or higher </td> <td> MAIL
+FROM </td> </tr>
+
+<tr> <td> milter_rcpt_macros </td> <td> 2 or higher </td> <td> RCPT
+TO </td> </tr>
+
+<tr> <td> milter_data_macros </td> <td> 4 or higher </td> <td> DATA
+</td> </tr>
+
+<tr> <td> milter_end_of_data_macros </td> <td> 2 or higher </td>
+<td> EOM </td> </tr>
+
+<tr> <td> milter_unknown_command_macros </td> <td> 3 or higher </td>
+<td> unknown command </td> </tr>
+
+</table>
+
+</blockquote>
+
+<h2><a name="workarounds">Workarounds</a></h2>
+
+<p> Sendmail Milter applications were originally developed for the
+Sendmail version 8 MTA, which has a different architecture than
+Postfix.  The result is that some Milter applications make assumptions
+that aren't true in a Postfix environment. </p>
+
+<ul>
+
+<li> <p> Some Milter applications log a warning that looks like
+this: </p>
+
+<blockquote> <pre>
+sid-filter[36540]: WARNING: sendmail symbol 'i' not available
+</pre>
+</blockquote>
+
+<p> And they may insert a message header with "unknown-msgid" like
+this: </p>
+
+<blockquote>
+<pre>
+X-SenderID: Sendmail Sender-ID Filter vx.y.z host.example.com &lt;unknown-msgid&gt;
+</pre>
+</blockquote>
+
+<p> This happens because the Milter application expects that the
+queue ID is known <i>before</i> the MTA accepts the MAIL FROM
+(sender) command.  Postfix, on the other hand, does not create a
+queue file until <i>after</i> Postfix accepts the first valid RCPT
+TO (recipient) command. This queue file name must be globally unique
+across multiple queue directories, so it cannot be chosen until the
+file is actually created. </p>
+
+<p> To work around the ugly message header from Milter applications,
+we add a little code to the Milter source to look up the queue ID
+after Postfix receives the end of the message. </p>
+
+<ul>
+
+<li> <p> Edit the filter source file (typically named
+<tt>dk-filter/dk-filter.c</tt> or similar). </p>
+
+<li> <p> Look up the <tt>mlfi_eom()</tt> function and add code near
+the top shown as <b>bold</b> text below: </p>
+
+</ul>
+
+<blockquote>
+<pre>
+sic = (Context) smfi_getpriv(ctx);
+assert(sic != NULL);
+<b>
+/*
+**  Determine the job ID for logging.
+*/
+if (sic->ctx_jobid == 0 || strcmp(sic->ctx_jobid, MSGIDUNKNOWN) == 0) {
+        char *jobid = smfi_getsymval(ctx, "i");
+        if (jobid != 0)
+                sic->ctx_jobid = jobid;
+}</b>
+</pre>
+</blockquote>
+
+<p> This does not remove the WARNING message, however. </p>
+
+<p> With some Milter applications we can fix both the WARNING and
+the "unknown-msgid" by postponing the call of <tt>mlfi_eoh()</tt>
+(or whatever routine logs the WARNING) until the end of the message.
+</p>
+
+<ul>
+
+<li> <p> Edit the filter source file (typically named
+<tt>sid-filter/sid-filter.c</tt> or similar). </p>
+
+<li> <p> Look up the <tt>smfilter</tt> table and replace
+<tt>mlfi_eoh</tt> (or whatever routine logs the WARNING) by NULL.
+</p>
+
+<li> <p> Look up the <tt>mlfi_eom()</tt> function and add code near
+the top that calls <tt>mlfi_eoh()</tt> as shown by the <b>bold</b>
+text below: </p>
+
+</ul>
+
+<blockquote>
+<pre>
+        assert(ctx != NULL);
+#endif /* !DEBUG */
+<b>
+        ret = mlfi_eoh(ctx);
+        if (ret != SMFIS_CONTINUE)
+                return ret;</b>
+</pre>
+</blockquote>
+
+<p> This works with sid-milter-0.2.10. Other Milter applications
+will dump core when you do this. </p>
+
+</ul>
+
+<h2><a name="limitations">Limitations</a></h2>
+
+<p> This section lists limitations of the Postfix Milter implementation.
+Some limitations will be removed disappear as support is extended
+over time. Of course the usual limitations of before-queue filtering
+will always apply. See the CONTENT_INSPECTION_README document for
+a discussion. </p>
+
+<ul>
+
+<li> <p> Postfix currently supports only applications that speak
+the Sendmail 8 Milter protocol versions 2..4. Support for other
+protocol types or protocol versions may be added later. </p>
+
+<li> <p> For applications that are written in C, you need to use
+the Sendmail libmilter library. A Postfix replacement may be 
+provided in the future. </p>
+
+<li> <p> There are TWO sets of mail filters: filters that are used
+for SMTP mail only (specified with the smtpd_milters parameter),
+and filters for non-SMTP mail (specified with the non_smtpd_milters
+parameter).  The non-SMTP filters are primarily for local submissions.
+</p>
+
+<li> <p> When mail is filtered by non-SMTP filters, the Postfix
+cleanup(8) server has to simulate the SMTP client CONNECT and
+DISCONNECT events, and the SMTP client EHLO, MAIL FROM, RCPT TO and
+DATA commands.  This works as expected, with only one exception:
+non-SMTP filters must not REJECT or TEMPFAIL simulated RCPT TO
+commands.  When a non-SMTP filter REJECTs or TEMPFAILs a recipient,
+Postfix will report a configuration error, and mail will stay in
+the queue.  </p>
+
+<li> <p> Postfix currently does not apply content filters to mail
+that is forwarded or aliased internally, or to mail that is generated
+internally such as bounces or Postmaster notifications. This may
+be a problem when you want to apply a signing Milter to such mail.
+</p>
+
+<li> <p> When you use the before-queue content filter for incoming
+SMTP mail (see SMTPD_PROXY_README), Milter applications have access
+only to the SMTP command information; they have no access to the
+message header or body, and cannot make modifications to the message
+or to the envelope. </p>
+
+<li> <p> Postfix 2.3 does not support Milter requests to replace
+the message body. Milter applications that request this unsupported
+operation will log a warning like this: </p>
+
+<blockquote>
+<pre>
+<i>application name</i>: st_optionneg[134563840]: 0x3d does not fulfill action requirements 0x1e
+</pre>
+</blockquote>
+
+<p> The solution is (to wait for) a Postfix version that supports
+the missing functionality.
+
+<li> <p> Most Milter configuration options are global. Future Postfix
+versions may support per-Milter timeouts, per-Milter error handling,
+etc. </p>
+
+</ul>
+
+</body>
+
+</html>
index 5d07c031358ea8646baddcc14caa96c0cdbd71c5..b8814aff77566c46c6d0ba7a3d841f99dcdc1ba4 100644 (file)
@@ -24,6 +24,7 @@ HTML  = ../html/ADDRESS_CLASS_README.html \
        ../html/LDAP_README.html \
        ../html/LINUX_README.html ../html/LMTP_README.html \
        ../html/LOCAL_RECIPIENT_README.html ../html/MAILDROP_README.html \
+       ../html/MILTER_README.html \
        ../html/MYSQL_README.html ../html/NFS_README.html \
        ../html/OVERVIEW.html \
        ../html/PACKAGE_README.html ../html/PCRE_README.html \
@@ -59,6 +60,7 @@ README        = ../README_FILES/ADDRESS_CLASS_README \
        ../README_FILES/LDAP_README \
        ../README_FILES/LINUX_README ../README_FILES/LMTP_README \
        ../README_FILES/LOCAL_RECIPIENT_README ../README_FILES/MAILDROP_README \
+       ../README_FILES/MILTER_README \
        ../README_FILES/MYSQL_README ../README_FILES/NFS_README \
        ../README_FILES/OVERVIEW \
        ../README_FILES/PACKAGE_README ../README_FILES/PCRE_README \
@@ -193,6 +195,9 @@ clobber:
 ../html/MAILDROP_README.html: MAILDROP_README.html
        $(POSTLINK) $? >$@
 
+../html/MILTER_README.html: MILTER_README.html
+       $(POSTLINK) $? >$@
+
 ../html/MYSQL_README.html: MYSQL_README.html
        $(POSTLINK) $? >$@
 
@@ -331,6 +336,9 @@ clobber:
 ../README_FILES/MAILDROP_README: MAILDROP_README.html
        $(HT2READ) $? >$@
 
+../README_FILES/MILTER_README: MILTER_README.html
+       $(HT2READ) $? >$@
+
 ../README_FILES/MYSQL_README: MYSQL_README.html
        $(HT2READ) $? >$@
 
index 93f0a3c721239c6dba585b12a87592c0bfd96856..461a00c3003e76c501fb2eb36893f28e5a5dcc1b 100644 (file)
@@ -174,7 +174,7 @@ are in directory <tt>/usr/local/lib</tt>:  </p>
 </ul>
 
 <p> If you need to apply other customizations (such as Berkeley DB
-databases, MySQL, PosgreSQL, LDAP or SASL), see the respective
+databases, MySQL, PostgreSQL, LDAP or SASL), see the respective
 Postfix README documents, and combine their "<tt>make makefiles</tt>"
 instructions with the instructions above:  </p>
 
@@ -224,11 +224,32 @@ key configuration </a>
 <h3><a name="server_cert_key">Server-side certificate and private
 key configuration </a> </h3>
 
-<p> In order to use TLS, the Postfix SMTP server needs a certificate
-and a private key. Both must be in "pem" format. The private key
-must not be encrypted, meaning:  the key must be accessible without
-password.  Both certificate and private key may be in the same
-file.  </p>
+<p> In order to use TLS, the Postfix SMTP server generally needs
+a certificate and a private key. Both must be in "PEM" format. The
+private key must not be encrypted, meaning:  the key must be accessible
+without password.  Both certificate and private key may be in the same
+file, in which case the certificate file should be owned by "root" and
+not be readable by any other user. If the key is stored separately,
+this applies to the key file only, and the certificate file may be
+"world-readable". </p>
+
+<p> Public Internet MX hosts without certificates signed by a "reputable"
+CA must generate, and be prepared to present to most clients, a
+self-signed or private-CA signed certificate. The client will not be
+able to authenticate the server, but unless it is running Postfix 2.3 or
+similar software, it will still insist on a server certificate. </p>
+
+<p> For servers that are <b>not</b> public Internet MX hosts, Postfix
+2.3 supports configurations with no certificates. This entails the
+use of just the anonymous TLS ciphers, which are not supported by
+typical SMTP clients. Since such clients will not, as a rule, fall
+back to plain text after a TLS handshake failure, the server will
+be unable to receive email from most TLS enabled clients. To avoid
+accidental configurations with no certificates, Postfix 2.3 enables
+certificate-less operation only when the administrator explicitly sets
+"smtpd_tls_cert_file&nbsp;=&nbsp;none". This ensures that new Postfix
+configurations with just "smtpd_use_tls&nbsp;=&nbsp;yes" added, will
+not accidentally run with no certificates. </p>
 
 <p> Both RSA and DSA certificates are supported. Typically you will
 only have RSA certificates issued by a commercial CA. In addition,
@@ -240,9 +261,9 @@ preferred. </p>
 
 <p> In order for remote SMTP clients to check the Postfix SMTP
 server certificates, the CA certificate (in case of a certificate
-chain, all CA certificates) must be available.  You should add
-these certificates to the server certificate, the server certificate
-first, then the issuing CA(s).  </p>
+chain, all CA certificates) must be available.  You should add any
+intermediate CA certificates to the server certificate: the server
+certificate first, then the intermediate CA(s).  </p>
 
 <p> Example: the certificate for "server.dom.ain" was issued by
 "intermediate CA" which itself has a certificate issued by "root
@@ -295,9 +316,19 @@ is correctly configured to supply its intermediate CA certificate). </p>
 </pre>  
 </blockquote>
 
+<p> Postfix 2.3 and later, TLS without certificates for servers serving
+exclusively anonymous-cipher capable clients: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtpd_tls_cert_file = none
+</pre>  
+</blockquote>
+
 <p> To verify a remote SMTP client certificate, the Postfix SMTP
 server needs to trust the certificates of the issuing certification
-authorities. These certificates in "pem" format can be stored in a
+authorities. These certificates in "PEM" format can be stored in a
 single $smtpd_tls_CAfile or in multiple files, one CA per file in
 the $smtpd_tls_CApath directory. If you use a directory, don't forget
 to create the necessary "hash" links with: </p>
@@ -319,16 +350,16 @@ privileges) from the files in the directory when the information
 is needed. Thus, the $smtpd_tls_CApath directory needs to be
 accessible inside the optional chroot jail. </p>
 
-<p> When you configure Postfix to request client certificates (by
-setting $smtpd_tls_ask_ccert = yes), any certificates in
-$smtpd_tls_CAfile are sent to the client, in order to allow it to
+<p> When you configure Postfix to request <a
+href="#server_vrfy_client">client certificates</a>, any CA certificates
+in $smtpd_tls_CAfile are sent to the client, in order to allow it to
 choose an identity signed by a CA you trust. If no $smtpd_tls_CAfile
-is specified, no preferred CA list is sent, and the client is free
-to choose an identity signed by any CA. Many clients use a fixed
-identity regardless of the preferred CA list and you may be able
-to reduce TLS negotiation overhead by installing client CA certificates
-mostly or only in $smtpd_tls_CApath. In the latter case you need
-not specify a $smtpd_tls_CAfile. </p>
+is specified, no preferred CA list is sent, and the client is free to
+choose an identity signed by any CA. Many clients use a fixed identity
+regardless of the preferred CA list and you may be able to reduce TLS
+negotiation overhead by installing client CA certificates mostly or
+only in $smtpd_tls_CApath. In the latter case you need not specify a
+$smtpd_tls_CAfile. </p>
 
 <p> Note, that unless client certificates are used to allow greater
 access to TLS authenticated clients, it is best to not ask for
@@ -348,7 +379,7 @@ the TLS handshake when client certificates are requested. </p>
 <h3><a name="server_logging"> Server-side TLS activity logging </a> </h3>
 
 <p> To get additional information about Postfix SMTP server TLS
-activity you can increase the loglevel from 0..4. Each logging
+activity you can increase the log level from 0..4. Each logging
 level also includes the information that is logged at a lower
 logging level. </p>
 
@@ -374,7 +405,7 @@ transmission after STARTTLS </td> </tr>
 
 </blockquote>
 
-<p> Use loglevel 3 only in case of problems. Use of loglevel 4 is
+<p> Use log level 3 only in case of problems. Use of log level 4 is
 strongly discouraged. </p>
 
 <p> Example: </p>
@@ -406,7 +437,7 @@ since the headers may be changed by intermediate servers. </p>
 
 <p> By default, TLS is disabled in the Postfix SMTP server, so no
 difference to plain Postfix is visible.  Explicitly switch it on
-using "smtpd_use_tls = yes". </p>
+using "smtpd_use_tls&nbsp;=&nbsp;yes". </p>
 
 <p> Example: </p>
  
@@ -425,11 +456,12 @@ SMTP clients, but does not require that clients use TLS encryption.
 is never offered due to insufficient privileges to access the server
 private key. This is intended behavior. </p>
 
-<p> You can ENFORCE the use of TLS, so that the Postfix SMTP server
-announces STARTTLS and accepts no mail without TLS encryption, by
-setting "smtpd_enforce_tls = yes". According to RFC 2487 this MUST
-NOT be applied in case of a publicly-referenced Postfix SMTP server.
-This option is off by default and should only seldom be used. </p>
+<p> <a name="server_enforce">You can ENFORCE the use of TLS</a>, so that
+the Postfix SMTP server announces STARTTLS and accepts no mail without
+TLS encryption, by setting "smtpd_enforce_tls&nbsp;=&nbsp;yes". According
+to RFC 2487 this MUST NOT be applied in case of a publicly-referenced
+Postfix SMTP server.  This option is off by default and should only
+seldom be used. </p>
 
 <p> Example: </p>
  
@@ -449,7 +481,7 @@ and OE (5.01 Mac on all ports). </p>
 
 <p> It is strictly discouraged to use this mode from main.cf. If
 you want to support this service, enable a special port in master.cf
-and specify "-o smtpd_tls_wrappermode = yes" as an smtpd(8) command
+and specify "-o smtpd_tls_wrappermode&nbsp;=&nbsp;yes" as an smtpd(8) command
 line option.  Port 465 (smtps) was once chosen for this feature.
 </p>
 
@@ -467,46 +499,43 @@ line option.  Port 465 (smtps) was once chosen for this feature.
 
 <p> To receive a remote SMTP client certificate, the Postfix SMTP
 server must explicitly ask for one (any contents of $smtpd_tls_CAfile
-are also sent to the client as a hint for choosing a certificate
-from a suitable CA). Unfortunately, Netscape clients will either
-complain if no matching client certificate is available or will
-offer the user client a list of certificates to choose from.
-Additionally some MTAs (notably some versions of qmail) are unable
-to complete TLS negotiation when client certificates are requested,
-and abort the SMTP session. So this option is "off" by default.
-You will however need the certificate if you want to use certificate
-based relaying with, for example, the permit_tls_clientcerts
-feature.  </p>
+are also sent to the client as a hint for choosing a certificate from
+a suitable CA). Unfortunately, Netscape clients will either complain
+if no matching client certificate is available or will offer the user
+client a list of certificates to choose from. Additionally some MTAs
+(notably some versions of qmail) are unable to complete TLS negotiation
+when client certificates are requested, and abort the SMTP session. So
+this option is "off" by default. You will however need the certificate
+if you want to use certificate based relaying with, for example, the
+permit_tls_clientcerts feature. A server that wants client certificates
+must first present its own certificate. While Postfix 2.3 by default
+offers anonymous ciphers to clients, these are automatically suppressed
+when the server is configured to ask for client certificates. </p>
 
 <p> Example: </p>
  
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtpd_tls_ask_ccert = no
+    smtpd_use_tls = yes
+    smtpd_tls_ask_ccert = yes
 </pre>
 </blockquote>
 
-<p> You may also decide to REQUIRE a remote SMTP client certificate
-before allowing TLS connections.  This feature is included for
-completeness, and implies "smtpd_tls_ask_ccert = yes".  </p>
-
-<p> Please be aware, that this will inhibit TLS connections without
-a proper client certificate and that it makes sense only when
-non-TLS submission is disabled (smtpd_enforce_tls = yes). Otherwise,
-clients could bypass the restriction by simply not using STARTTLS
-at all. </p>
-
-<p> When TLS is not enforced, the connection will be handled as
-if only "smtpd_tls_ask_ccert = yes" is specified, and a warning is
+<p> When TLS is <a href="#server_enforce">enforced</a> you may also decide
+to REQUIRE a remote SMTP client certificate for all TLS connections,
+by setting "smtpd_tls_req_ccert&nbsp;=&nbsp;yes". This feature implies
+"smtpd_tls_ask_ccert&nbsp;=&nbsp;yes". When TLS is not enforced,
+"smtpd_tls_req_ccert&nbsp;=&nbsp;yes" is ignored and a warning is
 logged. </p>
 
 <p> Example: </p>
+
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtpd_tls_req_ccert = no
+    smtpd_enforce_tls = yes
+    smtpd_tls_req_ccert = yes
 </pre>
 </blockquote>
 
@@ -527,15 +556,15 @@ CA issues special CA which then issues the actual certificate...)
 
 <h3><a name="server_tls_auth">Supporting AUTH over TLS only</a></h3>
 
-<p> Sending AUTH data over an unencrypted channel poses a security
-risk. When TLS layer encryption is required (smtpd_enforce_tls =
-yes), the Postfix SMTP server will announce and accept AUTH only
+<p> Sending AUTH data over an unencrypted channel poses a security risk.
+When TLS layer encryption is required (smtpd_enforce_tls&nbsp;=&nbsp;yes),
+the Postfix SMTP server will announce and accept AUTH only
 after the TLS layer has been activated with STARTTLS. When TLS
-layer encryption is optional (smtpd_enforce_tls = no), it may
+layer encryption is optional (smtpd_enforce_tls&nbsp;=&nbsp;no), it may
 however still be useful to only offer AUTH when TLS is active. To
 maintain compatibility with non-TLS clients, the default is to
 accept AUTH without encryption. In order to change this behavior,
-set "smtpd_tls_auth_only = yes". </p>
+set "smtpd_tls_auth_only&nbsp;=&nbsp;yes". </p>
 
 <p> Example: </p>
  
@@ -658,30 +687,50 @@ the user or host.</p>
 
 <h3><a name="server_cipher">Server-side cipher controls</a> </h3>
 
-<p> To influence the Postfix SMTP server cipher selection scheme,
-you can give cipherlist string.  A detailed description would go
-to far here; please refer to the OpenSSL documentation.  If you
-don't know what to do with it, simply don't touch it and leave the
-(openssl-)compiled in default! </p>
+<p> The description below is for Postfix 2.3; for Postfix &lt; 2.3 the
+smtpd_tls_cipherlist parameter specifies the acceptable ciphers as an
+explicit OpenSSL cipherlist. </p>
 
-<p> DO NOT USE " to enclose the string, specify just the string!!! </p>
+<p> The Postfix SMTP server supports 5 distinct cipher security levels
+as specified by the smtpd_tls_ciphers configuration parameter. The
+default value is "export" which is the only one appropriate for public
+MX hosts.  On private MX hosts or MSAs one can further restrict the
+OpenSSL cipherlist selection. </p>
+
+<p> By default anonymous ciphers are allowed, and automatically disabled
+when client certificates are requested. If clients are expected to always
+verify the server certificate you may want to exclude anonymous ciphers
+by setting "smtpd_tls_exclude_ciphers&nbsp;=&nbsp;aNULL".  One can't
+force a client to check the server certificate, so excluding anonymous
+ciphers is generally unnecessary. </p>
+
+<p> For a server that is not a public Internet MX host, Postfix 2.3
+supports configurations with no <a href="#server_cert_key">server
+certificates</a> that use <b>only</b> the anonymous ciphers. This is
+enabled by explicitly setting "smtpd_tls_cert_file&nbsp;=&nbsp;none"
+and not specifying an smtpd_tls_dcert_file. </p>
+
+<p> Example: (MSA that requires  TLS with reasonably secure ciphers) </p>
 
-<p> Example: </p>
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtpd_tls_cipherlist = DEFAULT
+    smtpd_use_tls = yes
+    smtpd_enforce_tls = yes
+    smtpd_tls_cert_file = /etc/postfix/cert.pem
+    smtpd_tls_key_file = /etc/postfix/key.pem
+    smtpd_tls_ciphers = medium
+    smtpd_tls_exclude_ciphers = aNULL, MD5
 </pre>
 </blockquote>
 
 <p> If you want to take advantage of ciphers with EDH, DH parameters
 are needed.  Instead of using the built-in DH parameters for both
-1024bit and 512bit, it is better to generate "own" parameters,
+1024bit and 512bit, it is better to generate your own parameters,
 since otherwise it would "pay" for a possible attacker to start a
 brute force attack against parameters that are used by everybody.
-For this reason, the parameters chosen are already different from
-those distributed with other TLS packages. </p>
+For this reason, the default parameters chosen by OpenSSL are already
+different from those distributed with other TLS packages. </p>
 
 <p> To generate your own set of DH parameters, use: </p>
 
@@ -723,6 +772,8 @@ handshake procedures.  </p>
 
 <ul>
 
+<li><a href="#client_lmtp_tls"> TLS support in the LMTP delivery agent </a>
+
 <li><a href="#client_cert_key">Client-side certificate and private
 key configuration </a>
 
@@ -731,19 +782,25 @@ key configuration </a>
 
 <li><a href="#client_tls_cache">Client-side TLS session cache</a>
 
-<li><a href="#client_tls_enable"> Enabling TLS in the Postfix SMTP client </a>
+<li><a href="#client_tls_limits"> Client TLS limitations </a>
 
-<li><a href="#client_tls_require"> Requiring TLS encryption </a>
+<li><a href="#client_tls_levels"> Client TLS security levels </a>
 
-<li><a href="#client_tls_nopeer"> Disabling server certificate verification </a>
+<li><a href="#client_tls_none"> Disabling TLS in the SMTP/LMTP client</a>
 
-<li><a href="#client_tls_per_site"> Per-site TLS policies </a>
+<li><a href="#client_tls_may"> Enabling TLS in the SMTP/LMTP client </a>
+
+<li><a href="#client_tls_encrypt"> Mandating TLS encryption </a>
+
+<li><a href="#client_tls_verify"> Mandating server certificate verification </a>
+
+<li><a href="#client_tls_secure"> Secure server certificate verification </a>
+
+<li><a href="#client_tls_policy"> Per-destination TLS policy </a>
 
-<!--
 <li><a href="#client_tls_obs"> Obsolete per-site TLS policy support </a>
--->
 
-<li><a href="#client_tls_harden"> Closing a DNS loophole with <!-- legacy --> per-site TLS policies </a>
+<li><a href="#client_tls_harden"> Closing a DNS loophole with obsolete per-site TLS policies </a>
 
 <li><a href="#client_tls_discover"> Discovering servers that support TLS </a>
 
@@ -755,9 +812,47 @@ key configuration </a>
 
 </ul>
 
+<h3><a name="client_lmtp_tls"> TLS support in the LMTP delivery agent </a>
+</h3>
+
+<p> In Postfix 2.3, the smtp(8) and lmtp(8) delivery agents have been
+merged into a single dual-purpose program. As a result the lmtp(8)
+delivery agent is no longer the poor cousin of the more extensively used
+smtp(8). Specifically, as of Postfix 2.3, all the TLS features described
+below apply equally to SMTP and LMTP, after replacing the "smtp_"
+prefix of the each parameter name with "lmtp_".
+
+<p> The LMTP delivery agent can communicate with LMTP servers listening
+on UNIX-domain sockets. When server certificate verification is enabled
+and the server is listening on a UNIX-domain socket, the $myhostname
+parameter is used to set the TLS verification <i>nexthop</i> and
+<i>hostname</i>. Note, opportunistic encryption of LMTP traffic over
+UNIX-domain sockets is futile. TLS is only useful in this context when
+it is mandatory, typically to allow at least one of the server or the
+client to authenticate the other. The "null" cipher grade may be
+appropriate in this context, when available on both client and server.
+The "null" ciphers provide authentication without encryption. </p>
+
 <h3><a name="client_cert_key">Client-side certificate and private
 key configuration </a> </h3>
 
+<p> Do not configure client certificates unless you <b>must</b> present
+client TLS certificates to one or more servers. Client certificates are
+not usually needed, and can cause problems in configurations that work
+well without them. The recommended setting is to let the defaults stand: </p>
+
+<blockquote>
+<pre>
+    smtp_tls_cert_file =
+    smtp_tls_dcert_file =
+    smtp_tls_key_file =
+    smtp_tls_dkey_file =
+</pre>
+</blockquote>
+
+<p> The best way to use the default settings is to comment out the above
+parameters in main.cf if present. </p>
+
 <p> During TLS startup negotiation the Postfix SMTP client may present
 a certificate to the remote SMTP server.  The Netscape client is
 rather clever here and lets the user select between only those
@@ -773,7 +868,7 @@ certificate is presented.  </p>
 
 <p> It is possible for the Postfix SMTP client to use the same
 key/certificate pair as the Postfix SMTP server.  If a certificate
-is to be presented, it must be in "pem" format. The private key
+is to be presented, it must be in "PEM" format. The private key
 must not be encrypted, meaning: it must be accessible without
 password. Both parts (certificate and private key) may be in the
 same file. </p>
@@ -953,119 +1048,727 @@ recommends a maximum of 24 hours.  </p>
 </pre>
 </blockquote>
 
-<h3><a name="client_tls_enable"> Enabling TLS in the Postfix SMTP
-client </a> </h3>
+<h3><a name="client_tls_limits"> Client TLS limitations </a>
+</h3>
+
+<p> The security properties of TLS communication channels are
+application specific. While the TLS protocol can provide a confidential,
+tamper-resistant, mutually authenticated channel between client
+and server, not all of these security features are applicable to every
+communication. </p>
+
+<p> For example, while mutual TLS authentication between browsers and web
+servers is possible, it is not practical, or even useful, for web-servers
+that serve the public to verify the identity of every potential user. In
+practice, most HTTPS transactions are asymmetric: the browser verifies
+the HTTPS server's identity, but the user remains anonymous. Much of
+the security policy is up to the client. If the client chooses to not
+verify the server's name, the server is not aware of this. There are many
+interesting browser security topics, but we shall not dwell
+on them here. Rather, our goal is to understand the security features
+of TLS in conjunction with SMTP. </p>
+
+<p> An important SMTP-specific observation is that a public MX host is
+even more at the mercy of the SMTP client than is an HTTPS server. Not only
+can it not enforce due care in the client's use of TLS, but it cannot even
+enforce the use of TLS, because TLS support in SMTP clients is still the
+exception rather than the rule. One cannot, in practice, limit access to
+one's MX hosts to just TLS-enabled clients. Such a policy would result
+in a vast reduction in one's ability to communicate by email with the
+world at large. </p>
+
+<p> One may be tempted to try enforcing TLS for mail from specific
+sending organizations, but this, too, runs into obstacles. One such
+obstacle is that we don't know who is (allegedly) sending mail until
+we see the "MAIL FROM:" SMTP command, and at that point, if TLS
+is not already in use, a potentially sensitive sender address (and
+with SMTP PIPELINING one or more of the recipients) has (have) already been
+leaked in the clear. Another obstacle is that mail from the sender to
+the recipient may be forwarded, and the forwarding organization may not
+have any security arrangements with the final destination. Bounces also
+need to be protected. These can only be identified by the IP address and
+HELO name of the connecting client, and it is difficult to keep track
+of all the potential IP addresses or HELO names of the outbound email
+servers of the sending organization. </p>
+
+<p> Consequently, TLS security for mail delivery to public MX hosts is
+almost entirely the client's responsibility. The server is largely a
+passive enabler of TLS security, the rest is up to the client. While the
+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
+attack. </p>
+
+<p> If clients instead attempted to verify the recipient domain name,
+an SMTP server for multiple domains would need to
+list all its email domain names in its certificate, and generate a
+new certificate each time a new domain were added. At least some CAs set
+fairly low limits (20 for one prominent CA) on the number of names that
+server certificates can contain. This approach is not consistent with
+current practice and does not scale. </p>
+
+<p> It is regrettably the case that TLS <i>secure-channels</i>
+(fully authenticated and immune to man-in-the-middle attacks) impose
+constraints on the sending and receiving sites that preclude ubiquitous
+deployment. One needs to manually configure this type of security for
+each destination domain, and in many cases implement non-default TLS
+<a href="#client_tls_policy">policy table</a> entries for additional
+domains hosted at a common secured destination. With Postfix 2.3, we
+make secure-channel configurations substantially easier to configure,
+but they will never be the norm. For the generic domain with which you
+have made no specific security arrangements, this security level is not
+a good fit. </p>
+
+<p> Given that strong authentication is not generally possible, and that
+verifiable certificates cost time and money, many servers that implement
+TLS use self-signed certificates or private CAs. This further limits
+the applicability of verified TLS on the public Internet. </p>
+
+<p> Historical note: while the documentation of these issues and many of the
+related features are new with Postfix 2.3, the issue was well
+understood before Postfix 1.0, when Lutz J&auml;nicke was designing
+the first unofficial Postfix TLS patch. See his original post <a
+href="http://www.imc.org/ietf-apps-tls/mail-archive/msg00304.html">http://www.imc.org/ietf-apps-tls/mail-archive/msg00304.html</a>
+and the first response <a
+href="http://www.imc.org/ietf-apps-tls/mail-archive/msg00305.html">http://www.imc.org/ietf-apps-tls/mail-archive/msg00305.html</a>.
+The problem is not even unique to SMTP or even TLS, similar issues exist
+for secure connections via aliases for HTTPS and Kerberos. SMTP merely
+uses indirect naming (via MX records) more frequently. </p>
+
+<h3><a name="client_tls_levels"> Client TLS security levels </a>
+</h3>
+
+<p> The TLS security levels listed below are described in more detail
+in the sections that follow.</p>
+
+<dl>
+<dt><b>none</b></dt>
+<dd><a href="#client_tls_none">No TLS.</a></dd>
+<dt><b>may</b></dt>
+<dd><a href="#client_tls_may">Opportunistic TLS.</a></dd>
+<dt><b>encrypt</b></dt>
+<dd><a href="#client_tls_encrypt">Mandatory TLS encryption.</a>
+<dt><b>verify</b></dt>
+<dd><a href="#client_tls_verify">Mandatory server certificate verification.</a>
+<dt><b>secure</b></dt>
+<dd><a href="#client_tls_secure">Secure-channel TLS.</a>
+</dl>
+
+<h3><a name="client_tls_none"> Disabling TLS in the SMTP/LMTP client </a>
+</h3>
 
-<p> By default, TLS is disabled in the Postfix SMTP client, so no
-difference to plain Postfix is visible.  If you enable TLS, the
-Postfix SMTP client will send STARTTLS when TLS support is announced
-by the remote SMTP server. </p>
+<p> At the "none" TLS security level, TLS encryption is
+disabled. This is the default security level. With Postfix 2.3 and later,
+it can be configured explicitly by setting "smtp_tls_security_level&nbsp;=&nbsp;none". </p>
 
-<p> When the server accepts the STARTTLS command, but the subsequent
-TLS handshake fails, and no other server is available, the Postfix SMTP
-client defers the delivery attempt, and the mail stays in the queue. After
-a handshake failure, the communications channel is in an indeterminate
-state and cannot be used for non-TLS deliveries. </p>
+<p> With Postfix 2.2 and earlier, or when smtp_tls_security_level is set to
+its default (backwards compatible) empty value, the appropriate configuration
+settings are "smtp_use_tls&nbsp;=&nbsp;no" and "smtp_enforce_tls&nbsp;=&nbsp;no".
+With either approach, TLS is not used even if supported by the server.
+For LMTP, use the corresponding "lmtp_" parameters. </p>
+
+<p> Per destination settings may override this default setting, in which case
+TLS is used selectively, only with destinations explicitly configured
+for TLS. </p>
+
+<p> You can disable TLS for a subset of destinations, while leaving
+it enabled for the rest. With the Postfix 2.3+ TLS <a
+href="#client_tls_policy">policy table</a>, specify the "none"
+security level. With the obsolete <a href="#client_tls_obs">per-site</a>
+table, specify the "NONE" keyword. </p>
+
+<h3><a name="client_tls_may"> Opportunistic TLS </a>
+</h3>
+
+<p> At the "may" TLS security level, TLS encryption is <i>opportunistic</i>.
+The SMTP transaction is encrypted if the STARTTLS ESMTP feature
+is supported by the server. Otherwise, messages are sent in the clear.
+With Postfix 2.3 and later, opportunistic TLS can be configured by
+setting "smtp_tls_security_level&nbsp;=&nbsp;may".
+
+<p> Since sending in the clear is acceptable, demanding stronger
+than default TLS security merely reduces inter-operability.  For
+this reason, Postfix 2.3 and later ignore the smtp_tls_mandatory_ciphers
+and smtp_tls_mandatory_protocols parameters at the "may"
+security level: all protocols are allowed, and "export" grade or
+better ciphers are used. </p>
+
+<p> With Postfix 2.2 and earlier, or when smtp_tls_security_level is
+set to its default (backwards compatible) empty value, the appropriate
+configuration settings are "smtp_use_tls&nbsp;=&nbsp;yes" and
+"smtp_enforce_tls&nbsp;=&nbsp;no".
+For LMTP use the corresponding "lmtp" parameters. </p>
+
+<p> With opportunistic TLS, mail delivery continues even if the
+server certificate is untrusted or bears the wrong name.  Starting
+with Postfix 2.3, when the TLS handshake fails for an opportunistic
+TLS session, rather than give up on mail delivery, the transaction
+is retried with TLS disabled. Trying an unencrypted connection makes
+it possible to deliver mail to sites with non-interoperable server
+TLS implementations. </p>
+
+<p> Opportunistic encryption is never used for LMTP over UNIX-domain
+sockets. The communications channel is already confidential without
+TLS, so the only potential benefit of TLS is authentication. Do not
+configure opportunistic TLS for LMTP deliveries over UNIX-domain sockets.
+Only configure TLS for LMTP over UNIX-domain sockets at the
+<a href="#client_tls_encrypt">encrypt</a> security level or higher.
+Attempts to configure opportunistic encryption of LMTP sessions will
+be ignored with a warning written to the mail logs. </p>
+
+<p> You can enable opportunistic TLS just for selected destinations. With
+the Postfix 2.3+ TLS <a href="#client_tls_policy">policy table</a>,
+specify the "may" security level. With the obsolete <a
+href="#client_tls_obs">per-site</a> table, specify the "MAY" keyword.</p>
+
+<p> This is the most common security level for TLS protected SMTP
+sessions, stronger security is not generally available and, if needed,
+is typically only configured on a per-destination basis. See the section
+on TLS <a href="#client_tls_limits">limitations</a> above. </p>
 
 <p> Example: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_security_level = may
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax: </p>
+
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
     smtp_use_tls = yes
+    smtp_enforce_tls = no
 </pre>
 </blockquote>
 
-<h3><a name="client_tls_require"> Requiring TLS encryption </a>
+<h3><a name="client_tls_encrypt"> Mandatory TLS encryption </a>
 </h3>
 
-<p> You can ENFORCE the use of TLS, so that the Postfix SMTP client
-will not deliver mail over unencrypted connections.  In this mode,
-the remote SMTP server hostname must match the information in the
-remote server certificate, and the server certificate must be issued
-by a CA that is trusted by the Postfix SMTP client.  If the remote
-server certificate doesn't verify or the remote SMTP server hostname
-doesn't match, and no other server is available, the delivery
-attempt is deferred and the mail stays in the queue.  </p>
-
-<p> The remote SMTP server hostname is verified against all names
-provided as dNSNames
-in the SubjectAlternativeName. If no dNSNames are specified, the
-CommonName is checked.  Verification may be turned off with the
-smtp_tls_enforce_peername option which is discussed below. </p>
-
-<p> Enforcing the use of TLS is useful if you know that you will
-only
-connect to servers that support RFC 2487 _and_ that present server
-certificates that meet the above requirements.  An example would
-be a client only sends email to one specific mailhub that offers
-the necessary STARTTLS support.  </p>
+<p> At the "encrypt" TLS security level, messages are sent only
+over TLS encrypted sessions. The SMTP transaction is aborted unless
+the STARTTLS ESMTP feature is supported by the server. If no
+suitable servers are found, the message will be deferred. With Postfix
+2.3 and later, mandatory TLS encryption can be configured by setting
+"smtp_tls_security_level&nbsp;=&nbsp;encrypt". Even though TLS encryption
+is always used, mail delivery continues if the server certificate is
+untrusted or bears the wrong name. </p>
+
+<p> At this security level and higher, the smtp_tls_mandatory_protocols
+and smtp_tls_mandatory_ciphers configuration parameters determine
+the list of sufficiently secure SSL protocol versions and the minimum
+cipher strength. If the protocol or cipher requirements are not
+met, the mail transaction is aborted.  The documentation for these
+parameters includes useful interoperability and security guidelines.
+</p>
+
+<p> With Postfix 2.2 and earlier, or when smtp_tls_security_level
+is set to its default (backwards compatible) empty value, the
+appropriate configuration settings are "smtp_enforce_tls&nbsp;=&nbsp;yes"
+and "smtp_tls_enforce_peername&nbsp;=&nbsp;no". For LMTP use the corresponding
+<i>lmtp_</i> parameters. </p>
+
+<p> Despite the potential for eliminating passive eavesdropping attacks,
+mandatory TLS encryption is not viable as a default security level for
+mail delivery to the public Internet. Most MX hosts do not support TLS at
+all, and some of those that do have broken implementations. On a host
+that delivers mail to the Internet, you should not configure mandatory
+TLS encryption as the default security level. </p>
+
+<p> You can enable mandatory TLS encryption just for specific destinations.
+With the Postfix 2.3+ TLS <a href="#client_tls_policy">policy
+table</a>, specify the "encrypt" security level. With the
+obsolete <a href="#client_tls_obs">per-site</a> table, specify the
+"MUST_NOPEERMATCH" keyword. While the obsolete approach still works
+with Postfix 2.3, it is strongly discouraged: users of Postfix 2.3+
+should use the new TLS policy settings. </p>
+
+<p> Examples: </p>
+
+<p> In the example below, traffic to <i>example.com</i> and its sub-domains
+via the corresponding MX hosts always uses TLS. The protocol version will be
+"SSLv3" or "TLSv1" (the default setting of smtp_tls_mandatory_protocols
+excludes "SSLv2"). Only high or medium strength (i.e. 128 bit or
+better) ciphers will be used by default for all "encrypt" security
+level sessions. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+
+/etc/postfix/tls_policy:
+    example.com       encrypt
+    .example.com      encrypt
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax (no support for sub-domains without resorting to
+regexp tables). With Postfix 2.3+, do not use the obsolete <a
+href="#client_tls_obs">per-site</a> table. </p>
 
-<p> Example: </p>
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtp_enforce_tls = yes
+    smtp_tls_per_site = hash:/etc/postfix/tls_per_site
+
+/etc/postfix/tls_per_site:
+    example.com       MUST_NOPEERMATCH
 </pre>
 </blockquote>
 
-<h3> <a name="client_tls_nopeer"> Disabling server certificate
-verification </a> </h3>
-
-<p> As of RFC 2487 the requirements for hostname checking for MTA
-clients are not set. When TLS is required (smtp_enforce_tls = yes),
-the option smtp_tls_enforce_peername can be set to "no" to disable
-strict remote SMTP server hostname checking. In this case, the mail
-delivery will proceed regardless of the CommonName etc. listed in
-the certificate. </p>
-
-<p>  Despite the potential for eliminating "man-in-the-middle" and
-other attacks, mandatory certificate/peername verification is not
-viable as a default Internet mail delivery policy at this time.  A
-significant fraction of TLS enabled MTAs uses self-signed certificates,
-or certificates that are signed by a private certificate authority.
-On a machine that delivers mail to the Internet, if you set
-smtp_enforce_tls = yes, you should probably also set
-smtp_tls_enforce_peername = no.  You can use the per-site TLS
-policies (see below) to enable full peer verification for specific
-destinations that are known to have verifiable TLS server certificates.
+<p> In the next example, secure message submission is configured
+via the MSA "<tt>[example.net]:587</tt>". TLS sessions are encrypted
+without authentication, because this MSA does not possess an acceptable
+certificate. This MSA is known to be capable of "TLSv1" and "high" grade
+ciphers, so these are selected via the <a href="#client_tls_policy">policy
+table</a>. </p>
+
+<p><b>Note:</b> the policy table lookup key is the verbatim next-hop
+specification from the recipient domain, transport(5) table or relayhost
+parameter, with any enclosing square brackets and optional port. Take
+care to be consistent: the suffixes ":smtp" or ":25" or no port suffix
+result in different policy table lookup keys, even though they are
+functionally equivalent nexthop specifications. Use at most one of these
+forms for all destinations. Below, the policy table has multiple keys,
+just in case the transport table entries are not specified consistently. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+
+/etc/services:
+    submission      587/tcp         msa             # mail message submission
+
+/etc/postfix/tls_policy:
+    [example.net]:587 encrypt protocols=TLSv1 ciphers=high
+    [example.net]:msa encrypt protocols=TLSv1 ciphers=high
+    [example.net]:submission encrypt protocols=TLSv1 ciphers=high
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax: </p>
+
+<p> <b>Note:</b> Avoid policy lookups with the bare hostname (for
+example, "example.net").  Instead,
+use the destination (for example, "[example.net]:587"), as the <a
+href="#client_tls_obs">per-site</a> table lookup key (a recipient domain
+or MX-enabled transport nexthop with no port suffix may look like a bare
+hostname, but is still a suitable <i>destination</i>). With Postfix 2.3+,
+do not use the obsolete <a href="#client_tls_obs">per-site</a> table;
+use the new <a href="#client_tls_policy">policy table</a> instead. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_per_site = hash:/etc/postfix/tls_per_site
+
+/etc/postfix/tls_per_site:
+    [example.net]:587   MUST_NOPEERMATCH
+</pre>
+</blockquote>
+
+<h3><a name="client_tls_verify"> Mandatory server certificate verification </a>
+</h3>
+
+<p> At the "verify" TLS security level, messages are sent only
+over TLS encrypted sessions for which server certificate verification
+succeeds. If no suitable servers are found, the message will be
+deferred. With Postfix 2.3 and later, mandatory server certificate
+verification can be configured by setting
+"smtp_tls_security_level&nbsp;=&nbsp;verify", the
+smtp_tls_verify_cert_match parameter can override the default
+"hostname" certificate match strategy. Fine-tuning the matching
+strategy is generally only appropriate for <a
+href="#client_tls_secure">secure-channel</a> destinations. </p>
+
+<p> With Postfix 2.2 and earlier, or when smtp_tls_security_level
+is set to its default (backwards compatible) empty value, the
+appropriate configuration settings are "smtp_enforce_tls&nbsp;=&nbsp;yes" and
+"smtp_tls_enforce_peername&nbsp;=&nbsp;yes". For LMTP use the corresponding
+<i>lmtp_</i> parameters. </p>
+
+<p> If the server certificate chain is trusted (see smtp_tls_CAfile
+and smtp_tls_CApath), any DNS names in the SubjectAlternativeName
+certificate extension are used to verify the server name.  If no
+DNS names are specified, the certificate CommonName is checked.
+If you want mandatory encryption without server certificate
+verification, see <a href="#client_tls_encrypt">above</a>. </p>
+
+<p> Despite the potential for eliminating "man-in-the-middle" and other
+attacks, mandatory certificate trust chain and subject name verification
+is not viable as a default Internet mail delivery policy.  Most MX hosts
+do not support TLS at all, and a significant portion of TLS enabled
+MTAs use self-signed certificates, or certificates that are signed by
+a private certificate authority. On a machine that delivers mail to
+the Internet, you should not configure mandatory server certificate
+verification as a default policy. </p>
+
+<p> Mandatory server certificate verification as a default security
+level may be appropriate if you know that you will only connect to
+servers that support RFC 2487 <i>and</i> that present verifiable
+server certificates. An example would be a client that sends all
+email to a central mailhub that offers the necessary STARTTLS
+support. In such cases, you can often use a <a
+href="#client_tls_secure">secure-channel</a> configuration instead.
 </p>
 
+<p> You can enable mandatory server certificate verification just
+for specific destinations.  With the Postfix 2.3+ TLS <a
+href="#client_tls_policy">policy table</a>, specify the "verify"
+security level. With the obsolete <a href="#client_tls_obs">per-site</a>
+table, specify the "MUST" keyword.  While the obsolete approach
+still works with Postfix 2.3, it is strongly discouraged: users of
+Postfix 2.3+ should use the new TLS policy settings. </p>
+
 <p> Example: </p>
+
+<p> In this example, the client encrypts all traffic to the
+<i>example.com</i> domain. The peer hostname is verified, but
+verification is vulnerable to DNS response forgery. Mail transmission
+to <i>example.com</i> recipients uses "high" grade ciphers. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    indexed = ${default_database_type}:${config_directory}/
+    smtp_tls_CAfile = ${config_directory}/CAfile.pem
+    smtp_tls_policy_maps = ${indexed}tls_policy
+
+/etc/postfix/tls_policy:
+    example.com       verify ciphers=high
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 syntax: </p>
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    indexed = ${default_database_type}:${config_directory}/
+    smtp_tls_CAfile = ${config_directory}/CAfile.pem
+    smtp_tls_per_site = ${indexed}tls_per_site
+
+/etc/postfix/tls_per_site:
+    example.com         MUST
+</pre>
+</blockquote>
+
+<h3><a name="client_tls_secure"> Secure server certificate verification </a>
+</h3>
+
+<p> At the <i>secure</i> TLS security level, messages are sent only over
+<i>secure-channel</i> TLS sessions where DNS forgery resistant server
+certificate verification succeeds. If no suitable servers are found, the
+message will be deferred. With Postfix 2.3 and later, secure-channels
+can be configured by setting "smtp_tls_security_level&nbsp;=&nbsp;secure".
+The smtp_tls_secure_cert_match parameter can override the default
+"nexthop, dot-nexthop" certificate match strategy. </p>
+
+<p> With Postfix 2.2 and earlier, or when smtp_tls_security_level
+is set to its default (backwards compatible) empty value, the
+appropriate configuration settings are "smtp_enforce_tls&nbsp;=&nbsp;yes"
+and "smtp_tls_enforce_peername&nbsp;=&nbsp;yes" with additional settings to
+<a href="#client_tls_harden">harden</a> peer certificate verification
+against forged DNS data. For LMTP, use the corresponding <i>lmtp_</i>
+parameters. </p>
+
+<p> If the server certificate chain is trusted (see smtp_tls_CAfile and
+smtp_tls_CApath), any DNS names in the SubjectAlternativeName certificate
+extension are used to verify the server name. If no DNS names are
+specified, the CommonName is checked. If you want mandatory encryption
+without server certificate verification, see <a
+href="#client_tls_encrypt">above</a>. </p>
+
+<p> Despite the potential for eliminating "man-in-the-middle" and other
+attacks, mandatory secure server certificate verification is not
+viable as a default Internet mail delivery policy.  Most MX hosts
+do not support TLS at all, and a significant portion of TLS enabled
+MTAs use self-signed certificates, or certificates that are signed
+by a private certificate authority. On a machine that delivers mail
+to the Internet, you should not configure secure TLS verification
+as a default policy. </p>
+
+<p> Mandatory secure server certificate verification as a default
+security level may be appropriate if you know that you will only
+connect to servers that support RFC 2487 <i>and</i> that present
+verifiable server certificates. An example would be a client that
+sends all email to a central mailhub that offers the necessary
+STARTTLS support. </p>
+
+<p> You can enable secure TLS verification just for specific destinations.
+With the Postfix 2.3+ TLS <a href="#client_tls_policy">policy table</a>,
+specify the "secure" security level. With the obsolete
+<a href="#client_tls_obs">per-site</a> table, specify the "MUST"
+keyword and <a href="#client_tls_harden">harden</a> the certificate
+verification against DNS forgery. While the obsolete approach still
+works with Postfix 2.3, it is strongly discouraged: users of Postfix 2.3+
+should use the new TLS policy settings. </p>
+
+<p> Examples: </p>
+
+<p> Secure-channel TLS without transport(5) table overrides: </p>
+
+<p> The client will encrypt all traffic and verify the destination name
+immune from forged DNS responses. MX lookups are still used to find
+the SMTP servers for <i>example.com</i>, but these are not used when
+checking the names in the server certificate(s). Rather, the requirement
+is that the MX hosts for <i>example.com</i> have trusted certificates
+with a subject name of <i>example.com</i> or a sub-domain, see the
+documentation for the smtp_tls_secure_cert_match parameter. </p>
+
+<p> The related domains <i>example.co.uk</i> and <i>example.co.jp</i> are
+hosted on the same MX hosts as the primary <i>example.com</i> domain, and
+traffic to these is secured by verifying the primary <i>example.com</i>
+domain in the server certificates. This frees the server administrator
+from needing the CA to sign certificates that list all the secondary
+domains. The downside is that clients that want secure channels to the
+secondary domains need explicit TLS <a href="#client_tls_policy">policy
+table</a> entries. </p>
+
+<p> Note, there are two ways to handle related domains.  The first is to
+use the default routing for each domain, but add policy table entries
+to override the expected certificate subject name.  The second is to
+override the next-hop in the transport table, and use a single policy
+table entry for the common nexthop.  We choose the first approach,
+because it works better when domain ownership changes. With the second
+approach we securely deliver mail to the wrong destination, with the
+first approach, authentication fails and mail stays in the local queue,
+the first approach is more appropriate in most cases. <p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_CAfile = /etc/postfix/CAfile.pem
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+
+/etc/postfix/transport:
+
+/etc/postfix/tls_policy:
+    example.com     secure
+    example.co.uk   secure match=example.com:.example.com
+    example.co.jp   secure match=example.com:.example.com
+</pre>
+</blockquote>
+
+<p> Secure-channel TLS with transport(5) table overrides: <p>
+
+<p> In this case traffic to <i>example.com</i> and its related domains
+is sent to a single logical gateway (to avoid a single point of failure,
+its name may resolve to one or more load-balancer addresses, or to the
+combined addresses of multiple physical hosts). All the physical hosts
+reachable via the gateway's IP addresses have the logical gateway name
+listed in their certificates. This secure-channel configuration can also
+be implemented via a <a href="#client_tls_harden">hardened</a> variant of
+the MUST policy in the obsolete <a href="#client_tls_obs">per-site</a>
+table. As stated above, this approach has the potential to mis-deliver
+email if the related domains change hands. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_CAfile = /etc/postfix/CAfile.pem
+    transport_maps = hash:/etc/postfix/transport
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+
+/etc/postfix/transport:
+    example.com     smtp:[tls.example.com]
+    example.co.uk   smtp:[tls.example.com]
+    example.co.jp   smtp:[tls.example.com]
+
+/etc/postfix/tls_policy:
+    [tls.example.com] secure match=tls.example.com
+</pre>
+</blockquote>
+
+<p> Postfix 2.2.9+ syntax: </p>
+
+<p> <b>Note:</b> Avoid policy lookups with the bare hostname (for
+example, "tls.example.com").  Instead, use the destination (for
+example, "[tls.example.com]") as the <a
+href="#client_tls_obs">per-site</a> table lookup key (a recipient domain
+or MX-enabled transport nexthop with no port suffix may look like a bare
+hostname, but is still a suitable <i>destination</i>). With Postfix 2.3+,
+do not use the obsolete <a href="#client_tls_obs">per-site</a> table;
+use the new <a href="#client_tls_policy">policy table</a> instead. </p>
+
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtp_enforce_tls = yes
-    smtp_tls_enforce_peername = no
+    smtp_cname_overrides_servername = no
+    smtp_tls_CAfile = /etc/postfix/CAfile.pem
+    transport_maps = hash:/etc/postfix/transport
+    smtp_tls_per_site = hash:/etc/postfix/tls_per_site
+
+/etc/postfix/transport:
+    example.com     smtp:[tls.example.com]
+    example.co.uk   smtp:[tls.example.com]
+    example.co.jp   smtp:[tls.example.com]
+
+/etc/postfix/tls_per_site:
+    [tls.example.com]       MUST
 </pre>
 </blockquote>
 
-<h3> <a name="client_tls_per_site"> Per-site TLS policies </a> </h3>
+<h3> <a name="client_tls_policy"> TLS policy table </a>
+</h3>
+
+<p> Postfix 2.3 introduces a new more flexible TLS policy table. For
+earlier releases, read the description of the obsolete Postfix 2.2 <a
+href="#client_tls_obs">per-site</a> table. </p>
 
 <p> A small fraction of servers offer STARTTLS but the negotiation
-consistently fails, leading to mail aging out of the queue and
-bouncing back to the sender. In such cases, you can use the per-site
-policies to disable TLS for the problem sites.  Alternatively, you
-can enable TLS for just a few specific sites and not enable it for
-all sites. </p>
+consistently fails. With Postfix 2.3, so long as encryption is not
+enforced, the delivery is immediately retried with TLS disabled.  You no
+longer need to explicitly disable TLS for the problem destinations.
+As soon as their TLS software or configuration is repaired, encryption
+will be used. </p>
+
+<p> The new policy table is specified via the smtp_tls_policy_maps
+parameter. This lists optional lookup tables with the Postfix SMTP client
+TLS security policy by next-hop destination. It supersedes the obsolete
+smtp_tls_per_site parameter. When $smtp_tls_policy_maps is not empty,
+the smtp_tls_per_site parameter is ignored (a warning is written to the
+logs if it is also non-empty). </p>
+
+<p> The TLS policy table is indexed by the full next-hop destination,
+which is either the recipient domain, or the verbatim next-hop
+specified in the transport table, $local_transport, $virtual_transport,
+$relay_transport or $default_transport. This includes any enclosing
+square brackets and any non-default destination server port suffix. The
+<a href="#client_lmtp_tls">LMTP</a> socket type prefix (inet: or unix:)
+is not included in the lookup key. </p>
+
+<p> Only the next-hop domain, or $myhostname with LMTP over UNIX-domain
+sockets, is used as the nexthop name for certificate verification. The
+port and any enclosing square brackets are used in the table lookup key,
+but are not used for server name verification. </p>
+
+<p> When the lookup key is a domain name without enclosing square brackets
+or any <i>:port</i> suffix (typically the recipient domain), and the full
+domain is not found in the table, just as with the transport(5) table,
+the parent domain starting with a leading "." is matched recursively. This
+allows one to specify a security policy for a recipient domain and all
+its sub-domains. </p>
+
+<p> The lookup result is a security level, followed by an optional
+list of whitespace and/or comma separated name=value attributes
+that override related main.cf settings.  The TLS security <a
+href="#client_tls_levels">levels</a> are described above. Below, we
+describe the corresponding table syntax: </p>
+
+<dl>
+
+<dt><b>none</b></dt>         
+<dd>No TLS. No additional attributes are supported at this level. </dd>
+
+<dt><b>may</b></dt>
+<dd>Opportunistic TLS. No additional attributes are supported at this
+level. </dd>
+
+<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. At this level and
+higher the optional "ciphers" attribute overrides the main.cf
+smtp_tls_mandatory_ciphers parameter and the optional "protocols"
+keyword overrides the main.cf smtp_tls_mandatory_protocols parameter.
+In the policy table, multiple protocols must be separated by colons,
+as attribute values may not contain whitespace or commas.</dd>
+
+<dt><b>verify</b></dt>
+<dd>Mandatory server certificate verification. The optional "match"
+attribute overrides the main.cf smtp_tls_verify_cert_match parameter.
+In the policy table, multiple match patterns and strategies must
+be separated by colons. </dd>
+
+<dt><b>secure</b></dt> <dd>Secure-channel TLS. The optional "match"
+attribute overrides the main.cf smtp_tls_secure_cert_match parameter. In
+the policy table, multiple match patterns and strategies must be separated
+by colons. The match attribute is useful when additional domains are
+supported by common server, the policy entries for the additional domains
+specify matching rules for the primary domain certificate. While transport
+table overrides routing secondary domains to the primary nexthop also
+allow secure verification, they risk delivery to the wrong destination
+when domains change hands or are re-assigned to new gateways. With the
+"match" attribute approach, routing is not perturbed, and mail is deferred
+if verification of a new MX host fails. </dd>
+
+</dl>
+
+<p>
+Example:
+</p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+/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.net             secure
+    .example.net            secure match=.example.net:example.net
+    [mail.example.org]:587  secure match=nexthop
+</pre>
+</blockquote>
 
-<!-- insert new-style TLS policy mechanism here
+<p> <b>Note:</b> The "hostname" strategy if listed in a non-default setting
+of smtp_tls_secure_cert_match or in the "match" attribute in the policy
+table can render the "secure" level vulnerable to DNS forgery. Do not use
+the "hostname" strategy for <a href="#client_tls_secure">secure-channel</a>
+configurations in environments where DNS security is not assured. </p>
 
 <h3> <a name="client_tls_obs"> Obsolete per-site TLS policy support 
 </a> </h3>
 
 <p> This section describes an obsolete per-site TLS policy mechanism.
-Unlike the newer mechanism it supports TLS policy lookup by server
-hostname, and lacks control over what names can appear in server
-certificates.  Because of this, the obsolete mechanism is vulnerable
-to false DNS hostname information in MX or CNAME records.  These
-attacks can be eliminated only with great difficulty.  </p>
-
--->
-
-<p> The smtp_tls_per_site table is searched for a policy that matches
+Unlike the Postfix 2.3 <a href="#client_tls_policy">policy table</a>
+mechanism, this uses as a policy lookup key a potentially untrusted
+server hostname, and lacks control over what names can appear in
+server certificates.  Because of this, the obsolete mechanism is
+typically vulnerable to false DNS hostname information in MX or
+CNAME records.  These attacks can be eliminated only with great
+difficulty. The new <a href="#client_tls_policy">policy table</a>
+makes <a href="#client_tls_secure">secure-channel</a> configurations
+easier and provides more control over the cipher and protocol selection
+for sessions with mandatory encryption. </p>
+
+<p> Avoid policy lookups with the bare hostname.  Instead, use the
+full destination nexthop (enclosed in [] with a possible ":port"
+suffix) as the per-site table lookup key (a recipient domain or
+MX-enabled transport nexthop with no port suffix may look like a bare
+hostname, but is still a suitable <i>destination</i>).  With Postfix 2.3+,
+use of the obsolete approach documented here is strongly discouraged:
+use the new <a href="#client_tls_policy">policy table</a> instead. </p>
+
+<p> Starting with Postfix 2.3, the underlying TLS enforcement levels are
+common to the obsolete per-site table and the new policy table. The
+main.cf smtp_tls_mandatory_ciphers and smtp_tls_mandatory_protocols
+parameters control the TLS ciphers and protocols for mandatory
+encryption regardless of which table is used. The
+smtp_tls_verify_cert_match parameter determines the match strategy
+for the obsolete "MUST" keyword in the same way as for the "verify"
+level in the new policy. </p>
+
+<p> With Postfix &lt; 2.3, the obsolete smtp_tls_cipherlist parameter
+is also applied for opportunistic TLS sessions, and should be used with
+care, or not at all. Setting cipherlist restrictions that are incompatible
+with a remote SMTP server render that server unreachable, TLS handshakes
+are always attempted and always fail. </p>
+
+<p> When smtp_tls_policy_maps is empty (default) and smtp_tls_per_site
+is not empty, the per-site table is searched for a policy that matches
 the following information:  </p>
 
 <blockquote>
@@ -1073,17 +1776,19 @@ the following information:  </p>
 <dl>
 
 <dt> remote SMTP server hostname </dt> <dd> This is simply the DNS
-name of the server that the Postfix SMTP client connects to; this
-name may be obtained from other DNS lookups, such as MX lookups or
-CNAME lookups. </dd>
-
-<dt> next-hop destination </dt> <dd> This is normally the domain
-portion of the recipient address, but it may be overruled by
-information from the transport(5) table, from the relayhost parameter
-setting, or from the relay_transport setting. When it's not the
-recipient domain, the next-hop destination can have the Postfix-specific
-form "<tt>[name]</tt>", <tt>[name]:port</tt>", "<tt>name</tt>" or
-"<tt>name:port</tt>".  </dd>
+name of the server that the Postfix SMTP client connects to; this name
+may be obtained from other DNS lookups, such as MX lookups or CNAME
+lookups. Use of the hostname lookup key is discouraged; always use the
+next-hop destination instead. </dd>
+
+<dt> next-hop destination </dt> <dd> This is normally the domain portion
+of the recipient address, but it may be overridden by information from
+the transport(5) table, from the relayhost parameter setting, or from
+the relay_transport setting. When it is not the recipient domain, the
+next-hop destination can have the Postfix-specific form "<tt>[name]</tt>",
+"<tt>[name]:port</tt>", "<tt>name</tt>" or "<tt>name:port</tt>".  This is
+the recommended lookup key for per-site policy lookups (and incidentally
+for <a href="SASL_README.html#client_sasl">SASL password</a> lookups). </dd>
 
 </dl>
 
@@ -1103,34 +1808,27 @@ side specify one of the following keywords:  </p>
 
 <dl>
 
-<dt> NONE </dt> <dd> Don't use TLS at all. This overrides a less
-specific <b>MAY</b> lookup result from the alternate host or next-hop
-lookup key, and overrides the global smtp_use_tls, smtp_enforce_tls,
-and smtp_tls_enforce_peername settings. </dd>
-
-<dt> MAY </dt> <dd> Try to use TLS if the server announces support,
-otherwise use the unencrypted connection. This has less precedence
-than a more specific result (including <b>NONE</b>) from the alternate
-host or next-hop lookup key, and has less precedence than the more
-specific global "smtp_enforce_tls = yes" or "smtp_tls_enforce_peername
-= yes".  </dd>
-
-<dt> MUST_NOPEERMATCH </dt> <dd> Require TLS encryption, but do not
-require that the remote SMTP server hostname matches the information
-in the remote SMTP server certificate, or that the server certificate
-was issued by a trusted CA. This overrides a less secure <b>NONE</b>
-or a less specific <b>MAY</b> lookup result from the alternate host
-or next-hop lookup key, and overrides the global smtp_use_tls,
-smtp_enforce_tls and smtp_tls_enforce_peername settings.  </dd>
-
-<dt> MUST </dt> <dd> Require TLS encryption, require that the remote
-SMTP server hostname matches the information in the remote SMTP
-server certificate, and require that the remote SMTP server certificate
-was issued by a trusted CA. This overrides a less secure <b>NONE</b>
-and <b>MUST_NOPEERMATCH</b> or a less specific <b>MAY</b> lookup
+<dt> NONE </dt> <dd> No TLS. This overrides a less specific "MAY" lookup
 result from the alternate host or next-hop lookup key, and overrides
-the global smtp_use_tls, smtp_enforce_tls and smtp_tls_enforce_peername
-settings.  </dd>
+the global smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername
+settings. </dd>
+
+<dt> MAY </dt> <dd> Opportunistic TLS. This has less precedence than
+a more specific result (including "NONE") from the alternate host or
+next-hop lookup key, and has less precedence than the more specific global
+"smtp_enforce_tls&nbsp;=&nbsp;yes" or "smtp_tls_enforce_peername&nbsp;=&nbsp;yes".  </dd>
+
+<dt> MUST_NOPEERMATCH </dt> <dd> Mandatory TLS encryption. This
+overrides a less secure "NONE" or a less specific "MAY" lookup result
+from the alternate host or next-hop lookup key, and overrides the global
+smtp_use_tls, smtp_enforce_tls and smtp_tls_enforce_peername settings.
+</dd>
+
+<dt> MUST </dt> <dd> Mandatory server certificate verification.
+This overrides a less secure "NONE" and "MUST_NOPEERMATCH" or a
+less specific "MAY" lookup result from the alternate host or next-hop
+lookup key, and overrides the global smtp_use_tls, smtp_enforce_tls
+and smtp_tls_enforce_peername settings.  </dd>
 
 </dl>
 
@@ -1144,8 +1842,8 @@ policies can be summarized as follows: </p>
 <li> <p> When neither the remote SMTP server hostname nor the
 next-hop destination are found in the smtp_tls_per_site table, the
 policy is based on smtp_use_tls, smtp_enforce_tls and
-smtp_tls_enforce_peername. Note: "smtp_enforce_tls = yes" and
-"smtp_tls_enforce_peername = yes" imply "smtp_use_tls = yes". </p>
+smtp_tls_enforce_peername. Note: "smtp_enforce_tls&nbsp;=&nbsp;yes" and
+"smtp_tls_enforce_peername&nbsp;=&nbsp;yes" imply "smtp_use_tls&nbsp;=&nbsp;yes". </p>
 
 <li> <p> When both hostname and next-hop destination lookups produce
 a result, the more specific per-site policy (NONE, MUST, etc)
@@ -1154,60 +1852,83 @@ policy (MUST, etc) overrides the less secure one (NONE).  </p>
 
 <li> <p> After the per-site policy lookups are combined, the result
 generally overrides the global policy. The exception is the less
-specific <b>MAY</b> per-site policy, which is overruled by the more
-specific global "smtp_enforce_tls = yes" with server certificate
+specific "MAY" per-site policy, which is overruled by the more
+specific global "smtp_enforce_tls&nbsp;=&nbsp;yes" with server certificate
 verification as specified with the smtp_tls_enforce_peername
 parameter.  </p>
 
 </ul>
 
 <h3> <a name="client_tls_harden"> Closing a DNS loophole with 
-<!-- legacy --> per-site TLS policies </a> </h3>
+obsolete per-site TLS policies </a> </h3>
+
+<p> For a general discussion of TLS security for SMTP see <a
+href="#client_tls_limits">TLS limitations</a> above. What follows applies
+only to Postfix 2.2.9 and subsequent Postfix 2.2 patch levels. Do
+not use this approach with Postfix 2.3+; instead see the instructions under <a
+href="#client_tls_secure">secure</a> server certificate verification. </p>
 
 <p> As long as no secure DNS lookup mechanism is available, false
-hostnames in MX or CNAME responses can change the server hostname
-that Postfix uses for TLS policy lookup and server certificate
-verification. Even with a perfect match between the server hostname
-and the server certificate, there is no guarantee that Postfix is
-connected to the right server.  To avoid this loophole take the
-following steps: </p>
+hostnames in MX or CNAME responses can change Postfix's notion of the
+server hostname that is used for TLS policy lookup and server certificate
+verification. Even with a perfect match between the server hostname and
+the server certificate, there is no guarantee that Postfix is connected
+to the right server.  To avoid this loophole, take all of the following
+steps: </p>
 
-<ul>
+<ol>
+
+<li> <p> Use a dedicated transport for all secure-channel deliveries. </p>
 
 <li> <p> Eliminate MX lookups. Specify local transport(5) table
 entries for sensitive domains with explicit smtp:[<i>mailhost</i>]
 or smtp:[<i>mailhost</i>]:<i>port</i> destinations (you can assure
-security of this table unlike DNS); in the smtp_tls_per_site table
-specify the value <b>MUST</b> for the key [<i>mailhost</i>] or
+security of this table unlike DNS); in the smtp_tls_per_site
+table, specify the value "MUST" for the key [<i>mailhost</i>] or
 smtp:[<i>mailhost</i>]:<i>port</i>. This prevents false hostname
-information in DNS MX records from changing the server hostname
-that Postfix uses for TLS policy lookup and server certificate
+information in DNS MX records from changing Postfix's notion of the
+server hostname that is used for TLS policy lookup and server certificate
 verification. </p>
 
-<li> <p> Disallow CNAME hostname overrides. In main.cf specify
-"smtp_cname_overrides_servername = no". This prevents false hostname
+<li> <p> Disallow CNAME hostname overrides. In main.cf, specify
+"smtp_cname_overrides_servername&nbsp;=&nbsp;no". This prevents false hostname
 information in DNS CNAME records from changing the server hostname
 that Postfix uses for TLS policy lookup and server certificate
-verification. This feature requires Postfix 2.2.9 or later.  </p>
+verification. This feature requires Postfix 2.2.9 or later.  The
+default value is "no" starting with Postfix 2.3. </p>
 
-</ul>
+</ol>
 
 <p> Example: </p>
 
-<blockquote> <pre>
-/etc/postfix/main.cf:
-    smtp_tls_per_site = hash:/etc/postfix/tls_per_site
-    relayhost = [msa.example.net]:587
+<p> We give the <a href="postconf.5.html#default_transport">non-default</a>
+"securetls" transport an explicit master.cf process limit, so that we
+don't raise its process limit when raising $default_process_limit. The
+total process limit for *all* transports should stay somewhat under 1024
+(the typical select() file descriptor limit); otherwise transports may
+be throttled under steady high load, compounding congestion.  It is not
+uncommon at high volume sites to set the default process limit to 500
+or more. </p>
 
-/etc/postfix/tls_per_site:
-    # relayhost exact nexthop match
-    [msa.example.net]:587       MUST
+<p> We also default the "securetls" transport TLS security level to
+<a href="#client_tls_verify">MUST</a>, obviating the need for <a
+href="#client_tls_obs">per-site</a> table entries for secure-channel
+destinations. </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    transport_maps = hash:/etc/postfix/transport
 
-    # TLS should not be used with the <i>example.org</i> MX hosts.
-    example.org                 NONE
+/etc/postfix/transport:
+    example.com         securetls:[tls.example.com]
 
-    # TLS should not be used with the host <i>smtp.example.com</i>.
-    smtp.example.com             NONE
+/etc/postfix/master.cf:
+    securetls unix  -       -       n       -       100     smtp
+        -o smtp_connection_cache_on_demand=no
+        -o smtp_connection_cache_destinations=
+        -o smtp_enforce_tls=yes
+        -o smtp_tls_enforce_peername=yes
 </pre>
 </blockquote>
 
@@ -1229,7 +1950,7 @@ postfix/smtp[pid]: Host offered STARTTLS: [hostname.example.com]
 </blockquote>
 
 <p> Example: </p>
+
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
@@ -1242,8 +1963,8 @@ postfix/smtp[pid]: Host offered STARTTLS: [hostname.example.com]
 <p> When verifying a remote SMTP server certificate, a verification
 depth of 1 is sufficient if the certificate is directly issued by
 a CA specified with smtp_tls_CAfile or smtp_tls_CApath.  The default
-value of 5 should also suffice for longer chains (root CA issues
-special CA which then issues the actual certificate...) </p>
+value of 5 should also suffice for longer chains (where the root CA issues
+a special CA certificate which then issues the actual certificate). </p>
 
 <p> Example: </p>
  
@@ -1256,20 +1977,33 @@ special CA which then issues the actual certificate...) </p>
 
 <h3> <a name="client_cipher">Client-side cipher controls </a> </h3>
 
-<p> To influence the Postfix SMTP client cipher selection scheme,
-you can give cipherlist string.  A detailed description would go
-to far here; please refer to the OpenSSL documentation.  If you
-don't know what to do with it, simply don't touch it and leave the
-(openssl-)compiled in default! </p>
-
-<p> DO NOT USE " to enclose the string, specify just the string!!! </p>
+<p> The Postfix SMTP client supports 5 distinct cipher security levels
+as specified by the smtp_tls_mandatory_ciphers configuration
+parameter. This setting controls the minimum acceptable SMTP client
+TLS cipher grade for use with mandatory TLS encryption. The default
+value "medium" is suitable for most destinations with which you may
+want to enforce TLS, and is beyond the reach of today's crypt-analytic
+methods. See smtp_tls_policy_maps for information on how to configure
+ciphers on a per-destination basis. </p>
+
+<p> By default anonymous ciphers are allowed, and automatically
+disabled when server certificates are verified. If you
+want to disable even at the "encrypt" security level, set
+"smtp_tls_mandatory_exclude_ciphers&nbsp;=&nbsp;aNULL",
+to disable anonymous ciphers even with opportunistic TLS, set
+"smtp_tls_exclude_ciphers&nbsp;=&nbsp;aNULL". There is generally no
+need to take these measures. Anonymous ciphers save bandwidth and TLS
+session cache space, if certificates are ignored, there is little point
+in requesting them. </p>
 
 <p> Example: </p>
  
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtp_tls_cipherlist = DEFAULT
+    smtp_tls_mandatory_ciphers = medium
+    smtp_tls_mandatory_exclude_ciphers = RC4, MD5
+    smtp_tls_exclude_ciphers = aNULL
 </pre>
 </blockquote>
 
@@ -1460,7 +2194,7 @@ steps ago. </p>
 <blockquote>
 <pre>
 % <b>openssl ca -out FOO-cert.pem -infiles FOO-req.pem</b>
-Uing configuration from /etc/ssl/openssl.cnf
+Using configuration from /etc/ssl/openssl.cnf
 Enter PEM pass phrase:<b>whatever</b>
 Check that the request matches the signature
 Signature ok
@@ -1494,20 +2228,26 @@ super-user privileges. </p>
 </blockquote>
 
 <li> <p> Configure Postfix, by adding the following to
-<tt>/etc/postfix/main.cf </tt>. </p>
+<tt>/etc/postfix/main.cf </tt>. It is generally best to not configure
+client certificates, unless there are servers which authenticate your mail
+submission via client certificates. Often servers that perform TLS client
+authentication will issue the required certificates signed by their own
+CA. If you configure the client certificate and key incorrectly, you
+will be unable to send mail to sites that request client certificate,
+but don't require them from all clients. </p>
 
 <blockquote>
 <pre>
 smtp_tls_CAfile = /etc/postfix/cacert.pem
-smtp_tls_cert_file = /etc/postfix/FOO-cert.pem
-smtp_tls_key_file = /etc/postfix/FOO-key.pem
-smtp_tls_session_cache_database = btree:/var/run/smtp_tls_session_cache
+smtp_tls_session_cache_database =
+    btree:/var/spool/postfix/smtp_tls_session_cache
 smtp_use_tls = yes
 smtpd_tls_CAfile = /etc/postfix/cacert.pem
 smtpd_tls_cert_file = /etc/postfix/FOO-cert.pem
 smtpd_tls_key_file = /etc/postfix/FOO-key.pem
 smtpd_tls_received_header = yes
-smtpd_tls_session_cache_database = btree:/var/run/smtpd_tls_session_cache
+smtpd_tls_session_cache_database =
+    btree:/var/spool/postfix/smtpd_tls_session_cache
 smtpd_use_tls = yes
 tls_random_source = dev:/dev/urandom
 </pre>
@@ -1542,7 +2282,7 @@ lines of code) is not included with Postfix. </p>
 of several kbytes or more, and that implements the sequence operation.
 In most cases, btree databases should be adequate.  </p>
 
-<p> NOTE:  You cannot use dbm databases. TLS session objects
+<p> NOTE:  You cannot use DBM databases. TLS session objects
 are too large. </p>
 
 <li> <p> master.cf: Specify "unix" instead of "fifo" as
@@ -1554,14 +2294,14 @@ generation (PRNG) pool, and in order to access the TLS session
 cache databases. Such a protocol cannot be run across fifos. </p>
 
 <li> <p> smtp_tls_per_site: the MUST_NOPEERMATCH per-site policy
-cannot override the global "smtp_tls_enforce_peername = yes" setting.
+cannot override the global "smtp_tls_enforce_peername&nbsp;=&nbsp;yes" setting.
 </p>
 
 <li> <p> smtp_tls_per_site: a combined (NONE + MAY) lookup result
 for (hostname and next-hop destination) produces counter-intuitive
 results for different main.cf settings.  TLS is enabled with
-"smtp_tls_enforce_peername = no", but it is disabled when both
-"smtp_enforce_tls = yes" and "smtp_tls_enforce_peername = yes".
+"smtp_tls_enforce_peername&nbsp;=&nbsp;no", but it is disabled when both
+"smtp_enforce_tls&nbsp;=&nbsp;yes" and "smtp_tls_enforce_peername&nbsp;=&nbsp;yes".
 </p>
 
 </ul>
index 51b3f80e265a46293f29f6945c9fc4944bab6fde..7491acd6204632b72fde608db029d5795a62b02d 100644 (file)
 # REJECT ACTIONS
 # .ad
 # .fi
-#      Postfix version 2.3 and later support enhanced status codes.
+#      Postfix version 2.3 and later support enhanced status codes
+#      as defined in RFC 3463.
 #      When no code is specified at the beginning of the \fItext\fR
 #      below, Postfix inserts a default enhanced status code of "5.7.1"
 #      in the case of reject actions, and "4.7.1" in the case of
 #      Reject the address etc. that matches the pattern, and respond with
 #      the numerical three-digit code and text. \fB4\fINN\fR means "try
 #      again later", while \fB5\fINN\fR means "do not try again".
+# .IP
+#      The reply code "421" causes Postfix to disconnect immediately
+#      (Postfix version 2.3 and later).
 # .IP "\fBREJECT \fIoptional text...\fR
 #      Reject the address etc. that matches the pattern. Reply with
 #      \fI$reject_code optional text...\fR when the optional text is
 # ENHANCED STATUS CODES
 # .ad
 # .fi
+#      Postfix version 2.3 and later support enhanced status codes
+#      as defined in RFC 3463.
 #      When an enhanced status code is specified in an access
 #      table, it is subject to modification. The following
 #      transformations are needed when the same access table is
index 9dbcabb7a8bd593ae4a0e7600e14e98b1ccaffd5..1e5210979deb83f8d9f3117d19ea0d2ebb5774a2 100644 (file)
@@ -3097,6 +3097,11 @@ filter. </dd>
 <dd>Disable header/body_checks. This is typically specified AFTER
 an external content filter. </dd>
 
+<dt><b><a name="no_milters">no_milters</a></b></dt>
+
+<dd>Disable Milter (mail filter) applications. This is typically
+specified AFTER an external content filter. </dd>
+
 </dl>
 
 <p>
@@ -8055,6 +8060,23 @@ system.  </p>
 <p> File with the Postfix SMTP server RSA certificate in PEM format.
 This file may also contain the server private key. </p>
 
+<p> Public Internet MX hosts without certificates signed by a "reputable"
+CA must generate, and be prepared to present to most clients, a
+self-signed or private-CA signed certificate. The client will not be
+able to authenticate the server, but unless it is running Postfix 2.3 or
+similar software, it will still insist on a server certificate. </p>
+
+<p> For servers that are <b>not</b> public Internet MX hosts, Postfix
+2.3 supports configurations with no certificates. This entails the use
+of just the anonymous TLS ciphers, which are not supported by typical
+SMTP clients. Since such clients will not, as a rule, fall back to plain
+text after a TLS handshake failure, the server will be unable to receive
+email from TLS enabled clients. To avoid accidental configurations with
+no certificates, Postfix 2.3 enables certificate-less operation only
+when the administrator explicitly sets "smtpd_tls_cert_file = none". This
+ensures that new Postfix configurations with just "smtpd_use_tls = yes"
+added, will not accidentally run with no certificates. </p>
+
 <p> Both RSA and DSA certificates are supported.  When both types
 are present, the cipher used determines which certificate will be
 presented to the client.  For Netscape and OpenSSL clients without
@@ -8085,6 +8107,8 @@ certificate and hence pass the "openssl verify -purpose sslserver
 smtpd_tls_cert_file = /etc/postfix/server.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_key_file $smtpd_tls_cert_file
 
 <p> File with the Postfix SMTP server RSA private key in PEM format.
@@ -8108,6 +8132,8 @@ This file may also contain the server private key. <p>
 smtpd_tls_dcert_file = /etc/postfix/server-dsa.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_dkey_file $smtpd_tls_dcert_file
 
 <p> File with the Postfix SMTP server DSA private key in PEM format.
@@ -8117,6 +8143,8 @@ with $smtpd_tls_dcert_file. </p>
 <p> The private key must not be encrypted. In other words, the key
 must be accessible without password. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_CAfile
 
 <p> The file with the certificate of the certification authority
@@ -8132,6 +8160,8 @@ list of trusted CAs if you want to use chroot-mode. </p>
 smtpd_tls_CAfile = /etc/postfix/CAcert.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_CApath
 
 <p> Directory with PEM format certificate authority certificates
@@ -8152,6 +8182,8 @@ feature is therefore not recommended. </p>
 smtpd_tls_CApath = /etc/postfix/certs
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_loglevel 0
 
 <p> Enable additional Postfix SMTP server logging of TLS activity.
@@ -8177,6 +8209,8 @@ transmission after STARTTLS. </dd>
 <p> Use "smtpd_tls_loglevel = 3" only in case of problems. Use of
 loglevel 4 is strongly discouraged. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_received_header no
 
 <p> Request that the Postfix SMTP server produces Received:  message
@@ -8186,6 +8220,8 @@ CommonName.  This is disabled by default, as the information may
 be modified in transit through other mail servers.  Only information
 that was recorded by the final destination can be trusted. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_use_tls no
 
 <p> Opportunistic mode: announce STARTTLS support to SMTP clients,
@@ -8195,6 +8231,8 @@ but do not require that clients use TLS encryption. </p>
 STARTTLS due to insufficient privileges to access the server private
 key. This is intended behavior. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_enforce_tls no
 
 <p> Enforcement mode: announce STARTTLS support to SMTP clients,
@@ -8209,6 +8247,8 @@ dedicated servers. </p>
 STARTTLS due to insufficient privileges to access the server private  
 key. This is intended behavior. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_wrappermode no
 
 <p> Run the Postfix SMTP server in the non-standard "wrapper" mode,
@@ -8219,6 +8259,8 @@ master.cf, and specify "-o smtpd_tls_wrappermode=yes" on the SMTP
 server's command line. Port 465 (smtps) was once chosen for this
 purpose. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_ask_ccert no
 
 <p> Ask a remote SMTP client for a client certificate. This
@@ -8226,20 +8268,22 @@ information is needed for certificate based mail relaying with,
 for example, the permit_tls_clientcerts feature. </p>
 
 <p> Some clients such as Netscape will either complain if no
-certificate is available (for the list of CAs in /etc/postfix/certs)
+certificate is available (for the list of CAs in $smtpd_tls_CAfile)
 or will offer multiple client certificates to choose from. This
 may be annoying, so this option is "off" by default. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_req_ccert no
 
 <p> When TLS encryption is enforced, require a remote SMTP client
 certificate in order to allow TLS connections to proceed.  This
 option implies "smtpd_tls_ask_ccert = yes". </p>
 
-<p> When TLS encryption is optional, remote SMTP clients can bypass
-the restriction by simply not using STARTTLS at all. For this reason
-a TLS connection will be handled as if only "smtpd_tls_ask_ccert
-= yes" is specified.  </p>
+<p> When TLS encryption is optional, this setting is ignored with
+a warning written to the mail log. </p>
+
+<p> This feature is available in Postfix 2.2 and later.  </p>
 
 %PARAM smtpd_tls_ccert_verifydepth 5
 
@@ -8249,18 +8293,30 @@ file.  The default value should also suffice for longer chains (the
 root CA issues special CA which then issues the actual certificate...).
 </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_auth_only no
 
 <p> When TLS encryption is optional in the Postfix SMTP server, do
 not announce or accept SASL authentication over unencrypted
 connections. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_session_cache_database
 
 <p> Name of the file containing the optional Postfix SMTP server
 TLS session cache. Specify a database type that supports enumeration,
 such as <b>btree</b> or <b>sdbm</b>; there is no need to support
-concurrent access.  The file is created if it does not exist.  </p>
+concurrent access.  The file is created if it does not exist. The smtpd(8)
+daemon does not use this parameter directly, rather the cache is
+implemented indirectly in the tlsmgr(8) daemon. This means that
+per-smtpd-instance master.cf overrides of this parameter are not
+effective. Note, that each of the cache databases supported by tlsmgr(8)
+daemon: $smtpd_tls_session_cache_database, $smtp_tls_session_cache_database
+(and with Postfix 2.3 and later $lmtp_session_cache_database), needs to be
+stored separately, it is not at this time possible to store multiple
+caches in a single database. </p>
 
 <p> Note: <b>dbm</b> databases are not suitable. TLS
 session objects are too large. </p>
@@ -8268,14 +8324,21 @@ session objects are too large. </p>
 <p> Example: </p>
 
 <pre>
-smtpd_tls_session_cache_database = btree:/var/postfix/smtpd_scache
+smtpd_tls_session_cache_database = btree:/var/spool/postfix/smtpd_scache
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_tls_session_cache_timeout 3600s
 
 <p> The expiration time of Postfix SMTP server TLS session cache
-information.  A cache cleanup is performed periodically every
-$smtpd_tls_session_cache_timeout seconds.  </p>
+information. A cache cleanup is performed periodically
+every $smtpd_tls_session_cache_timeout seconds. As with
+$smtpd_tls_session_cache_database, this parameter is implemented in the
+tlsmgr(8) daemon and therefore per-smtpd-instance master.cf overrides
+are not possible. </p>
+
+<p> This feature is available in Postfix 2.2 and later.  </p>
 
 %PARAM relay_clientcerts
 
@@ -8305,9 +8368,20 @@ See RESTRICTION_CLASS_README.</p>
 
 %PARAM smtpd_tls_cipherlist
 
-<p> Controls the Postfix SMTP server TLS cipher selection scheme.
-For details, see the OpenSSL documentation. Note: do not use ""
-quotes around the parameter value. </p>
+<p> Obsolete Postfix &lt; 2.3 control for the Postfix SMTP server TLS
+cipher list. It is easy to create inter-operability problems by choosing
+a non-default cipher list. Do not use a non-default TLS cipherlist for
+MX hosts on the public Internet. Clients that begin the TLS handshake,
+but are unable to agree on a common cipher, may not be able to send any
+email to the SMTP server. Using a restricted cipher list may be more
+appropriate for a dedicated MSA or an internal mailhub, where one can
+exert some control over the TLS software and settings of the connecting
+clients. </p>
+
+<p> <b>Note:</b> do not use "" quotes around the parameter value. </p>
+
+<p>This feature is available with Postfix version 2.2. It is not used with
+Postfix 2.3 and later; use smtpd_tls_ciphers instead. </p>
 
 %PARAM smtpd_tls_dh1024_param_file
 
@@ -8333,6 +8407,8 @@ Gathering Daemon EGD", available at http://www.lothar.com/tech/crypto/.
 smtpd_tls_dh1024_param_file = /etc/postfix/dh_1024.pem
 </pre>
 
+<p>This feature is available with Postfix version 2.2.</p>
+
 %PARAM smtpd_tls_dh512_param_file
 
 <p> File with DH parameters that the Postfix SMTP server should
@@ -8347,17 +8423,38 @@ configuration parameter.  </p>
 smtpd_tls_dh512_param_file = /etc/postfix/dh_512.pem
 </pre>
 
+<p>This feature is available with Postfix version 2.2.</p>
+
 %PARAM smtpd_starttls_timeout 300s
 
 <p> The time limit for Postfix SMTP server write and read operations
 during TLS startup and shutdown handshake procedures. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_cert_file
 
 <p> File with the Postfix SMTP client RSA certificate in PEM format.
 This file may also contain the client private key, and these may
 be the same as the server certificate and key file. </p>
 
+<p> Do not configure client certificates unless you <b>must</b> present
+client TLS certificates to one or more servers. Client certificates are
+not usually needed, and can cause problems in configurations that work
+well without them. The recommended setting is to let the defaults stand: </p>
+
+<blockquote>
+<pre>
+    smtp_tls_cert_file =
+    smtp_tls_dcert_file =
+    smtp_tls_key_file =
+    smtp_tls_dkey_file =
+</pre>
+</blockquote>
+
+<p> The best way to use the default settings is to comment out the above
+parameters in main.cf if present. </p>
+
 <p> In order to verify certificates, the CA certificate (in case
 of a certificate chain, all CA certificates) must be available.
 You should add these certificates to the server certificate, the
@@ -8382,6 +8479,8 @@ hence pass the "openssl verify -purpose sslclient ..." test. </p>
 smtp_tls_cert_file = /etc/postfix/client.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_key_file $smtp_tls_cert_file
 
 <p> File with the Postfix SMTP client RSA private key in PEM format.
@@ -8397,6 +8496,8 @@ must be accessible without password. </p>
 smtp_tls_key_file = $smtp_tls_cert_file
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_CAfile
 
 <p> The file with the certificate of the certification authority
@@ -8410,6 +8511,8 @@ client certificate file.  </p>
 smtp_tls_CAfile = /etc/postfix/CAcert.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_CApath
 
 <p> Directory with PEM format certificate authority certificates
@@ -8427,6 +8530,8 @@ must be inside the chroot jail. </p>
 smtp_tls_CApath = /etc/postfix/certs
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_loglevel 0
 
 <p> Enable additional Postfix SMTP client logging of TLS activity.
@@ -8452,12 +8557,22 @@ transmission after STARTTLS. </dd>
 <p> Use "smtp_tls_loglevel = 3" only in case of problems. Use of
 loglevel 4 is strongly discouraged. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_session_cache_database
 
 <p> Name of the file containing the optional Postfix SMTP client
 TLS session cache. Specify a database type that supports enumeration,
 such as <b>btree</b> or <b>sdbm</b>; there is no need to support
-concurrent access.  The file is created if it does not exist.  </p>
+concurrent access.  The file is created if it does not exist. The smtp(8)
+daemon does not use this parameter directly, rather the cache is
+implemented indirectly in the tlsmgr(8) daemon. This means that
+per-smtp-instance master.cf overrides of this parameter are not effective.
+Note, that each of the cache databases supported by tlsmgr(8) daemon:
+$smtpd_tls_session_cache_database, $smtp_tls_session_cache_database
+(and with Postfix 2.3 and later $lmtp_session_cache_database), needs to
+be stored separately, it is not at this time possible to store multiple
+caches in a single database. </p>
 
 <p> Note: <b>dbm</b> databases are not suitable. TLS
 session objects are too large. </p>
@@ -8465,23 +8580,33 @@ session objects are too large. </p>
 <p> Example: </p>
 
 <pre>
-smtp_tls_session_cache_database = btree:/var/postfix/smtp_scache
+smtp_tls_session_cache_database = btree:/var/spool/postfix/smtp_scache
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_session_cache_timeout 3600s
 
 <p> The expiration time of Postfix SMTP client TLS session cache
-information.  A cache cleanup is performed periodically every
-$smtp_tls_session_cache_timeout seconds.  </p>
+information.  A cache cleanup is performed periodically
+every $smtp_tls_session_cache_timeout seconds. As with
+$smtp_tls_session_cache_database, this parameter is implemented in the
+tlsmgr(8) daemon and therefore per-smtp-instance master.cf overrides
+are not possible. </p>
+
+<p> This feature is available in Postfix 2.2 and later.  </p>
 
 %PARAM smtp_use_tls no
 
 <p> Opportunistic mode: use TLS when a remote SMTP server announces
 STARTTLS support, otherwise send the mail in the clear. Beware:
-some SMTP servers offer STARTTLS even if it is not configured.  If
-the TLS handshake fails, and no other server is available, delivery
-is deferred and mail stays in the queue.  If this is a concern for
-you, use the smtp_tls_per_site feature instead.  </p>
+some SMTP servers offer STARTTLS even if it is not configured.  With
+Postfix &lt; 2.3, if the TLS handshake fails, and no other server is
+available, delivery is deferred and mail stays in the queue. If this
+is a concern for you, use the smtp_tls_per_site feature instead.  </p>
+
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_security_level instead. </p>
 
 %PARAM smtp_enforce_tls no
 
@@ -8503,6 +8628,9 @@ will only connect to servers that support RFC 2487 _and_ that
 provide valid server certificates.  Typical use is for clients that
 send all their email to a dedicated mailhub.  </p>
 
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_security_level instead. </p>
+
 %PARAM smtp_tls_enforce_peername yes
 
 <p> When TLS encryption is enforced, require that the remote SMTP
@@ -8519,14 +8647,24 @@ environment where special CAs are created.  If not used carefully,
 this option opens the danger of a "man-in-the-middle" attack (the
 CommonName of this attacker will be logged). </p>
 
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_security_level instead. </p>
+
 %PARAM smtp_tls_per_site
 
 <p> Optional lookup tables with the Postfix SMTP client TLS usage
 policy by next-hop destination and by remote SMTP server hostname.
 When both lookups succeed, the more specific per-site policy (NONE,
-MUST, etc) overrides the less specific one (MAY), and the more
-secure per-site policy (MUST, etc) overrides the less secure one
-(NONE).  </p>
+MUST, etc) overrides the less specific one (MAY), and the more secure
+per-site policy (MUST, etc) overrides the less secure one (NONE).
+With Postfix 2.3 and later smtp_tls_per_site is strongly discouraged:
+use smtp_tls_policy_maps instead. </p>
+
+<p> Use of the bare hostname as the per-site table lookup key is
+discouraged. Always use the full destination nexthop (enclosed in
+[] with a possible ":port" suffix). A recipient domain or MX-enabled
+transport next-hop with no port suffix may look like a bare hostname,
+but is still a suitable <i>destination</i>. </p>
 
 <p> Specify a next-hop destination or server hostname on the left-hand
 side; no wildcards are allowed. The next-hop destination is either
@@ -8567,34 +8705,24 @@ settings.  </dd>
 
 </dl>
 
+<p> The above keywords correspond to the "none", "may", "encrypt" and
+"verify" security levels for the new smtp_tls_security_level parameter
+introduced in Postfix 2.3. Starting with Postfix 2.3, and independently
+of how the policy is specified, the smtp_tls_mandatory_ciphers and
+smtp_tls_mandatory_protocols parameters only apply when TLS encryption
+is mandatory. Connections for which encryption is optional enable
+all "export" grade and better ciphers. </p>
+
 <p> As long as no secure DNS lookup mechanism is available, false
 hostnames in MX or CNAME responses can change the server hostname
 that Postfix uses for TLS policy lookup and server certificate
-verification. Even with a perfect match between the server hostname
-and the server certificate, there is no guarantee that Postfix is
-connected to the right server.  To avoid this loophole take the
-following steps: </p>
-
-<ul>
-
-<li> Disallow CNAME hostname overrides. In main.cf specify
-"smtp_cname_overrides_servername = no". This prevents false hostname
-information in DNS CNAME records from changing the server hostname
-that Postfix uses for TLS policy lookup and server certificate
-verification. This feature requires Postfix 2.2.9 or later.
+verification. Even with a perfect match between the server hostname and
+the server certificate, there is no guarantee that Postfix is connected
+to the right server.  See TLS_README (Closing a DNS loophole with obsolete
+per-site TLS policies) for a possible work-around. </p>
 
-<li> Eliminate MX lookups. Specify local transport(5) table entries
-for sensitive domains with explicit smtp:[mailhost] or smtp:[mailhost]:port
-destinations.  This prevents false hostname information in DNS MX
-records from changing the server hostname that Postfix uses for TLS
-policy lookup and server certificate verification.
-
-<li> Specify MUST for these mail hosts (including [ ] and port) in
-the smtp_tls_per_site table.
-
-</ul>
-
-<p> </p>
+<p> This feature is available in Postfix 2.2 and later. With
+Postfix 2.3 and later use smtp_tls_policy_maps instead. </p>
 
 %PARAM smtp_tls_scert_verifydepth 5
 
@@ -8604,6 +8732,8 @@ a CA listed in the CA files.  The default value (5) should suffice
 for longer chains (the root CA issues special CA which then issues
 the actual certificate...). </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_note_starttls_offer no
 
 <p> Log the hostname of a remote SMTP server that offers STARTTLS,
@@ -8615,17 +8745,31 @@ when TLS is not already enabled for that server. </p>
 postfix/smtp[pid]:  Host offered STARTTLS: [name.of.host]
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_cipherlist
 
-<p> Controls the Postfix SMTP client TLS cipher selection scheme.
-For details, see the OpenSSL documentation. Note: do not use ""
-quotes around the parameter value. </p>
+<p> Obsolete Postfix &lt; 2.3 control for the Postfix SMTP client TLS
+cipher list. As this feature applies to all security levels, it is easy
+to create inter-operability problems by choosing a non-default cipher
+list. Do not use a non-default TLS cipher list on hosts that deliver email
+to the public Internet: you will be unable to send email to servers that
+only support the ciphers you exclude. Using a restricted cipher list
+may be more appropriate for an internal MTA, where one can exert some
+control over the TLS software and settings of the peer servers. </p>
+
+<p> <b>Note:</b> do not use "" quotes around the parameter value. </p>
+
+<p> This feature is available in Postfix version 2.2. It is not used with
+Postfix 2.3 and later; use smtp_tls_mandatory_ciphers instead. </p>
 
 %PARAM smtp_starttls_timeout 300s
 
 <p> Time limit for Postfix SMTP client write and read operations
 during TLS startup and shutdown handshake procedures. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_dkey_file $smtp_tls_dcert_file
 
 <p> File with the Postfix SMTP client DSA private key in PEM format.
@@ -8635,6 +8779,8 @@ be accessible without password. </p>
 <p> This file may be combined with the server certificate file
 specified with $smtp_tls_cert_file. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_tls_dcert_file
 
 <p> File with the Postfix SMTP client DSA certificate in PEM format.
@@ -8649,6 +8795,8 @@ This file may also contain the server private key. </p>
 smtp_tls_dcert_file = /etc/postfix/client-dsa.pem
 </pre>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM tls_random_exchange_name ${config_directory}/prng_exch
 
 <p> Name of the pseudo random number generator (PRNG) state file
@@ -8659,6 +8807,8 @@ not exist, and its length is fixed at 1024 bytes.  </p>
 kept in the /var file system, instead of under $config_directory.
 The location should not be inside the chroot jail. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM tls_random_source see "postconf -d" output
 
 <p> The external entropy source for the in-memory tlsmgr(8) pseudo
@@ -8671,6 +8821,8 @@ device file.  </p>
 <p> Note: on OpenBSD systems specify /dev/arandom when /dev/urandom
 gives timeout errors.  </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM tls_random_bytes 32
 
 <p> The number of bytes that tlsmgr(8) reads from $tls_random_source
@@ -8679,6 +8831,8 @@ pool. The default of 32 bytes (256 bits) is good enough for 128bit
 symmetric keys.  If using EGD or a device file, a maximum of 255
 bytes is read. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM tls_random_reseed_period 3600s
 
 <p> The maximal time between attempts by tlsmgr(8) to re-seed the
@@ -8686,12 +8840,16 @@ in-memory pseudo random number generator (PRNG) pool from external
 sources.  The actual time between re-seeding attempts is calculated
 using the PRNG, and is between 0 and the time specified.  </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM tls_random_prng_update_period 3600s
 
 <p> The time between attempts by tlsmgr(8) to save the state of
 the pseudo random number generator (PRNG) to the file specified
 with $tls_random_exchange_name.  </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM tls_daemon_random_bytes 32
 
 <p> The number of pseudo-random bytes that an smtp(8) or smtpd(8)
@@ -8700,16 +8858,22 @@ internal pseudo random number generator (PRNG).  The default of 32
 bytes (equivalent to 256 bits) is sufficient to generate a 128bit
 (or 168bit) session key. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_sasl_tls_security_options $smtp_sasl_security_options
 
 <p> The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtpd_sasl_tls_security_options $smtpd_sasl_security_options
 
 <p> The SASL authentication security options that the Postfix SMTP
 server uses for TLS encrypted SMTP sessions. </p>
 
+<p> This feature is available in Postfix 2.2 and later.  </p>
+
 %PARAM smtp_generic_maps empty
 
 <p> Optional lookup tables that perform address rewriting in the
@@ -9234,9 +9398,10 @@ configuration parameter.  See there for details. </p>
 
 <p> The SASL authentication security options that the Postfix SMTP
 client uses for TLS encrypted SMTP sessions with a verified server
-certificate. </p>
+certificate. This feature is still under construction. It will not be
+included in the Postfix 2.3 release. </p>
 
-<p> This feature is available in Postfix 2.3 and later. </p>
+<p> This feature should be available in Postfix 2.4 and later. </p>
 
 %PARAM lmtp_sasl_tls_verified_security_options $lmtp_sasl_tls_security_options
 
@@ -9268,3 +9433,913 @@ ID. This complicates the logfile analysis of multi-recipient mail.
 </p>
 
 <p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_cert_file
+
+<p> The LMTP-specific version of the smtp_tls_cert_file
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_key_file $lmtp_tls_cert_file
+
+<p> The LMTP-specific version of the smtp_tls_key_file
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_dcert_file
+
+<p> The LMTP-specific version of the smtp_tls_dcert_file
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_dkey_file $lmtp_tls_dcert_file
+
+<p> The LMTP-specific version of the smtp_tls_dkey_file
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_CAfile
+
+<p> The LMTP-specific version of the smtp_tls_CAfile
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_CApath
+
+<p> The LMTP-specific version of the smtp_tls_CApath
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_loglevel 0
+
+<p> The LMTP-specific version of the smtp_tls_loglevel
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_session_cache_database
+
+<p> The LMTP-specific version of the smtp_tls_session_cache_database
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_session_cache_timeout  3600s
+
+<p> The LMTP-specific version of the smtp_tls_session_cache_timeout
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_policy_maps
+
+<p> Optional lookup tables with the Postfix SMTP client TLS security
+policy by next-hop destination; when a non-empty value is specified,
+this overrides the obsolete smtp_tls_per_site parameter.  See
+TLS_README for a more detailed discussion of TLS security levels.
+</p>
+
+<p> The TLS policy table is indexed by the full next-hop destination,
+which is either the recipient domain, or the verbatim next-hop
+specified in the transport table, $local_transport, $virtual_transport,
+$relay_transport or $default_transport. This includes any enclosing
+square brackets and any non-default destination server port suffix. The
+LMTP socket type prefix (inet: or unix:) is not included in the lookup
+key. </p>
+
+<p> Only the next-hop domain, or $myhostname with LMTP over UNIX-domain
+sockets, is used as the nexthop name for certificate verification. The
+port and any enclosing square brackets are used in the table lookup key,
+but are not used for server name verification. </p>
+
+<p> When the lookup key is a domain name without enclosing square brackets
+or any <i>:port</i> suffix (typically the recipient domain), and the full
+domain is not found in the table, just as with the transport(5) table,
+the parent domain starting with a leading "." is matched recursively. This
+allows one to specify a security policy for a recipient domain and all
+its sub-domains. </p>
+
+<p> The lookup result is a security level, followed by an optional list
+of whitespace and/or comma separated name=value attributes that override
+related main.cf settings. The TLS security levels in order of increasing
+security are: </p>
+
+<dl>
+
+<dt><b>none</b></dt>         
+<dd>No TLS. No additional attributes are supported at this level. </dd>
+
+<dt><b>may</b></dt>
+<dd>Opportunistic TLS. No additional attributes are supported at this
+level. Since sending in the clear is acceptable, demanding stronger
+than default TLS security parameters merely reduces inter-operability.
+Postfix 2.3 and later ignore the smtp_tls_mandatory_ciphers and
+smtp_tls_mandatory_protocols parameters at this security level; all
+protocols are allowed and "export" grade or better ciphers are used.
+When TLS handshakes fail, the connection is retried with TLS disabled.
+This allows mail delivery to sites with non-interoperable TLS
+implementations.</dd>
+
+<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. At this level
+and higher the optional "ciphers" attribute overrides the main.cf
+smtp_tls_mandatory_ciphers parameter and the optional "protocols"
+keyword overrides the main.cf smtp_tls_mandatory_protocols parameter.
+In the policy table, multiple protocols must be separated by colons,
+as attribute values may not contain whitespace or commas. </p>
+
+<dt><b>verify</b></dt> <dd>Mandatory TLS verification.  At this security
+level, DNS MX lookups are trusted to be secure enough, and the name
+verified in the server certificate is usually obtained indirectly via
+unauthenticated DNS MX lookups.  The optional "match" attribute overrides
+the main.cf smtp_tls_verify_cert_match parameter. In the policy table,
+multiple match patterns and strategies must be separated by colons.
+In practice explicit control over matching is more common with the
+"secure" policy, described below. </dd>
+
+<dt><b>secure</b></dt> <dd>Secure-channel TLS. At this security level, DNS
+MX lookups, though potentially used to determine the candidate next-hop
+gateway IP addresses, are <b>not</b> trusted to be secure enough for TLS
+peername verification. Instead, the default name verified in the server
+certificate is obtained directly from the next-hop, or is explicitly
+specified via the optional <b>match</b> attribute which overrides the
+main.cf smtp_tls_secure_cert_match parameter. In the policy table,
+multiple match patterns and strategies must be separated by colons.
+The match attribute is most useful when multiple domains are supported by
+common server, the policy entries for additional domains specify matching
+rules for the primary domain certificate. While transport table overrides
+routing the secondary domains to the primary nexthop also allow secure
+verification, they risk delivery to the wrong destination when domains
+change hands or are re-assigned to new gateways. With the "match"
+attribute approach, routing is not perturbed, and mail is deferred if
+verification of a new MX host fails. </dd>
+
+</dl>
+
+<p>
+Example:
+</p>
+
+<pre>
+main.cf:
+    smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+</pre>
+<pre>
+tls_policy:
+    example.edu                 none
+    example.mil                 may
+    example.gov                 encrypt protocols=TLSv1
+    example.com                 verify ciphers=high
+    example.net                 secure
+    .example.net                secure match=.example.net:example.net
+    [mail.example.org]:587      secure match=nexthop
+</pre>
+
+<p> <b>Note:</b> The <b>hostname</b> strategy if listed in a non-default
+setting of smtp_tls_secure_cert_match or in the <b>match</b> attribute
+in the policy table can render the <b>secure</b> level vulnerable to
+DNS forgery. Do not use the <b>hostname</b> strategy for secure-channel
+configurations in environments where DNS security is not assured. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_mandatory_protocols SSLv3, TLSv1
+
+<p> List of TLS protocol versions that are secure enough to be used
+with the "encrypt" security level and higher. In main.cf the values
+are separated by whitespace, commas or colons. In the policy table
+(see smtp_tls_policy_maps) the only valid 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> Since SSL version 2 has known protocol weaknesses and
+is now deprecated, the default setting only lists "SSLv3" and
+"TLSv1". This means that by default, SSL version 2 will not be used
+at the "encrypt" security level and higher. </p>
+
+<p> See the documentation of the smtp_tls_policy_maps parameter and
+TLS_README for more information about security levels. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_verify_cert_match hostname
+
+<p> The server certificate peername verification method for the
+"verify" TLS security level. In a "verify" TLS policy table
+($smtp_tls_policy_maps) entry the optional "match" attribute 
+overrides this main.cf setting. </p>
+
+<p> This parameter specifies one or more patterns or strategies separated
+by commas, whitespace or colons.  In the policy table the only valid
+separator is the colon character. </p>
+
+<p> Patterns specify domain names, or domain name suffixes: </p>
+
+<dl>
+
+<dt><i>example.com</i></dt> <dd> Match the <i>example.com</i> domain,
+i.e. one of the names the server certificate must be <i>example.com</i>,
+upper and lower case distinctions are ignored. </dd>
+
+<dt><i>.example.com</i></dt>
+<dd> Match subdomains of the <i>example.com</i> domain, i.e. match
+a name in the server certificate that consists of a non-zero number of
+labels followed by a <i>.example.com</i> suffix. Case distinctions are
+ignored.</dd>
+
+</dl>
+
+<p> Strategies specify a transformation from the next-hop domain
+to the expected name in the server certificate: </p>
+
+<dl>
+
+<dt>nexthop</dt>
+<dd> Match against the next-hop domain, which is either the recipient
+domain, or the transport next-hop configured for the domain stripped of
+any optional socket type prefix, enclosing square brackets and trailing
+port. When MX lookups are not suppressed, this is the original nexthop
+domain prior to the MX lookup, not the result of the MX lookup. For
+LMTP delivery via UNIX-domain sockets, the verified next-hop name is
+$myhostname.  This strategy is suitable for use with the "secure"
+policy. Case is ignored.</dd>
+
+<dt>dot-nexthop</dt>
+<dd> As above, but match server certificate names that are subdomains
+of the next-hop domain. Case is ignored.</dd>
+
+<dt>hostname</dt> <dd> Match against the hostname of the server, often
+obtained via an unauthenticated DNS MX lookup. For LMTP delivery via
+UNIX-domain sockets, the verified name is $myhostname. This matches
+the verification strategy of the "MUST" keyword in the obsolete
+smtp_tls_per_site table, and is suitable for use with the "verify"
+security level. When the next-hop name is enclosed in square brackets
+to suppress MX lookups, the "hostname" strategy is the same as the
+"nexthop" strategy. Case is ignored.</dd>
+
+</dl>
+
+<p>
+Sample main.cf setting:
+</p>
+
+<pre>
+smtp_tls_verify_cert_match = hostname, nexthop, dot-nexthop
+</pre>
+
+<p>
+Sample policy table override:
+</p>
+
+<pre>
+example.com     verify  match=hostname:nexthop
+.example.com    verify  match=example.com:.example.com:hostname
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_secure_cert_match nexthop, dot-nexthop
+
+<p> The server certificate peername verification method for the
+"secure" TLS security level. In a "secure" TLS policy table
+($smtp_tls_policy_maps) entry the optional "match" attribute
+overrides this main.cf setting. </p>
+
+<p> This parameter specifies one or more patterns or strategies separated
+by commas, whitespace or colons.  In the policy table the only valid
+separator is the colon character. </p>
+
+<p> For a description of the pattern and strategy syntax see the
+smtp_tls_verify_cert_match parameter. The "hostname" strategy should
+be avoided in this context, as in the absence of a secure global DNS, using
+the results of MX lookups in certificate verification is not immune to active
+(man-in-the-middle) attacks on DNS. </p>
+
+<p>
+Sample main.cf setting:
+</p>
+
+<pre>
+smtp_tls_secure_cert_match = nexthop
+</pre>
+
+<p>
+Sample policy table override:
+</p>
+
+<pre>
+example.net     secure match=example.com:.example.com
+.example.net    secure match=example.com:.example.com
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_policy_maps
+
+<p> The LMTP-specific version of the smtp_tls_policy_maps
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_mandatory_protocols SSLv3, TLSv1
+
+<p> The LMTP-specific version of the smtp_tls_mandatory_protocols
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_verify_cert_match hostname
+
+<p> The LMTP-specific version of the smtp_tls_verify_cert_match
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_secure_cert_match nexthop
+
+<p> The LMTP-specific version of the smtp_tls_secure_cert_match
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtpd_tls_protocols
+
+<p> The list of TLS protocols supported by the server. If empty the
+default list of protocols is used (i.e. all TLS protocol versions are
+supported). Any non-empty value is interpreted as 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> DO NOT set this to a non-default value on an MX-host,
+as some clients may not support any of the narrower set of protocols,
+and may be unable to fallback to plaintext sessions. If you restrict
+the protocol list on an MX host, you may lose mail. </p>
+
+<p> Example: </p>
+
+<pre>
+smtpd_tls_protocols = SSLv3, TLSv1
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_security_level
+
+<p> The default SMTP TLS security level for all destinations; when
+a non-empty value is specified, this overrides the obsolete parameters
+smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.  </p>
+
+<p> Specify one of the following security levels: </p>
+
+<dl>
+
+<dt><b>none</b></dt> <dd> TLS will not be used unless enabled for specific
+destinations via smtp_tls_policy_maps. </dd>
+
+<dt><b>may</b></dt>
+<dd> Opportunistic TLS. TLS will be used if supported by the server. Since
+sending in the clear is acceptable, demanding stronger than default TLS
+security parameters merely reduces inter-operability. Postfix 2.3 and
+later ignore the smtp_tls_mandatory_ciphers and
+smtp_tls_mandatory_protocols parameters at this security level; all
+protocols are allowed and "export" grade or better ciphers are used.
+When TLS handshakes fail, the connection is retried with TLS disabled.
+This allows mail delivery to sites with non-interoperable TLS
+implementations. </dd>
+
+<dt><b>encrypt</b></dt> <dd>Mandatory TLS encryption. Since a minimum
+level of security is intended, it reasonable to be specific about
+sufficiently secure protocol versions and ciphers. At this security level
+and higher, the main.cf parameters smtp_tls_mandatory_protocols and
+smtp_tls_mandatory_ciphers specify the TLS protocols and minimum
+cipher grade which the administrator considers secure enough for
+mandatory encrypted sessions. This security level is not an appropriate
+default for systems delivering mail to the Internet. </dd>
+
+<dt><b>verify</b></dt> <dd>Mandatory TLS verification. At this security
+level, DNS MX lookups are trusted to be secure enough, and the name
+verified in the server certificate is usually obtained indirectly
+via unauthenticated DNS MX lookups. The smtp_tls_verify_cert_match
+parameter controls how the server name is verified. In practice explicit
+control over matching is more common at the "secure" level, described
+below. This security level is not an appropriate default for systems
+delivering mail to the Internet. </dd>
+
+<dt><b>secure</b></dt> <dd>Secure-channel TLS.  At this security level,
+DNS MX lookups, though potentially used to determine the candidate
+next-hop gateway IP addresses, are <b>not</b> trusted to be secure enough
+for TLS peername verification. Instead, the default name verified in
+the server certificate is obtained from the next-hop domain as specified
+in the smtp_tls_secure_cert_match configuration parameter. The default
+matching rule is that a server certificate matches when its name is equal
+to or is a sub-domain of the nexthop domain. This security level is not
+an appropriate default for systems delivering mail to the Internet. </dd>
+
+</dl>
+
+<p>
+Examples:
+</p>
+
+<p>No TLS, old-style: smtp_use_tls=no and smtp_enforce_tls=no.</p>
+<pre>
+main.cf:
+    smtp_tls_security_level = none
+</pre>
+
+<p>Opportunistic TLS:</p>
+<pre>
+main.cf:
+    smtp_tls_security_level = may
+</pre>
+
+<p>Mandatory (high-grade) TLS encryption:</p>
+<pre>
+main.cf:
+    smtp_tls_security_level = encrypt
+    smtp_tls_mandatory_ciphers = high
+</pre>
+
+<p>Mandatory TLS verification, of hostname or nexthop domain:</p>
+<pre>
+main.cf:
+    smtp_tls_security_level = verify
+    smtp_tls_mandatory_ciphers = high
+    smtp_tls_verify_cert_match = hostname, nexthop, dot-nexthop
+</pre>
+
+<p>Secure channel TLS with exact nexthop name matching:</p>
+<pre>
+main.cf:
+    smtp_tls_security_level = secure
+    smtp_tls_mandatory_protocols = TLSv1
+    smtp_tls_mandatory_ciphers = high
+    smtp_tls_secure_cert_match = nexthop
+</pre>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtpd_milters empty
+
+<p> A list of Milter (mail filter) applications for new mail that
+arrives via the Postfix smtpd(8) server.  See the MILTER_README
+document for details.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM non_smtpd_milters empty
+
+<p> A list of Milter (mail filter) applications for new mail that
+does not arrive via the Postfix smtpd(8) server. This includes local
+submission via the sendmail(1) command line, new mail that arrives
+via the Postfix qmqpd(8) server, and old mail that is re-injected
+into the queue with "postsuper -r".  See the MILTER_README document
+for details.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_protocol 2
+
+<p> The mail filter protocol version and optional protocol extensions
+for communication with a Milter (mail filter) application. This
+information should match the protocol that is expected by the actual
+mail filter application.  </p>
+
+<p>Protocol versions: </p>
+
+<dl compact>
+
+<dt>2</dt> <dd>Use Sendmail 8 mail filter protocol version 2.</dd>
+
+<dt>3</dt> <dd>Use Sendmail 8 mail filter protocol version 3.</dd>
+
+<dt>4</dt> <dd>Use Sendmail 8 mail filter protocol version 4.</dd>
+
+</dl>
+
+<p>Protocol extensions: </p>
+
+<dl compact>
+
+<dt>no_header_reply</dt> <dd> Specify this when the Milter application
+will not reply for each individual message header.</dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_default_action tempfail
+
+<p> The default action when a Milter (mail filter) application is
+unavailable or mis-configured. Specify one of the following: </p>
+
+<dl compact>
+
+<dt>accept</dt> <dd>Proceed as if the mail filter was not present.
+</dd>
+
+<dt>reject</dt> <dd>Reject all further commands in this session
+with a permanent status code.</dd>
+
+<dt>tempfail</dt> <dd>Reject all further commands in this session
+with a temporary status code. </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_connect_timeout 30s
+
+<p> The time limit for connecting to a Milter (mail filter)
+application, and for negotiating protocol options. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit). </p>
+
+<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_command_timeout 30s
+
+<p> The time limit for sending an SMTP command to a Milter (mail
+filter) application, and for receiving the response.  </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit). </p>
+
+<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_content_timeout 300s
+
+<p> The time limit for sending message content to a Milter (mail
+filter) application, and for receiving the response.  </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit). </p>
+
+<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
+(weeks). The default time unit is s (seconds). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_connect_macros see postconf -n output
+
+<p> The macros that are sent to Milter (mail filter) applications
+after completion of an SMTP connection. See MILTER_README
+for a list of available macro names and their meanings. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_helo_macros see postconf -n output
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the SMTP HELO or EHLO command. See
+MILTER_README for a list of available macro names and their meanings.
+</p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_mail_macros see postconf -n output
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the SMTP MAIL FROM command. See MILTER_README
+for a list of available macro names and their meanings. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_rcpt_macros see postconf -n output
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the SMTP RCPT TO command. See MILTER_README
+for a list of available macro names and their meanings. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_data_macros see postconf -n output
+
+<p> The macros that are sent to version 4 or higher Milter (mail
+filter) applications after the SMTP DATA command. See MILTER_README
+for a list of available macro names and their meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_end_of_data_macros see postconf -n output
+
+<p> The macros that are sent to Milter (mail filter) applications
+after the message end-of-data. See MILTER_README for a list of
+available macro names and their meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_unknown_command_macros see postconf -n output
+
+<p> The macros that are sent to version 3 or higher Milter (mail
+filter) applications after an unknown SMTP command.  See MILTER_README
+for a list of available macro names and their meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_macro_daemon_name $myhostname
+
+<p> The {daemon_name} macro value for Milter (mail filter) applications.
+See MILTER_README for a list of available macro names and their
+meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM milter_macro_v $mail_name $mail_version
+
+<p> The {v} macro value for Milter (mail filter) applications.
+See MILTER_README for a list of available macro names and their
+meanings.  </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtpd_tls_ciphers export
+
+<p> The minimum acceptable SMTP server TLS cipher grade. It is easy to
+create inter-operability problems by choosing a non-default cipher grade.
+Do not use a stronger than default minimum cipher grade for MX hosts on
+the public Internet. Clients that begin the TLS handshake, but are unable
+to agree on a common cipher, may not be able to send any email to the
+SMTP server. Using a restricted cipher list may be more appropriate for a
+dedicated MSA or an internal mailhub, where one can exert some control over
+the TLS software and settings of the connecting clients. Configurations
+with no certificates are also not likely to inter-operate with most
+clients, see the notes for "smtpd_tls_cert_file". </p>
+
+<p> The following cipher grades are supported: </p>
+
+<dl>
+<dt><b>export</b></dt>
+<dd> Enable the mainstream "EXPORT" grade or better OpenSSL ciphers.
+This is the most appropriate setting for public MX hosts. The underlying
+cipherlist is specified via the tls_export_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_export_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "smtpd_tls_exclude_ciphers = aNULL". </dd>
+
+<dt><b>low</b></dt>
+<dd> Enable the mainstream "LOW" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_low_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_low_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "smtpd_tls_exclude_ciphers = aNULL". </dd>
+
+<dt><b>medium</b></dt>
+<dd> Enable the mainstream "MEDIUM" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_medium_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_medium_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers,
+set "smtpd_tls_exclude_ciphers = aNULL". </dd>
+
+<dt><b>high</b></dt>
+<dd> Enable only the mainstream "HIGH" grade OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_high_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_high_cipherlist includes anonymous ciphers, but these
+are automatically filtered out if the server is configured to ask for
+client certificates. If you must always exclude anonymous ciphers, set
+"smtpd_tls_exclude_ciphers = aNULL". </dd>
+
+<dt><b>null</b></dt>
+<dd> Enable only the "NULL" OpenSSL ciphers, these provide authentication
+without encryption.  This setting is only appropriate in the rare
+case that all clients are prepared to use NULL ciphers (not normally
+enabled in TLS clients). The underlying cipherlist is specified via the
+tls_null_cipherlist configuration parameter, which you are strongly
+encouraged to not change. The default value of tls_null_cipherlist
+excludes anonymous ciphers (OpenSSL 0.9.8 has NULL ciphers that offer
+data integrity without encryption or authentication). </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtpd_tls_exclude_ciphers
+
+<p> List of ciphers or cipher types to exclude from the SMTP server
+cipher list. This is not an OpenSSL cipherlist; it is a simple list
+separated by whitespace and/or commas. The elements are a single
+cipher, or one or more "+" separated cipher properties, in which
+case only ciphers matching <b>all</b> the properties are excluded. </p>
+
+<p> Examples (some of these will cause problems): </p>
+
+<pre>
+smtpd_tls_exclude_ciphers = aNULL
+smtpd_tls_exclude_ciphers = MD5, DES
+smtpd_tls_exclude_ciphers = DES+MD5
+smtpd_tls_exclude_ciphers = AES256-SHA, DES-CBC3-MD5
+smtpd_tls_exclude_ciphers = kEDH+aRSA
+</pre>
+
+<p> The first setting disables anonymous ciphers. The next setting
+disables ciphers that use the MD5 digest algorithm or the (single) DES
+encryption algorithm. The next setting disables ciphers that use MD5 and
+DES together.  The next setting disables the two ciphers "AES256-SHA"
+and "DES-CBC3-MD5". The last setting disables ciphers that use "EDH"
+key exchange with RSA authentication. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_mandatory_ciphers medium
+
+<p> The minimum SMTP client TLS cipher grade that is strong enough to
+be used with the "encrypt" security level and higher.  The default
+value "medium" is suitable for most destinations with which you may
+want to enforce TLS, and is beyond the reach of today's crypt-analytic
+methods. See smtp_tls_policy_maps for information on how to configure
+ciphers on a per-destination basis. </p>
+
+<p> The following cipher grades are supported: </p>
+
+<dl>
+<dt><b>export</b></dt>
+<dd> Enable the mainstream "EXPORT" grade or better OpenSSL
+ciphers.  This is always used for opportunistic encryption. It is
+not recommended for mandatory encryption unless you must enforce TLS
+with "crippled" peers. The underlying cipherlist is specified via the
+tls_export_cipherlist configuration parameter, which you are strongly
+encouraged to not change. The default value of tls_export_cipherlist
+includes anonymous ciphers, but these are automatically filtered out if
+the client is configured to verify server certificates. If you must 
+exclude anonymous ciphers also at the "encrypt" security level, set
+"smtp_tls_mandatory_exclude_ciphers = aNULL". </dd>
+
+<dt><b>low</b></dt>
+<dd> Enable the mainstream "LOW" grade or better OpenSSL ciphers.  This
+setting is only appropriate for internal mail servers.  The underlying
+cipherlist is specified via the tls_low_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_low_cipherlist includes anonymous ciphers, but these are
+automatically filtered out if the client is configured to verify server
+certificates. If you must exclude anonymous ciphers also at the "encrypt"
+security level, set "smtp_tls_mandatory_exclude_ciphers = aNULL". </dd>
+
+<dt><b>medium</b></dt>
+<dd> Enable the mainstream "MEDIUM" grade or better OpenSSL ciphers.
+The underlying cipherlist is specified via the tls_medium_cipherlist
+configuration parameter, which you are strongly encouraged to not change.
+The default value of tls_medium_cipherlist includes anonymous ciphers,
+but these are automatically filtered out if the client is configured to
+verify server certificates. If you must exclude anonymous ciphers also
+at the "encrypt" security level, set "smtp_tls_mandatory_exclude_ciphers
+= aNULL". </dd>
+
+<dt><b>high</b></dt>
+<dd> Enable only the mainstream "HIGH" grade OpenSSL ciphers.  This
+setting is appropriate when all mandatory TLS destinations support
+some of "HIGH" grade ciphers, this is not uncommon. The underlying
+cipherlist is specified via the tls_high_cipherlist configuration
+parameter, which you are strongly encouraged to not change. The default
+value of tls_high_cipherlist includes anonymous ciphers, but these are
+automatically filtered out if the client is configured to verify server
+certificates. If you must exclude anonymous ciphers also at the "encrypt"
+security level, set "smtp_tls_mandatory_exclude_ciphers = aNULL". </dd>
+
+<dt><b>null</b></dt>
+<dd> Enable only the "NULL" OpenSSL ciphers, these provide authentication
+without encryption.  This setting is only appropriate in the rare case
+that all servers are prepared to use NULL ciphers (not normally enabled
+in TLS servers). A plausible use-case is an LMTP server listening on a
+UNIX-domain socket that is configured to support "NULL" ciphers. The
+underlying cipherlist is specified via the tls_null_cipherlist
+configuration parameter, which you are strongly encouraged to not
+change. The default value of tls_null_cipherlist excludes anonymous
+ciphers (OpenSSL 0.9.8 has NULL ciphers that offer data integrity without
+encryption or authentication). </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_exclude_ciphers
+
+<p> List of ciphers or cipher types to exclude from the SMTP client cipher
+list at all security levels. This is not an OpenSSL cipherlist, it is
+a simple list separated by whitespace and/or commas. The elements are a
+single cipher, or one or more "+" separated cipher properties, in which
+case only ciphers matching <b>all</b> the properties are excluded. </p>
+
+<p> Examples (some of these will cause problems): </p>
+
+<pre>
+smtp_tls_exclude_ciphers = aNULL
+smtp_tls_exclude_ciphers = MD5, DES
+smtp_tls_exclude_ciphers = DES+MD5
+smtp_tls_exclude_ciphers = AES256-SHA, DES-CBC3-MD5
+smtp_tls_exclude_ciphers = kEDH+aRSA
+</pre>
+
+<p> The first setting, disables anonymous ciphers. The next setting
+disables ciphers that use the MD5 digest algorithm or the (single) DES
+encryption algorithm. The next setting disables ciphers that use MD5 and
+DES together.  The next setting disables the two ciphers "AES256-SHA"
+and "DES-CBC3-MD5". The last setting disables ciphers that use "EDH"
+key exchange with RSA authentication. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM smtp_tls_mandatory_exclude_ciphers
+
+<p> List of ciphers or cipher types to exclude from the SMTP client
+cipher list at the mandatory TLS security levels: "encrypt", "verify"
+and "secure". See smtp_tls_exclude_ciphers for syntax details. When
+both "exclude" parameters are defined, the combined list of ciphers is
+excluded (provided the TLS security level is "encrypt" or higher). </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM tls_high_cipherlist !EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH
+
+<p> The OpenSSL cipherlist for "HIGH" grade ciphers. This defines
+the meaning of the "high" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. You are
+strongly encouraged to not change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM tls_medium_cipherlist !EXPORT:!LOW:ALL:+RC4:@STRENGTH
+
+<p> The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers. This
+defines the meaning of the "medium" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. This is
+the default cipherlist for mandatory TLS encryption in the TLS
+client (with anonymous ciphers disabled when verifying server
+certificates). You are strongly encouraged to not change this
+setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM tls_low_cipherlist !EXPORT:ALL:+RC4:@STRENGTH
+
+<p> The OpenSSL cipherlist for "LOW" or higher grade ciphers. This defines
+the meaning of the "low" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. You are
+strongly encouraged to not change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM tls_export_cipherlist ALL:+RC4:@STRENGTH
+
+<p> The OpenSSL cipherlist for "EXPORT" or higher grade ciphers. This
+defines the meaning of the "export" setting in smtpd_tls_ciphers,
+smtp_tls_mandatory_ciphers and lmtp_tls_mandatory_ciphers. This is
+the cipherlist for the opportunistic ("may") TLS client security
+level and is the default cipherlist for the SMTP server. You are
+strongly encouraged to not change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM tls_null_cipherlist !aNULL:eNULL+kRSA
+
+<p> The OpenSSL cipherlist for "NULL" grade ciphers that provide
+authentication without encryption. This defines the meaning of the "null"
+setting in smtpd_tls_ciphers, smtp_tls_mandatory_ciphers and
+lmtp_tls_mandatory_ciphers.  You are strongly encouraged to not
+change this setting. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_mandatory_ciphers
+
+<p> The LMTP-specific version of the smtp_tls_mandatory_ciphers
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_exclude_ciphers
+
+<p> The LMTP-specific version of the smtp_tls_exclude_ciphers
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM lmtp_tls_mandatory_exclude_ciphers
+
+<p> The LMTP-specific version of the smtp_tls_mandatory_exclude_ciphers
+configuration parameter.  See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
index 19c7c32d77ed915de44b46e7725da8f28259abc1..653260ccc9763b04aa1243d68d5f2c36f7d944b5 100644 (file)
@@ -945,3 +945,40 @@ ipv
 itojun
 netmasks
 kluges
+APPENDDEF
+EOH
+EOM
+MSGIDUNKNOWN
+Milters
+SMFIS
+SenderID
+authen
+confINCDIRS
+confLIBDIRS
+ctx
+dnl
+eoh
+eom
+getpriv
+getsymval
+gzcat
+jobid
+libmilter
+ments
+milters
+mlfi
+msgid
+optionneg
+portnumber
+ptr
+sid
+smfi
+smfilter
+strcmp
+tempfail
+vx
+wich
+xf
+xxxxx
+yy
+zz
index f03d4417738b283cb692e2317fd65b3c4c7daf6a..e3e1d222a5969446cb3e170b69f494ccab7a6ada 100644 (file)
@@ -498,7 +498,7 @@ static time_t max_cache_time;               /* time of peak size */
 static void anvil_remote_expire(int unused_event, char *context)
 {
     ANVIL_REMOTE *anvil_remote = (ANVIL_REMOTE *) context;
-    char   *myname = "anvil_remote_expire";
+    const char *myname = "anvil_remote_expire";
 
     if (msg_verbose)
        msg_info("%s %s", myname, anvil_remote->ident);
@@ -521,7 +521,7 @@ static void anvil_remote_expire(int unused_event, char *context)
 static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident)
 {
     ANVIL_REMOTE *anvil_remote;
-    char   *myname = "anvil_remote_lookup";
+    const char *myname = "anvil_remote_lookup";
 
     if (msg_verbose)
        msg_info("%s fd=%d stream=0x%lx ident=%s",
@@ -566,7 +566,7 @@ static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char
 {
     ANVIL_REMOTE *anvil_remote;
     ANVIL_LOCAL *anvil_local;
-    char   *myname = "anvil_remote_conn_update";
+    const char *myname = "anvil_remote_conn_update";
 
     if (msg_verbose)
        msg_info("%s fd=%d stream=0x%lx ident=%s",
@@ -767,7 +767,7 @@ static void anvil_remote_disconnect(VSTREAM *client_stream, const char *ident)
 {
     ANVIL_REMOTE *anvil_remote;
     ANVIL_LOCAL *anvil_local;
-    char   *myname = "anvil_remote_disconnect";
+    const char *myname = "anvil_remote_disconnect";
 
     if (msg_verbose)
        msg_info("%s fd=%d stream=0x%lx ident=%s",
@@ -803,7 +803,7 @@ static void anvil_service_done(VSTREAM *client_stream, char *unused_service,
                                       char **unused_argv)
 {
     ANVIL_LOCAL *anvil_local;
-    char   *myname = "anvil_service_done";
+    const char *myname = "anvil_service_done";
 
     if (msg_verbose)
        msg_info("%s fd=%d stream=0x%lx",
index 1b64722b7508788b50aea4e49c6edd92e9fc0091..67042f3f6fc8b8e2518e6d20be4acc1af14b907e 100644 (file)
@@ -200,7 +200,7 @@ BOUNCE_TEMPLATES *bounce_templates;
 
 static int bounce_append_proto(char *service_name, VSTREAM *client)
 {
-    char   *myname = "bounce_append_proto";
+    const char *myname = "bounce_append_proto";
     int     flags;
 
     /*
@@ -270,7 +270,7 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
                                                char *, char *, char *, int,
                                                        BOUNCE_TEMPLATES *))
 {
-    char   *myname = "bounce_notify_proto";
+    const char *myname = "bounce_notify_proto";
     int     flags;
     int     dsn_ret;
 
@@ -327,7 +327,7 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
 
 static int bounce_verp_proto(char *service_name, VSTREAM *client)
 {
-    char   *myname = "bounce_verp_proto";
+    const char *myname = "bounce_verp_proto";
     int     flags;
     int     dsn_ret;
 
@@ -399,7 +399,7 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
 
 static int bounce_one_proto(char *service_name, VSTREAM *client)
 {
-    char   *myname = "bounce_one_proto";
+    const char *myname = "bounce_one_proto";
     int     flags;
     int     dsn_ret;
 
index 483d21ae1448c91fecfe69ef36903385d0cf55cf..a68850a5b533ca0554c7e04fd1eba3f9d96ce715 100644 (file)
@@ -104,7 +104,7 @@ static void bounce_cleanup_callback(void)
 
 void    bounce_cleanup_log(void)
 {
-    char   *myname = "bounce_cleanup_log";
+    const char *myname = "bounce_cleanup_log";
 
     /*
      * Sanity checks.
@@ -136,7 +136,7 @@ static void bounce_cleanup_sig(int sig)
 
 void    bounce_cleanup_register(char *service, char *queue_id)
 {
-    char   *myname = "bounce_cleanup_register";
+    const char *myname = "bounce_cleanup_register";
 
     /*
      * Sanity checks.
@@ -158,7 +158,7 @@ void    bounce_cleanup_register(char *service, char *queue_id)
 
 void    bounce_cleanup_unregister(void)
 {
-    char   *myname = "bounce_cleanup_unregister";
+    const char *myname = "bounce_cleanup_unregister";
 
     /*
      * Sanity checks.
index 9a1913103258c5b4cfa3e6f6cd9431a98ab3f9c6..4fcdf644cb60a25af0ea181cf7d8e60393a54ee4 100644 (file)
@@ -89,7 +89,7 @@ int     bounce_notify_verp(int flags, char *service, char *queue_name,
                                   int dsn_ret, char *verp_delims,
                                   BOUNCE_TEMPLATES *ts)
 {
-    char   *myname = "bounce_notify_verp";
+    const char *myname = "bounce_notify_verp";
     BOUNCE_INFO *bounce_info;
     int     bounce_status = 0;
     int     postmaster_status;
index 82681e1e15e47ceb8177d05c50f9f98428702300..958057df7b0bf3a2bd9ac011ea8c3f86b983ad81 100644 (file)
@@ -3,20 +3,21 @@ SRCS  = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \
        cleanup_extracted.c cleanup_state.c cleanup_rewrite.c \
        cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \
        cleanup_out_recipient.c cleanup_init.c cleanup_api.c \
-       cleanup_addr.c cleanup_bounce.c
+       cleanup_addr.c cleanup_bounce.c cleanup_milter.c
 OBJS   = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \
        cleanup_extracted.o cleanup_state.o cleanup_rewrite.o \
        cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \
        cleanup_out_recipient.o cleanup_init.o cleanup_api.o \
-       cleanup_addr.o cleanup_bounce.o
+       cleanup_addr.o cleanup_bounce.o cleanup_milter.o
 HDRS   =
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
-TESTPROG= cleanup_masquerade
+TESTPROG= cleanup_masquerade cleanup_milter
 PROG   = cleanup
 INC_DIR        = ../../include
-LIBS   = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a
+LIBS   = ../../lib/libmaster.a ../../lib/libmilter.a ../../lib/libglobal.a \
+       ../../lib/libutil.a
 
 .c.o:; $(CC) $(CFLAGS) -c $*.c
 
@@ -57,7 +58,14 @@ cleanup_masquerade: cleanup_masquerade.o
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS)
        mv junk cleanup_masquerade.o
 
-tests: cleanup_masquerade_test
+CLEANUP_MILTER_OBJS = cleanup_state.o cleanup_out.o cleanup_addr.o \
+       cleanup_out_recipient.o 
+cleanup_milter: cleanup_milter.o $(CLEANUP_MILTER_OBJS)
+       mv cleanup_milter.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(CLEANUP_MILTER_OBJS) $(LIBS) $(SYSLIBS)
+       mv junk cleanup_milter.o
+
+tests: cleanup_masquerade_test cleanup_milter_test
 
 root_tests:
 
@@ -74,6 +82,17 @@ cleanup_masquerade_test: cleanup_masquerade cleanup_masq.ref
        diff cleanup_masq.ref cleanup_masq.tmp
        rm -f cleanup_masq.tmp
 
+# Test queue file editing routines.
+
+cleanup_milter_test: cleanup_milter cleanup_milter.in1 cleanup_milter.ref1 \
+       test-queue-file ../postcat/postcat
+       cp test-queue-file test-queue-file.tmp
+       chmod u+w test-queue-file.tmp
+       ./cleanup_milter <cleanup_milter.in1
+       ../postcat/postcat -ov test-queue-file.tmp 2>/dev/null >cleanup_milter.tmp
+       diff cleanup_milter.ref1 cleanup_milter.tmp
+       rm -f test-queue-file.tmp cleanup_milter.tmp
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -100,6 +119,7 @@ cleanup.o: ../../include/mail_stream.h
 cleanup.o: ../../include/maps.h
 cleanup.o: ../../include/match_list.h
 cleanup.o: ../../include/match_ops.h
+cleanup.o: ../../include/milter.h
 cleanup.o: ../../include/mime_state.h
 cleanup.o: ../../include/msg.h
 cleanup.o: ../../include/mymalloc.h
@@ -135,6 +155,7 @@ cleanup_addr.o: ../../include/mail_stream.h
 cleanup_addr.o: ../../include/maps.h
 cleanup_addr.o: ../../include/match_list.h
 cleanup_addr.o: ../../include/match_ops.h
+cleanup_addr.o: ../../include/milter.h
 cleanup_addr.o: ../../include/mime_state.h
 cleanup_addr.o: ../../include/msg.h
 cleanup_addr.o: ../../include/mymalloc.h
@@ -171,6 +192,7 @@ cleanup_api.o: ../../include/mail_stream.h
 cleanup_api.o: ../../include/maps.h
 cleanup_api.o: ../../include/match_list.h
 cleanup_api.o: ../../include/match_ops.h
+cleanup_api.o: ../../include/milter.h
 cleanup_api.o: ../../include/mime_state.h
 cleanup_api.o: ../../include/msg.h
 cleanup_api.o: ../../include/msg_stats.h
@@ -208,6 +230,7 @@ cleanup_bounce.o: ../../include/mail_stream.h
 cleanup_bounce.o: ../../include/maps.h
 cleanup_bounce.o: ../../include/match_list.h
 cleanup_bounce.o: ../../include/match_ops.h
+cleanup_bounce.o: ../../include/milter.h
 cleanup_bounce.o: ../../include/mime_state.h
 cleanup_bounce.o: ../../include/msg.h
 cleanup_bounce.o: ../../include/msg_stats.h
@@ -243,6 +266,7 @@ cleanup_envelope.o: ../../include/mail_stream.h
 cleanup_envelope.o: ../../include/maps.h
 cleanup_envelope.o: ../../include/match_list.h
 cleanup_envelope.o: ../../include/match_ops.h
+cleanup_envelope.o: ../../include/milter.h
 cleanup_envelope.o: ../../include/mime_state.h
 cleanup_envelope.o: ../../include/msg.h
 cleanup_envelope.o: ../../include/mymalloc.h
@@ -278,6 +302,7 @@ cleanup_extracted.o: ../../include/mail_stream.h
 cleanup_extracted.o: ../../include/maps.h
 cleanup_extracted.o: ../../include/match_list.h
 cleanup_extracted.o: ../../include/match_ops.h
+cleanup_extracted.o: ../../include/milter.h
 cleanup_extracted.o: ../../include/mime_state.h
 cleanup_extracted.o: ../../include/msg.h
 cleanup_extracted.o: ../../include/mymalloc.h
@@ -308,9 +333,11 @@ cleanup_init.o: ../../include/mail_addr.h
 cleanup_init.o: ../../include/mail_conf.h
 cleanup_init.o: ../../include/mail_params.h
 cleanup_init.o: ../../include/mail_stream.h
+cleanup_init.o: ../../include/mail_version.h
 cleanup_init.o: ../../include/maps.h
 cleanup_init.o: ../../include/match_list.h
 cleanup_init.o: ../../include/match_ops.h
+cleanup_init.o: ../../include/milter.h
 cleanup_init.o: ../../include/mime_state.h
 cleanup_init.o: ../../include/msg.h
 cleanup_init.o: ../../include/mymalloc.h
@@ -338,6 +365,7 @@ cleanup_map11.o: ../../include/mail_stream.h
 cleanup_map11.o: ../../include/maps.h
 cleanup_map11.o: ../../include/match_list.h
 cleanup_map11.o: ../../include/match_ops.h
+cleanup_map11.o: ../../include/milter.h
 cleanup_map11.o: ../../include/mime_state.h
 cleanup_map11.o: ../../include/msg.h
 cleanup_map11.o: ../../include/mymalloc.h
@@ -366,6 +394,7 @@ cleanup_map1n.o: ../../include/mail_stream.h
 cleanup_map1n.o: ../../include/maps.h
 cleanup_map1n.o: ../../include/match_list.h
 cleanup_map1n.o: ../../include/match_ops.h
+cleanup_map1n.o: ../../include/milter.h
 cleanup_map1n.o: ../../include/mime_state.h
 cleanup_map1n.o: ../../include/msg.h
 cleanup_map1n.o: ../../include/mymalloc.h
@@ -392,6 +421,7 @@ cleanup_masquerade.o: ../../include/mail_stream.h
 cleanup_masquerade.o: ../../include/maps.h
 cleanup_masquerade.o: ../../include/match_list.h
 cleanup_masquerade.o: ../../include/match_ops.h
+cleanup_masquerade.o: ../../include/milter.h
 cleanup_masquerade.o: ../../include/mime_state.h
 cleanup_masquerade.o: ../../include/msg.h
 cleanup_masquerade.o: ../../include/mymalloc.h
@@ -430,6 +460,7 @@ cleanup_message.o: ../../include/mail_stream.h
 cleanup_message.o: ../../include/maps.h
 cleanup_message.o: ../../include/match_list.h
 cleanup_message.o: ../../include/match_ops.h
+cleanup_message.o: ../../include/milter.h
 cleanup_message.o: ../../include/mime_state.h
 cleanup_message.o: ../../include/msg.h
 cleanup_message.o: ../../include/mymalloc.h
@@ -449,18 +480,57 @@ cleanup_message.o: ../../include/vstream.h
 cleanup_message.o: ../../include/vstring.h
 cleanup_message.o: cleanup.h
 cleanup_message.o: cleanup_message.c
+cleanup_milter.o: ../../include/argv.h
+cleanup_milter.o: ../../include/attr.h
+cleanup_milter.o: ../../include/been_here.h
+cleanup_milter.o: ../../include/cleanup_user.h
+cleanup_milter.o: ../../include/dict.h
+cleanup_milter.o: ../../include/dsn_mask.h
+cleanup_milter.o: ../../include/header_opts.h
+cleanup_milter.o: ../../include/htable.h
+cleanup_milter.o: ../../include/iostuff.h
+cleanup_milter.o: ../../include/is_header.h
+cleanup_milter.o: ../../include/lex_822.h
+cleanup_milter.o: ../../include/mail_conf.h
+cleanup_milter.o: ../../include/mail_params.h
+cleanup_milter.o: ../../include/mail_proto.h
+cleanup_milter.o: ../../include/mail_stream.h
+cleanup_milter.o: ../../include/maps.h
+cleanup_milter.o: ../../include/match_list.h
+cleanup_milter.o: ../../include/match_ops.h
+cleanup_milter.o: ../../include/milter.h
+cleanup_milter.o: ../../include/mime_state.h
+cleanup_milter.o: ../../include/msg.h
+cleanup_milter.o: ../../include/mymalloc.h
+cleanup_milter.o: ../../include/nvtable.h
+cleanup_milter.o: ../../include/off_cvt.h
+cleanup_milter.o: ../../include/rec_attr_map.h
+cleanup_milter.o: ../../include/rec_type.h
+cleanup_milter.o: ../../include/record.h
+cleanup_milter.o: ../../include/resolve_clnt.h
+cleanup_milter.o: ../../include/string_list.h
+cleanup_milter.o: ../../include/stringops.h
+cleanup_milter.o: ../../include/sys_defs.h
+cleanup_milter.o: ../../include/tok822.h
+cleanup_milter.o: ../../include/vbuf.h
+cleanup_milter.o: ../../include/vstream.h
+cleanup_milter.o: ../../include/vstring.h
+cleanup_milter.o: cleanup.h
+cleanup_milter.o: cleanup_milter.c
 cleanup_out.o: ../../include/argv.h
 cleanup_out.o: ../../include/been_here.h
 cleanup_out.o: ../../include/cleanup_user.h
 cleanup_out.o: ../../include/dict.h
 cleanup_out.o: ../../include/header_opts.h
 cleanup_out.o: ../../include/htable.h
+cleanup_out.o: ../../include/lex_822.h
 cleanup_out.o: ../../include/mail_conf.h
 cleanup_out.o: ../../include/mail_params.h
 cleanup_out.o: ../../include/mail_stream.h
 cleanup_out.o: ../../include/maps.h
 cleanup_out.o: ../../include/match_list.h
 cleanup_out.o: ../../include/match_ops.h
+cleanup_out.o: ../../include/milter.h
 cleanup_out.o: ../../include/mime_state.h
 cleanup_out.o: ../../include/msg.h
 cleanup_out.o: ../../include/mymalloc.h
@@ -468,6 +538,7 @@ cleanup_out.o: ../../include/nvtable.h
 cleanup_out.o: ../../include/rec_type.h
 cleanup_out.o: ../../include/record.h
 cleanup_out.o: ../../include/resolve_clnt.h
+cleanup_out.o: ../../include/split_at.h
 cleanup_out.o: ../../include/string_list.h
 cleanup_out.o: ../../include/sys_defs.h
 cleanup_out.o: ../../include/tok822.h
@@ -498,6 +569,7 @@ cleanup_out_recipient.o: ../../include/mail_stream.h
 cleanup_out_recipient.o: ../../include/maps.h
 cleanup_out_recipient.o: ../../include/match_list.h
 cleanup_out_recipient.o: ../../include/match_ops.h
+cleanup_out_recipient.o: ../../include/milter.h
 cleanup_out_recipient.o: ../../include/mime_state.h
 cleanup_out_recipient.o: ../../include/msg.h
 cleanup_out_recipient.o: ../../include/msg_stats.h
@@ -528,6 +600,7 @@ cleanup_rewrite.o: ../../include/mail_stream.h
 cleanup_rewrite.o: ../../include/maps.h
 cleanup_rewrite.o: ../../include/match_list.h
 cleanup_rewrite.o: ../../include/match_ops.h
+cleanup_rewrite.o: ../../include/milter.h
 cleanup_rewrite.o: ../../include/mime_state.h
 cleanup_rewrite.o: ../../include/msg.h
 cleanup_rewrite.o: ../../include/mymalloc.h
@@ -558,6 +631,7 @@ cleanup_state.o: ../../include/mail_stream.h
 cleanup_state.o: ../../include/maps.h
 cleanup_state.o: ../../include/match_list.h
 cleanup_state.o: ../../include/match_ops.h
+cleanup_state.o: ../../include/milter.h
 cleanup_state.o: ../../include/mime_state.h
 cleanup_state.o: ../../include/mymalloc.h
 cleanup_state.o: ../../include/nvtable.h
index bc3ea6d770ac3d81ee605f66085e0a1110219007..6e555ca10c6d5e4991b82f181ce34c45c6ca1d84 100644 (file)
 /* .IP "\fBmessage_strip_characters (empty)\fR"
 /*     The set of characters that Postfix will remove from message
 /*     content.
+/* BEFORE QUEUE MILTER CONTROLS
+/* .ad
+/* .fi
+/*     As of version 2.3, Postfix supports the Sendmail version 8
+/*     Milter (mail filter) protocol. When mail is not received via
+/*     the smtpd(8) server, the cleanup(8) server will simulate
+/*     SMTP events to the extent that this is possible. For details
+/*     see the MILTER_README document.
+/* .IP "\fBnon_smtpd_milters (empty)\fR"
+/*     A list of Milter (mail filter) applications for new mail that
+/*     does not arrive via the Postfix \fBsmtpd\fR(8) server.
+/* .IP "\fBmilter_protocol (2)\fR"
+/*     The mail filter protocol version and optional protocol extensions
+/*     for communication with a Milter (mail filter) application.
+/* .IP "\fBmilter_default_action (tempfail)\fR"
+/*     The default action when a Milter (mail filter) application is
+/*     unavailable or mis-configured.
+/* .IP "\fBmilter_macro_daemon_name ($myhostname)\fR"
+/*     The {daemon_name} macro value for Milter (mail filter) applications.
+/* .IP "\fBmilter_macro_v ($mail_name $mail_version)\fR"
+/*     The {v} macro value for Milter (mail filter) applications.
+/* .IP "\fBmilter_connect_timeout (30s)\fR"
+/*     The time limit for connecting to a Milter (mail filter)
+/*     application, and for negotiating protocol options.
+/* .IP "\fBmilter_command_timeout (30s)\fR"
+/*     The time limit for sending an SMTP command to a Milter (mail
+/*     filter) application, and for receiving the response.
+/* .IP "\fBmilter_content_timeout (300s)\fR"
+/*     The time limit for sending message content to a Milter (mail
+/*     filter) application, and for receiving the response.
+/* .IP "\fBmilter_connect_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after completion of an SMTP connection.
+/* .IP "\fBmilter_helo_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the SMTP HELO or EHLO command.
+/* .IP "\fBmilter_mail_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the SMTP MAIL FROM command.
+/* .IP "\fBmilter_rcpt_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the SMTP RCPT TO command.
+/* .IP "\fBmilter_data_macros (see postconf -n output)\fR"
+/*     The macros that are sent to version 4 or higher Milter (mail
+/*     filter) applications after the SMTP DATA command.
+/* .IP "\fBmilter_unknown_command_macros (see postconf -n output)\fR"
+/*     The macros that are sent to version 3 or higher Milter (mail
+/*     filter) applications after an unknown SMTP command.
+/* .IP "\fBmilter_end_of_data_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the message end-of-data.
 /* MIME PROCESSING CONTROLS
 /* .ad
 /* .fi
 /* .na
 /* .nf
 /*     ADDRESS_REWRITING_README Postfix address manipulation
+/*     CONTENT_INSPECTION_README content inspection
 /* LICENSE
 /* .ad
 /* .fi
@@ -362,7 +414,7 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
     /*
      * Open a queue file and initialize state.
      */
-    state = cleanup_open();
+    state = cleanup_open(src);
 
     /*
      * Send the queue id to the client. Read client processing options. If we
@@ -388,7 +440,14 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
      * extracted from message headers.
      */
     while (CLEANUP_OUT_OK(state)) {
-       if ((type = rec_get(src, buf, 0)) < 0) {
+       if ((type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) < 0) {
+           state->errs |= CLEANUP_STAT_BAD;
+           break;
+       }
+       if (type == REC_TYPE_PTR || type == REC_TYPE_DTXT
+           || type == REC_TYPE_DRCP) {
+           msg_warn("%s: record type %d not allowed - discarding this message",
+                    state->queue_id, type);
            state->errs |= CLEANUP_STAT_BAD;
            break;
        }
index 04b5bc4990de2ef6123c17f7b89aa20132d379c8..2db16f4cfd904c956620ada06fea79e4afa947bd 100644 (file)
 #include <mime_state.h>
 #include <string_list.h>
 
+ /*
+  * Milter library.
+  */
+#include <milter.h>
+
  /*
   * These state variables are accessed by many functions, and there is only
   * one instance of each per message.
@@ -41,6 +46,7 @@ typedef struct CLEANUP_STATE {
     VSTRING *temp1;                    /* scratch buffer, local use only */
     VSTRING *temp2;                    /* scratch buffer, local use only */
     VSTRING *stripped_buf;             /* character stripped input */
+    VSTREAM *src;                      /* current input stream */
     VSTREAM *dst;                      /* current output stream */
     MAIL_STREAM *handle;               /* mail stream handle */
     char   *queue_name;                        /* queue name */
@@ -63,6 +69,10 @@ typedef struct CLEANUP_STATE {
     void    (*action) (struct CLEANUP_STATE *, int, const char *, ssize_t);
     off_t   data_offset;               /* start of message content */
     off_t   xtra_offset;               /* start of extra segment */
+    off_t   append_rcpt_pt_offset;     /* append recipient here */
+    off_t   append_rcpt_pt_target;     /* target of above record */
+    off_t   append_hdr_pt_offset;      /* append header here */
+    off_t   append_hdr_pt_target;      /* target of above record */
     ssize_t rcpt_count;                        /* recipient count */
     char   *reason;                    /* failure reason */
     NVTABLE *attr;                     /* queue file attribute list */
@@ -79,6 +89,7 @@ typedef struct CLEANUP_STATE {
 #ifdef DELAY_ACTION
     int     defer_delay;               /* deferred delivery */
 #endif
+    MILTERS *milters;                  /* mail filters */
 } CLEANUP_STATE;
 
  /*
@@ -114,6 +125,11 @@ extern MAPS *cleanup_rcpt_bcc_maps;
 extern VSTRING *cleanup_reject_chars;
 extern VSTRING *cleanup_strip_chars;
 
+ /*
+  * Milters.
+  */
+extern MILTERS *cleanup_milters;
+
  /*
   * Address canonicalization fine control.
   */
@@ -146,13 +162,13 @@ extern VSTRING *cleanup_bounce_path;
  /*
   * cleanup_state.c
   */
-extern CLEANUP_STATE *cleanup_state_alloc(void);
+extern CLEANUP_STATE *cleanup_state_alloc(VSTREAM *);
 extern void cleanup_state_free(CLEANUP_STATE *);
 
  /*
   * cleanup_api.c
   */
-extern CLEANUP_STATE *cleanup_open(void);
+extern CLEANUP_STATE *cleanup_open(VSTREAM *);
 extern void cleanup_control(CLEANUP_STATE *, int);
 extern int cleanup_flush(CLEANUP_STATE *);
 extern void cleanup_free(CLEANUP_STATE *);
@@ -173,6 +189,7 @@ extern CONFIG_TIME_TABLE cleanup_time_table[];
 extern void cleanup_out(CLEANUP_STATE *, int, const char *, ssize_t);
 extern void cleanup_out_string(CLEANUP_STATE *, int, const char *);
 extern void PRINTFLIKE(3, 4) cleanup_out_format(CLEANUP_STATE *, int, const char *,...);
+extern void cleanup_out_header(CLEANUP_STATE *, VSTRING *);
 
 #define CLEANUP_OUT_BUF(s, t, b) \
        cleanup_out((s), (t), vstring_str((b)), VSTRING_LEN((b)))
@@ -244,6 +261,19 @@ extern int cleanup_bounce(CLEANUP_STATE *);
 #define CLEANUP_MSG_STATS(stats, state) \
     MSG_STATS_INIT1(stats, incoming_arrival, state->arrival_time)
 
+ /*
+  * cleanup_milter.c.
+  */
+extern void cleanup_milter_receive(CLEANUP_STATE *, int);
+extern void cleanup_milter_inspect(CLEANUP_STATE *, MILTERS *);
+extern void cleanup_milter_emul_mail(CLEANUP_STATE *, MILTERS *, const char *);
+extern void cleanup_milter_emul_rcpt(CLEANUP_STATE *, MILTERS *, const char *);
+extern void cleanup_milter_emul_data(CLEANUP_STATE *, MILTERS *);
+
+#define CLEANUP_MILTER_OK(s) \
+    (((s)->flags & CLEANUP_FLAG_MILTER) != 0 \
+       && (s)->errs == 0 && ((s)->flags & CLEANUP_FLAG_DISCARD) == 0)
+
 /* LICENSE
 /* .ad
 /* .fi
index 1e1f20cd38b22aac0a96b82038824c89994bbe75..49123a90f9518304902f3d1e57c87b94136002c0 100644 (file)
@@ -187,6 +187,8 @@ void    cleanup_addr_bcc(CLEANUP_STATE *state, const char *bcc)
      * Note: BCC addresses are supplied locally, and must be rewritten in the
      * local address rewriting context.
      */
+#define NO_DSN_ORCPT   ((char *) 0)
+
     cleanup_rewrite_internal(MAIL_ATTR_RWR_LOCAL, clean_addr, bcc);
     if (state->flags & CLEANUP_FLAG_MAP_OK) {
        if (cleanup_rcpt_canon_maps
@@ -201,7 +203,7 @@ void    cleanup_addr_bcc(CLEANUP_STATE *state, const char *bcc)
            && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
            cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
     }
-    cleanup_out_recipient(state, (char *) 0, DSN_NOTIFY_NEVER,
+    cleanup_out_recipient(state, NO_DSN_ORCPT, DSN_NOTIFY_NEVER,
                          STR(clean_addr), STR(clean_addr));
     vstring_free(clean_addr);
 }
index 524e6c4aa4c517fe50928b6244a50b3768ea7750..4769289b80c061c65972c81f935334cfa9fdc146 100644 (file)
@@ -6,7 +6,8 @@
 /* SYNOPSIS
 /*     #include "cleanup.h"
 /*
-/*     CLEANUP_STATE *cleanup_open()
+/*     CLEANUP_STATE *cleanup_open(src)
+/*     VSTREAM *src;
 /*
 /*     void    cleanup_control(state, flags)
 /*     CLEANUP_STATE *state;
 /*     Enable header/body filtering. This should be enabled only with mail
 /*     that enters Postfix, not with locally forwarded mail or with bounce
 /*     messages.
+/* .IP CLEANUP_FLAG_MILTER
+/*     Enable Milter applications. This should be enabled only with mail
+/*     that enters Postfix, not with locally forwarded mail or with bounce
+/*     messages.
 /* .IP CLEANUP_FLAG_MAP_OK
 /*     Enable canonical and virtual mapping, and address masquerading.
 /* .PP
 #include <mail_stream.h>
 #include <mail_flow.h>
 
+/* Milter library. */
+
+#include <milter.h>
+
 /* Application-specific. */
 
 #include "cleanup.h"
 
 /* cleanup_open - open queue file and initialize */
 
-CLEANUP_STATE *cleanup_open(void)
+CLEANUP_STATE *cleanup_open(VSTREAM *src)
 {
     CLEANUP_STATE *state;
-    static char *log_queues[] = {
+    static const char *log_queues[] = {
        MAIL_QUEUE_DEFER,
        MAIL_QUEUE_BOUNCE,
        MAIL_QUEUE_TRACE,
        0,
     };
-    char  **cpp;
+    const char **cpp;
 
     /*
      * Initialize private state.
      */
-    state = cleanup_state_alloc();
+    state = cleanup_state_alloc(src);
 
     /*
      * Open the queue file. Save the queue file name in a global variable, so
@@ -210,6 +219,21 @@ int     cleanup_flush(CLEANUP_STATE *state)
     if (state->flags & CLEANUP_FLAG_DISCARD)
        state->errs = 0;
 
+    /*
+     * Apply external mail filter.
+     * 
+     * XXX Include test for a built-in action to tempfail this message.
+     */
+    if (CLEANUP_MILTER_OK(state)) {
+       if (state->milters)
+           cleanup_milter_inspect(state, state->milters);
+       else if (cleanup_milters) {
+           cleanup_milter_emul_data(state, cleanup_milters);
+           if (CLEANUP_MILTER_OK(state))
+               cleanup_milter_inspect(state, cleanup_milters);
+       }
+    }
+
     /*
      * If there was an error that requires us to generate a bounce message
      * (mail submitted with the Postfix sendmail command, mail forwarded by
@@ -247,6 +271,8 @@ int     cleanup_flush(CLEANUP_STATE *state)
      * (or else the queue manager would grab it too early) and updating our
      * own idea of the queue file name for error recovery and for error
      * reporting purposes.
+     * 
+     * XXX Include test for a built-in action to tempfail this message.
      */
     if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) {
        if ((state->flags & CLEANUP_FLAG_HOLD) != 0
@@ -336,5 +362,12 @@ int     cleanup_flush(CLEANUP_STATE *state)
 
 void    cleanup_free(CLEANUP_STATE *state)
 {
+
+    /*
+     * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after
+     * we have started.
+     */
+    if (cleanup_milters != 0 && state->milters == 0)
+       milter_disc_event(cleanup_milters);
     cleanup_state_free(state);
 }
index cc7ca9a642bce129e0c53e3f9022d5463dae9bec..e6deb9586997ff1821176cfa9fbb569f5784c94a 100644 (file)
@@ -179,6 +179,7 @@ int     cleanup_bounce(CLEANUP_STATE *state)
            (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
            cleanup_bounce_append(state, &recipient, &dsn);
            /* FALLTHROUGH */
+       case REC_TYPE_DRCP:                     /* canceled recipient */
        case REC_TYPE_DONE:                     /* can't happen */
            if (orig_rcpt != 0) {
                myfree(orig_rcpt);
index ba296e044ef86b591611f684783ce988fd1bc558..eb4171bdfe53a06ebe8d13392d4f3dc42f5009f6 100644 (file)
@@ -106,6 +106,7 @@ void    cleanup_envelope(CLEANUP_STATE *state, int type,
 static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
                                             const char *buf, ssize_t len)
 {
+    const char *myname = "cleanup_envelope_process";
     char   *attr_name;
     char   *attr_value;
     const char *error_text;
@@ -113,6 +114,7 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
     int     junk;
     int     mapped_type = type;
     const char *mapped_buf = buf;
+    int     milter_count;
 
 #ifdef DELAY_ACTION
     int     defer_delay;
@@ -143,6 +145,18 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
        return;
     }
 #endif
+    if (type == REC_TYPE_MILT_COUNT) {
+       /* Not part of queue file format. */
+       if (state->milters != 0) {
+           msg_warn("%s: message rejected: too many milter instances",
+                    state->queue_id);
+           state->errs |= CLEANUP_STAT_BAD;
+           return;
+       }
+       if ((milter_count = atoi(buf)) > 0)
+           cleanup_milter_receive(state, milter_count);
+       return;
+    }
 
     /*
      * Map DSN attribute name to pseudo record type so that we don't have to
@@ -247,6 +261,10 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
        if (state->orig_rcpt == 0)
            state->orig_rcpt = mystrdup(buf);
        cleanup_addr_recipient(state, buf);
+       if (cleanup_milters != 0
+           && state->milters == 0
+           && CLEANUP_MILTER_OK(state))
+           cleanup_milter_emul_rcpt(state, cleanup_milters, buf);
        myfree(state->orig_rcpt);
        state->orig_rcpt = 0;
        if (state->dsn_orcpt != 0) {
@@ -256,7 +274,7 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
        state->dsn_notify = 0;
        return;
     }
-    if (type == REC_TYPE_DONE) {
+    if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) {
        if (state->orig_rcpt != 0) {
            myfree(state->orig_rcpt);
            state->orig_rcpt = 0;
@@ -303,7 +321,17 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
     }
     if (type == REC_TYPE_MESG) {
        state->action = cleanup_message;
-       state->flags &= ~CLEANUP_FLAG_INRCPT;
+       if (state->flags & CLEANUP_FLAG_INRCPT) {
+           if (state->milters || cleanup_milters) {
+               /* Make room to append recipient. */
+               if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0)
+                   msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
+               cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
+               if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0)
+                   msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
+           }
+           state->flags &= ~CLEANUP_FLAG_INRCPT;
+       }
        return;
     }
 
@@ -347,6 +375,10 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
            return;
        }
        cleanup_addr_sender(state, buf);
+       if (cleanup_milters != 0
+           && state->milters == 0
+           && CLEANUP_MILTER_OK(state))
+           cleanup_milter_emul_mail(state, cleanup_milters, buf);
        return;
     }
     if (mapped_type == REC_TYPE_DSN_ENVID) {
index 9f09b8dcee1e42b9488de406299b497cbcf811d0..60f0ab345e267dedeb2d97800cd4dda70decb596 100644 (file)
@@ -98,6 +98,7 @@ void    cleanup_extracted(CLEANUP_STATE *state, int type,
 void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
                                          const char *buf, ssize_t len)
 {
+    const char *myname = "cleanup_extracted_process";
     const char *encoding;
     char   *attr_name;
     char   *attr_value;
@@ -123,7 +124,6 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
            state->flags |= extra_opts;
        return;
     }
-
 #ifdef DELAY_ACTION
     if (type == REC_TYPE_DELAY) {
        /* Not part of queue file format. */
@@ -200,6 +200,10 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
        if (state->orig_rcpt == 0)
            state->orig_rcpt = mystrdup(buf);
        cleanup_addr_recipient(state, buf);
+       if (cleanup_milters != 0
+           && state->milters == 0
+           && CLEANUP_MILTER_OK(state))
+           cleanup_milter_emul_rcpt(state, cleanup_milters, buf);
        myfree(state->orig_rcpt);
        state->orig_rcpt = 0;
        if (state->dsn_orcpt != 0) {
@@ -209,7 +213,7 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
        state->dsn_notify = 0;
        return;
     }
-    if (type == REC_TYPE_DONE) {
+    if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) {
        if (state->orig_rcpt != 0) {
            myfree(state->orig_rcpt);
            state->orig_rcpt = 0;
@@ -254,6 +258,15 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
        return;
     }
     if (type == REC_TYPE_END) {
+       /* Make room to append recipient. */
+       if ((state->milters || cleanup_milters)
+           && state->append_rcpt_pt_offset < 0) {
+           if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0)
+               msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
+           cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
+           if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0)
+               msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
+       }
        state->flags &= ~CLEANUP_FLAG_INRCPT;
        state->flags |= CLEANUP_FLAG_END_SEEN;
        cleanup_extracted_finish(state);
index 797609778b50f14df0678857599af4e0e7ed3e6a..2791f32e6fe1c59f94f796c2b99035f954297d66 100644 (file)
@@ -90,6 +90,7 @@
 
 #include <mail_addr.h>
 #include <mail_params.h>
+#include <mail_version.h>              /* milter_macro_v */
 #include <ext_prop.h>
 #include <flush_clnt.h>
 
@@ -144,6 +145,21 @@ char   *var_remote_rwr_domain;             /* header-only surrogate */
 char   *var_msg_reject_chars;          /* reject these characters */
 char   *var_msg_strip_chars;           /* strip these characters */
 int     var_verp_bounce_off;           /* don't verp the bounces */
+int     var_milt_conn_time;            /* milter connect/handshake timeout */
+int     var_milt_cmd_time;             /* milter command timeout */
+int     var_milt_msg_time;             /* milter content timeout */
+char   *var_milt_protocol;             /* Sendmail 8 milter protocol */
+char   *var_milt_def_action;           /* default milter action */
+char   *var_milt_daemon_name;          /* {daemon_name} macro value */
+char   *var_milt_v;                    /* {v} macro value */
+char   *var_milt_conn_macros;          /* connect macros */
+char   *var_milt_helo_macros;          /* HELO macros */
+char   *var_milt_mail_macros;          /* MAIL FROM macros */
+char   *var_milt_rcpt_macros;          /* RCPT TO macros */
+char   *var_milt_data_macros;          /* DATA macros */
+char   *var_milt_eod_macros;           /* end-of-data macros */
+char   *var_milt_unk_macros;           /* unknown command macros */
+char   *var_cleanup_milters;           /* non-SMTP mail */
 
 CONFIG_INT_TABLE cleanup_int_table[] = {
     VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
@@ -163,6 +179,9 @@ CONFIG_BOOL_TABLE cleanup_bool_table[] = {
 
 CONFIG_TIME_TABLE cleanup_time_table[] = {
     VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
+    VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, &var_milt_conn_time, 1, 0,
+    VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, &var_milt_cmd_time, 1, 0,
+    VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, &var_milt_msg_time, 1, 0,
     0,
 };
 
@@ -190,6 +209,18 @@ CONFIG_STR_TABLE cleanup_str_table[] = {
     VAR_REM_RWR_DOMAIN, DEF_REM_RWR_DOMAIN, &var_remote_rwr_domain, 0, 0,
     VAR_MSG_REJECT_CHARS, DEF_MSG_REJECT_CHARS, &var_msg_reject_chars, 0, 0,
     VAR_MSG_STRIP_CHARS, DEF_MSG_STRIP_CHARS, &var_msg_strip_chars, 0, 0,
+    VAR_MILT_PROTOCOL, DEF_MILT_PROTOCOL, &var_milt_protocol, 1, 0,
+    VAR_MILT_DEF_ACTION, DEF_MILT_DEF_ACTION, &var_milt_def_action, 1, 0,
+    VAR_MILT_DAEMON_NAME, DEF_MILT_DAEMON_NAME, &var_milt_daemon_name, 1, 0,
+    VAR_MILT_V, DEF_MILT_V, &var_milt_v, 1, 0,
+    VAR_MILT_CONN_MACROS, DEF_MILT_CONN_MACROS, &var_milt_conn_macros, 0, 0,
+    VAR_MILT_HELO_MACROS, DEF_MILT_HELO_MACROS, &var_milt_helo_macros, 0, 0,
+    VAR_MILT_MAIL_MACROS, DEF_MILT_MAIL_MACROS, &var_milt_mail_macros, 0, 0,
+    VAR_MILT_RCPT_MACROS, DEF_MILT_RCPT_MACROS, &var_milt_rcpt_macros, 0, 0,
+    VAR_MILT_DATA_MACROS, DEF_MILT_DATA_MACROS, &var_milt_data_macros, 0, 0,
+    VAR_MILT_EOD_MACROS, DEF_MILT_EOD_MACROS, &var_milt_eod_macros, 0, 0,
+    VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0,
+    VAR_CLEANUP_MILTERS, DEF_CLEANUP_MILTERS, &var_cleanup_milters, 0, 0,
     0,
 };
 
@@ -224,6 +255,11 @@ VSTRING *cleanup_strip_chars;
   */
 int     cleanup_ext_prop_mask;
 
+ /*
+  * Milter support.
+  */
+MILTERS *cleanup_milters;
+
 /* cleanup_all - callback for the runtime error handler */
 
 void    cleanup_all(void)
@@ -344,6 +380,20 @@ void    cleanup_pre_jail(char *unused_name, char **unused_argv)
        cleanup_rcpt_bcc_maps =
            maps_create(VAR_RCPT_BCC_MAPS, var_rcpt_bcc_maps,
                        DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+    if (*var_cleanup_milters)
+       cleanup_milters = milter_create(var_cleanup_milters,
+                                      var_milt_conn_time,
+                                      var_milt_cmd_time,
+                                      var_milt_msg_time,
+                                      var_milt_protocol,
+                                      var_milt_def_action,
+                                      var_milt_conn_macros,
+                                      var_milt_helo_macros,
+                                      var_milt_mail_macros,
+                                      var_milt_rcpt_macros,
+                                      var_milt_data_macros,
+                                      var_milt_eod_macros,
+                                      var_milt_unk_macros);
 
     flush_init();
 }
index 62a0ec7eb08ba0b2c2edea11c7b9f01d710c9a18..c990cdd7c6e71614324f25484116d6b7b1f93361 100644 (file)
 
 #include "cleanup.h"
 
-/* cleanup_out_header - output one header as a bunch of records */
-
-static void cleanup_out_header(CLEANUP_STATE *state, VSTRING *header_buf)
-{
-    char   *start = vstring_str(header_buf);
-    char   *line;
-    char   *next_line;
-
-    /*
-     * Prepend a tab to continued header lines that went through the address
-     * rewriting machinery. See cleanup_fold_header(state) below for the form
-     * of such header lines. NB: This code destroys the header. We could try
-     * to avoid clobbering it, but we're not going to use the data any
-     * further.
-     */
-    for (line = start; line; line = next_line) {
-       next_line = split_at(line, '\n');
-       if (line == start || IS_SPACE_TAB(*line)) {
-           cleanup_out_string(state, REC_TYPE_NORM, line);
-       } else {
-           cleanup_out_format(state, REC_TYPE_NORM, "\t%s", line);
-       }
-    }
-}
-
 /* cleanup_fold_header - wrap address list header */
 
 static void cleanup_fold_header(CLEANUP_STATE *state, VSTRING *header_buf)
@@ -275,7 +250,7 @@ static void cleanup_act_log(CLEANUP_STATE *state,
 {
     const char *attr;
 
-    if ((attr = nvtable_find(state->attr, MAIL_ATTR_ORIGIN)) == 0)
+    if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0)
        attr = "unknown";
     vstring_sprintf(state->temp1, "%s: %s: %s %.200s from %s;",
                    state->queue_id, action, class, content, attr);
@@ -283,9 +258,9 @@ static void cleanup_act_log(CLEANUP_STATE *state,
        vstring_sprintf_append(state->temp1, " from=<%s>", state->sender);
     if (state->recip)
        vstring_sprintf_append(state->temp1, " to=<%s>", state->recip);
-    if ((attr = nvtable_find(state->attr, MAIL_ATTR_PROTO_NAME)) != 0)
+    if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0)
        vstring_sprintf_append(state->temp1, " proto=%s", attr);
-    if ((attr = nvtable_find(state->attr, MAIL_ATTR_HELO_NAME)) != 0)
+    if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0)
        vstring_sprintf_append(state->temp1, " helo=<%s>", attr);
     if (text && *text)
        vstring_sprintf_append(state->temp1, ": %s", text);
@@ -317,44 +292,31 @@ static const char *cleanup_act(CLEANUP_STATE *state, char *context,
 #define CLEANUP_ACT_DROP 0
 
     /*
-     * CLEANUP_STAT_CONT causes cleanup(8) to send bounces if
-     * CLEANUP_FLAG_BOUNCE is set, which causes pickup(8) to throw away the
-     * queue file after cleanup(8) reports success.
-     * 
-     * This is wrong in the case of temporary rejects. Another problem is that
-     * cleanup(8) clients look at the state->reason value only when
-     * CLEANUP_STAT_CONT is set.
-     * 
-     * We could kludge around this in the cleanup server by ignoring
-     * CLEANUP_FLAG_BOUNCE for temporary rejects, but that is fragile. It
-     * exposes clients to status codes that they until now never had to
-     * handle.
-     * 
-     * As a safe workaround for temporary rejects we return CLEANUP_STAT_WRITE.
-     * But we really want to report the true cause (server configuration
-     * error or otherwise).
+     * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason
+     * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates
+     * queue record processing, and prevents bounces from being sent.
      */
     if (STREQUAL(value, "REJECT", command_len)) {
        CLEANUP_STAT_DETAIL *detail;
 
-       if (state->reason == 0) {
-           if (*optional_text) {
-               state->reason = dsn_prepend("5.7.1", optional_text);
-               if (*state->reason != '4' && *state->reason != '5') {
-                   msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
-                            optional_text);
-                   *state->reason = '4';
-               }
-           } else {
-               detail = cleanup_stat_detail(CLEANUP_STAT_CONT);
-               state->reason = dsn_prepend(detail->dsn, detail->text);
+       if (state->reason)
+           myfree(state->reason);
+       if (*optional_text) {
+           state->reason = dsn_prepend("5.7.1", optional_text);
+           if (*state->reason != '4' && *state->reason != '5') {
+               msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
+                        optional_text);
+               *state->reason = '4';
            }
+       } else {
+           detail = cleanup_stat_detail(CLEANUP_STAT_CONT);
+           state->reason = dsn_prepend(detail->dsn, detail->text);
        }
        if (*state->reason == '4')
-           state->errs = CLEANUP_STAT_WRITE;
+           state->errs |= CLEANUP_STAT_DEFER;
        else
            state->errs |= CLEANUP_STAT_CONT;
-       state->flags &= ~CLEANUP_FLAG_FILTER;
+       state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
        cleanup_act_log(state, "reject", context, buf, state->reason);
        return (buf);
     }
@@ -380,7 +342,7 @@ static const char *cleanup_act(CLEANUP_STATE *state, char *context,
     if (STREQUAL(value, "DISCARD", command_len)) {
        cleanup_act_log(state, "discard", context, buf, optional_text);
        state->flags |= CLEANUP_FLAG_DISCARD;
-       state->flags &= ~CLEANUP_FLAG_FILTER;
+       state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
        return (buf);
     }
     if (STREQUAL(value, "HOLD", command_len)) {
@@ -453,7 +415,7 @@ static const char *cleanup_act(CLEANUP_STATE *state, char *context,
                myfree(state->redirect);
            state->redirect = mystrdup(optional_text);
            cleanup_act_log(state, "redirect", context, buf, optional_text);
-           state->flags &= ~CLEANUP_FLAG_FILTER;
+           state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
        }
        return (buf);
     }
@@ -627,6 +589,7 @@ static void cleanup_header_callback(void *context, int header_class,
 
 static void cleanup_header_done_callback(void *context)
 {
+    const char *myname = "cleanup_header_done_callback";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
     char    time_stamp[1024];          /* XXX locale dependent? */
     struct tm *tp;
@@ -720,6 +683,19 @@ static void cleanup_header_done_callback(void *context)
 
     if ((state->headers_seen & VISIBLE_RCPT) == 0)
        cleanup_out_format(state, REC_TYPE_NORM, "%s", var_rcpt_witheld);
+
+    /*
+     * Place a dummy PTR record right after the last header so that we can
+     * append headers without having to worry about clobbering the
+     * end-of-content marker.
+     */
+    if (state->milters || cleanup_milters) {
+       if ((state->append_hdr_pt_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+       cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
+       if ((state->append_hdr_pt_target = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+    }
 }
 
 /* cleanup_body_callback - output one body record */
@@ -821,6 +797,9 @@ static void cleanup_message_headerbody(CLEANUP_STATE *state, int type,
      * current file position so we can compute the message size lateron.
      */
     else if (type == REC_TYPE_XTRA) {
+       if (state->milters || cleanup_milters)
+           /* Make room for body modification. */
+           cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
        state->mime_errs = mime_state_update(state->mime_state, type, buf, len);
        /* Ignore header truncation after primary message headers. */
        state->mime_errs &= ~MIME_ERR_TRUNC_HEADER;
@@ -858,7 +837,7 @@ static void cleanup_mime_error_callback(void *context, int err_code,
      * primary message headers.
      */
     if ((err_code & ~MIME_ERR_TRUNC_HEADER) != 0) {
-       if ((origin = nvtable_find(state->attr, MAIL_ATTR_ORIGIN)) == 0)
+       if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0)
            origin = MAIL_ATTR_ORG_NONE;
 #define TEXT_LEN (len < 100 ? (int) len : 100)
        msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>",
@@ -871,7 +850,7 @@ static void cleanup_mime_error_callback(void *context, int err_code,
 
 void    cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t len)
 {
-    char   *myname = "cleanup_message";
+    const char *myname = "cleanup_message";
     int     mime_options;
 
     /*
diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c
new file mode 100644 (file)
index 0000000..9a2d47c
--- /dev/null
@@ -0,0 +1,1544 @@
+/*++
+/* NAME
+/*     cleanup_milter 3
+/* SUMMARY
+/*     external mail filter support
+/* SYNOPSIS
+/*     #include <cleanup.h>
+/*
+/*     void    cleanup_milter_receive(state, count)
+/*     CLEANUP_STATE *state;
+/*     int     count;
+/*
+/*     void    cleanup_milter_inspect(state, milters)
+/*     CLEANUP_STATE *state;
+/*     MILTERS *milters;
+/*
+/*     cleanup_milter_emul_mail(state, milters, sender)
+/*     CLEANUP_STATE *state;
+/*     MILTERS *milters;
+/*     const char *sender;
+/*
+/*     cleanup_milter_emul_rcpt(state, milters, recipient)
+/*     CLEANUP_STATE *state;
+/*     MILTERS *milters;
+/*     const char *recipient;
+/*
+/*     cleanup_milter_emul_data(state, milters)
+/*     CLEANUP_STATE *state;
+/*     MILTERS *milters;
+/* DESCRIPTION
+/*     This module implements support for Sendmail-style mail
+/*     filter (milter) applications, including in-place queue file
+/*     modification.
+/*
+/*     cleanup_milter_receive() receives mail filter definitions,
+/*     typically from an smtpd(8) server process, and registers
+/*     local call-back functions for macro expansion and for queue
+/*     file modification.
+/*
+/*     cleanup_milter_inspect() subjects a message to inspection
+/*     by mail filters. Each filter can accept or reject the message
+/*     and can request changes to the recipient list, to message
+/*     headers, and to replace the message body.
+/*
+/*     cleanup_milter_emul_mail() emulates connect, helo and mail
+/*     events for mail that does not arrive via the smtpd(8) server.
+/*     This pretends that mail arrives from localhost/127.0.0.1
+/*     via ESMTP.  This code reports a server configuration error
+/*     condition when the milter rejects the emulated commands.
+/*
+/*     cleanup_milter_emul_rcpt() emulates an rcpt() event for
+/*     non-SMTP mail. See cleanup_milter_emul_mail() for the
+/*     handling of reject replies.
+/*
+/*     cleanup_milter_emul_data() emulates a data event for non-SMTP
+/*     mail.  See cleanup_milter_emul_mail() for the handling of
+/*     reject replies.
+/* SEE ALSO
+/*     milter(3) generic mail filter interface
+/* BUGS
+/*     Postfix prepends its own Received: header when it receives
+/*     mail from outside, or when it forwards mail internally.
+/*     This header is seen by mail filters, and is present when
+/*     mail filters edit the queue file.
+/* DIAGNOSTICS
+/*     Fatal errors: memory allocation problem.
+/*     Panic: interface violation.
+/* 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 <sys/socket.h>                        /* AF_INET */
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <off_cvt.h>
+#include <dsn_mask.h>
+#include <rec_type.h>
+#include <cleanup_user.h>
+#include <record.h>
+#include <rec_attr_map.h>
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <lex_822.h>
+#include <is_header.h>
+
+/* Application-specific. */
+
+#include <cleanup.h>
+
+ /*
+  * How Postfix edits queue file information:
+  * 
+  * Mail filter applications (Milters) can send modification requests after
+  * receiving the end of the message body.  Postfix implements these
+  * modifications in the cleanup server, so that it can edit the queue file
+  * in place. This avoids the temporary files that would be needed when
+  * modifications were implemented in the SMTP server (where possible,
+  * Postfix does not store the whole message in main memory). Once a Milter
+  * is done editing, the queue file can be used as input for the next Milter,
+  * and so on. Finally, the cleanup server changes file permissions, calls
+  * fsync(), and waits for successful completion.
+  * 
+  * To implement in-place queue file edits, we need to introduce surprisingly
+  * little change to the existing Postfix queue file structure.  All we need
+  * is a way to mark a record as deleted, and to jump from one place in the
+  * queue file to another. We could implement deleted records with jumps, but
+  * marking is simpler, and it preserves information that may be useful for
+  * archival purposes.
+  * 
+  * Postfix does not store queue files as plain text files. Instead all
+  * information is stored in records with an explicit type and length for
+  * sender, recipient, arrival time, and so on.  Even the content that makes
+  * up the message header and body is stored as records with explicit types
+  * and lengths.  This organization makes it very easy to mark a record as
+  * deleted, and to introduce the pointer records that we will use to jump
+  * from one place in a queue file to another place.
+  * 
+  * - Deleting a recipient or message header is easiest - simply modify the
+  * record type into one that is skipped by the software that delivers mail.
+  * We won't try to reuse the deleted recipient or message header for other
+  * purposes. When deleting a recipient, we must delete all recipient records
+  * that result from virtual alias expansion of the original recipient
+  * address. When deleting a long message header, multiple queue file records
+  * may need to be deleted. We use REC_TYPE_DRCP for deleted recipients, and
+  * REC_TYPE_DTXT for deleted text.
+  * 
+  * - Replacing a header record involves pointer records. A record is replaced
+  * by overwriting it with a forward pointer to space after the end of the
+  * queue file, putting the new record there, followed by a reverse pointer
+  * to the record that follows the replaced information. If the replaced
+  * record is shorter than a pointer record, we relocate the records that
+  * follow it to the new area as well, until we have enough space for the
+  * forward pointer record. See below for a discussion on what it takes to
+  * make this safe. Sendmail mail filters currently do not replace individual
+  * body records, but we could add support for this if need be.
+  * 
+  * - Inserting a header record is like replacing one, except that we also
+  * relocate the record that is being overwritten by the forward pointer.
+  * 
+  * - Appending a recipient or header record involves pointer records as well.
+  * To make this convenient, the queue file already contains a dummy pointer
+  * record at the place where we want to append recipient or header content.
+  * To append, change the dummy pointer into a forward pointer to space after
+  * the end of a message, put the new recipient or header record there,
+  * followed by a reverse pointer to the record that follows the forward
+  * pointer.
+  * 
+  * - To append another record of the same type, replace the reverse pointer by
+  * a forward pointer to space after the end of a message, put the new record
+  * there, followed by the value of the reverse pointer that we replace.
+  * Thus, there is no one-to-one correspondence between forward and backward
+  * pointers. Instead, there can be multiple forward pointers for one reverse
+  * pointer.
+  * 
+  * Making queue file modifications safe:
+  * 
+  * Postfix queue files are segmented. The first segment is for envelope
+  * records, the second for message header and body content, and the third
+  * segment is for information that was extracted or generated from the
+  * message header or body content.  Each segment is terminated by a marker
+  * record. For now we don't want to change their location. That is, we want
+  * to avoid moving the records that mark the start or end of a queue file
+  * segment.
+  * 
+  * To ensure that we can always replace a header or body record by a pointer
+  * record, without having to relocate a marker record, the cleanup server
+  * places a dummy pointer record at the end of the recipients and at the end
+  * of the message header. To support message body modifications, a dummy
+  * pointer record will also be needed at the end of the message content.
+  * 
+  * When a mail filter wants to replace an entire body, we have the option to
+  * overwrite existing body records until we run out of space, and then
+  * writing a pointer to space at the end of the queue file, followed by the
+  * remainder of the body, and a pointer to the marker that ends the message
+  * content segment.
+  * 
+  * With all these changes, REC_TYPE_END is no longer guaranteed to be the last
+  * record in a queue file. If an application were to read beyond the
+  * REC_TYPE_END marker, it would go into an infinite loop, because records
+  * after REC_TYPE_END alternate with reverse pointers to the middle of the
+  * queue file. For robustness, the record reading routine skips forward to
+  * end-of-file position after reading the REC_TYPE_END marker.
+  */
+
+/*#define msg_verbose  2*/
+
+#define STR(x)         vstring_str(x)
+#define LEN(x)         VSTRING_LEN(x)
+
+/* cleanup_add_header - append message header */
+
+static void cleanup_add_header(void *context, char *name, char *value)
+{
+    const char *myname = "cleanup_add_header";
+    CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+    VSTRING *buf;
+    off_t   reverse_ptr_offset;
+    off_t   new_hdr_offset;
+
+    /*
+     * To simplify implementation, the cleanup server writes a dummy "header
+     * append" pointer record after the last message header. We cache both
+     * the location and the target of the current "header append" pointer
+     * record.
+     */
+    if (state->append_hdr_pt_offset < 0)
+       msg_panic("%s: no header append pointer location", myname);
+    if (state->append_hdr_pt_target < 0)
+       msg_panic("%s: no header append pointer target", myname);
+
+    /*
+     * Allocate space after the end of the queue file, and write the header
+     * record(s), followed by a reverse pointer record that points to the
+     * target of the old "header append" pointer record. This reverse pointer
+     * record becomes the new "header append" pointer record.
+     */
+    if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    buf = vstring_alloc(100);
+    vstring_sprintf(buf, "%s: %s", name, value);
+    cleanup_out_header(state, buf);
+    vstring_free(buf);
+    if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0)
+       msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+                      (long) state->append_hdr_pt_target);
+
+    /*
+     * Pointer flipping: update the old "header append" pointer record value
+     * with the location of the new header record.
+     */
+    if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+                      (long) new_hdr_offset);
+
+    /*
+     * Update the in-memory "header append" pointer record location with the
+     * location of the reverse pointer record that follows the new header.
+     * The target of the "header append" pointer record does not change; it's
+     * always the record that follows the dummy pointer record that was
+     * written while Postfix received the message.
+     */
+    state->append_hdr_pt_offset = reverse_ptr_offset;
+}
+
+/* cleanup_find_header - find specific header instance */
+
+static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index,
+                                    const char *header_label, VSTRING *buf,
+                                        int *prec_type,
+                                        int allow_ptr_backup)
+{
+    const char *myname = "cleanup_find_header";
+    off_t   curr_offset;               /* offset after found record */
+    off_t   ptr_offset;                        /* pointer to found record */
+    VSTRING *ptr_buf;
+    int     rec_type;
+    int     last_type;
+    ssize_t len;
+
+    if (msg_verbose)
+       msg_info("%s: index %ld name \"%s\"",
+             myname, (long) index, header_label ? header_label : "(none)");
+
+    /*
+     * Sanity checks.
+     */
+    if (index < 1)
+       msg_panic("%s: bad header index %ld", myname, (long) index);
+
+    /*
+     * Skip to the start of the message content, and read records until we
+     * either find the specified header, or until we hit the end of the
+     * headers.
+     * 
+     * The index specifies the header instance: 1 is the first one. The header
+     * label specifies the header name. A null pointer matches any header.
+     * 
+     * When the specified header is not found, the result value is -1.
+     * 
+     * When the specified header is found, its first record is stored in the
+     * caller-provided read buffer, and the result value is the queue file
+     * offset of that record. The file read position is left at the start of
+     * the next queue file record, which can be the remainder of a
+     * multi-record header.
+     * 
+     * When a header is found and allow_ptr_backup is non-zero, then the result
+     * is either the first record of that header, or it is the pointer record
+     * that points to the first record of that header. In the latter case,
+     * the file read position is undefined. Returning the pointer allows us
+     * to do some optimizations when inserting text multiple times at the
+     * same place.
+     * 
+     * XXX We don't use the MIME processor here. It not only buffers up the
+     * input, it also reads the record that follows a complete header before
+     * it invokes the header call-back action. This complicates the way that
+     * we discover header offsets and boundaries. Worse is that the MIME
+     * processor is unaware that multi-record message headers can have PTR
+     * records in the middle. This means that we can't correctly mark
+     * multi-record text as deleted. We could avoid the latter by never
+     * breaking up multi-record headers.
+     * 
+     * XXX The draw-back of not using the MIME processor is that we have to
+     * duplicate some of its logic here and in the routines that delete or
+     * modify header records. To minimize the duplication we define an ugly
+     * macro that is used in all code that scans for header boundaries.
+     */
+#define GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset) \
+    if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) \
+       msg_fatal("%s: read file %s: %m", myname, cleanup_path); \
+    if (msg_verbose > 1) \
+       msg_info("%s: read: %ld: %.*s", myname, (long) curr_offset, \
+                LEN(buf) > 30 ? 30 : LEN(buf), STR(buf)); \
+    if (rec_type == REC_TYPE_DTXT) \
+       continue; \
+    if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT \
+       && rec_type != REC_TYPE_PTR) \
+       break;
+
+    if (vstream_fseek(state->dst, state->data_offset, SEEK_SET) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    for (ptr_buf = 0, ptr_offset = 0, last_type = 0; /* void */ ; /* void */ ) {
+       if ((curr_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+       /* Caution: this macro terminates the loop at end-of-message. */
+       /* Don't do complex processing while breaking out of this loop. */
+       GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset);
+       /* Caution: don't assume ptr->header. This may be header-ptr->body. */
+       if (rec_type == REC_TYPE_PTR) {
+           if (rec_goto(state->dst, STR(buf)) < 0)
+               msg_fatal("%s: read file %s: %m",
+                         myname, cleanup_path);
+           /* Save PTR record, in case it points to the start of a header. */
+           if (allow_ptr_backup) {
+               ptr_offset = curr_offset;
+               if (ptr_buf == 0)
+                   ptr_buf = vstring_alloc(100);
+               vstring_strcpy(ptr_buf, STR(buf));
+           }
+           /* Don't update last_type; PTR can happen after REC_TYPE_CONT. */
+           continue;
+       }
+       /* The middle of a multi-record header. */
+       else if (last_type == REC_TYPE_CONT || IS_SPACE_TAB(STR(buf)[0])) {
+           /* Reset the saved PTR record. */
+           ptr_offset = 0;
+       }
+       /* No more message headers. */
+       else if ((len = is_header(STR(buf))) == 0) {
+           break;
+       }
+       /* This the start of a message header. */
+       else if ((header_label == 0
+                 || (strncasecmp(header_label, STR(buf), len) == 0
+                     && (IS_SPACE_TAB(STR(buf)[len])
+                         || STR(buf)[len] == ':')))
+                && --index == 0) {
+           /* If we have a saved PTR record, it points to start of header. */
+           break;
+       }
+       last_type = rec_type;
+    }
+
+    /*
+     * In case of failure, return negative start position.
+     */
+    if (index > 0) {
+       curr_offset = -1;
+    }
+
+    /*
+     * Optionally return the saved PTR record instead of the start of the
+     * message header. In that case the file read position is undefined
+     * (actually it is after the first text record that follows this header).
+     */
+    else {
+       if (ptr_offset != 0) {
+           rec_type = REC_TYPE_PTR;
+           curr_offset = ptr_offset;
+           vstring_strcpy(buf, STR(ptr_buf));
+       }
+       *prec_type = rec_type;
+    }
+    if (ptr_buf)
+       vstring_free(ptr_buf);
+
+    if (msg_verbose)
+       msg_info("%s: index %ld name %s type %d offset %ld",
+                myname, (long) index, header_label ?
+                header_label : "(none)", rec_type, (long) curr_offset);
+
+    return (curr_offset);
+}
+
+/* cleanup_patch_header - patch new header into an existing header */
+
+static void cleanup_patch_header(CLEANUP_STATE *state,
+                                        const char *new_hdr_name,
+                                        const char *new_hdr_value,
+                                        off_t old_rec_offset,
+                                        int rec_type,
+                                        VSTRING *old_rec_buf,
+                                        ssize_t avail_space,
+                                        off_t read_offset)
+{
+    const char *myname = "cleanup_patch_header";
+    VSTRING *buf = vstring_alloc(100);
+    off_t   new_hdr_offset;
+    off_t   saved_read_offset;
+    off_t   write_offset;
+
+    if (msg_verbose)
+       msg_info("%s: \"%s\" \"%s\" at %ld",
+                myname, new_hdr_name, new_hdr_value, (long) old_rec_offset);
+
+    /*
+     * Allocate space after the end of the queue file, and save the new
+     * header and existing record(s) until we have enough space to replace
+     * the saved record(s) by a forward pointer record. If the saved record
+     * was not a PTR record, follow the saved records by a reverse pointer
+     * record that points to the record after the original location of the
+     * last saved record. Note: while moving data, we may move the "header
+     * append" pointer record, so we have to update the in-memory offset for
+     * that record.
+     * 
+     * We update the queue file in a safe manner: save the new header and the
+     * existing records after the end of the queue file, write the reverse
+     * pointer, and only then overwrite the saved records with the forward
+     * pointer to the new header.
+     */
+
+    /*
+     * old_rec_offset, rec_type, and old_rec_buf specify the record that we
+     * are about to overwrite with a pointer record. If the record needs to
+     * be saved (i.e. old_rec_type > 0), the buffer contains the data content
+     * of exactly one PTR or text record.
+     * 
+     * When rec_type specifies a text record, avail_space specifies the amount
+     * of contiguous space for the forward pointer record before the records
+     * beginning at read_offset. It's the text record data size, or the
+     * amount of contiguous space at the start of a multi-record header.
+     * 
+     * When rec_type specifies a pointer record, the avail_space and read_offset
+     * arguments are ignored.
+     */
+
+    /*
+     * Write the new header to a new location after the end of the queue
+     * file.
+     */
+    if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    vstring_sprintf(buf, "%s: %s", new_hdr_name, new_hdr_value);
+    cleanup_out_header(state, buf);
+    if (msg_verbose > 1)
+       msg_info("%s: %ld: write %.*s", myname, (long) new_hdr_offset,
+                LEN(buf) > 30 ? 30 : LEN(buf), STR(buf));
+
+    /*
+     * Optionally, save the existing text record or pointer record that will
+     * be overwritten with the forward pointer.
+     */
+    if (rec_type > 0) {
+       CLEANUP_OUT_BUF(state, rec_type, old_rec_buf);
+       if (msg_verbose > 1)
+           msg_info("%s: write %.*s", myname, LEN(old_rec_buf) > 30 ?
+                    30 : LEN(old_rec_buf), STR(old_rec_buf));
+    }
+
+    /*
+     * Save additional existing records to make space to write the forward
+     * pointer. We go for simplicity instead of speed, because we rarely need
+     * to do this. Special case: don't create additional space after saving a
+     * pointer record. Requirement: the message headers (and body) always end
+     * in a pointer record.
+     */
+    while (rec_type != REC_TYPE_PTR && avail_space < REC_TYPE_PTR_SIZE) {
+       /* Read existing text or pointer record. */
+       if (vstream_fseek(state->dst, read_offset, SEEK_SET) < 0)
+           msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+       if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0)
+           msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+       if (msg_verbose > 1)
+           msg_info("%s: %ld: read %.*s", myname, (long) read_offset,
+                    LEN(buf) > 30 ? 30 : LEN(buf), STR(buf));
+       if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT
+           && rec_type != REC_TYPE_PTR && rec_type != REC_TYPE_DTXT)
+           msg_panic("%s: non-text/ptr record type %d in header, file %s",
+                     myname, rec_type, cleanup_path);
+       saved_read_offset = read_offset;
+       if ((read_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+       avail_space += (read_offset - saved_read_offset);
+       /* Save the text or pointer record. */
+       if ((write_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
+           msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+       CLEANUP_OUT_BUF(state, rec_type, buf);
+       if (msg_verbose > 1)
+           msg_info("%s: %ld: write %.*s", myname, (long) write_offset,
+                    LEN(buf) > 30 ? 30 : LEN(buf), STR(buf));
+       /* Update cached location of "append header" pointer record. */
+       if (saved_read_offset == state->append_hdr_pt_offset)
+           state->append_hdr_pt_offset = write_offset;
+    }
+
+    /*
+     * If the saved records didn't already end with an old PTR record, write
+     * the reverse pointer after the saved records.
+     */
+    if (rec_type != REC_TYPE_PTR) {
+       cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+                          (long) read_offset);
+       if (msg_verbose > 1)
+           msg_info("%s: write PTR %ld", myname, (long) read_offset);
+    }
+
+    /*
+     * Write the forward pointer over the old record. Generally, a pointer
+     * record will be shorter than a header record, so there will be a gap in
+     * the queue file before the next record. In other words, we must always
+     * follow pointer records otherwise we get out of sync with the data.
+     */
+    if (vstream_fseek(state->dst, old_rec_offset, SEEK_SET) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+                      (long) new_hdr_offset);
+    if (msg_verbose > 1)
+       msg_info("%s: %ld: write PTR %ld", myname, (long) old_rec_offset,
+                (long) new_hdr_offset);
+
+    /*
+     * Note: state->append_hdr_pt_target never changes.
+     */
+    vstring_free(buf);
+}
+
+/* cleanup_ins_header - insert message header */
+
+static void cleanup_ins_header(void *context, ssize_t index,
+                                      char *new_hdr_name,
+                                      char *new_hdr_value)
+{
+    const char *myname = "cleanup_ins_header";
+    CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+    VSTRING *old_rec_buf = vstring_alloc(100);
+    off_t   old_rec_offset;
+    int     old_rec_type;
+    off_t   read_offset;
+    ssize_t avail_space;
+
+    if (msg_verbose)
+       msg_info("%s: %ld \"%s\" \"%s\"",
+                myname, (long) index, new_hdr_name, new_hdr_value);
+
+    /*
+     * Look for a header at the specified position. If none exists, simply
+     * append the header to the linked list at the "header append" pointer
+     * record. Otherwise, save both the new and the existing header to new
+     * storage at the end of the queue file, and link the new storage with a
+     * forward and reverse pointer.
+     * 
+     * The lookup result may be a pointer record. This allows us to make some
+     * optimization when multiple insert operations happen in the same place.
+     * 
+     * Index 1 is the top-most header.
+     */
+#define NO_HEADER_NAME ((char *) 0)
+#define ALLOW_PTR_BACKUP       1
+
+    if (index < 1)
+       index = 1;
+    old_rec_offset = cleanup_find_header(state, index, NO_HEADER_NAME,
+                                        old_rec_buf, &old_rec_type,
+                                        ALLOW_PTR_BACKUP);
+    if (old_rec_offset < 0) {
+       cleanup_add_header(context, new_hdr_name, new_hdr_value);
+    } else {
+       if (old_rec_type == REC_TYPE_PTR) {
+           read_offset = -1;
+           avail_space = -1;
+       } else {
+           if ((read_offset = vstream_ftell(state->dst)) < 0)
+               msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+           avail_space = LEN(old_rec_buf);
+       }
+       cleanup_patch_header(state, new_hdr_name, new_hdr_value,
+                            old_rec_offset, old_rec_type, old_rec_buf,
+                            avail_space, read_offset);
+    }
+    vstring_free(old_rec_buf);
+}
+
+/* cleanup_upd_header - modify or append message header */
+
+static void cleanup_upd_header(void *context, ssize_t index,
+                                      char *new_hdr_name,
+                                      char *new_hdr_value)
+{
+    const char *myname = "cleanup_upd_header";
+    CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+    VSTRING *rec_buf;
+    off_t   old_rec_offset;
+    ssize_t avail_space;
+    off_t   read_offset;
+    off_t   saved_read_offset;
+    int     rec_type;
+    int     last_type;
+    int     jumped;
+
+    if (msg_verbose)
+       msg_info("%s: %ld \"%s\" \"%s\"",
+                myname, (long) index, new_hdr_name, new_hdr_value);
+
+    /*
+     * Sanity check.
+     */
+    if (*new_hdr_name == 0)
+       msg_panic("%s: null header name", myname);
+
+    /*
+     * Find the header that is being modified. If none is found, simply
+     * append the header to the linked list at the "header append" pointer
+     * record. Otherwise, find the end of the old header, save the new header
+     * to new storage at the end of the queue file, and link the new storage
+     * with a forward and reverse pointer.
+     * 
+     * The lookup result will never be a pointer record.
+     * 
+     * Index 1 is the first matching header instance.
+     */
+#define DONT_SAVE_RECORD       0
+#define NO_PTR_BACKUP          0
+
+    rec_buf = vstring_alloc(100);
+    old_rec_offset = cleanup_find_header(state, index, new_hdr_name,
+                                        rec_buf, &last_type,
+                                        NO_PTR_BACKUP);
+    if (old_rec_offset < 0) {
+       cleanup_add_header(context, new_hdr_name, new_hdr_value);
+    } else {
+       /* Find the end of this header. */
+       avail_space = LEN(rec_buf);
+       if ((read_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+       for (jumped = 0; /* void */ ; /* void */ ) {
+           saved_read_offset = read_offset;
+           /* Caution: this macro terminates the loop at end-of-message. */
+           /* Don't do complex processing while breaking out of this loop. */
+           GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, read_offset);
+           if ((read_offset = vstream_ftell(state->dst)) < 0)
+               msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+           if (rec_type == REC_TYPE_PTR) {
+               if (jumped == 0) {
+                   /* Enough contiguous space for writing a PTR record. */
+                   avail_space += read_offset - saved_read_offset;
+                   jumped = 1;
+               }
+               if (rec_goto(state->dst, STR(rec_buf)) < 0)
+                   msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+               /* Don't update last_type; PTR may follow REC_TYPE_CONT. */
+               continue;
+           }
+           /* Start of header or message body. */
+           if (last_type != REC_TYPE_CONT && !IS_SPACE_TAB(STR(rec_buf)[0]))
+               break;
+           if (jumped == 0)
+               avail_space += read_offset - saved_read_offset;
+           last_type = rec_type;
+       }
+       cleanup_patch_header(state, new_hdr_name, new_hdr_value,
+                            old_rec_offset, DONT_SAVE_RECORD, (VSTRING *) 0,
+                            avail_space, saved_read_offset);
+    }
+    vstring_free(rec_buf);
+}
+
+/* cleanup_del_header - delete message header */
+
+static void cleanup_del_header(void *context, ssize_t index, char *hdr_name)
+{
+    const char *myname = "cleanup_del_header";
+    CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+    VSTRING *rec_buf;
+    off_t   header_offset;
+    off_t   curr_offset;
+    int     rec_type;
+    int     last_type;
+
+    if (msg_verbose)
+       msg_info("%s: %ld \"%s\"", myname, (long) index, hdr_name);
+
+    /*
+     * Sanity check.
+     */
+    if (*hdr_name == 0)
+       msg_panic("%s: null header name", myname);
+
+    /*
+     * Find the header that is being deleted, and write over it with a
+     * "deleted text" record type. We first read all the record offsets of
+     * this header, and then mark the records as deleted. If headers were
+     * guaranteed to be contiguous in the queue file (no PTRs in the middle)
+     * then we could use cleanup_out_header() instead of doing multiple seek
+     * + write operations.
+     * 
+     * The lookup result will never be a pointer record.
+     * 
+     * Index 1 is the first matching header instance.
+     */
+    rec_buf = vstring_alloc(100);
+    header_offset = cleanup_find_header(state, index, hdr_name, rec_buf,
+                                       &last_type, NO_PTR_BACKUP);
+    /* Memory usage for header offsets is limited by header_size_limit. */
+    if (header_offset > 0) {
+       ssize_t off_len = 1;
+       ssize_t off_used = 1;
+       off_t  *off_list = (off_t *) mymalloc(off_len * sizeof(*off_list));
+       int     n;
+
+       off_list[0] = header_offset;
+       for (;;) {
+           curr_offset = vstream_ftell(state->dst);
+           /* Caution: this macro terminates the loop at end-of-message. */
+           /* Don't do complex processing while breaking out of this loop. */
+           GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, curr_offset);
+           if (rec_type == REC_TYPE_PTR) {
+               if (rec_goto(state->dst, STR(rec_buf)) < 0)
+                   msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+               /* Don't update last_type; PTR may follow REC_TYPE_CONT. */
+               continue;
+           }
+           /* Start of header or message body. */
+           if (last_type != REC_TYPE_CONT && !IS_SPACE_TAB(STR(rec_buf)[0]))
+               break;
+           /* Save this header text record offset. */
+           if (off_used >= off_len) {
+               off_len *= 2;
+               off_list = (off_t *) myrealloc((char *) off_list,
+                                              off_len * sizeof(*off_list));
+           }
+           off_list[off_used++] = curr_offset;
+           last_type = rec_type;
+       }
+       /* Mark the header text records as deleted. */
+       for (n = 0; n < off_used; n++)
+           if (rec_put_type(state->dst, REC_TYPE_DTXT, off_list[n]) < 0)
+               msg_fatal("%s: write file %s: %m", myname, cleanup_path);
+       myfree((char *) off_list);
+    }
+    vstring_free(rec_buf);
+}
+
+/* cleanup_add_rcpt - append recipient address */
+
+static void cleanup_add_rcpt(void *context, char *rcpt)
+{
+    const char *myname = "cleanup_add_rcpt";
+    CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+    off_t   new_rcpt_offset;
+    off_t   reverse_ptr_offset;
+
+    if (msg_verbose)
+       msg_info("%s: \"%s\"", myname, rcpt);
+
+    /*
+     * To simplify implementation, the cleanup server writes a dummy
+     * "recipient append" pointer record after the last recipient. We cache
+     * both the location and the target of the current "recipient append"
+     * pointer record.
+     */
+    if (state->append_rcpt_pt_offset < 0)
+       msg_panic("%s: no recipient append pointer location", myname);
+    if (state->append_rcpt_pt_target < 0)
+       msg_panic("%s: no recipient append pointer target", myname);
+
+    /*
+     * Allocate space after the end of the queue file, and write the
+     * recipient record, followed by a reverse pointer record that points to
+     * the target of the old "recipient append" pointer record. This reverse
+     * pointer record becomes the new "recipient append" pointer record.
+     * 
+     * We update the queue file in a safe manner: save the new recipient after
+     * the end of the queue file, write the reverse pointer, and only then
+     * overwrite the old "recipient append" pointer with the forward pointer
+     * to the new recipient.
+     */
+#define NO_DSN_ORCPT   ((char *) 0)
+
+    if ((new_rcpt_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    cleanup_addr_bcc(state, rcpt);
+    if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0)
+       msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+                      (long) state->append_rcpt_pt_target);
+
+    /*
+     * Pointer flipping: update the old "recipient append" pointer record
+     * value to the location of the new recipient record.
+     */
+    if (vstream_fseek(state->dst, state->append_rcpt_pt_offset, SEEK_SET) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+                      (long) new_rcpt_offset);
+
+    /*
+     * Update the in-memory "recipient append" pointer record location with
+     * the location of the reverse pointer record that follows the new
+     * recipient. The target of the "recipient append" pointer record does
+     * not change; it's always the record that follows the dummy pointer
+     * record that was written while Postfix received the message.
+     */
+    state->append_rcpt_pt_offset = reverse_ptr_offset;
+}
+
+/* cleanup_del_rcpt - remove recipient and all its expansions */
+
+static void cleanup_del_rcpt(void *context, char *rcpt)
+{
+    const char *myname = "cleanup_del_rcpt";
+    CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+    off_t   curr_offset;
+    VSTRING *buf;
+    char   *attr_name;
+    char   *attr_value;
+    char   *dsn_orcpt = 0;             /* XXX for dup filter cleanup */
+    int     dsn_notify = 0;            /* XXX for dup filter cleanup */
+    char   *orig_rcpt = 0;
+    char   *start;
+    int     rec_type;
+    int     junk;
+    int     count = 0;
+
+    if (msg_verbose)
+       msg_info("%s: \"%s\"", myname, rcpt);
+
+    /*
+     * Virtual aliasing and other address rewriting happens after the mail
+     * filter sees the envelope address. Therefore we must delete all
+     * recipient records whose Postfix (not DSN) original recipient address
+     * matches the specified address.
+     * 
+     * As the number of recipients may be very large we can't do an efficient
+     * two-pass implementation (collect record offsets first, then mark
+     * records as deleted). Instead we mark records as soon as we find them.
+     * This is less efficient because we do (seek-write-read) for each marked
+     * recipient, instead of (seek-write). It's unlikely that VSTREAMs will
+     * be made smart enough to eliminate unnecessary I/O with small seeks.
+     * 
+     * XXX When Postfix original recipients are turned off, we have no option
+     * but to match against the expanded and rewritten recipient address.
+     * 
+     * XXX Remove the (dsn_orcpt, dsn_notify, orcpt, recip) tuple from the
+     * duplicate recipient filter.
+     */
+    if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
+       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    buf = vstring_alloc(100);
+    while (CLEANUP_OUT_OK(state)) {
+       if ((curr_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+       if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) <= 0)
+           msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+       if (rec_type == REC_TYPE_END)
+           break;
+       /* Skip over message content. */
+       if (rec_type == REC_TYPE_MESG) {
+           if (vstream_fseek(state->dst, state->xtra_offset, SEEK_SET) < 0)
+               msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+           continue;
+       }
+       start = STR(buf);
+       if (rec_type == REC_TYPE_PTR) {
+           if (rec_goto(state->dst, start) < 0)
+               msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+           continue;
+       }
+       /* Map attribute names to pseudo record type. */
+       if (rec_type == REC_TYPE_ATTR) {
+           if (split_nameval(STR(buf), &attr_name, &attr_value) != 0
+               || *attr_value == 0)
+               continue;
+           if ((junk = rec_attr_map(attr_name)) != 0) {
+               start = attr_value;
+               rec_type = junk;
+           }
+       }
+       switch (rec_type) {
+       case REC_TYPE_DSN_ORCPT:                /* RCPT TO ORCPT parameter */
+           if (dsn_orcpt != 0)                 /* can't happen */
+               myfree(dsn_orcpt);
+           dsn_orcpt = mystrdup(start);
+           break;
+       case REC_TYPE_DSN_NOTIFY:               /* RCPT TO NOTIFY parameter */
+           if (alldig(start) && (junk = atoi(start)) > 0
+               && DSN_NOTIFY_OK(junk))
+               dsn_notify = junk;
+           else
+               dsn_notify = 0;
+           break;
+       case REC_TYPE_ORCP:                     /* unmodified RCPT TO address */
+           if (orig_rcpt != 0)                 /* can't happen */
+               myfree(orig_rcpt);
+           orig_rcpt = mystrdup(start);
+           break;
+       case REC_TYPE_RCPT:                     /* rewritten RCPT TO address */
+           if (strcmp(orig_rcpt ? orig_rcpt : start, rcpt) == 0) {
+               if (vstream_fseek(state->dst, curr_offset, SEEK_SET) < 0)
+                   msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+               if (REC_PUT_BUF(state->dst, REC_TYPE_DRCP, buf) < 0) {
+                   msg_warn("%s: write queue file: %m", state->queue_id);
+                   state->errs |= CLEANUP_STAT_WRITE;
+               }
+               count++;
+           }
+           /* FALLTHROUGH */
+       case REC_TYPE_DRCP:                     /* canceled recipient */
+       case REC_TYPE_DONE:                     /* can't happen */
+           if (orig_rcpt != 0) {
+               myfree(orig_rcpt);
+               orig_rcpt = 0;
+           }
+           if (dsn_orcpt != 0) {
+               myfree(dsn_orcpt);
+               dsn_orcpt = 0;
+           }
+           dsn_notify = 0;
+           break;
+       }
+    }
+    if (orig_rcpt != 0)                                /* can't happen */
+       myfree(orig_rcpt);
+    if (dsn_orcpt != 0)                                /* can't happen */
+       myfree(dsn_orcpt);
+    vstring_free(buf);
+
+    if (msg_verbose)
+       msg_info("%s: deleted %d records for recipient \"%s\"",
+                myname, count, rcpt);
+}
+
+/* cleanup_repl_body - replace message body */
+
+static void cleanup_repl_body(void *context, VSTRING *body)
+{
+    const char *myname = "cleanup_repl_body";
+
+    msg_panic("%s: message body replace operation is not implemented", myname);
+}
+
+/* cleanup_milter_eval - expand macro */
+
+static const char *cleanup_milter_eval(const char *name, void *ptr)
+{
+    CLEANUP_STATE *state = (CLEANUP_STATE *) ptr;
+
+    /*
+     * Canonicalize the name.
+     */
+    if (*name != '{') {                                /* } */
+       vstring_sprintf(state->temp1, "{%s}", name);
+       name = STR(state->temp1);
+    }
+
+    /*
+     * System macros.
+     */
+    if (strcmp(name, S8_MAC_DAEMON_NAME) == 0)
+       return (var_milt_daemon_name);
+    if (strcmp(name, S8_MAC_V) == 0)
+       return (var_milt_v);
+
+    /*
+     * Connect macros.
+     */
+    if (strcmp(name, S8_MAC_J) == 0)
+       return (var_myhostname);
+    if (strcmp(name, S8_MAC_CLIENT_ADDR) == 0)
+       return (nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_ADDR));
+    if (strcmp(name, S8_MAC_CLIENT_NAME) == 0)
+       return (nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_NAME));
+
+    /*
+     * MAIL FROM macros.
+     */
+    if (strcmp(name, S8_MAC_I) == 0)
+       return (state->queue_id);
+#ifdef USE_SASL_AUTH
+    if (strcmp(name, S8_MAC_AUTH_TYPE) == 0)
+       return (nvtable_find(state->attr, MAIL_ATTR_SASL_METHOD));
+    if (strcmp(name, S8_MAC_AUTH_AUTHEN) == 0)
+       return (nvtable_find(state->attr, MAIL_ATTR_SASL_USERNAME));
+    if (strcmp(name, S8_MAC_AUTH_AUTHOR) == 0)
+       return (nvtable_find(state->attr, MAIL_ATTR_SASL_SENDER));
+#endif
+#if 0
+    if (strcmp(name, S8_MAC_MAIL_ADDR) == 0)
+       return (state->sender);
+#endif
+
+    /*
+     * RCPT TO macros.
+     */
+#if 0
+    if (strcmp(name, S8_MAC_RCPT_ADDR) == 0)
+       return (state->recipient);
+#endif
+    return (0);
+}
+
+/* cleanup_milter_receive - receive milter instances */
+
+void    cleanup_milter_receive(CLEANUP_STATE *state, int count)
+{
+    state->milters = milter_receive(state->src, count);
+    milter_macro_callback(state->milters, cleanup_milter_eval, (void *) state);
+    milter_edit_callback(state->milters,
+                        cleanup_add_header, cleanup_upd_header,
+                        cleanup_ins_header, cleanup_del_header,
+                        cleanup_add_rcpt, cleanup_del_rcpt,
+                        cleanup_repl_body, (void *) state);
+}
+
+/* cleanup_milter_apply - apply Milter reponse, non-zero if rejecting */
+
+static const char *cleanup_milter_apply(CLEANUP_STATE *state, const char *resp)
+{
+    const char *myname = "cleanup_milter_apply";
+    const char *action;
+    const char *text;
+    const char *attr;
+    const char *ret = 0;
+
+    if (msg_verbose)
+       msg_info("%s: %s", myname, resp);
+    switch (resp[0]) {
+    case 'H':
+       /* XXX Should log the reason here. */
+       state->flags |= CLEANUP_FLAG_HOLD;
+       action = "milter-hold";
+       text = "milter triggers HOLD action";
+       break;
+    case 'D':
+       state->flags |= CLEANUP_FLAG_DISCARD;
+       action = "milter-discard";
+       text = "milter triggers DISCARD action";
+       break;
+    case 'S':
+       /* XXX Can this happen after end-of-message? */
+       state->flags |= CLEANUP_STAT_CONT;
+       action = "milter-reject";
+       text = cleanup_strerror(CLEANUP_STAT_CONT);
+       break;
+
+       /*
+        * Override permanent reject with temporary reject. This happens when
+        * the cleanup server has to bounce (hard reject) but is unable to
+        * store the message (soft reject). After a temporary reject we stop
+        * inspecting queue file records, so it can't be overruled by
+        * something else.
+        * 
+        * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason
+        * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates
+        * queue record processing, and prevents bounces from being sent.
+        * 
+        * XXX Multi-line replies are messy, We should eliminate not only the
+        * CRLF, but also the SMTP status and the enhanced status code that
+        * follows.
+        */
+    case '4':
+       if (state->reason)
+           myfree(state->reason);
+       ret = state->reason = mystrdup(resp + 4);
+       printable(state->reason, '_');
+       state->errs |= CLEANUP_STAT_DEFER;
+       action = "milter-reject";
+       text = resp + 4;
+       break;
+    case '5':
+       if (state->reason)
+           myfree(state->reason);
+       ret = state->reason = mystrdup(resp + 4);
+       printable(state->reason, '_');
+       state->errs |= CLEANUP_STAT_CONT;
+       action = "milter-reject";
+       text = resp + 4;
+       break;
+    default:
+       msg_panic("%s: unexpected mail filter reply: %s", myname, resp);
+    }
+    vstring_sprintf(state->temp1, "%s: %s;", state->queue_id, action);
+    if (state->sender)
+       vstring_sprintf_append(state->temp1, " from=<%s>", state->sender);
+    if (state->recip)
+       vstring_sprintf_append(state->temp1, " to=<%s>", state->recip);
+    if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0)
+       vstring_sprintf_append(state->temp1, " proto=%s", attr);
+    if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0)
+       vstring_sprintf_append(state->temp1, " helo=<%s>", attr);
+    vstring_sprintf_append(state->temp1, ": %s", text);
+    msg_info("%s", vstring_str(state->temp1));
+
+    return (ret);
+}
+
+/* cleanup_milter_inspect - run message through mail filter */
+
+void    cleanup_milter_inspect(CLEANUP_STATE *state, MILTERS *milters)
+{
+    const char *myname = "cleanup_milter";
+    const char *resp;
+
+    if (msg_verbose)
+       msg_info("enter %s", myname);
+
+    /*
+     * Process mail filter replies. The reply format is verified by the mail
+     * filter library.
+     */
+    if ((resp = milter_message(milters, state->handle->stream,
+                              state->data_offset)) != 0)
+       cleanup_milter_apply(state, resp);
+    if (msg_verbose)
+       msg_info("leave %s", myname);
+}
+
+/* cleanup_milter_emul_mail - emulate connect/ehlo/mail event */
+
+void    cleanup_milter_emul_mail(CLEANUP_STATE *state,
+                                        MILTERS *milters,
+                                        const char *addr)
+{
+    const char *resp;
+    const char *client_name;
+    const char *client_addr;
+    const char *proto_attr;
+    const char *client_port;
+    int     client_af;
+    const char *helo;
+    const char *argv[2];
+
+    /*
+     * Per-connection initialization.
+     */
+    milter_macro_callback(milters, cleanup_milter_eval, (void *) state);
+    milter_edit_callback(milters,
+                        cleanup_add_header, cleanup_upd_header,
+                        cleanup_ins_header, cleanup_del_header,
+                        cleanup_add_rcpt, cleanup_del_rcpt,
+                        cleanup_repl_body, (void *) state);
+
+    /*
+     * Either the cleanup client specifies a name, address and protocol, or
+     * we have a local submission and pretend localhost/127.0.0.1/AF_INET.
+     */
+#define NO_CLIENT_PORT "0"
+
+    client_name = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_NAME);
+    client_addr = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_ADDR);
+    client_port = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_PORT);
+    proto_attr = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_AF);
+    if (client_name == 0 || client_addr == 0 || proto_attr == 0
+       || !alldig(proto_attr)) {
+       client_name = "localhost";
+       client_addr = "127.0.0.1";
+       client_af = AF_INET;
+    } else
+       client_af = atoi(proto_attr);
+    if (client_port == 0)
+       client_port = NO_CLIENT_PORT;
+
+    /*
+     * Emulate SMTP events.
+     */
+    if ((resp = milter_conn_event(milters, client_name, client_addr,
+                                 client_port, client_af)) != 0) {
+       cleanup_milter_apply(state, resp);
+       return;
+    }
+#define PRETEND_ESMTP  1
+
+    if (CLEANUP_MILTER_OK(state)) {
+       if ((helo = nvtable_find(state->attr, MAIL_ATTR_ACT_HELO_NAME)) == 0)
+           helo = client_name;
+       if ((resp = milter_helo_event(milters, helo, PRETEND_ESMTP)) != 0) {
+           cleanup_milter_apply(state, resp);
+           return;
+       }
+    }
+    if (CLEANUP_MILTER_OK(state)) {
+       argv[0] = addr;
+       argv[1] = 0;
+       if ((resp = milter_mail_event(milters, argv)) != 0) {
+           cleanup_milter_apply(state, resp);
+           return;
+       }
+    }
+}
+
+/* cleanup_milter_emul_rcpt - emulate rcpt event */
+
+void    cleanup_milter_emul_rcpt(CLEANUP_STATE *state,
+                                        MILTERS *milters,
+                                        const char *addr)
+{
+    const char *resp;
+    const char *argv[2];
+
+    /*
+     * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason
+     * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates
+     * queue record processing, and prevents bounces from being sent.
+     */
+    argv[0] = addr;
+    argv[1] = 0;
+    if ((resp = milter_rcpt_event(milters, argv)) != 0
+       && cleanup_milter_apply(state, resp) != 0) {
+       msg_warn("%s: milter configuration error: can't reject recipient "
+                "in non-smtpd(8) submission", state->queue_id);
+       msg_warn("%s: deferring delivery of this message", state->queue_id);
+       if (state->reason)
+           myfree(state->reason);
+       state->reason = mystrdup("4.3.5 Server configuration error");
+       state->errs |= CLEANUP_STAT_DEFER;
+    }
+}
+
+/* cleanup_milter_emul_data - emulate data event */
+
+void    cleanup_milter_emul_data(CLEANUP_STATE *state, MILTERS *milters)
+{
+    const char *resp;
+
+    if ((resp = milter_data_event(milters)) != 0)
+       cleanup_milter_apply(state, resp);
+}
+
+#ifdef TEST
+
+ /*
+  * Queue file editing driver for regression tests.
+  */
+#include <stdio.h>
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+#include <mail_addr.h>
+#include <mail_version.h>
+
+#undef msg_verbose
+
+char   *cleanup_path;
+VSTRING *cleanup_trace_path;
+VSTRING *cleanup_strip_chars;
+int     cleanup_comm_canon_flags;
+MAPS   *cleanup_comm_canon_maps;
+int     cleanup_ext_prop_mask;
+ARGV   *cleanup_masq_domains;
+int     cleanup_masq_flags;
+MAPS   *cleanup_rcpt_bcc_maps;
+int     cleanup_rcpt_canon_flags;
+MAPS   *cleanup_rcpt_canon_maps;
+MAPS   *cleanup_send_bcc_maps;
+int     cleanup_send_canon_flags;
+MAPS   *cleanup_send_canon_maps;
+int     var_dup_filter_limit = DEF_DUP_FILTER_LIMIT;
+char   *var_empty_addr = DEF_EMPTY_ADDR;
+int     var_enable_orcpt = DEF_ENABLE_ORCPT;
+MAPS   *cleanup_virt_alias_maps;
+char   *var_milt_daemon_name = "host.example.com";
+char   *var_milt_v = DEF_MILT_V;
+
+/* Dummies to satisfy unused external references. */
+
+int     cleanup_masquerade_internal(VSTRING *addr, ARGV *masq_domains)
+{
+    msg_panic("cleanup_masquerade_internal dummy");
+}
+
+int     cleanup_rewrite_internal(const char *context, VSTRING *result,
+                                        const char *addr)
+{
+    vstring_strcpy(result, addr);
+    return (0);
+}
+
+int     cleanup_map11_internal(CLEANUP_STATE *state, VSTRING *addr,
+                                      MAPS *maps, int propagate)
+{
+    msg_panic("cleanup_map11_internal dummy");
+}
+
+ARGV   *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr,
+                                      MAPS *maps, int propagate)
+{
+    msg_panic("cleanup_map1n_internal dummy");
+}
+
+void    cleanup_envelope(CLEANUP_STATE *state, int type, const char *buf,
+                                ssize_t len)
+{
+    msg_panic("cleanup_envelope dummy");
+}
+
+static void usage(void)
+{
+    msg_warn("usage:");
+    msg_warn("    verbose on|off");
+    msg_warn("    open pathname");
+    msg_warn("    close");
+    msg_warn("    add_header index name [value]");
+    msg_warn("    ins_header index name [value]");
+    msg_warn("    upd_header index name [value]");
+    msg_warn("    del_header index name");
+    msg_warn("    add_rcpt addr");
+    msg_warn("    del_rcpt addr");
+}
+
+static void flatten_args(VSTRING *buf, char **argv)
+{
+    char  **cpp;
+
+    VSTRING_RESET(buf);
+    for (cpp = argv; *cpp; cpp++) {
+       vstring_strcat(buf, *cpp);
+       if (cpp[1])
+           VSTRING_ADDCH(buf, ' ');
+    }
+    VSTRING_TERMINATE(buf);
+}
+
+static void open_queue_file(CLEANUP_STATE *state, const char *path)
+{
+    VSTRING *buf = vstring_alloc(100);
+    off_t   curr_offset;
+    int     rec_type;
+    long    msg_seg_len;
+    long    data_offset;
+    long    rcpt_count;
+    long    qmgr_opts;
+
+    if ((state->dst = vstream_fopen(path, O_RDWR, 0)) == 0) {
+       msg_warn("open %s: %m", path);
+    } else {
+       cleanup_path = mystrdup(path);
+       for (;;) {
+           if ((curr_offset = vstream_ftell(state->dst)) < 0)
+               msg_fatal("file %s: vstream_ftell: %m", cleanup_path);
+           if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0)
+               msg_fatal("file %s: missing SIZE or PTR record", cleanup_path);
+           if (rec_type == REC_TYPE_SIZE) {
+               if (sscanf(STR(buf), "%ld %ld %ld %ld",
+                          &msg_seg_len, &data_offset,
+                          &rcpt_count, &qmgr_opts) != 4)
+                   msg_fatal("file %s: bad SIZE record: %s",
+                             cleanup_path, STR(buf));
+               state->data_offset = data_offset;
+               state->xtra_offset = data_offset + msg_seg_len;
+           } else if (rec_type == REC_TYPE_PTR) {
+               if (state->data_offset < 0)
+                   msg_fatal("file %s: missing SIZE record", cleanup_path);
+               if (curr_offset < state->data_offset) {
+                   if (state->append_rcpt_pt_offset < 0) {
+                       state->append_rcpt_pt_offset = curr_offset;
+                       if (atol(STR(buf)) != 0)
+                           msg_fatal("file %s: bad dummy recipient PTR record: %s",
+                                     cleanup_path, STR(buf));
+                       if ((state->append_rcpt_pt_target =
+                            vstream_ftell(state->dst)) < 0)
+                           msg_fatal("file %s: vstream_ftell: %m", cleanup_path);
+                   }
+               } else if (curr_offset < state->xtra_offset) {
+                   if (state->append_hdr_pt_offset < 0) {
+                       state->append_hdr_pt_offset = curr_offset;
+                       if (atol(STR(buf)) != 0)
+                           msg_fatal("file %s: bad dummy header PTR record: %s",
+                                     cleanup_path, STR(buf));
+                       if ((state->append_hdr_pt_target =
+                            vstream_ftell(state->dst)) < 0)
+                           msg_fatal("file %s: vstream_ftell: %m", cleanup_path);
+                       break;
+                   }
+               }
+           }
+       }
+       if (msg_verbose) {
+           msg_info("append_rcpt_pt_offset %ld append_rcpt_pt_target %ld",
+                    (long) state->append_rcpt_pt_offset,
+                    (long) state->append_rcpt_pt_target);
+           msg_info("append_hdr_pt_offset %ld append_hdr_pt_target %ld",
+                    (long) state->append_hdr_pt_offset,
+                    (long) state->append_hdr_pt_target);
+       }
+    }
+    vstring_free(buf);
+}
+
+static void close_queue_file(CLEANUP_STATE *state)
+{
+    (void) vstream_fclose(state->dst);
+    state->dst = 0;
+    myfree(cleanup_path);
+    cleanup_path = 0;
+}
+
+int     main(int unused_argc, char **argv)
+{
+    VSTRING *inbuf = vstring_alloc(100);
+    VSTRING *arg_buf = vstring_alloc(100);
+    char   *bufp;
+    int     istty = isatty(vstream_fileno(VSTREAM_IN));
+    CLEANUP_STATE *state = cleanup_state_alloc((VSTREAM *) 0);
+
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+    var_line_limit = DEF_LINE_LIMIT;
+
+    for (;;) {
+       ARGV   *argv;
+       ssize_t index;
+
+       if (istty) {
+           vstream_printf("- ");
+           vstream_fflush(VSTREAM_OUT);
+       }
+       if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
+           break;
+
+       bufp = vstring_str(inbuf);
+       if (!istty) {
+           vstream_printf("> %s\n", bufp);
+           vstream_fflush(VSTREAM_OUT);
+       }
+       if (*bufp == '#' || *bufp == 0 || allspace(bufp))
+           continue;
+       argv = argv_split(bufp, " ");
+       if (argv->argc == 0) {
+           msg_warn("missing command");
+       } else if (strcmp(argv->argv[0], "?") == 0) {
+           usage();
+       } else if (strcmp(argv->argv[0], "verbose") == 0) {
+           if (argv->argc != 2) {
+               msg_warn("bad verbose argument count: %d", argv->argc);
+           } else if (strcmp(argv->argv[1], "on") == 0) {
+               msg_verbose = 2;
+           } else if (strcmp(argv->argv[1], "off") == 0) {
+               msg_verbose = 0;
+           } else {
+               msg_warn("bad verbose argument");
+           }
+       } else if (strcmp(argv->argv[0], "open") == 0) {
+           if (state->dst != 0) {
+               msg_info("closing %s", VSTREAM_PATH(state->dst));
+               close_queue_file(state);
+           }
+           if (argv->argc != 2) {
+               msg_warn("bad open argument count: %d", argv->argc);
+           } else {
+               open_queue_file(state, argv->argv[1]);
+           }
+       } else if (state->dst == 0) {
+           msg_warn("no open queue file");
+       } else if (strcmp(argv->argv[0], "close") == 0) {
+           close_queue_file(state);
+       } else if (strcmp(argv->argv[0], "add_header") == 0) {
+           if (argv->argc < 2) {
+               msg_warn("bad add_header argument count: %d", argv->argc);
+           } else {
+               flatten_args(arg_buf, argv->argv + 2);
+               cleanup_add_header(state, argv->argv[2], STR(arg_buf));
+           }
+       } else if (strcmp(argv->argv[0], "ins_header") == 0) {
+           if (argv->argc < 3) {
+               msg_warn("bad ins_header argument count: %d", argv->argc);
+           } else if ((index = atoi(argv->argv[1])) < 1) {
+               msg_warn("bad ins_header index value");
+           } else {
+               flatten_args(arg_buf, argv->argv + 3);
+               cleanup_ins_header(state, index, argv->argv[2], STR(arg_buf));
+           }
+       } else if (strcmp(argv->argv[0], "upd_header") == 0) {
+           if (argv->argc < 3) {
+               msg_warn("bad upd_header argument count: %d", argv->argc);
+           } else if ((index = atoi(argv->argv[1])) < 1) {
+               msg_warn("bad upd_header index value");
+           } else {
+               flatten_args(arg_buf, argv->argv + 3);
+               cleanup_upd_header(state, index, argv->argv[2], STR(arg_buf));
+           }
+       } else if (strcmp(argv->argv[0], "del_header") == 0) {
+           if (argv->argc != 3) {
+               msg_warn("bad del_header argument count: %d", argv->argc);
+           } else if ((index = atoi(argv->argv[1])) < 1) {
+               msg_warn("bad del_header index value");
+           } else {
+               cleanup_del_header(state, index, argv->argv[2]);
+           }
+       } else if (strcmp(argv->argv[0], "add_rcpt") == 0) {
+           if (argv->argc != 2) {
+               msg_warn("bad add_rcpt argument count: %d", argv->argc);
+           } else {
+               cleanup_add_rcpt(state, argv->argv[1]);
+           }
+       } else if (strcmp(argv->argv[0], "del_rcpt") == 0) {
+           if (argv->argc != 2) {
+               msg_warn("bad del_rcpt argument count: %d", argv->argc);
+           } else {
+               cleanup_del_rcpt(state, argv->argv[1]);
+           }
+       } else {
+           msg_warn("bad command: %s", argv->argv[0]);
+       }
+       argv_free(argv);
+    }
+    vstring_free(inbuf);
+    vstring_free(arg_buf);
+    cleanup_state_free(state);
+
+    return (0);
+}
+
+#endif
diff --git a/postfix/src/cleanup/cleanup_milter.in1 b/postfix/src/cleanup/cleanup_milter.in1
new file mode 100644 (file)
index 0000000..d47d7eb
--- /dev/null
@@ -0,0 +1,12 @@
+#verbose on
+open test-queue-file.tmp
+add_rcpt xxxx
+add_rcpt yyyy
+del_rcpt alias@tail.porcupine.org
+del_rcpt yyyy
+ins_header 2 X-Test-Header test header value 1
+ins_header 2 X-Test-Header test header value 2
+del_header 2 X-Test-Header
+ins_header 3 X-Test-Header test header value 3
+upd_header 1 X X-replaced-header replacement header text
+close
diff --git a/postfix/src/cleanup/cleanup_milter.ref1 b/postfix/src/cleanup/cleanup_milter.ref1
new file mode 100644 (file)
index 0000000..0a54d2e
--- /dev/null
@@ -0,0 +1,53 @@
+*** ENVELOPE RECORDS test-queue-file.tmp ***
+        0 message_size:             428             654               3               0
+       65 message_arrival_time: Sat May 13 21:04:18 2006
+       84 create_time: Sat May 13 21:04:27 2006
+      108 named_attribute: rewrite_context=local
+      131 sender: wietse@porcupine.org
+      153 named_attribute: client_name=tail.porcupine.org
+      185 named_attribute: client_address=IPv6:2001:240:587:0:2d0:b7ff:febe:ca9f
+      240 named_attribute: message_origin=tail.porcupine.org[2001:240:587:0:2d0:b7ff:febe:ca9f]
+      310 named_attribute: helo_name=tail.porcupine.org
+      340 named_attribute: protocol_name=SMTP
+      360 named_attribute: dsn_orig_rcpt=rfc822;wietse@porcupine.org
+      403 original_recipient: wietse@porcupine.org
+      425 recipient: wietse@porcupine.org
+      447 named_attribute: dsn_orig_rcpt=rfc822;alias@tail.porcupine.org
+      494 original_recipient: alias@tail.porcupine.org
+      520 canceled_recipient: wietse@porcupine.org
+      542 named_attribute: dsn_orig_rcpt=rfc822;alias@tail.porcupine.org
+      589 original_recipient: alias@tail.porcupine.org
+      615 canceled_recipient: root@porcupine.org
+      635 pointer_record:            1103
+     1103 named_attribute: notify_flags=1
+     1119 original_recipient: xxxx
+     1125 recipient: xxxx
+     1131 pointer_record:            1148
+     1148 named_attribute: notify_flags=1
+     1164 original_recipient: yyyy
+     1170 canceled_recipient: yyyy
+     1176 pointer_record:             652
+      652 *** MESSAGE CONTENTS test-queue-file.tmp ***
+      654 regular_text: Received: from tail.porcupine.org (tail.porcupine.org [IPv6:2001:240:587:0:2d0:b7ff:febe:ca9f])
+      751 regular_text:        by tail.porcupine.org (Postfix) with SMTP id E0F703D1E36;
+      811 regular_text:        Sat, 13 May 2006 21:04:18 -0400 (EDT)
+      851 pointer_record:            1264
+     1264 regular_text: X-Test-Header: test header value 2
+     1300 pointer_record:            1317
+     1317 regular_text: X-Test-Header: test header value 3
+     1353 pointer_record:            1193
+     1193 deleted_text: X-Test-Header: test header value 1
+     1229 pointer_record:            1370
+     1370 regular_text: X: X-replaced-header replacement header text
+     1416 pointer_record:             881
+      881 regular_text: Y: 1234567
+      893 regular_text: Message-Id: <20060514010427.E0F703D1E36@tail.porcupine.org>
+      954 regular_text: Date: Sat, 13 May 2006 21:04:18 -0400 (EDT)
+      999 regular_text: From: wietse@porcupine.org
+     1027 regular_text: To: undisclosed-recipients:;
+     1057 pointer_record:               0
+     1074 regular_text: 
+     1076 regular_text: text
+     1082 *** HEADER EXTRACTED test-queue-file.tmp ***
+     1084 pointer_record:               0
+     1101 *** MESSAGE FILE END test-queue-file.tmp ***
index f72d941295b7322162cb0bba26cd4c8ecf0060e7..c816373727da144e7e425f659865e5b77643588a 100644 (file)
 /*     CLEANUP_STATE *state;
 /*     int     type;
 /*     const char *format;
+/*
+/*     void    cleanup_out_header(state, buf)
+/*     CLEANUP_STATE *state;
+/*     VSTRING *buf;
 /* DESCRIPTION
 /*     This module writes records to the output stream.
 /*
 /*
 /*     cleanup_out_format() formats its arguments and writes
 /*     the result as a record.
+/*
+/*     cleanup_out_header() outputs a multi-line header as records
+/*     of the specified type. The input is expected to be newline
+/*     separated (not newline terminated), and is modified.
 /* LICENSE
 /* .ad
 /* .fi
@@ -71,6 +79,7 @@
 #include <msg.h>
 #include <vstring.h>
 #include <vstream.h>
+#include <split_at.h>
 
 /* Global library. */
 
@@ -78,6 +87,7 @@
 #include <rec_type.h>
 #include <cleanup_user.h>
 #include <mail_params.h>
+#include <lex_822.h>
 
 /* Application-specific. */
 
@@ -104,6 +114,8 @@ void    cleanup_out(CLEANUP_STATE *state, int type, const char *string, ssize_t
 
 #define TEXT_RECORD(t) ((t) == REC_TYPE_NORM || (t) == REC_TYPE_CONT)
 
+    if (var_line_limit <= 0)
+       msg_panic("cleanup_out: bad line length limit: %d", var_line_limit);
     do {
        if (len > var_line_limit && TEXT_RECORD(type)) {
            err = rec_put(state->dst, REC_TYPE_CONT, string, var_line_limit);
@@ -148,3 +160,28 @@ void    cleanup_out_format(CLEANUP_STATE *state, int type, const char *fmt,...)
     va_end(ap);
     CLEANUP_OUT_BUF(state, type, vp);
 }
+
+/* cleanup_out_header - output one multi-line header as a bunch of records */
+
+void    cleanup_out_header(CLEANUP_STATE *state, VSTRING *header_buf)
+{
+    char   *start = vstring_str(header_buf);
+    char   *line;
+    char   *next_line;
+
+    /*
+     * Prepend a tab to continued header lines that went through the address
+     * rewriting machinery. See cleanup_fold_header(state) below for the form
+     * of such header lines. NB: This code destroys the header. We could try
+     * to avoid clobbering it, but we're not going to use the data any
+     * further.
+     */
+    for (line = start; line; line = next_line) {
+       next_line = split_at(line, '\n');
+       if (line == start || IS_SPACE_TAB(*line)) {
+           cleanup_out_string(state, REC_TYPE_NORM, line);
+       } else {
+           cleanup_out_format(state, REC_TYPE_NORM, "\t%s", line);
+       }
+    }
+}
index bc5d68eba9fda17cb3fcaab1e7443b2c800c7b94..495ece4b3afac92a29de29450b59e1965d6372cc 100644 (file)
@@ -6,7 +6,8 @@
 /* SYNOPSIS
 /*     #include "cleanup.h"
 /*
-/*     CLEANUP_STATE *cleanup_state_alloc(void)
+/*     CLEANUP_STATE *cleanup_state_alloc(src)
+/*     VSTREAM *src;
 /*
 /*     void    cleanup_state_free(state)
 /*     CLEANUP_STATE *state;
 #include <mime_state.h>
 #include <mail_proto.h>
 
+/* Milter library. */
+
+#include <milter.h>
+
 /* Application-specific. */
 
 #include "cleanup.h"
 
 /* cleanup_state_alloc - initialize global state */
 
-CLEANUP_STATE *cleanup_state_alloc(void)
+CLEANUP_STATE *cleanup_state_alloc(VSTREAM *src)
 {
     CLEANUP_STATE *state = (CLEANUP_STATE *) mymalloc(sizeof(*state));
 
@@ -61,6 +66,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
     state->temp2 = vstring_alloc(10);
     if (cleanup_strip_chars)
        state->stripped_buf = vstring_alloc(10);
+    state->src = src;
     state->dst = 0;
     state->handle = 0;
     state->queue_name = 0;
@@ -83,10 +89,14 @@ CLEANUP_STATE *cleanup_state_alloc(void)
     state->action = cleanup_envelope;
     state->data_offset = -1;
     state->xtra_offset = -1;
+    state->append_rcpt_pt_offset = -1;
+    state->append_rcpt_pt_target = -1;
+    state->append_hdr_pt_offset = -1;
+    state->append_hdr_pt_target = -1;
     state->rcpt_count = 0;
     state->reason = 0;
     state->attr = nvtable_create(10);
-    nvtable_update(state->attr, MAIL_ATTR_ORIGIN, MAIL_ATTR_ORG_LOCAL);
+    nvtable_update(state->attr, MAIL_ATTR_LOG_ORIGIN, MAIL_ATTR_ORG_LOCAL);
     state->mime_state = 0;
     state->mime_errs = 0;
     state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL;
@@ -97,6 +107,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
     state->dsn_notify = 0;
     state->dsn_orcpt = 0;
     state->verp_delims = 0;
+    state->milters = 0;
     return (state);
 }
 
@@ -141,5 +152,7 @@ void    cleanup_state_free(CLEANUP_STATE *state)
        myfree(state->dsn_orcpt);
     if (state->verp_delims)
        myfree(state->verp_delims);
+    if (state->milters)
+       milter_free(state->milters);
     myfree((char *) state);
 }
diff --git a/postfix/src/cleanup/test-queue-file b/postfix/src/cleanup/test-queue-file
new file mode 100755 (executable)
index 0000000..da59ed9
Binary files /dev/null and b/postfix/src/cleanup/test-queue-file differ
index 5fb3e997e3122ea37691b865e8dd405ab90108eb..04855889b105e7fd16191911752f2a61ce93b096 100644 (file)
 
 static int deliver_message(DELIVER_REQUEST *request)
 {
-    char   *myname = "deliver_message";
+    const char *myname = "deliver_message";
     VSTREAM *src;
     int     result = 0;
     int     status;
index 1a13a76138d02051f472066eda82d388c5671440..2aeb98f03157b349dcaeea9dfd8d0151eb0690b5 100644 (file)
 
 static int deliver_message(DELIVER_REQUEST *request)
 {
-    char   *myname = "deliver_message";
+    const char *myname = "deliver_message";
     VSTREAM *src;
     int     result = 0;
     int     status;
index ce98ec8b47b7746b2698a4cbc924daedf96fa9e5..4b47e30a75634a81645863784eab2e44f06b5ace 100644 (file)
@@ -267,7 +267,7 @@ static int flush_policy_ok(const char *site)
 
 static int flush_add_service(const char *site, const char *queue_id)
 {
-    char   *myname = "flush_add_service";
+    const char *myname = "flush_add_service";
     VSTRING *site_path;
     int     status;
 
@@ -294,7 +294,7 @@ static int flush_add_service(const char *site, const char *queue_id)
 
 static int flush_add_path(const char *path, const char *queue_id)
 {
-    char   *myname = "flush_add_path";
+    const char *myname = "flush_add_path";
     VSTREAM *log;
 
     /*
@@ -343,7 +343,7 @@ static int flush_add_path(const char *path, const char *queue_id)
 
 static int flush_send_service(const char *site, int how)
 {
-    char   *myname = "flush_send_service";
+    const char *myname = "flush_send_service";
     VSTRING *site_path;
     int     status;
 
@@ -503,7 +503,7 @@ static int flush_send_path(const char *path, int how)
 
 static int flush_refresh_service(int max_age)
 {
-    char   *myname = "flush_refresh_service";
+    const char *myname = "flush_refresh_service";
     SCAN_DIR *scan;
     char   *site_path;
     struct stat st;
index ddda6cc072534dc95a362bbc6a26642d7be6fcc8..21ec0d22dddf947be59d5302cc01fa67a19514ea 100644 (file)
@@ -1517,10 +1517,13 @@ recipient_list.o: recipient_list.c
 recipient_list.o: recipient_list.h
 record.o: ../../include/msg.h
 record.o: ../../include/mymalloc.h
+record.o: ../../include/stringops.h
 record.o: ../../include/sys_defs.h
 record.o: ../../include/vbuf.h
 record.o: ../../include/vstream.h
 record.o: ../../include/vstring.h
+record.o: off_cvt.h
+record.o: rec_type.h
 record.o: record.c
 record.o: record.h
 remove.o: ../../include/sys_defs.h
index 54a3059cf04e6e39c1f89c2ed73f57853af187ab..31c696205e5b13bae2cecc7222e98c2bce8466ab 100644 (file)
@@ -220,7 +220,7 @@ int     bounce_append(int flags, const char *id, MSG_STATS *stats,
      */
     else {
        char   *my_status = mystrdup(my_dsn.status);
-       char   *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
+       const char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
 
        /*
         * Supply default action.
index 11ba3606198d479a3157545570439c332a5a816b..3603f04c08f4afd2cfd67aed9077092b4aaaf57c 100644 (file)
@@ -59,6 +59,7 @@
   * because cleanup_strerror() can report only one error.
   */
 static CLEANUP_STAT_DETAIL cleanup_stat_map[] = {
+    CLEANUP_STAT_DEFER, 451, "4.7.1", "service unavailable",
     CLEANUP_STAT_PROXY, 451, "4.3.0", "queue file write error",
     CLEANUP_STAT_BAD, 451, "4.3.0", "internal protocol error",
     CLEANUP_STAT_RCPT, 550, "5.1.0", "no recipients specified",
index 1b9135da0f297bd7dd39db912af102b090b272c3..4853782ecb4c6f8cc9036426ca79486fcfe2174e 100644 (file)
@@ -51,6 +51,7 @@ static struct cleanup_flag_map cleanup_flag_map[] = {
     CLEANUP_FLAG_DISCARD, "discard_message",
     CLEANUP_FLAG_BCC_OK, "enable_automatic_bcc",
     CLEANUP_FLAG_MAP_OK, "enable_address_mapping",
+    CLEANUP_FLAG_MILTER, "enable_milters",
 };
 
 /* cleanup_strflags - map flags code to printable string */
index 238ae0c077d00b547a50e8cf8e767fb7495722c5..ede715d4147c5a1d1ee8ee088b52b438306b4446 100644 (file)
   */
 #define CLEANUP_FLAG_NONE      0       /* No special features */
 #define CLEANUP_FLAG_BOUNCE    (1<<0)  /* Bounce bad messages */
-#define CLEANUP_FLAG_FILTER    (1<<1)  /* Enable content filter */
+#define CLEANUP_FLAG_FILTER    (1<<1)  /* Enable header/body checks */
 #define CLEANUP_FLAG_HOLD      (1<<2)  /* Place message on hold */
 #define CLEANUP_FLAG_DISCARD   (1<<3)  /* Discard message silently */
 #define CLEANUP_FLAG_BCC_OK    (1<<4)  /* Ok to add auto-BCC addresses */
 #define CLEANUP_FLAG_MAP_OK    (1<<5)  /* Ok to map addresses */
+#define CLEANUP_FLAG_MILTER    (1<<6)  /* Enable Milter applications */
 
+#define CLEANUP_FLAG_FILTER_ALL        (CLEANUP_FLAG_FILTER | CLEANUP_FLAG_MILTER)
  /*
   * These are normally set when receiving mail from outside.
   */
 #define CLEANUP_FLAG_MASK_EXTERNAL \
-       (CLEANUP_FLAG_FILTER | CLEANUP_FLAG_BCC_OK | CLEANUP_FLAG_MAP_OK)
+       (CLEANUP_FLAG_FILTER_ALL | CLEANUP_FLAG_BCC_OK | CLEANUP_FLAG_MAP_OK)
 
  /*
   * These are normally set when generating notices or when forwarding mail
 
  /*
   * Diagnostics.
+  * 
+  * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason attribute,
+  * but CLEANUP_STAT_DEFER takes precedence. It terminates queue record
+  * processing, and prevents bounces from being sent.
   */
 #define CLEANUP_STAT_OK                0       /* Success. */
 #define CLEANUP_STAT_BAD       (1<<0)  /* Internal protocol error */
 #define CLEANUP_STAT_HOPS      (1<<4)  /* Too many hops */
 #define CLEANUP_STAT_RCPT      (1<<6)  /* No recipients found */
 #define CLEANUP_STAT_PROXY     (1<<7)  /* Proxy reject */
+#define CLEANUP_STAT_DEFER     (1<<8)  /* Temporary reject */
 
  /*
   * These are set when we can't bounce even if we were asked to.
   */
 #define CLEANUP_STAT_MASK_CANT_BOUNCE \
-       (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)
+       (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_DEFER)
 
  /*
   * These are set when we can't examine every record of a message.
   */
 #define CLEANUP_STAT_MASK_INCOMPLETE \
-       (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE)
+       (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE \
+           | CLEANUP_STAT_DEFER)
 
  /*
   * Mapping from status code to DSN detail and free text.
index 7adaf9ca2fa514c3e1f4d5d76e556cf529ad980a..e04f36696bdbf56c4298e9bcd4ad2fa6680250d0 100644 (file)
@@ -48,7 +48,7 @@
 /*     needed later in each call of \fIdb_common_expand\fR. A non-zero return
 /*     value indicates that data-depedent '%' expansions were found in the input
 /*     template.
-/*     
+/*
 /*     \fIdb_common_expand\fR expands the specifiers in \fIformat\fR.
 /*     When the input data lacks all fields needed for the expansion, zero
 /*     is returned and the query or result should be skipped. Otherwise
   */
 #include "db_common.h"
 
-#define        DB_COMMON_KEY_DOMAIN    (1 << 0)        /* Need lookup key domain */
-#define        DB_COMMON_KEY_USER      (1 << 1)        /* Need lookup key localpart */
-#define        DB_COMMON_VALUE_DOMAIN  (1 << 2)        /* Need result domain */
-#define        DB_COMMON_VALUE_USER    (1 << 3)        /* Need result localpart */
-#define        DB_COMMON_KEY_PARTIAL   (1 << 4)        /* Key uses input substrings */
+#define        DB_COMMON_KEY_DOMAIN    (1 << 0)/* Need lookup key domain */
+#define        DB_COMMON_KEY_USER      (1 << 1)/* Need lookup key localpart */
+#define        DB_COMMON_VALUE_DOMAIN  (1 << 2)/* Need result domain */
+#define        DB_COMMON_VALUE_USER    (1 << 3)/* Need result localpart */
+#define        DB_COMMON_KEY_PARTIAL   (1 << 4)/* Key uses input substrings */
 
 typedef struct {
-    DICT    *dict;
+    DICT   *dict;
     STRING_LIST *domain;
-    int      flags;
-    int      nparts;
-} DB_COMMON_CTX;
+    int     flags;
+    int     nparts;
+}       DB_COMMON_CTX;
 
 /* db_common_parse - validate query or result template */
 
-int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
+int     db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
 {
-    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)*ctxPtr;
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) * ctxPtr;
     const char *cp;
     int     dynamic = 0;
 
     if (ctx == 0) {
-       ctx = (DB_COMMON_CTX *)(*ctxPtr = mymalloc(sizeof *ctx));
+       ctx = (DB_COMMON_CTX *) (*ctxPtr = mymalloc(sizeof *ctx));
        ctx->dict = dict;
        ctx->domain = 0;
        ctx->flags = 0;
        ctx->nparts = 0;
     }
-
     for (cp = format; *cp; ++cp)
-       if (*cp == '%')
+       if (*cp == '%')
            switch (*++cp) {
            case '%':
-               break;
+               break;
            case 'u':
-               ctx->flags |= 
+               ctx->flags |=
                    query ? DB_COMMON_KEY_USER | DB_COMMON_KEY_PARTIAL
-                         : DB_COMMON_VALUE_USER;
+                   : DB_COMMON_VALUE_USER;
                dynamic = 1;
                break;
            case 'd':
-               ctx->flags |= 
+               ctx->flags |=
                    query ? DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_PARTIAL
-                         : DB_COMMON_VALUE_DOMAIN;
+                   : DB_COMMON_VALUE_DOMAIN;
+               dynamic = 1;
+               break;
+           case 's':
+           case 'S':
                dynamic = 1;
                break;
-           case 's': case 'S':
-               dynamic = 1;
-               break;
            case 'U':
-               ctx->flags |= DB_COMMON_KEY_PARTIAL | DB_COMMON_KEY_USER;
-               dynamic = 1;
+               ctx->flags |= DB_COMMON_KEY_PARTIAL | DB_COMMON_KEY_USER;
+               dynamic = 1;
                break;
-           case '1': case '2': case '3': case '4': case '5':
-           case '6': case '7': case '8': case '9':
-               /*
+           case '1':
+           case '2':
+           case '3':
+           case '4':
+           case '5':
+           case '6':
+           case '7':
+           case '8':
+           case '9':
+
+               /*
                 * Find highest %[1-9] index in query template. Input keys
                 * will be constrained to those with at least this many
-                * domain components. This makes the db_common_expand()
-                * code safe from invalid inputs.
+                * domain components. This makes the db_common_expand() code
+                * safe from invalid inputs.
                 */
-               if (ctx->nparts < *cp - '0')
+               if (ctx->nparts < *cp - '0')
                    ctx->nparts = *cp - '0';
                /* FALLTHROUGH */
            case 'D':
-               ctx->flags |= DB_COMMON_KEY_PARTIAL | DB_COMMON_KEY_DOMAIN;
-               dynamic = 1;
+               ctx->flags |= DB_COMMON_KEY_PARTIAL | DB_COMMON_KEY_DOMAIN;
+               dynamic = 1;
                break;
            default:
                msg_fatal("db_common_parse: %s: Invalid %s template: %s",
@@ -227,33 +235,34 @@ int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
 
 /* db_common_parse_domain - parse domain matchlist*/
 
-void db_common_parse_domain(CFG_PARSER *parser, void *ctxPtr)
+void    db_common_parse_domain(CFG_PARSER *parser, void *ctxPtr)
 {
-    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxPtr;
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
     char   *domainlist;
-    char   *myname = "db_common_parse_domain";
+    const char *myname = "db_common_parse_domain";
 
     domainlist = cfg_get_str(parser, "domain", "", 0, 0);
     if (*domainlist) {
        ctx->domain = string_list_init(MATCH_FLAG_NONE, domainlist);
        if (ctx->domain == 0)
+
            /*
             * The "domain" optimization skips input keys that may in fact
             * have unwanted matches in the database, so failure to create
             * the match list is fatal.
             */
            msg_fatal("%s: %s: domain match list creation using '%s' failed",
-                     myname, parser->name, domainlist);
+                     myname, parser->name, domainlist);
     }
     myfree(domainlist);
 }
 
 /* db_common_dict_partial - Does query use partial lookup keys? */
 
-int db_common_dict_partial(void *ctxPtr)
+int     db_common_dict_partial(void *ctxPtr)
 {
-#if 0  /* Breaks recipient_delimiter */
-    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxPtr;
+#if 0                                  /* Breaks recipient_delimiter */
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
 
     return (ctx->domain || ctx->flags & DB_COMMON_KEY_PARTIAL);
 #endif
@@ -262,22 +271,22 @@ int db_common_dict_partial(void *ctxPtr)
 
 /* db_common_free_ctx - free parse context */
 
-void db_common_free_ctx(void *ctxPtr)
+void    db_common_free_ctx(void *ctxPtr)
 {
-    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxPtr;
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
 
     if (ctx->domain)
-       string_list_free(ctx->domain);
-    myfree((char *)ctxPtr);
+       string_list_free(ctx->domain);
+    myfree((char *) ctxPtr);
 }
 
 /* db_common_expand - expand query and result templates */
 
-int db_common_expand(void *ctxArg, const char *format, const char *value,
-                    const char *key, VSTRING *result,
-                    db_quote_callback_t quote_func)
+int     db_common_expand(void *ctxArg, const char *format, const char *value,
+                                const char *key, VSTRING *result,
+                                db_quote_callback_t quote_func)
 {
-    char   *myname = "db_common_expand";
+    const char *myname = "db_common_expand";
     DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxArg;
     const char *vdomain = 0;
     const char *kdomain = 0;
@@ -465,7 +474,7 @@ int db_common_expand(void *ctxArg, const char *format, const char *value,
                    || ctx->nparts < *cp - '0')
                    msg_panic("%s: %s: %s: bad query/result template context",
                              myname, ctx->dict->name, format);
-               if (!parts || parts->argc <  ctx->nparts)
+               if (!parts || parts->argc < ctx->nparts)
                    msg_panic("%s: %s: %s: key has too few domain labels",
                              myname, ctx->dict->name, format);
                QUOTE_VAL(ctx->dict, quote_func,
@@ -495,9 +504,9 @@ int db_common_expand(void *ctxArg, const char *format, const char *value,
 
 /* db_common_check_domain - check domain list */
 
-int db_common_check_domain(void *ctxPtr, const char *addr)
+int     db_common_check_domain(void *ctxPtr, const char *addr)
 {
-    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxPtr;
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxPtr;
     char   *domain;
 
     if (ctx->domain) {
@@ -513,9 +522,9 @@ int db_common_check_domain(void *ctxPtr, const char *addr)
 
 /* db_common_sql_build_query -- build query for SQL maptypes */
 
-void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
+void    db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
 {
-    char   *myname = "db_common_sql_build_query";
+    const char *myname = "db_common_sql_build_query";
     char   *table;
     char   *select_field;
     char   *where_field;
@@ -526,22 +535,22 @@ void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
      */
     if ((table = cfg_get_str(parser, "table", NULL, 1, 0)) == 0)
        msg_fatal("%s: 'table' parameter not defined", myname);
-    
+
     if ((select_field = cfg_get_str(parser, "select_field", NULL, 1, 0)) == 0)
        msg_fatal("%s: 'select_field' parameter not defined", myname);
 
     if ((where_field = cfg_get_str(parser, "where_field", NULL, 1, 0)) == 0)
-            msg_fatal("%s: 'where_field' parameter not defined", myname);
+       msg_fatal("%s: 'where_field' parameter not defined", myname);
 
     additional_conditions = cfg_get_str(parser, "additional_conditions",
-                                       "", 0, 0);
+                                       "", 0, 0);
 
     vstring_sprintf(query, "SELECT %s FROM %s WHERE %s='%%s' %s",
-                    select_field, table, where_field,
-                    additional_conditions);
-    
+                   select_field, table, where_field,
+                   additional_conditions);
+
     myfree(table);
     myfree(select_field);
     myfree(where_field);
-    myfree(additional_conditions); 
+    myfree(additional_conditions);
 }
index c7096c97321938e7f62e6a063b20286dc09aad0a..97b42bd4e6e6c9553982c6471765ea533340ca55 100644 (file)
@@ -82,7 +82,7 @@ static int saved_level = UNUSED_SAVED_LEVEL;
 
 void    debug_peer_init(void)
 {
-    char   *myname = "debug_peer_init";
+    const char *myname = "debug_peer_init";
 
     /*
      * Sanity check.
index d2bb1ef2dd3dc9fa438ff9fae4733ddc2e84aa25..6fe3888923cc17de97b06dc72bc773a2f5f5fa83 100644 (file)
@@ -46,7 +46,7 @@
 
 void    deliver_completed(VSTREAM *stream, long offset)
 {
-    char   *myname = "deliver_completed";
+    const char *myname = "deliver_completed";
 
     if (offset == -1)
        return;
index 23abc30d334578f5e0ef93f00c911c740bbdf711..603b04b383a31d3ba115e54c4c360495ddc0a030 100644 (file)
@@ -109,10 +109,10 @@ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
               ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, request->dsn_envid,
               ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, request->dsn_ret,
               ATTR_TYPE_FUNC, msg_stats_print, (void *) &request->msg_stats,
-              ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, request->client_name,
-              ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, request->client_addr,
-              ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, request->client_proto,
-              ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, request->client_helo,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto,
+              ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, request->client_helo,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, request->sasl_method,
             ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, request->sasl_sender,
index 5c6e546dde97851a64c29dbd2c88e98045d1c312..d9ccc0c22c5090419ef3ab1ca0402b347c6ea722 100644 (file)
@@ -144,6 +144,7 @@ static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
 {
     DSN    *hop_status;
     int     err;
+
     /* XXX This DSN structure initialization bypasses integrity checks. */
     static DSN dummy_dsn = {"", "", "", "", "", "", ""};
 
@@ -178,7 +179,7 @@ static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
 
 static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
 {
-    char   *myname = "deliver_request_get";
+    const char *myname = "deliver_request_get";
     const char *path;
     struct stat st;
     static VSTRING *queue_name;
@@ -209,7 +210,7 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
        queue_id = vstring_alloc(10);
        nexthop = vstring_alloc(10);
        encoding = vstring_alloc(10);
-        address = vstring_alloc(10);
+       address = vstring_alloc(10);
        client_name = vstring_alloc(10);
        client_addr = vstring_alloc(10);
        client_proto = vstring_alloc(10);
@@ -238,10 +239,10 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
                  ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
                  ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret,
               ATTR_TYPE_FUNC, msg_stats_scan, (void *) &request->msg_stats,
-                 ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, client_name,
-                 ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, client_addr,
-                 ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, client_proto,
-                 ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, client_helo,
+                 ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, client_name,
+                 ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, client_addr,
+                 ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, client_proto,
+                 ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, client_helo,
                  ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, sasl_method,
                  ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, sasl_username,
                  ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, sasl_sender,
index 2888ac5d4cd92641498b2598b5b2b14d7a220dd1..bd2c4fda725dad56042b985682bf1cac68039ab1 100644 (file)
@@ -322,7 +322,7 @@ static void dict_ldap_timeout(int unused_sig)
 
 static void dict_ldap_logprint(LDAP_CONST char *data)
 {
-    char   *myname = "dict_ldap_debug";
+    const char *myname = "dict_ldap_debug";
     char   *buf,
            *p;
 
@@ -428,7 +428,7 @@ static int search_st(LDAP *ld, char *base, int scope, char *query,
 #ifdef LDAP_API_FEATURE_X_OPENLDAP
 static void dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
 {
-    char   *myname = "dict_ldap_set_tls_options";
+    const char *myname = "dict_ldap_set_tls_options";
     int     rc;
 
     if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
@@ -489,7 +489,7 @@ static void dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
 /* Establish a connection to the LDAP server. */
 static int dict_ldap_connect(DICT_LDAP *dict_ldap)
 {
-    char   *myname = "dict_ldap_connect";
+    const char *myname = "dict_ldap_connect";
     int     rc = 0;
 
 #ifdef LDAP_OPT_NETWORK_TIMEOUT
@@ -766,7 +766,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
     struct berval **vals;
     int     valcount;
     LDAPURLDesc *url;
-    char   *myname = "dict_ldap_get_values";
+    const char *myname = "dict_ldap_get_values";
 
     if (++recursion == 1)
        expansion = 0;
@@ -938,7 +938,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
 
 static const char *dict_ldap_lookup(DICT *dict, const char *name)
 {
-    char   *myname = "dict_ldap_lookup";
+    const char *myname = "dict_ldap_lookup";
     DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
     LDAPMessage *res = 0;
     static VSTRING *base;
@@ -1158,7 +1158,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
 
 static void dict_ldap_close(DICT *dict)
 {
-    char   *myname = "dict_ldap_close";
+    const char *myname = "dict_ldap_close";
     DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
     LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
     BINHASH_INFO *ht = dict_ldap->ht;
@@ -1200,7 +1200,7 @@ static void dict_ldap_close(DICT *dict)
 
 DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
 {
-    char   *myname = "dict_ldap_open";
+    const char *myname = "dict_ldap_open";
     DICT_LDAP *dict_ldap;
     VSTRING *url_list;
     char   *s;
index 0e82eaa0fc1d4d82d454bc5501ea860018fd4420..44f49bc16fbd5f6ee9cf779c26decd56a3fac793 100644 (file)
@@ -289,7 +289,7 @@ static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
 
 static const char *dict_mysql_lookup(DICT *dict, const char *name)
 {
-    char   *myname = "dict_mysql_lookup";
+    const char *myname = "dict_mysql_lookup";
     DICT_MYSQL *dict_mysql = (DICT_MYSQL *)dict;
     PLMYSQL *pldb = dict_mysql->pldb;
     MYSQL_RES *query_res;
index cedeec0bbce5b7300f6a34ef1b0c3b86cdb54edf..42ff90b3c2c79a3157e075af98e05bd43ad16a44 100644 (file)
@@ -320,7 +320,7 @@ static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
 
 static const char *dict_pgsql_lookup(DICT *dict, const char *name)
 {
-    char   *myname = "dict_pgsql_lookup";
+    const char *myname = "dict_pgsql_lookup";
     PGSQL_RES *query_res;
     DICT_PGSQL *dict_pgsql;
     PLPGSQL *pldb;
index 12948c69ebd832d44c37ad555e3d9247370ec956..7f7e65d36f3632ae5b2bf5a4d99d4eca49b28fd1 100644 (file)
@@ -105,7 +105,7 @@ void    flush_init(void)
 
 int     flush_purge(void)
 {
-    char   *myname = "flush_purge";
+    const char *myname = "flush_purge";
     int     status;
 
     if (msg_verbose)
@@ -131,7 +131,7 @@ int     flush_purge(void)
 
 int     flush_refresh(void)
 {
-    char   *myname = "flush_refresh";
+    const char *myname = "flush_refresh";
     int     status;
 
     if (msg_verbose)
@@ -157,7 +157,7 @@ int     flush_refresh(void)
 
 int     flush_send(const char *site)
 {
-    char   *myname = "flush_send";
+    const char *myname = "flush_send";
     int     status;
 
     if (msg_verbose)
@@ -187,7 +187,7 @@ int     flush_send(const char *site)
 
 int     flush_add(const char *site, const char *queue_id)
 {
-    char   *myname = "flush_add";
+    const char *myname = "flush_add";
     int     status;
 
     if (msg_verbose)
index d348d954d45c24f9431f353e9cde1b2991cc46e2..46b8812809c20f7cf18fdb81f90dafb68a4923de 100644 (file)
 /*     given in parentheses:
 /* .IP "no_unknown_recipient_checks (INPUT_TRANSP_UNKNOWN_RCPT)"
 /*     Do not try to reject unknown recipients.
-/* .IP "no_address_mappings (INPUT_TRANSP_ADDRESS_MAPPING)
+/* .IP "no_address_mappings (INPUT_TRANSP_ADDRESS_MAPPING)"
 /*     Disable canonical address mapping, virtual alias map expansion,
 /*     address masquerading, and automatic BCC recipients.
-/* .IP "no_header_body_checkss (INPUT_TRANSP_HEADER_BODY)
+/* .IP "no_header_body_checks (INPUT_TRANSP_HEADER_BODY)"
 /*     Disable header/body_checks.
+/* .IP "no_milters (INPUT_TRANSP_MILTER)"
+/*     Disable Milter applications.
 /*
 /*     input_transp_cleanup() takes a bunch of cleanup processing
 /*     flags and updates them according to the settings in the
@@ -71,6 +73,7 @@ int     input_transp_mask(const char *param_name, const char *pattern)
        "no_unknown_recipient_checks", INPUT_TRANSP_UNKNOWN_RCPT,
        "no_address_mappings", INPUT_TRANSP_ADDRESS_MAPPING,
        "no_header_body_checks", INPUT_TRANSP_HEADER_BODY,
+       "no_milters", INPUT_TRANSP_MILTER,
        0,
     };
 
@@ -90,6 +93,8 @@ int     input_transp_cleanup(int cleanup_flags, int transp_mask)
        cleanup_flags &= ~(CLEANUP_FLAG_BCC_OK | CLEANUP_FLAG_MAP_OK);
     if (transp_mask & INPUT_TRANSP_HEADER_BODY)
        cleanup_flags &= ~CLEANUP_FLAG_FILTER;
+    if (transp_mask & INPUT_TRANSP_MILTER)
+       cleanup_flags &= ~CLEANUP_FLAG_MILTER;
     if (msg_verbose)
        msg_info("after %s: cleanup flags = %s",
                 myname, cleanup_strflags(cleanup_flags));
index e324d44228e9215889b42e0df14a8f9005af21f3..74f5a4124646aa48641815220ee8d59f62a1847e 100644 (file)
@@ -17,6 +17,7 @@
 #define INPUT_TRANSP_UNKNOWN_RCPT      (1<<0)
 #define INPUT_TRANSP_ADDRESS_MAPPING   (1<<1)
 #define INPUT_TRANSP_HEADER_BODY       (1<<2)
+#define INPUT_TRANSP_MILTER            (1<<3)
 
 extern int input_transp_mask(const char *, const char *);
 extern int input_transp_cleanup(int, int);
index 1ebbbc20b0dcf38b241cd15af00adb18481c18f9..6769daa8633453f7a553aa0b6e2eac0e34c2e0e0 100644 (file)
@@ -94,7 +94,7 @@
 
 const char *mail_addr_find(MAPS *path, const char *address, char **extp)
 {
-    char   *myname = "mail_addr_find";
+    const char *myname = "mail_addr_find";
     const char *result;
     char   *ratsign = 0;
     char   *full_key;
@@ -200,7 +200,7 @@ int     main(int argc, char **argv)
      * Initialize.
      */
     mail_conf_read();
-    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK  | DICT_FLAG_FOLD_FIX);
+    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
     while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
        extent = 0;
        result = mail_addr_find(path, STR(buffer), &extent);
index c8a6e2c9fb216403f2e105f1c6fcde3be1ff7673..6f835d39929b96c19c5faebefc45b4121e31ba26 100644 (file)
@@ -77,7 +77,7 @@
 ARGV   *mail_addr_map(MAPS *path, const char *address, int propagate)
 {
     VSTRING *buffer = 0;
-    char   *myname = "mail_addr_map";
+    const char *myname = "mail_addr_map";
     const char *string;
     char   *ratsign;
     char   *extension = 0;
index 42753d8542486e7031ea202b3d5f4ed69234503b..3a2b30e832de2a4ddeb2f56b3201bba4eafce6ec 100644 (file)
@@ -127,7 +127,7 @@ int     mail_copy(const char *sender,
                          VSTREAM *src, VSTREAM *dst,
                          int flags, const char *eol, DSN_BUF *why)
 {
-    char   *myname = "mail_copy";
+    const char *myname = "mail_copy";
     VSTRING *buf;
     char   *bp;
     off_t   orig_length;
index c2e16059453c4428908e06f50243bde1ca7ac44d..047045353b1b059e56427e172c2c051b5feb677c 100644 (file)
@@ -241,33 +241,6 @@ char   *var_verp_filter;
 int     var_in_flow_delay;
 char   *var_par_dom_match;
 char   *var_config_dirs;
-#ifdef USE_TLS
-char   *var_tls_rand_exch_name;
-char   *var_smtpd_tls_cert_file;
-char   *var_smtpd_tls_key_file;
-char   *var_smtpd_tls_dcert_file;
-char   *var_smtpd_tls_dkey_file;
-char   *var_smtpd_tls_CAfile;
-char   *var_smtpd_tls_CApath;
-char   *var_smtpd_tls_cipherlist;
-char   *var_smtpd_tls_dh512_param_file;
-char   *var_smtpd_tls_dh1024_param_file;
-int     var_smtpd_tls_loglevel;
-char   *var_smtpd_tls_scache_db;
-int     var_smtpd_tls_scache_timeout;
-char   *var_smtp_tls_cert_file;
-char   *var_smtp_tls_key_file;
-char   *var_smtp_tls_dcert_file;
-char   *var_smtp_tls_dkey_file;
-char   *var_smtp_tls_CAfile;
-char   *var_smtp_tls_CApath;
-char   *var_smtp_tls_cipherlist;
-int     var_smtp_tls_loglevel;
-char   *var_smtp_tls_scache_db;
-int     var_smtp_tls_scache_timeout;
-char   *var_tls_daemon_rand_source;
-int     var_tls_daemon_rand_bytes;
-#endif
 
 char   *var_import_environ;
 char   *var_export_environ;
@@ -515,27 +488,6 @@ void    mail_params_init()
        VAR_FLUSH_SERVICE, DEF_FLUSH_SERVICE, &var_flush_service, 1, 0,
        VAR_VERIFY_SERVICE, DEF_VERIFY_SERVICE, &var_verify_service, 1, 0,
        VAR_TRACE_SERVICE, DEF_TRACE_SERVICE, &var_trace_service, 1, 0,
-#ifdef USE_TLS
-       VAR_TLS_RAND_EXCH_NAME, DEF_TLS_RAND_EXCH_NAME, &var_tls_rand_exch_name, 0, 0,
-       VAR_SMTPD_TLS_CERT_FILE, DEF_SMTPD_TLS_CERT_FILE, &var_smtpd_tls_cert_file, 0, 0,
-       VAR_SMTPD_TLS_KEY_FILE, DEF_SMTPD_TLS_KEY_FILE, &var_smtpd_tls_key_file, 0, 0,
-       VAR_SMTPD_TLS_DCERT_FILE, DEF_SMTPD_TLS_DCERT_FILE, &var_smtpd_tls_dcert_file, 0, 0,
-       VAR_SMTPD_TLS_DKEY_FILE, DEF_SMTPD_TLS_DKEY_FILE, &var_smtpd_tls_dkey_file, 0, 0,
-       VAR_SMTPD_TLS_CA_FILE, DEF_SMTPD_TLS_CA_FILE, &var_smtpd_tls_CAfile, 0, 0,
-       VAR_SMTPD_TLS_CA_PATH, DEF_SMTPD_TLS_CA_PATH, &var_smtpd_tls_CApath, 0, 0,
-       VAR_SMTPD_TLS_CLIST, DEF_SMTPD_TLS_CLIST, &var_smtpd_tls_cipherlist, 0, 0,
-       VAR_SMTPD_TLS_512_FILE, DEF_SMTPD_TLS_512_FILE, &var_smtpd_tls_dh512_param_file, 0, 0,
-       VAR_SMTPD_TLS_1024_FILE, DEF_SMTPD_TLS_1024_FILE, &var_smtpd_tls_dh1024_param_file, 0, 0,
-       VAR_SMTPD_TLS_SCACHE_DB, DEF_SMTPD_TLS_SCACHE_DB, &var_smtpd_tls_scache_db, 0, 0,
-       VAR_SMTP_TLS_CERT_FILE, DEF_SMTP_TLS_CERT_FILE, &var_smtp_tls_cert_file, 0, 0,
-       VAR_SMTP_TLS_KEY_FILE, DEF_SMTP_TLS_KEY_FILE, &var_smtp_tls_key_file, 0, 0,
-       VAR_SMTP_TLS_DCERT_FILE, DEF_SMTP_TLS_DCERT_FILE, &var_smtp_tls_dcert_file, 0, 0,
-       VAR_SMTP_TLS_DKEY_FILE, DEF_SMTP_TLS_DKEY_FILE, &var_smtp_tls_dkey_file, 0, 0,
-       VAR_SMTP_TLS_CA_FILE, DEF_SMTP_TLS_CA_FILE, &var_smtp_tls_CAfile, 0, 0,
-       VAR_SMTP_TLS_CA_PATH, DEF_SMTP_TLS_CA_PATH, &var_smtp_tls_CApath, 0, 0,
-       VAR_SMTP_TLS_CLIST, DEF_SMTP_TLS_CLIST, &var_smtp_tls_cipherlist, 0, 0,
-       VAR_SMTP_TLS_SCACHE_DB, DEF_SMTP_TLS_SCACHE_DB, &var_smtp_tls_scache_db, 0, 0,
-#endif
        0,
     };
     static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
@@ -558,11 +510,6 @@ void    mail_params_init()
        VAR_TOKEN_LIMIT, DEF_TOKEN_LIMIT, &var_token_limit, 1, 0,
        VAR_MIME_MAXDEPTH, DEF_MIME_MAXDEPTH, &var_mime_maxdepth, 1, 0,
        VAR_MIME_BOUND_LEN, DEF_MIME_BOUND_LEN, &var_mime_bound_len, 1, 0,
-#ifdef USE_TLS
-       VAR_SMTPD_TLS_LOGLEVEL, DEF_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, 0, 0,
-       VAR_SMTP_TLS_LOGLEVEL, DEF_SMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0,
-       VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 1, 0,
-#endif
        VAR_DELAY_MAX_RES, DEF_DELAY_MAX_RES, &var_delay_max_res, MIN_DELAY_MAX_RES, MAX_DELAY_MAX_RES,
        0,
     };
@@ -576,10 +523,6 @@ void    mail_params_init()
        VAR_FORK_DELAY, DEF_FORK_DELAY, &var_fork_delay, 1, 0,
        VAR_FLOCK_DELAY, DEF_FLOCK_DELAY, &var_flock_delay, 1, 0,
        VAR_FLOCK_STALE, DEF_FLOCK_STALE, &var_flock_stale, 1, 0,
-#ifdef USE_TLS
-       VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0,
-       VAR_SMTP_TLS_SCACHTIME, DEF_SMTP_TLS_SCACHTIME, &var_smtp_tls_scache_timeout, 0, 0,
-#endif
        VAR_DAEMON_TIMEOUT, DEF_DAEMON_TIMEOUT, &var_daemon_timeout, 1, 0,
        VAR_IN_FLOW_DELAY, DEF_IN_FLOW_DELAY, &var_in_flow_delay, 0, 10,
        0,
index 0f6695db2d5fe2a8e00af2dd9cc92cc7d5062bfd..443fe52e9a56df63d0c0b2f34832431e7c4c6e9e 100644 (file)
@@ -1139,9 +1139,17 @@ extern char *var_smtpd_tls_CAfile;
 #define DEF_SMTPD_TLS_CA_PATH  ""
 extern char *var_smtpd_tls_CApath;
 
-#define VAR_SMTPD_TLS_CLIST    "smtpd_tls_cipherlist"
-#define DEF_SMTPD_TLS_CLIST    ""
-extern char *var_smtpd_tls_cipherlist;
+#define VAR_SMTPD_TLS_PROTO    "smtpd_tls_protocols"
+#define DEF_SMTPD_TLS_PROTO    ""
+extern char *var_smtpd_tls_protocols;
+
+#define VAR_SMTPD_TLS_CIPHERS  "smtpd_tls_ciphers"
+#define DEF_SMTPD_TLS_CIPHERS  "export"
+extern char *var_smtpd_tls_ciphers;
+
+#define VAR_SMTPD_TLS_EXCL_CIPH  "smtpd_tls_exclude_ciphers"
+#define DEF_SMTPD_TLS_EXCL_CIPH  ""
+extern char *var_smtpd_tls_excl_ciph;
 
 #define VAR_SMTPD_TLS_512_FILE "smtpd_tls_dh512_param_file"
 #define DEF_SMTPD_TLS_512_FILE ""
@@ -1195,6 +1203,12 @@ extern bool var_smtp_enforce_tls;
 #define DEF_LMTP_TLS_ENFORCE_PN        1
 extern bool var_smtp_tls_enforce_peername;
 
+#define VAR_SMTP_TLS_LEVEL     "smtp_tls_security_level"
+#define DEF_SMTP_TLS_LEVEL     ""
+#define VAR_LMTP_TLS_LEVEL     "lmtp_tls_security_level"
+#define DEF_LMTP_TLS_LEVEL     ""
+extern char *var_smtp_tls_level;
+
 #define VAR_SMTP_TLS_SCERT_VD  "smtp_tls_scert_verifydepth"
 #define DEF_SMTP_TLS_SCERT_VD  5
 #define VAR_LMTP_TLS_SCERT_VD  "lmtp_tls_scert_verifydepth"
@@ -1203,35 +1217,64 @@ extern int var_smtp_tls_scert_vd;
 
 #define VAR_SMTP_TLS_CERT_FILE "smtp_tls_cert_file"
 #define DEF_SMTP_TLS_CERT_FILE ""
+#define VAR_LMTP_TLS_CERT_FILE "lmtp_tls_cert_file"
+#define DEF_LMTP_TLS_CERT_FILE ""
 extern char *var_smtp_tls_cert_file;
 
 #define VAR_SMTP_TLS_KEY_FILE  "smtp_tls_key_file"
 #define DEF_SMTP_TLS_KEY_FILE  "$smtp_tls_cert_file"
+#define VAR_LMTP_TLS_KEY_FILE  "lmtp_tls_key_file"
+#define DEF_LMTP_TLS_KEY_FILE  "$lmtp_tls_cert_file"
 extern char *var_smtp_tls_key_file;
 
 #define VAR_SMTP_TLS_DCERT_FILE "smtp_tls_dcert_file"
 #define DEF_SMTP_TLS_DCERT_FILE ""
+#define VAR_LMTP_TLS_DCERT_FILE "lmtp_tls_dcert_file"
+#define DEF_LMTP_TLS_DCERT_FILE ""
 extern char *var_smtp_tls_dcert_file;
 
 #define VAR_SMTP_TLS_DKEY_FILE "smtp_tls_dkey_file"
 #define DEF_SMTP_TLS_DKEY_FILE "$smtp_tls_dcert_file"
+#define VAR_LMTP_TLS_DKEY_FILE "lmtp_tls_dkey_file"
+#define DEF_LMTP_TLS_DKEY_FILE "$lmtp_tls_dcert_file"
 extern char *var_smtp_tls_dkey_file;
 
 #define VAR_SMTP_TLS_CA_FILE   "smtp_tls_CAfile"
 #define DEF_SMTP_TLS_CA_FILE   ""
+#define VAR_LMTP_TLS_CA_FILE   "lmtp_tls_CAfile"
+#define DEF_LMTP_TLS_CA_FILE   ""
 extern char *var_smtp_tls_CAfile;
 
 #define VAR_SMTP_TLS_CA_PATH   "smtp_tls_CApath"
 #define DEF_SMTP_TLS_CA_PATH   ""
+#define VAR_LMTP_TLS_CA_PATH   "lmtp_tls_CApath"
+#define DEF_LMTP_TLS_CA_PATH   ""
 extern char *var_smtp_tls_CApath;
 
-#define VAR_SMTP_TLS_CLIST     "smtp_tls_cipherlist"
-#define DEF_SMTP_TLS_CLIST     ""
-extern char *var_smtp_tls_cipherlist;
+#define VAR_SMTP_TLS_CIPHERS   "smtp_tls_mandatory_ciphers"
+#define DEF_SMTP_TLS_CIPHERS   "medium"
+#define VAR_LMTP_TLS_CIPHERS   "lmtp_tls_mandatory_ciphers"
+#define DEF_LMTP_TLS_CIPHERS   "medium"
+extern char *var_smtp_tls_ciphers;
+
+#define VAR_SMTP_TLS_EXCL_CIPH  "smtp_tls_exclude_ciphers"
+#define DEF_SMTP_TLS_EXCL_CIPH  ""
+#define VAR_LMTP_TLS_EXCL_CIPH  "lmtp_tls_exclude_ciphers"
+#define DEF_LMTP_TLS_EXCL_CIPH  ""
+extern char *var_smtp_tls_excl_ciph;
+
+#define VAR_SMTP_TLS_MAND_EXCL  "smtp_tls_mandatory_exclude_ciphers"
+#define DEF_SMTP_TLS_MAND_EXCL  ""
+#define VAR_LMTP_TLS_MAND_EXCL  "lmtp_tls_mandatory_exclude_ciphers"
+#define DEF_LMTP_TLS_MAND_EXCL  ""
+extern char *var_smtp_tls_mand_excl;
 
 #define VAR_SMTP_TLS_LOGLEVEL  "smtp_tls_loglevel"
 #define DEF_SMTP_TLS_LOGLEVEL  0
-extern int var_smtp_tls_loglevel;
+#define VAR_LMTP_TLS_LOGLEVEL  "lmtp_tls_loglevel"
+#define DEF_LMTP_TLS_LOGLEVEL  0
+extern int var_smtp_tls_loglevel;      /* In smtp(8) and tlsmgr(8) */
+extern int var_lmtp_tls_loglevel;      /* In tlsmgr(8) */
 
 #define VAR_SMTP_TLS_NOTEOFFER "smtp_tls_note_starttls_offer"
 #define DEF_SMTP_TLS_NOTEOFFER 0
@@ -1241,11 +1284,45 @@ extern bool var_smtp_tls_note_starttls_offer;
 
 #define VAR_SMTP_TLS_SCACHE_DB "smtp_tls_session_cache_database"
 #define DEF_SMTP_TLS_SCACHE_DB ""
+#define VAR_LMTP_TLS_SCACHE_DB "lmtp_tls_session_cache_database"
+#define DEF_LMTP_TLS_SCACHE_DB ""
 extern char *var_smtp_tls_scache_db;
+extern char *var_lmtp_tls_scache_db;
 
 #define VAR_SMTP_TLS_SCACHTIME "smtp_tls_session_cache_timeout"
 #define DEF_SMTP_TLS_SCACHTIME "3600s"
+#define VAR_LMTP_TLS_SCACHTIME "lmtp_tls_session_cache_timeout"
+#define DEF_LMTP_TLS_SCACHTIME "3600s"
 extern int var_smtp_tls_scache_timeout;
+extern int var_lmtp_tls_scache_timeout;
+
+#define VAR_SMTP_TLS_POLICY    "smtp_tls_policy_maps"
+#define DEF_SMTP_TLS_POLICY    ""
+#define VAR_LMTP_TLS_POLICY    "lmtp_tls_policy_maps"
+#define DEF_LMTP_TLS_POLICY    ""
+extern char *var_smtp_tls_policy;
+
+#define VAR_SMTP_TLS_PROTO     "smtp_tls_mandatory_protocols"
+#define DEF_SMTP_TLS_PROTO     "SSLv3, TLSv1"
+#define VAR_LMTP_TLS_PROTO     "lmtp_tls_mandatory_protocols"
+#define DEF_LMTP_TLS_PROTO     "SSLv3, TLSv1"
+extern char *var_smtp_tls_protocols;
+
+#define VAR_SMTP_TLS_VFY_CMATCH        "smtp_tls_verify_cert_match"
+#define DEF_SMTP_TLS_VFY_CMATCH        "hostname"
+#define VAR_LMTP_TLS_VFY_CMATCH        "lmtp_tls_verify_cert_match"
+#define DEF_LMTP_TLS_VFY_CMATCH        "hostname"
+extern char *var_smtp_tls_vfy_cmatch;
+
+ /*
+  * There are no MX lookups for LMTP, so verify == secure
+  */
+#define VAR_SMTP_TLS_SEC_CMATCH        "smtp_tls_secure_cert_match"
+#define DEF_SMTP_TLS_SEC_CMATCH        "nexthop, dot-nexthop"
+#define VAR_LMTP_TLS_SEC_CMATCH        "lmtp_tls_secure_cert_match"
+#define DEF_LMTP_TLS_SEC_CMATCH        "nexthop"
+extern char *var_smtp_tls_sec_cmatch;
+
 
  /*
   * SASL authentication support, SMTP server side.
@@ -1337,12 +1414,15 @@ extern char *var_smtp_sasl_type;
 #define DEF_LMTP_SASL_TLS_OPTS "$" VAR_LMTP_SASL_OPTS
 extern char *var_smtp_sasl_tls_opts;
 
+#ifdef SNAPSHOT                                /* XXX: Not yet */
 #define VAR_SMTP_SASL_TLSV_OPTS        "smtp_sasl_tls_verified_security_options"
 #define DEF_SMTP_SASL_TLSV_OPTS        "$" VAR_SMTP_SASL_TLS_OPTS
 #define VAR_LMTP_SASL_TLSV_OPTS        "lmtp_sasl_tls_verified_security_options"
 #define DEF_LMTP_SASL_TLSV_OPTS        "$" VAR_LMTP_SASL_TLS_OPTS
 extern char *var_smtp_sasl_tlsv_opts;
 
+#endif
+
  /*
   * LMTP server. The soft error limit determines how many errors an LMTP
   * client may make before we start to slow down; the hard error limit
@@ -2555,6 +2635,102 @@ extern bool var_smtp_sender_auth;
 #define DEF_LMTP_CNAME_OVERR           0
 extern bool var_smtp_cname_overr;
 
+ /*
+  * TLS cipherlists
+  */
+#define VAR_TLS_HIGH_CLIST     "tls_high_cipherlist"
+#define DEF_TLS_HIGH_CLIST     "!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH"
+extern char *var_tls_high_clist;
+
+#define VAR_TLS_MEDIUM_CLIST   "tls_medium_cipherlist"
+#define DEF_TLS_MEDIUM_CLIST   "!EXPORT:!LOW:ALL:+RC4:@STRENGTH"
+extern char *var_tls_medium_clist;
+
+#define VAR_TLS_LOW_CLIST      "tls_low_cipherlist"
+#define DEF_TLS_LOW_CLIST      "!EXPORT:ALL:+RC4:@STRENGTH"
+extern char *var_tls_low_clist;
+
+#define VAR_TLS_EXPORT_CLIST   "tls_export_cipherlist"
+#define DEF_TLS_EXPORT_CLIST   "ALL:+RC4:@STRENGTH"
+extern char *var_tls_export_clist;
+
+#define VAR_TLS_NULL_CLIST     "tls_null_cipherlist"
+#define DEF_TLS_NULL_CLIST     "!aNULL:eNULL+kRSA"
+extern char *var_tls_null_clist;
+
+ /*
+  * Sendmail-style mail filter support.
+  */
+#define VAR_SMTPD_MILTERS              "smtpd_milters"
+#define DEF_SMTPD_MILTERS              ""
+extern char *var_smtpd_milters;
+
+#define VAR_CLEANUP_MILTERS            "non_smtpd_milters"
+#define DEF_CLEANUP_MILTERS            ""
+extern char *var_cleanup_milters;
+
+#define VAR_MILT_DEF_ACTION            "milter_default_action"
+#define DEF_MILT_DEF_ACTION            "tempfail"
+extern char *var_milt_def_action;
+
+#define VAR_MILT_CONN_MACROS           "milter_connect_macros"
+#define DEF_MILT_CONN_MACROS           "j {daemon_name} v"
+extern char *var_milt_conn_macros;
+
+#define VAR_MILT_HELO_MACROS           "milter_helo_macros"
+#define DEF_MILT_HELO_MACROS           "{tls_version} {cipher} {cipher_bits}" \
+                                       " {cert_subject} {cert_issuer}"
+extern char *var_milt_helo_macros;
+
+#define VAR_MILT_MAIL_MACROS           "milter_mail_macros"
+#define DEF_MILT_MAIL_MACROS           "i {auth_type} {auth_authen}" \
+                                       " {auth_author} {mail_addr}"
+extern char *var_milt_mail_macros;
+
+#define VAR_MILT_RCPT_MACROS           "milter_rcpt_macros"
+#define DEF_MILT_RCPT_MACROS           "i {rcpt_addr}"
+extern char *var_milt_rcpt_macros;
+
+#define VAR_MILT_DATA_MACROS           "milter_data_macros"
+#define DEF_MILT_DATA_MACROS           "i"
+extern char *var_milt_data_macros;
+
+#define VAR_MILT_UNK_MACROS            "milter_unknown_command_macros"
+#define DEF_MILT_UNK_MACROS            ""
+extern char *var_milt_unk_macros;
+
+#define VAR_MILT_EOD_MACROS            "milter_end_of_data_macros"
+#define DEF_MILT_EOD_MACROS            "i"
+extern char *var_milt_eod_macros;
+
+#define VAR_MILT_CONN_TIME             "milter_connect_timeout"
+#define DEF_MILT_CONN_TIME             "30s"
+extern int var_milt_conn_time;
+
+#define VAR_MILT_CMD_TIME              "milter_command_timeout"
+#define DEF_MILT_CMD_TIME              "30s"
+extern int var_milt_cmd_time;
+
+#define VAR_MILT_MSG_TIME              "milter_content_timeout"
+#define DEF_MILT_MSG_TIME              "300s"
+extern int var_milt_msg_time;
+
+#define VAR_MILT_PROTOCOL              "milter_protocol"
+#define DEF_MILT_PROTOCOL              "2"
+extern char *var_milt_protocol;
+
+#define VAR_MILT_DEF_ACTION            "milter_default_action"
+#define DEF_MILT_DEF_ACTION            "tempfail"
+extern char *var_milt_def_action;
+
+#define VAR_MILT_DAEMON_NAME           "milter_macro_daemon_name"
+#define DEF_MILT_DAEMON_NAME           "$" VAR_MYHOSTNAME
+extern char *var_milt_daemon_name;
+
+#define VAR_MILT_V                     "milter_macro_v"
+#define DEF_MILT_V                     "$" VAR_MAIL_NAME " $" VAR_MAIL_VERSION
+extern char *var_milt_v;
+
 /* LICENSE
 /* .ad
 /* .fi
index 26e41a332c1991433b0d7373a72d54337b93eff2..5ca8c8cb1e2f1976c9fe7a66847a51175503d2b3 100644 (file)
@@ -165,15 +165,24 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_ENC_8BIT     "8bit"  /* 8BITMIME equivalent */
 #define MAIL_ATTR_ENC_7BIT     "7bit"  /* 7BIT equivalent */
 #define MAIL_ATTR_ENC_NONE     ""      /* encoding unknown */
-#define MAIL_ATTR_CLIENT       "client"/* client name[addr] */
-#define MAIL_ATTR_CLIENT_NAME  "client_name"   /* client hostname */
-#define MAIL_ATTR_REVERSE_CLIENT_NAME "reverse_client_name"
-#define MAIL_ATTR_FORWARD_CLIENT_NAME "forward_client_name"
-#define MAIL_ATTR_CLIENT_ADDR  "client_address"        /* client address */
-#define MAIL_ATTR_HELO_NAME    "helo_name"     /* SMTP helo name */
-#define MAIL_ATTR_PROTO_NAME   "protocol_name" /* SMTP/ESMTP/QMQP/... */
+
+#define MAIL_ATTR_LOG_CLIENT_NAME "log_client_name"    /* client hostname */
+#define MAIL_ATTR_LOG_CLIENT_ADDR "log_client_address" /* client address */
+#define MAIL_ATTR_LOG_HELO_NAME        "log_helo_name" /* SMTP helo name */
+#define MAIL_ATTR_LOG_PROTO_NAME "log_protocol_name"   /* SMTP/ESMTP/QMQP */
+#define MAIL_ATTR_LOG_ORIGIN   "log_message_origin"    /* hostname[address] */
+
+#define MAIL_ATTR_ACT_CLIENT   "client"/* client name addr */
+#define MAIL_ATTR_ACT_CLIENT_NAME "client_name"        /* client name */
+#define MAIL_ATTR_ACT_CLIENT_ADDR "client_address"     /* client address */
+#define MAIL_ATTR_ACT_CLIENT_PORT "client_port"        /* client TCP port */
+#define MAIL_ATTR_ACT_CLIENT_AF        "client_address_type"   /* AF_INET etc. */
+#define MAIL_ATTR_ACT_HELO_NAME        "helo_name"     /* SMTP helo name */
+#define MAIL_ATTR_ACT_PROTO_NAME "protocol_name"       /* SMTP/ESMTP/QMQP */
+#define MAIL_ATTR_ACT_REVERSE_CLIENT_NAME "reverse_client_name"
+#define MAIL_ATTR_ACT_FORWARD_CLIENT_NAME "forward_client_name"
+
 #define MAIL_ATTR_PROTO_STATE  "protocol_state"        /* MAIL/RCPT/... */
-#define MAIL_ATTR_ORIGIN       "message_origin"        /* hostname[address] */
 #define MAIL_ATTR_ORG_NONE     "unknown"       /* origin unknown */
 #define MAIL_ATTR_ORG_LOCAL    "local" /* local submission */
 
index 86b4a88f2e0a17a55167551be1eaf64481041cd4..0d997080c6aac8aee6d36eb83f544933141b3da3 100644 (file)
 const char *mail_queue_dir(VSTRING *buf, const char *queue_name,
                                   const char *queue_id)
 {
-    char   *myname = "mail_queue_dir";
+    const char *myname = "mail_queue_dir";
     static VSTRING *private_buf = 0;
     static VSTRING *hash_buf = 0;
     static ARGV *hash_queue_names = 0;
@@ -218,7 +218,7 @@ const char *mail_queue_path(VSTRING *buf, const char *queue_name,
 
 int     mail_queue_mkdirs(const char *path)
 {
-    char   *myname = "mail_queue_mkdirs";
+    const char *myname = "mail_queue_mkdirs";
     char   *saved_path = mystrdup(path);
     int     ret;
 
@@ -310,7 +310,7 @@ int     mail_queue_id_ok(const char *queue_id)
 VSTREAM *mail_queue_enter(const char *queue_name, mode_t mode,
                                  struct timeval * tp)
 {
-    char   *myname = "mail_queue_enter";
+    const char *myname = "mail_queue_enter";
     static VSTRING *id_buf;
     static int pid;
     static VSTRING *path_buf;
index ce4ad033f2c7f8e02736deed2da8a1709cd809fe..ca9613e86a0ae75d0a37f468c03cbf43ed54f7c9 100644 (file)
@@ -434,7 +434,7 @@ MAIL_STREAM *mail_stream_command(const char *command)
 
 void    mail_stream_ctl(MAIL_STREAM *info, int op,...)
 {
-    char   *myname = "mail_stream_ctl";
+    const char *myname = "mail_stream_ctl";
     va_list ap;
     char   *new_queue = 0;
     char   *string_value;
index 7822af8ea91eb1bd34c17776b919b363aeed7293..219859fb9a1e36cde26eb8fdce0016b468391f08 100644 (file)
   * snapshots are called a.b-yyyymmdd, where a=major release number, b=minor
   * release number, c=patchlevel, and yyyymmdd is the release date:
   * yyyy=year, mm=month, dd=day.
-  * 
+  *
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20060614"
+#define MAIL_RELEASE_DATE      "20060629"
 #define MAIL_VERSION_NUMBER    "2.3"
 
 #ifdef SNAPSHOT
 
 #ifdef NONPROD
 # define MAIL_VERSION_PROD     "-nonprod"
-#else
-# define MAIL_VERSION_PROD
+#endif
+
+#ifndef MAIL_VERSION_PROD
+# define MAIL_VERSION_PROD     ""
 #endif
 
 #define VAR_MAIL_VERSION       "mail_version"
index d1c705b91bf9ad8ade0c152bbad119a133d35536..f0cb35526d4bf42640a063ea230460156353ca8a 100644 (file)
@@ -158,7 +158,7 @@ MAPS   *maps_create(const char *title, const char *map_names, int dict_flags)
 
 const char *maps_find(MAPS *maps, const char *name, int flags)
 {
-    char   *myname = "maps_find";
+    const char *myname = "maps_find";
     char  **map_name;
     const char *expansion;
     DICT   *dict;
index bcffef8dd5c52094894e5543f27d85df842dad32..b0694bb07b07778401a599a41b3e7c641eff8225 100644 (file)
@@ -46,7 +46,7 @@
 
 int     mark_corrupt(VSTREAM *src)
 {
-    char   *myname = "mark_corrupt";
+    const char *myname = "mark_corrupt";
     uid_t   saved_uid;
     gid_t   saved_gid;
 
index 5727ac89831371c812183651cf2d0001afe01581..66b93fb7ad381cafa83551e3cd0eb091ceeb91e5 100644 (file)
@@ -414,15 +414,19 @@ static MIME_ENCODING mime_encoding_map[] = {      /* RFC 2045 */
   * offsets for header and body segments.
   */
 #define HEAD_OUT(ptr, info, len) do { \
-       (ptr)->head_out((ptr)->app_context, (ptr)->curr_state, \
-                       (info), (ptr)->output_buffer, (ptr)->head_offset); \
-       (ptr)->head_offset += (len) + 1; \
+       if ((ptr)->head_out) { \
+           (ptr)->head_out((ptr)->app_context, (ptr)->curr_state, \
+                           (info), (ptr)->output_buffer, (ptr)->head_offset); \
+           (ptr)->head_offset += (len) + 1; \
+       } \
     } while(0)
 
 #define BODY_OUT(ptr, rec_type, text, len) do { \
-       (ptr)->body_out((ptr)->app_context, (rec_type), \
-                       (text), (len), (ptr)->body_offset); \
-       (ptr)->body_offset += (len) + 1; \
+       if ((ptr)->body_out) { \
+           (ptr)->body_out((ptr)->app_context, (rec_type), \
+                           (text), (len), (ptr)->body_offset); \
+           (ptr)->body_offset += (len) + 1; \
+       } \
     } while(0)
 
 /* mime_state_push - push boundary onto stack */
index 9a910a032043cd336e116188ff3f07165fde6bd2..12192ca3b88b18b5ab676086758ca20dbecf1e3b 100644 (file)
@@ -87,7 +87,7 @@ const char *mynetworks(void)
     static VSTRING *result;
 
     if (result == 0) {
-       char   *myname = "mynetworks";
+       const char *myname = "mynetworks";
        INET_ADDR_LIST *my_addr_list;
        INET_ADDR_LIST *my_mask_list;
        unsigned shift;
index c9ea78c995dbb1de32280fd53bf8a8a10960f1c6..362aaf954cebc28604993fe0e807a3dbb12a3ad4 100644 (file)
@@ -81,7 +81,7 @@ static void own_inet_addr_init(INET_ADDR_LIST *addr_list,
     INET_ADDR_LIST local_masks;
     char   *hosts;
     char   *host;
-    char   *sep = " \t,";
+    const char *sep = " \t,";
     char   *bufp;
     int     nvirtual;
     int     nlocal;
@@ -217,7 +217,7 @@ static void proxy_inet_addr_init(INET_ADDR_LIST *addr_list)
 {
     char   *hosts;
     char   *host;
-    char   *sep = " \t,";
+    const char *sep = " \t,";
     char   *bufp;
 
     /*
index 2ccbd25283b6a4d7e2b4124e32f538d233301735..0fac5f7a86d6d4280bee3a1780db01bdc16f632a 100644 (file)
@@ -190,7 +190,7 @@ static int pipe_command_maxtime;    /* available time to complete */
 
 static void get_pipe_args(struct pipe_args * args, va_list ap)
 {
-    char   *myname = "get_pipe_args";
+    const char *myname = "get_pipe_args";
     int     key;
 
     /*
@@ -286,7 +286,7 @@ static ssize_t pipe_command_write(int fd, void *buf, size_t len,
                                          void *unused_context)
 {
     int     maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0;
-    char   *myname = "pipe_command_write";
+    const char *myname = "pipe_command_write";
 
     /*
      * Don't wait when all available time was already used up.
@@ -309,7 +309,7 @@ static ssize_t pipe_command_read(int fd, void *buf, ssize_t len,
                                         void *unused_context)
 {
     int     maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0;
-    char   *myname = "pipe_command_read";
+    const char *myname = "pipe_command_read";
 
     /*
      * Don't wait when all available time was already used up.
@@ -349,7 +349,7 @@ static int pipe_command_wait_or_kill(pid_t pid, WAIT_STATUS_T *statusp, int sig,
                                             uid_t kill_uid, gid_t kill_gid)
 {
     int     maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 1;
-    char   *myname = "pipe_command_wait_or_kill";
+    const char *myname = "pipe_command_wait_or_kill";
     int     n;
 
     /*
@@ -385,7 +385,7 @@ static void pipe_child_cleanup(void)
 
 int     pipe_command(VSTREAM *src, DSN_BUF *why,...)
 {
-    char   *myname = "pipe_command";
+    const char *myname = "pipe_command";
     va_list ap;
     VSTREAM *cmd_in_stream;
     VSTREAM *cmd_out_stream;
index ec53be5efa7581fbaed72ff8a1f16510e3a4615e..15618eb1bbb7b79a494ab420a0c9ac756703ed42 100644 (file)
@@ -209,7 +209,7 @@ static void post_mail_init(VSTREAM *stream, const char *sender,
     rec_fprintf(stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
                REC_TYPE_TIME_ARG(now));
     rec_fprintf(stream, REC_TYPE_ATTR, "%s=%s",
-               MAIL_ATTR_ORIGIN, MAIL_ATTR_ORG_LOCAL);
+               MAIL_ATTR_LOG_ORIGIN, MAIL_ATTR_ORG_LOCAL);
     rec_fprintf(stream, REC_TYPE_ATTR, "%s=%d",
                MAIL_ATTR_TRACE_FLAGS, trace_flags);
     rec_fputs(stream, REC_TYPE_FROM, sender);
@@ -262,7 +262,7 @@ VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient,
 static void post_mail_open_event(int event, char *context)
 {
     POST_MAIL_STATE *state = (POST_MAIL_STATE *) context;
-    char   *myname = "post_mail_open_event";
+    const char *myname = "post_mail_open_event";
 
     switch (event) {
 
index 0ed52ef8559d416628a6217b676989d2eb7e3cb3..8ba87b43e052e1b7656853d730c0acf89a58adbe 100644 (file)
@@ -48,14 +48,17 @@ REC_TYPE_NAME rec_type_names[] = {
     REC_TYPE_FILT, "content_filter",
     REC_TYPE_FROM, "sender",
     REC_TYPE_DONE, "done_recipient",
+    REC_TYPE_DRCP, "canceled_recipient",
     REC_TYPE_RCPT, "recipient",
     REC_TYPE_ORCP, "original_recipient",
     REC_TYPE_WARN, "warning_message_time",
     REC_TYPE_ATTR, "named_attribute",
+    REC_TYPE_PTR, "pointer_record",
     REC_TYPE_KILL, "killed_record",
     REC_TYPE_MESG, "message_content",
     REC_TYPE_CONT, "unterminated_text",
     REC_TYPE_NORM, "regular_text",
+    REC_TYPE_DTXT, "deleted_text",
     REC_TYPE_XTRA, "extracted_info",
     REC_TYPE_RRTO, "return_receipt",
     REC_TYPE_ERTO, "errors_to",
index 28dea627aaac8b8947dd76f4b722af34b1f24b67..bf158f934bf88c2f04b90aee1f32bb5fe704739b 100644 (file)
@@ -44,6 +44,7 @@
 #define REC_TYPE_DONE  'D'             /* delivered recipient, optional */
 #define REC_TYPE_RCPT  'R'             /* todo recipient, optional */
 #define REC_TYPE_ORCP  'O'             /* original recipient, optional */
+#define REC_TYPE_DRCP  '/'             /* canceled recipient, optional */
 #define REC_TYPE_WARN  'W'             /* warning message time */
 #define REC_TYPE_ATTR  'A'             /* named attribute for extensions */
 #define REC_TYPE_KILL  'K'             /* killed record */
 
 #define REC_TYPE_CONT  'L'             /* long data record */
 #define REC_TYPE_NORM  'N'             /* normal data record */
+#define REC_TYPE_DTXT  'w'             /* deleted data record */
 
 #define REC_TYPE_XTRA  'X'             /* start extracted records */
 
 #define REC_TYPE_RRTO  'r'             /* return-receipt, from headers */
 #define REC_TYPE_ERTO  'e'             /* errors-to, from headers */
 #define REC_TYPE_PRIO  'P'             /* priority */
+#define REC_TYPE_PTR   'p'             /* pointer indirection */
 #define REC_TYPE_VERP  'V'             /* VERP delimiters */
 
 #define REC_TYPE_DSN_RET       '<'     /* DSN full/hdrs */
 #define REC_TYPE_DSN_ORCPT     'o'     /* DSN orig rcpt address */
 #define REC_TYPE_DSN_NOTIFY    'n'     /* DSN notify flags */
 
+#define REC_TYPE_MILT_COUNT    'm'
+
 #define REC_TYPE_END   'E'             /* terminator, required */
 
  /*
   * What I expect to see in a "pure recipient" sequence at the end of the
   * initial or extracted envelope segments, respectively. When a queue file
   * contains pure recipient sequences only, then the queue manager will not
-  * have to read all the queue file records before starting delivery. This
-  * is often the case with list mail, where such optimization is desirable.
+  * have to read all the queue file records before starting delivery. This is
+  * often the case with list mail, where such optimization is desirable.
   */
-#define REC_TYPE_ENV_RECIPIENT "MDROKon"
-#define REC_TYPE_EXT_RECIPIENT "EDROKon"
+#define REC_TYPE_ENV_RECIPIENT "MDRO/Kon"
+#define REC_TYPE_EXT_RECIPIENT "EDRO/Kon"
 
  /*
   * The types of records that I expect to see while processing different
   * Note: REC_TYPE_FILT and REC_TYPE_CONT are encoded with the same 'L'
   * constant, and it  is too late to change that now.
   */
-#define REC_TYPE_ENVELOPE      "MCTcFILSDROWVA>K<ion"
-#define REC_TYPE_CONTENT       "XLN"
-#define REC_TYPE_EXTRACT       "EDROPreAFIL>Kon"
+#define REC_TYPE_ENVELOPE      "MCTcFILSDRO/WVA>K<ion"
+#define REC_TYPE_CONTENT       "XLNw"
+#define REC_TYPE_EXTRACT       "EDRO/PreAFIL>Kon"
 
  /*
   * The subset of inputs that the postdrop command allows.
   * text record), recipient count, and queue manager hints. These are all
   * fixed-width fields so they can be updated in place. Queue manager hints
   * are defined in qmgr_user.h
+  * 
+  * See also: REC_TYPE_PTR_FORMAT below.
   */
 #define REC_TYPE_SIZE_FORMAT   "%15ld %15ld %15ld %15ld"
 #define REC_TYPE_SIZE_CAST1    long
        (tv).tv_usec = atol(_p); \
     } while (0)
 
+ /*
+  * Pointer records are used to edit a queue file in place before it is
+  * committed. When a record is appended or modified, we patch it into the
+  * existing record stream with a pointer to storage in a heap after the
+  * end-of-message marker; the new content is followed by a pointer record
+  * back to the existing record stream.
+  * 
+  * We need to have a few dummy pointer records in place at strategic places
+  * (after the last recipient, after the last header) so that we can always
+  * append recipients or append/modify headers without having to move message
+  * segment terminators.
+  * 
+  * We also need to have a dummy PTR record at the end of the content, so that
+  * we can always replace the message content without having to move the
+  * end-of-message marker.
+  * 
+  * A dummy PTR record has a null argument.
+  * 
+  * See also: REC_TYPE_SIZE_FORMAT above.
+  */
+#define REC_TYPE_PTR_FORMAT    "%15ld"
+#define REC_TYPE_PTR_SIZE      15
+
  /*
   * Programmatic interface.
   */
index 15cc1bd600b1a8e2e6fca8a16d5a025f34394695..6e4a78dcbdd211e9bddcc6aa6d2645fa3f858e73 100644 (file)
 /*     VSTRING *buf;
 /*     ssize_t maxsize;
 /*
+/*     int     rec_get_raw(stream, buf, maxsize, flags)
+/*     VSTREAM *stream;
+/*     VSTRING *buf;
+/*     ssize_t maxsize;
+/*     int     flags;
+/*
 /*     int     rec_put(stream, type, data, len)
 /*     VSTREAM *stream;
 /*     int     type;
 /*     int     type;
 /*     const char *format;
 /*     va_list ap;
+/*
+/*     int     rec_goto(stream, where)
+/*     VSTREAM *stream;
+/*     const char *where;
 /* DESCRIPTION
 /*     This module reads and writes typed variable-length records.
 /*     Each record contains a 1-byte type code (0..255), a length
 /*     (1 or more bytes) and as much data as the length specifies.
 /*
-/*     rec_get() retrieves a record from the named record stream
+/*     rec_get_raw() retrieves a record from the named record stream
 /*     and returns the record type. The \fImaxsize\fR argument is
 /*     zero, or specifies a maximal acceptable record length.
 /*     The result is REC_TYPE_EOF when the end of the file was reached,
 /*     and REC_TYPE_ERROR in case of a bad record. The result buffer is
 /*     null-terminated for convenience. Records may contain embedded
-/*     null characters.
+/*     null characters. The \fIflags\fR argument specifies zero or
+/*     more of the following:
+/* .IP REC_FLAG_FOLLOW_PTR
+/*     Follow PTR records, instead of exposing them to the application.
+/* .IP REC_FLAG_SKIP_DTXT
+/*     Skip "deleted text" records, instead of exposing them to
+/*     the application.
+/* .IP REC_FLAG_SEEK_END
+/*     Seek to the end-of-file upon reading a REC_TYPE_END record.
+/* .PP
+/*     Specify REC_FLAG_NONE to request no special processing,
+/*     and REC_FLAG_DEFAULT for normal use.
+/*
+/*     rec_get() is a wrapper around rec_get_raw() that always
+/*     enables the REC_FLAG_FOLLOW_PTR and REC_FLAG_SKIP_DTXT
+/*     features.
 /*
 /*     rec_put() stores the specified record and returns the record
 /*     type, or REC_TYPE_ERROR in case of problems.
 /*     REC_PUT_BUF() is a wrapper for rec_put() that makes it
 /*     easier to handle VSTRING buffers. It is an unsafe macro
 /*     that evaluates some arguments more than once.
+/*
+/*     rec_goto() takes the argument of a pointer record and moves
+/*     the file pointer to the specified location. A zero position
+/*     means do nothing. The result is REC_TYPE_ERROR in case of
+/*     failure.
 /* DIAGNOSTICS
 /*     Panics: interface violations. Fatal errors: insufficient memory.
 /*     Warnings: corrupted file.
 #include <mymalloc.h>
 #include <vstream.h>
 #include <vstring.h>
+#include <stringops.h>
 
 /* Global library. */
 
+#include <off_cvt.h>
+#include <rec_type.h>
 #include <record.h>
 
 /* rec_put_type - update record type field */
 
-int     rec_put_type(VSTREAM *stream, int type, long offset)
+int     rec_put_type(VSTREAM *stream, int type, off_t offset)
 {
     if (type < 0 || type > 255)
        msg_panic("rec_put_type: bad record type %d", type);
 
     if (msg_verbose > 2)
-       msg_info("rec_put_type: %d at %ld", type, offset);
+       msg_info("rec_put_type: %d at %ld", type, (long) offset);
 
     if (vstream_fseek(stream, offset, SEEK_SET) < 0
        || VSTREAM_PUTC(type, stream) != type) {
@@ -169,11 +202,11 @@ int     rec_put(VSTREAM *stream, int type, const char *data, ssize_t len)
     return (type);
 }
 
-/* rec_get - retrieve typed record */
+/* rec_get_raw - retrieve typed record */
 
-int     rec_get(VSTREAM *stream, VSTRING *buf, ssize_t maxsize)
+int     rec_get_raw(VSTREAM *stream, VSTRING *buf, ssize_t maxsize, int flags)
 {
-    char   *myname = "rec_get";
+    const char *myname = "rec_get";
     int     type;
     ssize_t len;
     int     len_byte;
@@ -185,57 +218,95 @@ int     rec_get(VSTREAM *stream, VSTRING *buf, ssize_t maxsize)
     if (maxsize < 0)
        msg_panic("%s: bad record size limit: %ld", myname, (long) maxsize);
 
-    /*
-     * Extract the record type.
-     */
-    if ((type = VSTREAM_GETC(stream)) == VSTREAM_EOF)
-       return (REC_TYPE_EOF);
-
-    /*
-     * Find out the record data length. Return an error result when the
-     * record data length is malformed or when it exceeds the acceptable
-     * limit.
-     */
-    for (len = 0, shift = 0; /* void */ ; shift += 7) {
-       if (shift >= (int) (NBBY * sizeof(int))) {
-           msg_warn("%s: too many length bits, record type %d",
-                    VSTREAM_PATH(stream), type);
+    for (;;) {
+
+       /*
+        * Extract the record type.
+        */
+       if ((type = VSTREAM_GETC(stream)) == VSTREAM_EOF)
+           return (REC_TYPE_EOF);
+
+       /*
+        * Find out the record data length. Return an error result when the
+        * record data length is malformed or when it exceeds the acceptable
+        * limit.
+        */
+       for (len = 0, shift = 0; /* void */ ; shift += 7) {
+           if (shift >= (int) (NBBY * sizeof(int))) {
+               msg_warn("%s: too many length bits, record type %d",
+                        VSTREAM_PATH(stream), type);
+               return (REC_TYPE_ERROR);
+           }
+           if ((len_byte = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
+               msg_warn("%s: unexpected EOF reading length, record type %d",
+                        VSTREAM_PATH(stream), type);
+               return (REC_TYPE_ERROR);
+           }
+           len |= (len_byte & 0177) << shift;
+           if ((len_byte & 0200) == 0)
+               break;
+       }
+       if (len < 0 || (maxsize > 0 && len > maxsize)) {
+           msg_warn("%s: illegal length %ld, record type %d",
+                    VSTREAM_PATH(stream), (long) len, type);
+           while (len-- > 0 && VSTREAM_GETC(stream) != VSTREAM_EOF)
+                /* void */ ;
            return (REC_TYPE_ERROR);
        }
-       if ((len_byte = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
-           msg_warn("%s: unexpected EOF reading length, record type %d",
-                    VSTREAM_PATH(stream), type);
+
+       /*
+        * Reserve buffer space for the result, and read the record data into
+        * the buffer.
+        */
+       VSTRING_RESET(buf);
+       VSTRING_SPACE(buf, len);
+       if (vstream_fread(stream, vstring_str(buf), len) != len) {
+           msg_warn("%s: unexpected EOF in data, record type %d length %ld",
+                    VSTREAM_PATH(stream), type, (long) len);
            return (REC_TYPE_ERROR);
        }
-       len |= (len_byte & 0177) << shift;
-       if ((len_byte & 0200) == 0)
+       VSTRING_AT_OFFSET(buf, len);
+       VSTRING_TERMINATE(buf);
+       if (msg_verbose > 2)
+           msg_info("%s: type %c len %ld data %.10s", myname,
+                    type, (long) len, vstring_str(buf));
+
+       /*
+        * Transparency options.
+        */
+       if (flags == 0)
            break;
+       if (type == REC_TYPE_PTR && (flags & REC_FLAG_FOLLOW_PTR) != 0
+           && (type = rec_goto(stream, vstring_str(buf))) != REC_TYPE_ERROR)
+           continue;
+       if (type == REC_TYPE_DTXT && (flags & REC_FLAG_SKIP_DTXT) != 0)
+           continue;
+       if (type == REC_TYPE_END && (flags & REC_FLAG_SEEK_END) != 0)
+           (void) vstream_fseek(stream, (off_t) 0, SEEK_END);
+       break;
     }
-    if (len < 0 || (maxsize > 0 && len > maxsize)) {
-       msg_warn("%s: illegal length %ld, record type %d",
-                VSTREAM_PATH(stream), (long) len, type);
-       while (len-- > 0 && VSTREAM_GETC(stream) != VSTREAM_EOF)
-            /* void */ ;
-       return (REC_TYPE_ERROR);
-    }
+    return (type);
+}
 
-    /*
-     * Reserve buffer space for the result, and read the record data into the
-     * buffer.
-     */
-    VSTRING_RESET(buf);
-    VSTRING_SPACE(buf, len);
-    if (vstream_fread(stream, vstring_str(buf), len) != len) {
-       msg_warn("%s: unexpected EOF in data, record type %d length %ld",
-                VSTREAM_PATH(stream), type, (long) len);
+/* rec_goto - follow PTR record */
+
+int     rec_goto(VSTREAM *stream, const char *buf)
+{
+    off_t   offset;
+
+    while (ISSPACE(*buf))
+       buf++;
+    if ((offset = off_cvt_string(buf)) < 0) {
+       msg_warn("%s: malformed pointer record value: %s",
+                VSTREAM_PATH(stream), buf);
+       return (REC_TYPE_ERROR);
+    } else if (offset > 0 && vstream_fseek(stream, offset, SEEK_SET) < 0) {
+       msg_warn("%s: seek error after pointer record: %m",
+                VSTREAM_PATH(stream));
        return (REC_TYPE_ERROR);
+    } else {
+       return (0);
     }
-    VSTRING_AT_OFFSET(buf, len);
-    VSTRING_TERMINATE(buf);
-    if (msg_verbose > 2)
-       msg_info("%s: type %c len %ld data %.10s", myname,
-                type, (long) len, vstring_str(buf));
-    return (type);
 }
 
 /* rec_vfprintf - write formatted string to record */
index 32e724cfb9aa277d120047fcdfda35c8c4926d9b..6e75734a394234cd21d0a1bada00d2b71276d852 100644 (file)
  /*
   * Functional interface.
   */
-extern int rec_get(VSTREAM *, VSTRING *, ssize_t);
+extern int rec_get_raw(VSTREAM *, VSTRING *, ssize_t, int);
 extern int rec_put(VSTREAM *, int, const char *, ssize_t);
-extern int rec_put_type(VSTREAM *, int, long);
+extern int rec_put_type(VSTREAM *, int, off_t);
 extern int PRINTFLIKE(3, 4) rec_fprintf(VSTREAM *, int, const char *,...);
 extern int rec_fputs(VSTREAM *, int, const char *);
+extern int rec_goto(VSTREAM *, const char *);
 
 #define REC_PUT_BUF(v, t, b) rec_put((v), (t), vstring_str(b), VSTRING_LEN(b))
 
+#define REC_FLAG_NONE  (0)
+#define REC_FLAG_FOLLOW_PTR    (1<<0)          /* follow PTR records */
+#define REC_FLAG_SKIP_DTXT     (1<<1)          /* skip DTXT records */
+#define REC_FLAG_SEEK_END      (1<<2)          /* seek EOF after END record */
+
+#define REC_FLAG_DEFAULT \
+       (REC_FLAG_FOLLOW_PTR | REC_FLAG_SKIP_DTXT | REC_FLAG_SEEK_END)
+
+#define rec_get(fp, buf, limit) \
+       rec_get_raw((fp), (buf), (limit), REC_FLAG_DEFAULT)
+
  /*
   * Stuff that needs <stdarg.h>
   */
index 182c8688509e2113a135b01b99ec078eb9523a74..1b9ae4e8d87e6ba59f835d6b83b5fb3947a1aec7 100644 (file)
@@ -161,7 +161,7 @@ void    resolve_clnt_init(RESOLVE_REPLY *reply)
 void    resolve_clnt(const char *class, const char *sender,
                             const char *addr, RESOLVE_REPLY *reply)
 {
-    char   *myname = "resolve_clnt";
+    const char *myname = "resolve_clnt";
     VSTREAM *stream;
     int     server_flags;
 
index ce5ebfc1b5bd6fa51c6166b7a9c35e73e640af02..dc613efc06ddd848b9f26af50de18ba031e770db 100644 (file)
 
 static uid_t dict_owner(char *table)
 {
-    char   *myname = "dict_owner";
+    const char *myname = "dict_owner";
     DICT   *dict;
     struct stat st;
 
@@ -126,7 +126,7 @@ static uid_t dict_owner(char *table)
 int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
                              char *name, int *statusp)
 {
-    char   *myname = "deliver_alias";
+    const char *myname = "deliver_alias";
     const char *alias_result;
     char   *saved_alias_result;
     char   *owner;
index a5a3bfd91c5d3c77e90efafd7c967598903ff33a..8873d15bccdb512e07ad6d67acea7b7e9aed589c 100644 (file)
@@ -83,7 +83,7 @@
 
 int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command)
 {
-    char   *myname = "deliver_command";
+    const char *myname = "deliver_command";
     DSN_BUF *why = state.msg_attr.why;
     int     cmd_status;
     int     deliver_status;
index 79f3d98762c664327cea9fffbde8734dc6d3576c..e26d241ffce753224169bd279d1781586e7a4804 100644 (file)
@@ -93,7 +93,7 @@
 
 int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
 {
-    char   *myname = "deliver_dotforward";
+    const char *myname = "deliver_dotforward";
     struct stat st;
     VSTRING *path;
     struct mypasswd *mypwd;
index 863a48f305f790a9e5190481abc01468c37b9171..d93ae90b0555e9166e64e10a40b2910ce05c3f0e 100644 (file)
@@ -81,7 +81,7 @@
 
 int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
 {
-    char   *myname = "deliver_file";
+    const char *myname = "deliver_file";
     struct stat st;
     MBOX   *mp;
     DSN_BUF *why = state.msg_attr.why;
index 4674b7e0b66ea193351272341e7e4512c6511f23..74cb642b352aa5a1a6394fd511f6b72dccaffaf6 100644 (file)
@@ -177,10 +177,10 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
        rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \
     } while (0)
 
-    PASS_ATTR(cleanup, MAIL_ATTR_CLIENT_NAME, request->client_name);
-    PASS_ATTR(cleanup, MAIL_ATTR_CLIENT_ADDR, request->client_addr);
-    PASS_ATTR(cleanup, MAIL_ATTR_PROTO_NAME, request->client_proto);
-    PASS_ATTR(cleanup, MAIL_ATTR_HELO_NAME, request->client_helo);
+    PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name);
+    PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr);
+    PASS_ATTR(cleanup, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto);
+    PASS_ATTR(cleanup, MAIL_ATTR_LOG_HELO_NAME, request->client_helo);
     PASS_ATTR(cleanup, MAIL_ATTR_SASL_METHOD, request->sasl_method);
     PASS_ATTR(cleanup, MAIL_ATTR_SASL_USERNAME, request->sasl_username);
     PASS_ATTR(cleanup, MAIL_ATTR_SASL_SENDER, request->sasl_sender);
@@ -243,7 +243,7 @@ int     forward_append(DELIVER_ATTR attr)
 static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
                                DELIVER_ATTR attr, char *delivered)
 {
-    char   *myname = "forward_send";
+    const char *myname = "forward_send";
     VSTRING *buffer = vstring_alloc(100);
     int     status;
     int     rec_type = 0;
index a8bb52efde959362260d832d688f857c6f6ef657..1158832491dfd3083a951a8b0f271974cdfad7fd 100644 (file)
@@ -78,7 +78,7 @@
 
 int     deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
 {
-    char   *myname = "deliver_include";
+    const char *myname = "deliver_include";
     struct stat st;
     struct mypasswd *file_pwd = 0;
     int     status;
index f0848c34a90b131799694f41901e8745edf97ccb..43a2596c645a59a8e0f13589aeaf26bfb52f841c 100644 (file)
@@ -652,7 +652,7 @@ MAPS   *alias_maps;
 
 static int local_deliver(DELIVER_REQUEST *rqst, char *service)
 {
-    char   *myname = "local_deliver";
+    const char *myname = "local_deliver";
     RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len;
     RECIPIENT *rcpt;
     int     rcpt_stat;
index 4b754caa70fe1757c001c62f9e3b101d97eef63e..04cead1b90cbd09e009727a3dcdc3a5e5d4470ed 100644 (file)
@@ -87,7 +87,7 @@
 
 static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_mailbox_file";
+    const char *myname = "deliver_mailbox_file";
     char   *spool_dir;
     char   *mailbox;
     DSN_BUF *why = state.msg_attr.why;
@@ -241,7 +241,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
 
 int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
 {
-    char   *myname = "deliver_mailbox";
+    const char *myname = "deliver_mailbox";
     int     status;
     struct mypasswd *mbox_pwd;
     char   *path;
index 2860f7769e9e34f13d2d377c010fc7f32dd6259f..a1ef4932ad3dd31511938d17cb7ccaec0d1bf9cb 100644 (file)
@@ -76,7 +76,7 @@
 
 int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
 {
-    char   *myname = "deliver_maildir";
+    const char *myname = "deliver_maildir";
     char   *newdir;
     char   *tmpdir;
     char   *curdir;
index 614bd2113bb861861d248ac4efc07a55ed9b5335..e3bab10d0924489967c2029cc48ae914a26cce5e 100644 (file)
@@ -97,7 +97,7 @@
 
 static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_switch";
+    const char *myname = "deliver_switch";
     int     status = 0;
     struct stat st;
     struct mypasswd *mypwd;
@@ -197,7 +197,7 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
 
 int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_recipient";
+    const char *myname = "deliver_recipient";
     int     rcpt_stat;
 
     /*
index 2a2f6833706768980c1a4a817798dc764a1c08ea..89c330325e4c00eeace390c7a8e8ee8401ab706c 100644 (file)
@@ -85,7 +85,7 @@ int     deliver_resolve_addr(LOCAL_STATE state, USER_ATTR usr_attr, char *addr)
 
 int     deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
 {
-    char   *myname = "deliver_resolve_tree";
+    const char *myname = "deliver_resolve_tree";
     RESOLVE_REPLY reply;
     int     status;
     ssize_t ext_len;
index 4293c2f92a5e71b1662e033869002cb255e41cc9..cc80acfa6aaf798caa1435d918289ec6f13837ea 100644 (file)
@@ -81,7 +81,7 @@
 
 int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_unknown";
+    const char *myname = "deliver_unknown";
     int     status;
     VSTRING *expand_luser;
     static MAPS *transp_maps;
index b0cd7c1028c9e2ec11adbb8ae51c84cd6097ee80..15cab0d1a410a75739885f393aa93c20230f3c3f 100644 (file)
@@ -68,7 +68,7 @@
 
 ssize_t mail_flow_get(ssize_t len)
 {
-    char   *myname = "mail_flow_get";
+    const char *myname = "mail_flow_get";
     char    buf[BUFFER_SIZE];
     struct stat st;
     ssize_t count;
@@ -103,7 +103,7 @@ ssize_t mail_flow_get(ssize_t len)
 
 ssize_t mail_flow_put(ssize_t len)
 {
-    char   *myname = "mail_flow_put";
+    const char *myname = "mail_flow_put";
     char    buf[BUFFER_SIZE];
     ssize_t count;
     ssize_t n = 0;
@@ -132,7 +132,7 @@ ssize_t mail_flow_put(ssize_t len)
 
 ssize_t mail_flow_count(void)
 {
-    char   *myname = "mail_flow_count";
+    const char *myname = "mail_flow_count";
     ssize_t count;
 
     if ((count = peekfd(MASTER_FLOW_READ)) < 0)
index 496f6078da6d190d5566806cbcfad603d743b17d..b697d92407020cefffb7db2db22a8b21510b3b57 100644 (file)
@@ -92,7 +92,7 @@ static void master_avail_event(int event, char *context)
 
 void    master_avail_listen(MASTER_SERV *serv)
 {
-    char   *myname = "master_avail_listen";
+    const char *myname = "master_avail_listen";
     time_t  now;
     int     n;
 
@@ -140,7 +140,7 @@ void    master_avail_cleanup(MASTER_SERV *serv)
 
 void    master_avail_more(MASTER_SERV *serv, MASTER_PROC *proc)
 {
-    char   *myname = "master_avail_more";
+    const char *myname = "master_avail_more";
     int     n;
 
     /*
@@ -164,7 +164,7 @@ void    master_avail_more(MASTER_SERV *serv, MASTER_PROC *proc)
 
 void    master_avail_less(MASTER_SERV *serv, MASTER_PROC *proc)
 {
-    char   *myname = "master_avail_less";
+    const char *myname = "master_avail_less";
 
     /*
      * This child is no longer available for servicing connection requests.
index 22c1835ce79944306001e86f921206a3c58b8891..82db2f0c93a5d343a238e08371f92029ea2ba17c 100644 (file)
@@ -122,7 +122,7 @@ void    fset_master_ent(char *path)
 
 void    set_master_ent()
 {
-    char   *myname = "set_master_ent";
+    const char *myname = "set_master_ent";
 
     if (master_fp != 0)
        msg_panic("%s: configuration file still open", myname);
@@ -137,7 +137,7 @@ void    set_master_ent()
 
 void    end_master_ent()
 {
-    char   *myname = "end_master_ent";
+    const char *myname = "end_master_ent";
 
     if (master_fp == 0)
        msg_panic("%s: configuration file not open", myname);
@@ -150,7 +150,7 @@ void    end_master_ent()
 
 static NORETURN fatal_with_context(char *format,...)
 {
-    char   *myname = "fatal_with_context";
+    const char *myname = "fatal_with_context";
     VSTRING *vp = vstring_alloc(100);
     va_list ap;
 
index ed9b492d22b2273e5ffb1cce3e6f3daebd6802d3..68ae57d0f344125b038117f8e3a71581eea1de0a 100644 (file)
@@ -20,7 +20,7 @@ int     master_flow_pipe[2];
 
 void    master_flow_init(void)
 {
-    char   *myname = "master_flow_init";
+    const char *myname = "master_flow_init";
 
     if (pipe(master_flow_pipe) < 0)
        msg_fatal("%s: pipe: %m", myname);
index 0ff87838939c0521b62496e1e48bac1545097510..984b5dc057b7e6e649d35957d422ade7933c8b78 100644 (file)
@@ -70,7 +70,7 @@
 
 void    master_listen_init(MASTER_SERV *serv)
 {
-    char   *myname = "master_listen_init";
+    const char *myname = "master_listen_init";
     char   *end_point;
     int     n;
     MAI_HOSTADDR_STR hostaddr;
@@ -151,7 +151,7 @@ void    master_listen_init(MASTER_SERV *serv)
 
 void    master_listen_cleanup(MASTER_SERV *serv)
 {
-    char   *myname = "master_listen_cleanup";
+    const char *myname = "master_listen_cleanup";
     int     n;
 
     /*
index d1247479b0b6c3ef89f1abaaa18e1a13df20b5c6..052cbd1db6c01ada2ac818828aa829e3322d74d4 100644 (file)
@@ -64,7 +64,7 @@
 
 int     master_notify(int pid, unsigned generation, int status)
 {
-    char   *myname = "master_notify";
+    const char *myname = "master_notify";
     MASTER_STATUS stat;
 
     /*
index bef31e0f6d7544015c7a3a6977fa28031641f300..fc76efe0d96887eb663028878b3091bea8737eea 100644 (file)
@@ -168,7 +168,7 @@ static void master_sigchld(int sig)
 
 static void master_sigdeath(int sig)
 {
-    char   *myname = "master_sigdeath";
+    const char *myname = "master_sigdeath";
     struct sigaction action;
     pid_t   pid = getpid();
 
@@ -207,7 +207,7 @@ static void master_sigdeath(int sig)
 
 void    master_sigsetup(void)
 {
-    char   *myname = "master_sigsetup";
+    const char *myname = "master_sigsetup";
     struct sigaction action;
     static int sigs[] = {
        SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSEGV, SIGTERM,
index 41fe033926a68b0a77c9e5eaa3a819ea11e0e632..672a9ba2a1b136aaa30cc485f1feada298f6b3c2 100644 (file)
@@ -133,7 +133,7 @@ static void master_throttle(MASTER_SERV *serv)
 
 void    master_spawn(MASTER_SERV *serv)
 {
-    char   *myname = "master_spawn";
+    const char *myname = "master_spawn";
     MASTER_PROC *proc;
     MASTER_PID pid;
     int     n;
@@ -291,7 +291,7 @@ void    master_reap_child(void)
        if (msg_verbose)
            msg_info("master_reap_child: pid %d", pid);
        if ((proc = (MASTER_PROC *) binhash_find(master_child_table,
-                                       (char *) &pid, sizeof(pid))) == 0)
+                                         (char *) &pid, sizeof(pid))) == 0)
            msg_panic("master_reap: unknown pid: %d", pid);
        serv = proc->serv;
        if (!NORMAL_EXIT_STATUS(status)) {
index 28eb99c197440117f1e2ca5b56881f55a40b3190..edaee854bb119052d8449db35d330110f9f6b895 100644 (file)
@@ -59,7 +59,7 @@
 
 static void master_status_event(int event, char *context)
 {
-    char   *myname = "master_status_event";
+    const char *myname = "master_status_event";
     MASTER_SERV *serv = (MASTER_SERV *) context;
     MASTER_STATUS stat;
     MASTER_PROC *proc;
@@ -149,7 +149,7 @@ static void master_status_event(int event, char *context)
 
 void    master_status_init(MASTER_SERV *serv)
 {
-    char   *myname = "master_status_init";
+    const char *myname = "master_status_init";
 
     /*
      * Sanity checks.
@@ -176,7 +176,7 @@ void    master_status_init(MASTER_SERV *serv)
 
 void    master_status_cleanup(MASTER_SERV *serv)
 {
-    char   *myname = "master_status_cleanup";
+    const char *myname = "master_status_cleanup";
 
     /*
      * Sanity checks.
index f04e91dd6cfa6f8df7abef202325d8c314e57d55..8a431001f024bfaa5933b2b5f85d366d58ff7a5a 100644 (file)
@@ -75,7 +75,7 @@
 
 static void master_wakeup_timer_event(int unused_event, char *context)
 {
-    char   *myname = "master_wakeup_timer_event";
+    const char *myname = "master_wakeup_timer_event";
     MASTER_SERV *serv = (MASTER_SERV *) context;
     int     status;
     static char wakeup = TRIGGER_REQ_WAKEUP;
@@ -154,7 +154,7 @@ static void master_wakeup_timer_event(int unused_event, char *context)
 
 void    master_wakeup_init(MASTER_SERV *serv)
 {
-    char   *myname = "master_wakeup_init";
+    const char *myname = "master_wakeup_init";
 
     if (serv->wakeup_time == 0 || (serv->flags & MASTER_FLAG_CONDWAKE))
        return;
@@ -168,7 +168,7 @@ void    master_wakeup_init(MASTER_SERV *serv)
 
 void    master_wakeup_cleanup(MASTER_SERV *serv)
 {
-    char   *myname = "master_wakeup_cleanup";
+    const char *myname = "master_wakeup_cleanup";
 
     /*
      * Cleanup, even when the wakeup feature has been turned off. There might
index fdf8832250760e9ded0894bfff5697d619e0052c..34bfac50775941aba253e0f3b1b5fda29953f4c7 100644 (file)
@@ -458,7 +458,7 @@ static void multi_server_accept_inet(int unused_event, char *context)
 
 NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
 {
-    char   *myname = "multi_server_main";
+    const char *myname = "multi_server_main";
     VSTREAM *stream = 0;
     char   *root_dir = 0;
     char   *user_name = 0;
index 6cfefa16cb1bdad653e6618de1f68541c4ea25a8..2b0c92675219a6ebf1b404d637b15483c8817356 100644 (file)
@@ -374,7 +374,7 @@ static void single_server_accept_inet(int unused_event, char *context)
 
 NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
 {
-    char   *myname = "single_server_main";
+    const char *myname = "single_server_main";
     VSTREAM *stream = 0;
     char   *root_dir = 0;
     char   *user_name = 0;
index b7e672429718b78d6f4bb3fc8b827db82d5ba444..471a44c79fb0a12ed5bb4d9228716de480b5de4f 100644 (file)
@@ -262,7 +262,7 @@ static void trigger_server_wakeup(int fd)
 
 static void trigger_server_accept_fifo(int unused_event, char *context)
 {
-    char   *myname = "trigger_server_accept_fifo";
+    const char *myname = "trigger_server_accept_fifo";
     int     listen_fd = CAST_CHAR_PTR_TO_INT(context);
 
     if (trigger_server_lock != 0
@@ -286,7 +286,7 @@ static void trigger_server_accept_fifo(int unused_event, char *context)
 
 static void trigger_server_accept_local(int unused_event, char *context)
 {
-    char   *myname = "trigger_server_accept_local";
+    const char *myname = "trigger_server_accept_local";
     int     listen_fd = CAST_CHAR_PTR_TO_INT(context);
     int     time_left = 0;
     int     fd;
@@ -332,7 +332,7 @@ static void trigger_server_accept_local(int unused_event, char *context)
 
 static void trigger_server_accept_pass(int unused_event, char *context)
 {
-    char   *myname = "trigger_server_accept_pass";
+    const char *myname = "trigger_server_accept_pass";
     int     listen_fd = CAST_CHAR_PTR_TO_INT(context);
     int     time_left = 0;
     int     fd;
@@ -378,7 +378,7 @@ static void trigger_server_accept_pass(int unused_event, char *context)
 
 NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,...)
 {
-    char   *myname = "trigger_server_main";
+    const char *myname = "trigger_server_main";
     char   *root_dir = 0;
     char   *user_name = 0;
     int     debug_me = 0;
diff --git a/postfix/src/milter/.indent.pro b/postfix/src/milter/.indent.pro
new file mode 120000 (symlink)
index 0000000..5c837ec
--- /dev/null
@@ -0,0 +1 @@
+../../.indent.pro
\ No newline at end of file
diff --git a/postfix/src/milter/Makefile.in b/postfix/src/milter/Makefile.in
new file mode 100644 (file)
index 0000000..755ecf5
--- /dev/null
@@ -0,0 +1,119 @@
+SHELL  = /bin/sh
+SRCS   = milter.c milter8.c
+OBJS   = milter.o milter8.o
+HDRS   = milter.h
+TESTSRC        = 
+DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+INCL   =
+LIB    = libmilter.a
+TESTPROG= milter test-milter
+
+LIBS   = ../../lib/libglobal.a ../../lib/libutil.a
+LIB_DIR        = ../../lib
+INC_DIR        = ../../include
+MAKES  =
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+all: $(LIB)
+
+$(OBJS): ../../conf/makedefs.out
+
+Makefile: Makefile.in
+       (cat ../../conf/makedefs.out $?) >$@
+
+test:  $(TESTPROG)
+
+tests:
+
+root_tests:
+
+$(LIB):        $(OBJS)
+       $(AR) $(ARFL) $(LIB) $?
+       $(RANLIB) $(LIB)
+
+$(LIB_DIR)/$(LIB): $(LIB)
+       cp $(LIB) $(LIB_DIR)
+       $(RANLIB) $(LIB_DIR)/$(LIB)
+
+update: $(LIB_DIR)/$(LIB) $(HDRS)
+       -for i in $(HDRS); \
+       do \
+         cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \
+       done
+       cd $(INC_DIR); chmod 644 $(HDRS)
+
+printfck: $(OBJS) $(PROG)
+       rm -rf printfck
+       mkdir printfck
+       cp *.h printfck
+       sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+       set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+       cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+       lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+       rm -f *.o $(LIB) *core $(TESTPROG) junk
+       rm -rf printfck
+
+tidy:  clean
+
+milter:        milter.c $(LIB) $(LIBS)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+       mv junk $@.o
+
+test-milter: test-milter.c
+       cc -o $@ $? -lmilter -lpthread
+
+depend: $(MAKES)
+       (sed '1,/^# do not edit/!d' Makefile.in; \
+       set -e; for i in [a-z][a-z0-9]*.c; do \
+           $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+           -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \
+           -e 's/o: \.\//o: /' -e p -e '}' ; \
+       done | sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+       @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+milter.o: ../../include/argv.h
+milter.o: ../../include/attr.h
+milter.o: ../../include/iostuff.h
+milter.o: ../../include/mail_proto.h
+milter.o: ../../include/msg.h
+milter.o: ../../include/mymalloc.h
+milter.o: ../../include/rec_type.h
+milter.o: ../../include/record.h
+milter.o: ../../include/stringops.h
+milter.o: ../../include/sys_defs.h
+milter.o: ../../include/vbuf.h
+milter.o: ../../include/vstream.h
+milter.o: ../../include/vstring.h
+milter.o: milter.c
+milter.o: milter.h
+milter8.o: ../../include/argv.h
+milter8.o: ../../include/attr.h
+milter8.o: ../../include/connect.h
+milter8.o: ../../include/header_opts.h
+milter8.o: ../../include/iostuff.h
+milter8.o: ../../include/is_header.h
+milter8.o: ../../include/mail_proto.h
+milter8.o: ../../include/mime_state.h
+milter8.o: ../../include/msg.h
+milter8.o: ../../include/mymalloc.h
+milter8.o: ../../include/name_code.h
+milter8.o: ../../include/name_mask.h
+milter8.o: ../../include/rec_type.h
+milter8.o: ../../include/record.h
+milter8.o: ../../include/split_at.h
+milter8.o: ../../include/stringops.h
+milter8.o: ../../include/sys_defs.h
+milter8.o: ../../include/vbuf.h
+milter8.o: ../../include/vstream.h
+milter8.o: ../../include/vstring.h
+milter8.o: milter.h
+milter8.o: milter8.c
+test-milter.o: test-milter.c
diff --git a/postfix/src/milter/milter.c b/postfix/src/milter/milter.c
new file mode 100644 (file)
index 0000000..94bfa01
--- /dev/null
@@ -0,0 +1,919 @@
+/*++
+/* NAME
+/*     milter 3
+/* SUMMARY
+/*     generic MTA-side mail filter interface
+/* SYNOPSIS
+/*     #include <milter.h>
+/*
+/*     MILTERS *milter_create(milter_names, conn_timeout, cmd_timeout,
+/*                                     msg_timeout, protocol, def_action,
+/*                                     conn_macros, helo_macros,
+/*                                     mail_macros, rcpt_macros,
+/*                                     data_macros, eod_macros,
+/*                                     unk_macros)
+/*     const char *milter_names;
+/*     int     conn_timeout;
+/*     int     cmd_timeout;
+/*     int     msg_timeout;
+/*     const char *protocol;
+/*     const char *def_action;
+/*     const char *conn_macros;
+/*     const char *helo_macros;
+/*     const char *mail_macros;
+/*     const char *rcpt_macrps;
+/*     const char *data_macros;
+/*     const char *eod_macros;
+/*     const char *unk_macros;
+/*
+/*     void    milter_free(milters)
+/*     MILTERS *milters;
+/*
+/*     void    milter_macro_callback(milters, mac_lookup, mac_context)
+/*     const char *(*mac_lookup)(const char *name, void *context);
+/*     void    *mac_context;
+/*
+/*     void    milter_edit_callback(milters, add_header, upd_header,
+/*                                     ins_header, del_header, add_rcpt,
+/*                                     del_rcpt, repl_body, context)
+/*     MILTERS *milters;
+/*     void    (*add_header) (void *context, char *name, char *value);
+/*     void    (*upd_header) (void *context, ssize_t index,
+/*                             char *name, char *value);
+/*     void    (*ins_header) (void *context, ssize_t index,
+/*                             char *name, char *value);
+/*     void    (*del_header) (void *context, ssize_t index, char *name);
+/*     void    (*add_rcpt) (void *context, char *rcpt);
+/*     void    (*del_rcpt) (void *context, char *rcpt);
+/*     void    (*repl_body) (void *context, VSTRING *body);
+/*     void    *context;
+/*
+/*     const char *milter_conn_event(milters, client_name, client_addr,
+/*                                     client_port, addr_family)
+/*     MILTERS *milters;
+/*     const char *client_name;
+/*     const char *client_addr;
+/*     const char *client_port;
+/*     int     addr_family;
+/*
+/*     const char *milter_disc_event(milters)
+/*     MILTERS *milters;
+/*
+/*     const char *milter_helo_event(milters, helo_name, esmtp_flag)
+/*     MILTERS *milters;
+/*     const char *helo_name;
+/*     int     esmtp_flag;
+/*
+/*     const char *milter_mail_event(milters, argv)
+/*     MILTERS *milters;
+/*     const char **argv;
+/*
+/*     const char *milter_rcpt_event(milters, argv)
+/*     MILTERS *milters;
+/*     const char **argv;
+/*
+/*     const char *milter_data_event(milters)
+/*     MILTERS *milters;
+/*
+/*     const char *milter_unknown_event(milters, command)
+/*     MILTERS *milters;
+/*     const char *command;
+/*
+/*     const char *milter_other_event(milters)
+/*     MILTERS *milters;
+/*
+/*     const char *milter_message(milters, qfile, data_offset)
+/*     MILTERS *milters;
+/*     VSTREAM *qfile;
+/*     off_t   data_offset;
+/*
+/*     const char *milter_abort(milters)
+/*     MILTERS *milters;
+/*
+/*     int     milter_send(milters, fp)
+/*     MILTERS *milters;
+/*     VSTREAM *fp;
+/*
+/*     MILTERS *milter_receive(fp, count)
+/*     VSTREAM *fp;
+/*     int     count;
+/* DESCRIPTION
+/*     The functions in this module manage one or more milter (mail
+/*     filter) clients. Currently, only the Sendmail 8 filter
+/*     protocol is supported.
+/*
+/*     The functions that inspect content or envelope commands
+/*     return either an SMTP reply ([45]XX followed by enhanced
+/*     status code and text), "D" (discard), "H" (quarantine), or
+/*     a null pointer, which means "no news is good news".
+/*
+/*     milter_create() instantiates the milter clients specified
+/*     with the milter_names argument.  The conn_macros etc.
+/*     arguments specify the names of macros that are sent to the
+/*     mail filter applications upon a connect etc. event. This
+/*     function should be called during process initialization,
+/*     before entering a chroot jail. The timeout parameters specify
+/*     time limits for the completion of the specified request
+/*     classes. The protocol parameter specifies a protocol version
+/*     and optional extensions.  When the milter application is
+/*     unavailable, the milter client will go into a suitable error
+/*     state as specified with the def_action parameter (i.e.
+/*     reject, tempfail or accept all subsequent events).
+/*
+/*     milter_free() disconnects from the milter instances that
+/*     are still opened, and destroys the data structures created
+/*     by milter_create(). This function is safe to call at any
+/*     point after milter_create().
+/*
+/*     milter_macro_callback() specifies a call-back function and
+/*     context for macro lookup. This function must be called
+/*     before milter_conn_event().
+/*
+/*     milter_edit_callback() specifies call-back functions and
+/*     context for editing the queue file after the end-of-data
+/*     is received. This function must be called before milter_message();
+/*
+/*     milter_conn_event() reports an SMTP client connection event
+/*     to the specified milter instances, after sending the macros
+/*     specified with the milter_create() conn_macros argument.
+/*     This function must be called before reporting any other
+/*     events.
+/*
+/*     milter_disc_event() reports an SMTP client disconnection
+/*     event to the specified milter instances. No events can
+/*     reported after this call, not even abort() events.
+/*
+/*     milter_helo_event() reports a HELO or EHLO event to the
+/*     specified milter instances, after sending the macros that
+/*     were specified with the milter_create() helo_macros argument.
+/*
+/*     milter_mail_event() reports a MAIL FROM event to the specified
+/*     milter instances, after sending the macros that were specified
+/*     with the milter_create() mail_macros argument.
+/*
+/*     milter_rcpt_event() reports an RCPT TO event to the specified
+/*     milter instances, after sending the macros that were specified
+/*     with the milter_create() rcpt_macros argument.
+/*
+/*     milter_data_event() reports a DATA event to the specified
+/*     milter instances, after sending the macros that were specified
+/*     with the milter_create() data_macros argument.
+/*
+/*     milter_unknown_event() reports an unknown command event to
+/*     the specified milter instances, after sending the macros
+/*     that were specified with the milter_create() unk_macros
+/*     argument.
+/*
+/*     milter_other_event() returns the current default mail filter
+/*     reply for the current SMTP connection state; it does not
+/*     change milter states. A null pointer result means that all
+/*     is well. This function can be used for SMTP commands such
+/*     as AUTH, STARTTLS that don't have their own milter event
+/*     routine.
+/*
+/*     milter_message() sends the message header and body to the
+/*     to the specified milter instances, and sends the macros
+/*     specified with the milter_create() eod_macros argument at
+/*     the end.  Each milter sees the result of any changes made
+/*     by a preceding milter. This function must be called with
+/*     as argument an open Postfix queue file.
+/*
+/*     milter_abort() cancels a mail transaction in progress. This
+/*     function is safe to call anywhere between connect and
+/*     disconnect events.
+/*
+/*     milter_send() sends a list of mail filters over the specified
+/*     stream. When given a null list pointer, a "no filter"
+/*     indication is sent.  The result is non-zero in case of
+/*     error.
+/*
+/*     milter_receive() receives the specified number of mail
+/*     filters over the specified stream. The result is a null
+/*     pointer when no milters were sent, or when an error happened.
+/* SEE ALSO
+/*     milter8(3) Sendmail 8 Milter protocol
+/* DIAGNOSTICS
+/*     Panic: interface violation.
+/*     Fatal errors: memory allocation problem.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <argv.h>
+#include <attr.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <record.h>
+#include <rec_type.h>
+
+/* Postfix Milter library. */
+
+#include <milter.h>
+
+/* Application-specific. */
+
+ /*
+  * SLMs.
+  */
+#define STR(x) vstring_str(x)
+
+/* milter_macro_lookup - look up macros */
+
+static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names)
+{
+    const char *myname = "milter_macro_lookup";
+    char   *saved_names = mystrdup(macro_names);
+    char   *cp = saved_names;
+    ARGV   *argv = argv_alloc(10);
+    const char *value;
+    const char *name;
+
+    while ((name = mystrtok(&cp, ", \t\r\n")) != 0) {
+       if (msg_verbose)
+           msg_info("%s: \"%s\"", myname, name);
+       if ((value = milters->mac_lookup(name, milters->mac_context)) != 0) {
+           if (msg_verbose)
+               msg_info("%s: result \"%s\"", myname, value);
+           argv_add(argv, name, value, (char *) 0);
+       }
+    }
+    myfree(saved_names);
+    return (argv);
+}
+
+/* milter_macro_callback - specify macro lookup */
+
+void    milter_macro_callback(MILTERS *milters,
+                          const char *(*mac_lookup) (const char *, void *),
+                                     void *mac_context)
+{
+    milters->mac_lookup = mac_lookup;
+    milters->mac_context = mac_context;
+}
+
+/* milter_edit_callback - specify queue file edit call-back information */
+
+void    milter_edit_callback(MILTERS *milters,
+                               void (*add_header) (void *, char *, char *),
+                      void (*upd_header) (void *, ssize_t, char *, char *),
+                      void (*ins_header) (void *, ssize_t, char *, char *),
+                              void (*del_header) (void *, ssize_t, char *),
+                                    void (*add_rcpt) (void *, char *),
+                                    void (*del_rcpt) (void *, char *),
+                                    void (*repl_body) (void *, VSTRING *),
+                                    void *chg_context)
+{
+    milters->add_header = add_header;
+    milters->upd_header = upd_header;
+    milters->ins_header = ins_header;
+    milters->del_header = del_header;
+    milters->add_rcpt = add_rcpt;
+    milters->del_rcpt = del_rcpt;
+    milters->repl_body = repl_body;
+    milters->chg_context = chg_context;
+}
+
+/* milter_conn_event - report connect event */
+
+const char *milter_conn_event(MILTERS *milters,
+                                     const char *client_name,
+                                     const char *client_addr,
+                                     const char *client_port,
+                                     unsigned addr_family)
+{
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros;
+
+    if (msg_verbose)
+       msg_info("report connect to all milters");
+    macros = milter_macro_lookup(milters, milters->conn_macros);
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
+       resp = m->conn_event(m, client_name, client_addr, client_port,
+                            addr_family, macros);
+    argv_free(macros);
+    return (resp);
+}
+
+/* milter_helo_event - report helo event */
+
+const char *milter_helo_event(MILTERS *milters, const char *helo_name,
+                                     int esmtp_flag)
+{
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros;
+
+    if (msg_verbose)
+       msg_info("report helo to all milters");
+    macros = milters->helo_macros == 0 ? 0 :
+       milter_macro_lookup(milters, milters->helo_macros);
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
+       resp = m->helo_event(m, helo_name, esmtp_flag, macros);
+    if (macros)
+       argv_free(macros);
+    return (resp);
+}
+
+/* milter_mail_event - report mail from event */
+
+const char *milter_mail_event(MILTERS *milters, const char **argv)
+{
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros;
+
+    if (msg_verbose)
+       msg_info("report sender to all milters");
+    macros = milters->mail_macros == 0 ? 0 :
+       milter_macro_lookup(milters, milters->mail_macros);
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
+       resp = m->mail_event(m, argv, macros);
+    if (macros)
+       argv_free(macros);
+    return (resp);
+}
+
+/* milter_rcpt_event - report rcpt to event */
+
+const char *milter_rcpt_event(MILTERS *milters, const char **argv)
+{
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros;
+
+    if (msg_verbose)
+       msg_info("report recipient to all milters");
+    macros = milters->rcpt_macros == 0 ? 0 :
+       milter_macro_lookup(milters, milters->rcpt_macros);
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
+       resp = m->rcpt_event(m, argv, macros);
+    if (macros)
+       argv_free(macros);
+    return (resp);
+}
+
+/* milter_data_event - report data event */
+
+const char *milter_data_event(MILTERS *milters)
+{
+    const char *myname = "milter_data_event";
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros = 0;
+
+    if (msg_verbose)
+       msg_info("report data to all milters");
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
+       if (m->data_event) {
+           if (macros == 0 && milters->data_macros)
+               macros = milter_macro_lookup(milters, milters->data_macros);
+           resp = m->data_event(m, macros);
+       } else {
+           if (msg_verbose)
+               msg_info("%s: skip milter %s (command unimplemented)",
+                        myname, m->name);
+       }
+    }
+    if (macros)
+       argv_free(macros);
+    return (resp);
+}
+
+/* milter_unknown_event - report unknown command */
+
+const char *milter_unknown_event(MILTERS *milters, const char *command)
+{
+    const char *myname = "milter_unknown_event";
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros = 0;
+
+    if (msg_verbose)
+       msg_info("report unknown command to all milters");
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
+       if (m->unknown_event) {
+           if (macros == 0 && milters->unk_macros)
+               macros = milter_macro_lookup(milters, milters->unk_macros);
+           resp = m->unknown_event(m, command, macros);
+       } else {
+           if (msg_verbose)
+               msg_info("%s: skip milter %s (command unimplemented)",
+                        myname, m->name);
+       }
+    }
+    if (macros)
+       argv_free(macros);
+    return (resp);
+}
+
+/* milter_other_event - other SMTP event */
+
+const char *milter_other_event(MILTERS *milters)
+{
+    const char *resp;
+    MILTER *m;
+
+    if (msg_verbose)
+       msg_info("query milter states for other event");
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
+       resp = m->other_event(m);
+    return (resp);
+}
+
+/* milter_message - inspect message content */
+
+const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset)
+{
+    const char *resp;
+    MILTER *m;
+    ARGV   *macros;
+
+    if (msg_verbose)
+       msg_info("inspect content by all milters");
+    macros = milters->eod_macros == 0 ? 0 :
+       milter_macro_lookup(milters, milters->eod_macros);
+    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
+       resp = m->message(m, fp, data_offset, macros);
+    if (macros)
+       argv_free(macros);
+    return (resp);
+}
+
+/* milter_abort - cancel message receiving state, all milters */
+
+void    milter_abort(MILTERS *milters)
+{
+    MILTER *m;
+
+    if (msg_verbose)
+       msg_info("abort all milters");
+    for (m = milters->milter_list; m != 0; m = m->next)
+       m->abort(m);
+}
+
+/* milter_disc_event - report client disconnect event to all milters */
+
+void    milter_disc_event(MILTERS *milters)
+{
+    MILTER *m;
+
+    if (msg_verbose)
+       msg_info("disconnect event to all milters");
+    for (m = milters->milter_list; m != 0; m = m->next)
+       m->disc_event(m);
+}
+
+/* milter_create - create milter list */
+
+MILTERS *milter_create(const char *names,
+                              int conn_timeout,
+                              int cmd_timeout,
+                              int msg_timeout,
+                              const char *protocol,
+                              const char *def_action,
+                              const char *conn_macros,
+                              const char *helo_macros,
+                              const char *mail_macros,
+                              const char *rcpt_macros,
+                              const char *data_macros,
+                              const char *eod_macros,
+                              const char *unk_macros)
+{
+    MILTERS *milters;
+    MILTER *head = 0;
+    MILTER *tail = 0;
+    char   *name;
+    MILTER *milter;
+    const char *sep = ", \t\r\n";
+
+    /*
+     * Parse the milter list.
+     */
+    milters = (MILTERS *) mymalloc(sizeof(*milters));
+    if (names != 0) {
+       char   *saved_names = mystrdup(names);
+       char   *cp = saved_names;
+
+       while ((name = mystrtok(&cp, sep)) != 0) {
+           milter = milter8_create(name, conn_timeout, cmd_timeout,
+                                   msg_timeout, protocol, def_action,
+                                   milters);
+           if (head == 0) {
+               head = milter;
+           } else {
+               tail->next = milter;
+           }
+           tail = milter;
+       }
+       myfree(saved_names);
+    }
+    milters->milter_list = head;
+    milters->mac_lookup = 0;
+    milters->mac_context = 0;
+    milters->conn_macros = mystrdup(conn_macros);
+    milters->helo_macros = mystrdup(helo_macros);
+    milters->mail_macros = mystrdup(mail_macros);
+    milters->rcpt_macros = mystrdup(rcpt_macros);
+    milters->data_macros = mystrdup(data_macros);
+    milters->eod_macros = mystrdup(eod_macros);
+    milters->unk_macros = mystrdup(unk_macros);
+    milters->add_header = 0;
+    milters->upd_header = milters->ins_header = 0;
+    milters->del_header = 0;
+    milters->add_rcpt = milters->del_rcpt = 0;
+    milters->chg_context = 0;
+    return (milters);
+}
+
+/* milter_free - destroy all milters */
+
+void    milter_free(MILTERS *milters)
+{
+    MILTER *m;
+    MILTER *next;
+
+    if (msg_verbose)
+       msg_info("free all milters");
+    for (m = milters->milter_list; m != 0; m = next)
+       next = m->next, m->free(m);
+    if (milters->conn_macros)
+       myfree(milters->conn_macros);
+    if (milters->helo_macros)
+       myfree(milters->helo_macros);
+    if (milters->mail_macros)
+       myfree(milters->mail_macros);
+    if (milters->rcpt_macros)
+       myfree(milters->rcpt_macros);
+    if (milters->rcpt_macros)
+       myfree(milters->data_macros);
+    if (milters->eod_macros)
+       myfree(milters->eod_macros);
+    if (milters->unk_macros)
+       myfree(milters->unk_macros);
+    myfree((char *) milters);
+}
+
+ /*
+  * Temporary protocol to send /receive milter instances. This needs to be
+  * extended with type information when we support both Sendmail8 and
+  * Sendmail X protocols.
+  */
+#define MAIL_ATTR_MILT_CONN    "conn_macros"
+#define MAIL_ATTR_MILT_HELO    "helo_macros"
+#define MAIL_ATTR_MILT_MAIL    "mail_macros"
+#define MAIL_ATTR_MILT_RCPT    "rcpt_macros"
+#define MAIL_ATTR_MILT_DATA    "data_macros"
+#define MAIL_ATTR_MILT_EOD     "eod_macros"
+#define MAIL_ATTR_MILT_UNK     "unk_macros"
+
+/* milter_send - send Milter instances over stream */
+
+int     milter_send(MILTERS *milters, VSTREAM *stream)
+{
+    MILTER *m;
+    int     status = 0;
+    int     count = 0;
+
+    /*
+     * XXX Optimization: send only the filters that are actually used in the
+     * remote process. No point sending a filter that looks at HELO commands
+     * to a cleanup server. For now we skip only the filters that are known
+     * to be disabled (either in global error state or in global accept
+     * state).
+     */
+    if (milters != 0)
+       for (m = milters->milter_list; m != 0; m = m->next)
+           if (m->active(m))
+               count++;
+    if (count == 0)
+       return (0);
+    (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count);
+
+    /*
+     * Send the filter macro names.
+     */
+    (void) attr_print(stream, ATTR_FLAG_MORE,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_CONN, milters->conn_macros,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_HELO, milters->helo_macros,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_MAIL, milters->mail_macros,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_RCPT, milters->rcpt_macros,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_DATA, milters->data_macros,
+                     ATTR_TYPE_STR, MAIL_ATTR_MILT_EOD, milters->eod_macros,
+                     ATTR_TYPE_STR, MAIL_ATTR_MILT_UNK, milters->unk_macros,
+                     ATTR_TYPE_END);
+
+    /*
+     * Send the filter instances.
+     */
+    for (m = milters->milter_list; m != 0; m = m->next)
+       if (m->active(m) && (status = m->send(m, stream)) != 0)
+           break;
+    if (status != 0
+       || attr_scan(stream, ATTR_FLAG_STRICT,
+                    ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
+                    ATTR_TYPE_END) != 1
+       || status != 0) {
+       msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream));
+       return (-1);
+    }
+    return (0);
+}
+
+/* milter_receive - receive milters from stream */
+
+MILTERS *milter_receive(VSTREAM *stream, int count)
+{
+    MILTERS *milters;
+    MILTER *head = 0;
+    MILTER *tail = 0;
+    MILTER *milter = 0;
+    VSTRING *conn_macros;
+    VSTRING *helo_macros;
+    VSTRING *mail_macros;
+    VSTRING *rcpt_macros;
+    VSTRING *data_macros;
+    VSTRING *eod_macros;
+    VSTRING *unk_macros;
+
+    if (count == 0)
+       return (0);
+
+    /*
+     * Receive filter macros.
+     */
+#define FREE_BUFFERS() do { \
+       vstring_free(conn_macros); vstring_free(helo_macros); \
+       vstring_free(mail_macros); vstring_free(rcpt_macros); \
+       vstring_free(data_macros); vstring_free(eod_macros); \
+       vstring_free(unk_macros); \
+   } while (0)
+
+    conn_macros = vstring_alloc(10);
+    helo_macros = vstring_alloc(10);
+    mail_macros = vstring_alloc(10);
+    rcpt_macros = vstring_alloc(10);
+    data_macros = vstring_alloc(10);
+    eod_macros = vstring_alloc(10);
+    unk_macros = vstring_alloc(10);
+    if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_CONN, conn_macros,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_HELO, helo_macros,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_MAIL, mail_macros,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_RCPT, rcpt_macros,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_DATA, data_macros,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_EOD, eod_macros,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_UNK, unk_macros,
+                 ATTR_TYPE_END) != 7) {
+       FREE_BUFFERS();
+       return (0);
+    }
+#define NO_MILTERS     ((char *) 0)
+#define NO_TIMEOUTS    0, 0, 0
+#define NO_PROTOCOL    ((char *) 0)
+#define NO_ACTION      ((char *) 0)
+
+    milters = milter_create(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION,
+                           STR(conn_macros), STR(helo_macros),
+                           STR(mail_macros), STR(rcpt_macros),
+                           STR(data_macros), STR(eod_macros),
+                           STR(unk_macros));
+    FREE_BUFFERS();
+
+    /*
+     * Receive the filters.
+     */
+    for (; count > 0; count--) {
+       if ((milter = milter8_receive(stream, milters)) == 0) {
+           msg_warn("cannot receive milters via service %s socket",
+                    VSTREAM_PATH(stream));
+           milter_free(milters);
+           return (0);
+       }
+       if (head == 0) {
+           head = milter;
+       } else {
+           tail->next = milter;
+       }
+       tail = milter;
+    }
+    milters->milter_list = head;
+
+    (void) attr_print(stream, ATTR_FLAG_NONE,
+                     ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
+                     ATTR_TYPE_END);
+    return (milters);
+}
+
+#ifdef TEST
+
+ /*
+  * Proof-of-concept test program. This can be used interactively, but is
+  * typically used for automated regression tests from a script.
+  */
+
+/* System library. */
+
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include "msg_vstream.h"
+#include "vstring_vstream.h"
+
+/* Global library. */
+
+#include <mail_params.h>
+
+int     var_milt_conn_time = 10;
+int     var_milt_cmd_time = 10;
+int     var_milt_msg_time = 100;
+char   *var_milt_protocol = DEF_MILT_PROTOCOL;
+char   *var_milt_def_action = DEF_MILT_DEF_ACTION;
+
+static void usage(void)
+{
+    vstream_fprintf(VSTREAM_ERR, "usage: \n"
+                   "   create names...         create and connect\n"
+#if 0
+                   "   conn_macros names...    define connect macros\n"
+                   "   helo_macros names...    define helo command macros\n"
+                   "   mail_macros names...    define mail command macros\n"
+                   "   rcpt_macros names...    define rcpt command macros\n"
+                   "   data_macros names...    define data command macros\n"
+                   "   unk_macros names...     unknown command macros\n"
+                   "   message_macros names... define message macros\n"
+#endif
+                   "   free                    disconnect and destroy\n"
+                   "   connect name addr port family\n"
+                   "   helo hostname\n"
+                   "   ehlo hostname\n"
+                   "   mail from sender...\n"
+                   "   rcpt to recipient...\n"
+                   "   data\n"
+                   "   disconnect\n"
+                   "   unknown command\n");
+    vstream_fflush(VSTREAM_ERR);
+}
+
+int     main(int argc, char **argv)
+{
+    MILTERS *milters = 0;
+    char   *conn_macros, *helo_macros, *mail_macros, *rcpt_macros;
+    char   *data_macros, *eod_macros, *unk_macros;
+    VSTRING *inbuf = vstring_alloc(100);
+    char   *bufp;
+    char   *cmd;
+    int     ch;
+    int     istty = isatty(vstream_fileno(VSTREAM_IN));
+
+    conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros
+       = eod_macros = unk_macros = "";
+
+    msg_vstream_init(argv[0], VSTREAM_ERR);
+    while ((ch = GETOPT(argc, argv, "V:v")) > 0) {
+       switch (ch) {
+       default:
+           msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]);
+       case 'a':
+           var_milt_def_action = optarg;
+           break;
+       case 'p':
+           var_milt_protocol = optarg;
+           break;
+       case 'v':
+           msg_verbose++;
+           break;
+       }
+    }
+    optind = OPTIND;
+
+    for (;;) {
+       const char *resp = 0;
+       ARGV   *argv;
+       char  **args;
+
+       if (istty) {
+           vstream_printf("- ");
+           vstream_fflush(VSTREAM_OUT);
+       }
+       if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
+           break;
+       bufp = vstring_str(inbuf);
+       if (!istty) {
+           vstream_printf("> %s\n", bufp);
+           vstream_fflush(VSTREAM_OUT);
+       }
+       if (*bufp == '#')
+           continue;
+       cmd = mystrtok(&bufp, " ");
+       if (cmd == 0) {
+           usage();
+           continue;
+       }
+       argv = argv_split(bufp, " ");
+       args = argv->argv;
+       if (strcmp(cmd, "create") == 0 && argv->argc == 1) {
+           if (milters != 0) {
+               msg_warn("deleting existing milters");
+               milter_free(milters);
+           }
+           milters = milter_create(args[0], var_milt_conn_time, 
+                               var_milt_cmd_time, var_milt_msg_time, 
+                               var_milt_protocol, var_milt_def_action,
+                                   conn_macros, helo_macros, mail_macros,
+                                   rcpt_macros, data_macros, eod_macros,
+                                   unk_macros);
+       } else if (strcmp(cmd, "free") == 0 && argv->argc == 0) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           milter_free(milters);
+           milters = 0;
+       } else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_conn_event(milters, args[0], args[1], args[2],
+                                strcmp(args[3], "AF_INET") == 0 ? AF_INET :
+                              strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 :
+                                strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX :
+                                    AF_UNSPEC);
+       } else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_helo_event(milters, args[0], 0);
+       } else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_helo_event(milters, args[0], 1);
+       } else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_mail_event(milters, (const char **) args);
+       } else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_rcpt_event(milters, (const char **) args);
+       } else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_unknown_event(milters, args[0]);
+       } else if (strcmp(cmd, "data") == 0 && argv->argc == 0) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           resp = milter_data_event(milters);
+       } else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) {
+           if (milters == 0) {
+               msg_warn("no milters");
+               continue;
+           }
+           milter_disc_event(milters);
+       } else {
+           usage();
+       }
+       if (resp != 0)
+           msg_info("%s", resp);
+       argv_free(argv);
+    }
+    if (milters != 0)
+       milter_free(milters);
+    vstring_free(inbuf);
+    return (0);
+}
+
+#endif
diff --git a/postfix/src/milter/milter.h b/postfix/src/milter/milter.h
new file mode 100644 (file)
index 0000000..2cf63e5
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef _MILTER_H_INCLUDED_
+#define _MILTER_H_INCLUDED_
+
+/*++
+/* NAME
+/*     milter 3h
+/* SUMMARY
+/*     smtp server
+/* SYNOPSIS
+/*     Postfix MTA-side Milter implementation
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+
+ /*
+  * Each Milter handle is an element of a null-terminated linked list. The
+  * functions are virtual so that we can support multiple MTA-side Milter
+  * implementations. The Sendmail 8 and Sendmail X Milter-side APIs are too
+  * different to implement the MTA side as a single hybrid.
+  */
+typedef struct MILTER {
+    char   *name;                      /* full name including transport */
+    struct MILTER *next;               /* linkage */
+    struct MILTERS *parent;            /* parent information */
+    const char *(*conn_event) (struct MILTER *, const char *, const char *, const char *, unsigned, ARGV *);
+    const char *(*helo_event) (struct MILTER *, const char *, int, ARGV *);
+    const char *(*mail_event) (struct MILTER *, const char **, ARGV *);
+    const char *(*rcpt_event) (struct MILTER *, const char **, ARGV *);
+    const char *(*data_event) (struct MILTER *, ARGV *);
+    const char *(*message) (struct MILTER *, VSTREAM *, off_t, ARGV *);
+    const char *(*unknown_event) (struct MILTER *, const char *, ARGV *);
+    const char *(*other_event) (struct MILTER *);
+    void    (*abort) (struct MILTER *);
+    void    (*disc_event) (struct MILTER *);
+    int     (*active) (struct MILTER *);
+    int     (*send) (struct MILTER *, VSTREAM *);
+    void    (*free) (struct MILTER *);
+} MILTER;
+
+extern MILTER *milter8_create(const char *, int, int, int, const char *, const char *, struct MILTERS *);
+extern MILTER *milter8_receive(VSTREAM *, struct MILTERS *);
+
+ /*
+  * A bunch of Milters.
+  */
+typedef struct MILTERS {
+    MILTER *milter_list;               /* linked list of Milters */
+    const char *(*mac_lookup) (const char *, void *);
+    void   *mac_context;               /* macro lookup context */
+    char   *conn_macros;               /* macros for connect event */
+    char   *helo_macros;               /* macros for HELO/EHLO command */
+    char   *mail_macros;               /* macros for MAIL FROM command */
+    char   *rcpt_macros;               /* macros for RCPT TO command */
+    char   *data_macros;               /* macros for DATA command */
+    char   *eod_macros;                        /* macros for END-OF-DATA command */
+    char   *unk_macros;                        /* macros for unknown command */
+    void   *chg_context;               /* context for queue file changes */
+    void    (*add_header) (void *, char *, char *);
+    void    (*upd_header) (void *, ssize_t, char *, char *);
+    void    (*del_header) (void *, ssize_t, char *);
+    void    (*ins_header) (void *, ssize_t, char *, char *);
+    void    (*add_rcpt) (void *, char *);
+    void    (*del_rcpt) (void *, char *);
+    void    (*repl_body) (void *, VSTRING *);
+} MILTERS;
+
+typedef const char *(*MILTER_MAC_LOOKUP_FN) (const char *, void *);
+typedef void (*MILTER_ADD_HEADER_FN) (void *, char *, char *);
+typedef void (*MILTER_EDIT_HEADER_FN) (void *, ssize_t, char *, char *);
+typedef void (*MILTER_DEL_HEADER_FN) (void *, ssize_t, char *);
+typedef void (*MILTER_EDIT_RCPT_FN) (void *, char *);
+typedef void (*MILTER_EDIT_BODY_FN) (void *, VSTRING *);
+
+extern MILTERS *milter_create(const char *, int, int, int,
+                                     const char *, const char *,
+                                     const char *, const char *,
+                                     const char *, const char *,
+                                     const char *, const char *,
+                                     const char *);
+extern void milter_macro_callback(MILTERS *, MILTER_MAC_LOOKUP_FN, void *);
+extern void milter_edit_callback(MILTERS *milters, MILTER_ADD_HEADER_FN,
+                              MILTER_EDIT_HEADER_FN, MILTER_EDIT_HEADER_FN,
+                                 MILTER_DEL_HEADER_FN, MILTER_EDIT_RCPT_FN,
+                                  MILTER_EDIT_RCPT_FN, MILTER_EDIT_BODY_FN,
+                                        void *);
+extern const char *milter_conn_event(MILTERS *, const char *, const char *, const char *, unsigned);
+extern const char *milter_helo_event(MILTERS *, const char *, int);
+extern const char *milter_mail_event(MILTERS *, const char **);
+extern const char *milter_rcpt_event(MILTERS *, const char **);
+extern const char *milter_data_event(MILTERS *);
+extern const char *milter_message(MILTERS *, VSTREAM *, off_t);
+extern const char *milter_unknown_event(MILTERS *, const char *);
+extern const char *milter_other_event(MILTERS *);
+extern void milter_abort(MILTERS *);
+extern void milter_disc_event(MILTERS *);
+extern int milter_send(MILTERS *, VSTREAM *);
+extern MILTERS *milter_receive(VSTREAM *, int);
+extern void milter_free(MILTERS *);
+
+ /*
+  * Sendmail 8 macro names. We support forms with and without the {}.
+  */
+#define S8_MAC__               "{_}"   /* sender resolve */
+#define S8_MAC_J               "{j}"   /* myhostname */
+#define S8_MAC_V               "{v}"   /* mail_name + mail_version */
+
+#define S8_MAC_DAEMON_NAME     "{daemon_name}"
+#define S8_MAC_IF_NAME         "{if_name}"
+#define S8_MAC_IF_ADDR         "{if_addr}"
+
+#define S8_MAC_CLIENT_ADDR     "{client_addr}"
+#define S8_MAC_CLIENT_CONN     "{client_connections}"
+#define S8_MAC_CLIENT_NAME     "{client_name}"
+#define S8_MAC_CLIENT_PTR      "{client_ptr}"
+#define S8_MAC_CLIENT_RES      "{client_resolve}"
+
+#define S8_MAC_TLS_VERSION     "{tls_version}"
+#define S8_MAC_CIPHER          "{cipher}"
+#define S8_MAC_CIPHER_BITS     "{cipher_bits}"
+#define S8_MAC_CERT_SUBJECT    "{cert_subject}"
+#define S8_MAC_CERT_ISSUER     "{cert_issuer}"
+
+#define S8_MAC_I               "{i}"   /* queue ID */
+#define S8_MAC_AUTH_TYPE       "{auth_type}"   /* SASL method */
+#define S8_MAC_AUTH_AUTHEN     "{auth_authen}" /* SASL username */
+#define S8_MAC_AUTH_AUTHOR     "{auth_author}" /* SASL sender */
+
+#define S8_MAC_MAIL_MAILER     "{mail_mailer}" /* sender transport */
+#define S8_MAC_MAIL_HOST       "{mail_host}"   /* sender nexthop */
+#define S8_MAC_MAIL_ADDR       "{mail_addr}"   /* sender address */
+
+#define S8_MAC_RCPT_MAILER     "{rcpt_mailer}" /* recip transport */
+#define S8_MAC_RCPT_HOST       "{rcpt_host}"   /* recip nexthop */
+#define S8_MAC_RCPT_ADDR       "{rcpt_addr}"   /* recip address */
+
+/* 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
+/*--*/
+
+#endif
diff --git a/postfix/src/milter/milter8.c b/postfix/src/milter/milter8.c
new file mode 100644 (file)
index 0000000..f2bbebd
--- /dev/null
@@ -0,0 +1,2337 @@
+/*++
+/* NAME
+/*     milter8 3
+/* SUMMARY
+/*     MTA-side Sendmail 8 Milter protocol
+/* SYNOPSIS
+/*     #include <milter8.h>
+/*
+/*     MILTER  *milter8_create(name, conn_timeout, cmd_timeout, msg_timeout,
+/*                             protocol, def_action, parent)
+/*     const char *name;
+/*     int conn_timeout;
+/*     int cmd_timeout;
+/*     int msg_timeout;
+/*     const char *protocol;
+/*     const char *def_action;
+/*     MILTERS *parent;
+/*
+/*     MILTER  *milter8_receive(stream)
+/*     VSTREAM *stream;
+/* DESCRIPTION
+/*     This modulde implements the MTA side of the Sendmail 8 mail
+/*     filter protocol.
+/*
+/*     milter8_create() creates a MILTER data structure with virtual
+/*     functions that implement a client for the Sendmail 8 Milter
+/*     protocol. These virtual functions are then invoked via the
+/*     milter(3) interface.  The *timeout, protocol and def_action
+/*     arguments come directly from milter_create(). The parent
+/*     argument specifies a context for content editing.
+/*
+/*     milter8_receive() receives a mail filter definition from the
+/*     specified stream. The result is zero in case of success.
+/*
+/*     Arguments:
+/* .IP name
+/*     The Milter application endpoint, either inet:host:port or
+/*     unix:/pathname.
+/* DIAGNOSTICS
+/*     Panic: interface violation.  Fatal errors: out of memory.
+/* CONFIGURATION PARAMETERS
+/*     milter8_protocol, protocol version and extensions
+/* SEE ALSO
+/*     milter(3) generic Milter interface
+/* 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Sendmail 8 Milter protocol. */
+
+#ifdef USE_LIBMILTER_INCLUDES
+
+ /*
+  * Use the include files that match the installed libmilter library. This
+  * requires that the libmilter files are installed before Postfix can be
+  * built with milter support, and requires that Postfix is rebuilt whenever
+  * protocol version in these files changes. The other option (below) is to
+  * use our own protocol definitions.
+  */
+#include <libmilter/mfapi.h>
+#include <libmilter/mfdef.h>
+
+ /*
+  * Compatibility for missing definitions or for names that have changed over
+  * time.
+  */
+#ifndef SMFIF_CHGBODY
+#define SMFIF_CHGBODY  SMFIF_MODBODY
+#endif
+#ifndef SMFIF_CHGHDRS
+#define SMFIF_CHGHDRS  SMFIF_MODHDRS
+#endif
+#if defined(SMFIC_UNKNOWN) && !defined(SMFIP_NOUNKNOWN)
+#define SMFIP_NOUNKNOWN        (1L<<8) /* MTA should not send unknown cmd */
+#endif
+#if defined(SMFIC_DATA) && !defined(SMFIP_NODATA)
+#define SMFIP_NODATA           (1L<<9) /* MTA should not send DATA */
+#endif
+
+#else
+
+ /*
+  * Use our own protocol definitions, so that Postfix can be built even when
+  * libmilter is not installed. This means that we must specify the libmilter
+  * protocol version in main.cf. The other option (above) is to compile
+  * Postfix with the installed libmilter include files and to support only
+  * that protocol version.
+  */
+
+ /*
+  * Commands from MTA to filter.
+  */
+#define SMFIC_ABORT            'A'     /* Abort */
+#define SMFIC_BODY             'B'     /* Body chunk */
+#define SMFIC_CONNECT          'C'     /* Connection information */
+#define SMFIC_MACRO            'D'     /* Define macro */
+#define SMFIC_BODYEOB          'E'     /* final body chunk (End) */
+#define SMFIC_HELO             'H'     /* HELO/EHLO */
+#define SMFIC_HEADER           'L'     /* Header */
+#define SMFIC_MAIL             'M'     /* MAIL from */
+#define SMFIC_EOH              'N'     /* EOH */
+#define SMFIC_OPTNEG           'O'     /* Option negotiation */
+#define SMFIC_QUIT             'Q'     /* QUIT */
+#define SMFIC_RCPT             'R'     /* RCPT to */
+#define SMFIC_DATA             'T'     /* DATA */
+#define SMFIC_UNKNOWN          'U'     /* Any unknown command */
+
+ /*
+  * Responses from filter to MTA.
+  */
+#define SMFIR_ADDRCPT          '+'     /* add recipient */
+#define SMFIR_DELRCPT          '-'     /* remove recipient */
+#define SMFIR_ACCEPT           'a'     /* accept */
+#define SMFIR_REPLBODY         'b'     /* replace body (chunk) */
+#define SMFIR_CONTINUE         'c'     /* continue */
+#define SMFIR_DISCARD          'd'     /* discard */
+#define SMFIR_CONN_FAIL                'f'     /* cause a connection failure */
+#define SMFIR_CHGHEADER                'm'     /* change header */
+#define SMFIR_PROGRESS         'p'     /* progress */
+#define SMFIR_REJECT           'r'     /* reject */
+#define SMFIR_TEMPFAIL         't'     /* tempfail */
+#define SMFIR_SHUTDOWN         '4'     /* 421: shutdown (internal to MTA) */
+#define SMFIR_ADDHEADER                'h'     /* add header */
+#define SMFIR_INSHEADER                'i'     /* insert header */
+#define SMFIR_REPLYCODE                'y'     /* reply code etc */
+#define SMFIR_QUARANTINE       'q'     /* quarantine */
+
+ /*
+  * Commands that the filter does not want to receive, and replies that the
+  * filter will not send.
+  */
+#define SMFIP_NOCONNECT                (1L<<0) /* MTA should not send connect info */
+#define SMFIP_NOHELO           (1L<<1) /* MTA should not send HELO info */
+#define SMFIP_NOMAIL           (1L<<2) /* MTA should not send MAIL info */
+#define SMFIP_NORCPT           (1L<<3) /* MTA should not send RCPT info */
+#define SMFIP_NOBODY           (1L<<4) /* MTA should not send body */
+#define SMFIP_NOHDRS           (1L<<5) /* MTA should not send headers */
+#define SMFIP_NOEOH            (1L<<6) /* MTA should not send EOH */
+#define SMFIP_NOHREPL          (1L<<7) /* filter will not reply per header */
+#define SMFIP_NOUNKNOWN        (1L<<8) /* MTA should not send unknown cmd */
+#define SMFIP_NODATA           (1L<<9) /* MTA should not send DATA */
+
+ /*
+  * Modifications that the filter may request at the end of the message body.
+  */
+#define SMFIF_ADDHDRS          (1L<<0) /* filter may add headers */
+#define SMFIF_CHGBODY          (1L<<1) /* filter may replace body */
+#define SMFIF_ADDRCPT          (1L<<2) /* filter may add recipients */
+#define SMFIF_DELRCPT          (1L<<3) /* filter may delete recipients */
+#define SMFIF_CHGHDRS          (1L<<4) /* filter may change/delete headers */
+#define SMFIF_QUARANTINE       (1L<<5) /* filter may quarantine envelope */
+
+ /*
+  * Network protocol families, used when sending CONNECT information.
+  */
+#define SMFIA_UNKNOWN          'U'     /* unknown */
+#define SMFIA_UNIX             'L'     /* unix/local */
+#define SMFIA_INET             '4'     /* inet */
+#define SMFIA_INET6            '6'     /* inet6 */
+
+ /*
+  * How much buffer space is available for receiving body content.
+  */
+#define MILTER_CHUNK_SIZE      65535   /* body chunk size */
+
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <split_at.h>
+#include <connect.h>
+#include <argv.h>
+#include <name_mask.h>
+#include <name_code.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <rec_type.h>
+#include <record.h>
+#include <mime_state.h>
+#include <is_header.h>
+
+/* Postfix Milter library. */
+
+#include <milter.h>
+
+/* Application-specific. */
+
+/*#define msg_verbose 2*/
+
+ /*
+  * Sendmail 8 mail filter client.
+  */
+typedef struct {
+    MILTER  m;                         /* parent class */
+    int     conn_timeout;              /* connect timeout */
+    int     cmd_timeout;               /* per-command timeout */
+    int     msg_timeout;               /* content inspection timeout */
+    char   *protocol;                  /* protocol version/extension */
+    char   *def_action;                        /* action if unavailable */
+    int     version;                   /* application protocol version */
+    int     rq_mask;                   /* application requests (SMFIF_*) */
+    int     ev_mask;                   /* application events (SMFIP_*) */
+#ifndef USE_LIBMILTER_INCLUDES
+    int     np_mask;                   /* events outside my protocol version */
+#endif
+    VSTRING *buf;                      /* I/O buffer */
+    VSTRING *body;                     /* I/O buffer */
+    VSTREAM *fp;                       /* stream or null (closed) */
+
+    /*
+     * Following fields must be reset after successful CONNECT, to avoid
+     * leakage from one use to another.
+     */
+    int     state;                     /* MILTER8_STAT_mumble */
+    char   *def_reply;                 /* error response or null */
+} MILTER8;
+
+ /*
+  * XXX Sendmail 8 libmilter automatically closes the MTA-to-filter socket
+  * when it finds out that the SMTP client has disconnected. Because of this
+  * behavior, Postfix has to open a new MTA-to-filter socket each time an
+  * SMTP client connects.
+  */
+#define LIBMILTER_AUTO_DISCONNECT
+
+ /*
+  * Milter internal state. For the external representation we use SMTP
+  * replies (4XX X.Y.Z text, 5XX X.Y.Z text) and one-letter strings
+  * (H=quarantine, D=discard, S=shutdown).
+  */
+#define MILTER8_STAT_ERROR     1       /* error, must be non-zero */
+#define MILTER8_STAT_CLOSED    2       /* no connection */
+#define MILTER8_STAT_READY     3       /* wait for connect event */
+#define MILTER8_STAT_ENVELOPE  4       /* in envelope */
+#define MILTER8_STAT_MESSAGE   5       /* in message */
+#define MILTER8_STAT_ACCEPT_CON        6       /* accept all commands */
+#define MILTER8_STAT_ACCEPT_MSG        7       /* accept one message */
+#define MILTER8_STAT_REJECT_CON        8       /* reject all commands */
+
+ /*
+  * Protocol formatting requests. Note: the terms "long" and "short" refer to
+  * the data types manipulated by htonl(), htons() and friends. These types
+  * are network specific, not host platform specific.
+  */
+#define MILTER8_DATA_END       0       /* no more arguments */
+#define MILTER8_DATA_HLONG     1       /* host long */
+#define MILTER8_DATA_BUFFER    2       /* network-formatted buffer */
+#define MILTER8_DATA_STRING    3       /* null-terminated string */
+#define MILTER8_DATA_NSHORT    4       /* network short */
+#define MILTER8_DATA_ARGV      5       /* array of null-terminated strings */
+#define MILTER8_DATA_OCTET     6       /* byte */
+
+ /*
+  * We don't accept insane amounts of data.
+  */
+#define XXX_MAX_DATA   (MILTER_CHUNK_SIZE * 2)
+#define XXX_TIMEOUT    10
+
+#ifndef USE_LIBMILTER_INCLUDES
+
+ /*
+  * If we're not using Sendmail's libmilter include files, then we implement
+  * the protocol up to and including version 4, and configure in main.cf what
+  * protocol version we will use. However, we must send only events that are
+  * defined for the specified protocol version, otherwise libmilter will
+  * disconnect.
+  * 
+  * The following events are supported by all milter protocol implementations.
+  */
+#define MILTER8_V1_PROTO_MASK \
+       (SMFIP_NOCONNECT | SMFIP_NOHELO | SMFIP_NOMAIL | SMFIP_NORCPT | \
+       SMFIP_NOBODY | SMFIP_NOHDRS | SMFIP_NOEOH)
+
+ /*
+  * What events we can send to the milter application. The milter8_protocol
+  * parameter can specify a protocol version as well as protocol extensions
+  * such as "no_header_reply", a feature that speeds up the protocol by not
+  * sending a filter reply for every individual message header.
+  * 
+  * This looks unclean because the user can specify multiple protocol versions,
+  * but that is taken care of by the table that follows this one.
+  */
+static NAME_CODE milter8_event_masks[] = {
+    "2", MILTER8_V1_PROTO_MASK,
+    "3", MILTER8_V1_PROTO_MASK | SMFIP_NOUNKNOWN,
+    "4", MILTER8_V1_PROTO_MASK | SMFIP_NOUNKNOWN | SMFIP_NODATA,
+    "no_header_reply", SMFIP_NOHREPL,
+    0, -1,
+};
+
+ /*
+  * The following table lets us use the same milter8_protocol parameter
+  * setting to derive the protocol version number. In this case we ignore
+  * protocol extensions such as "no_header_reply", and require that exactly
+  * one version number is specified.
+  */
+static NAME_CODE milter8_versions[] = {
+    "2", 2,
+    "3", 3,
+    "4", 4,
+    "no_header_reply", 0,
+    0, -1,
+};
+
+#endif
+
+ /*
+  * Tables to map the above symbolic constants to printable strings. We use
+  * NAME_CODE for commands and replies, and NAME_MASK for bit mask values.
+  */
+static NAME_CODE smfic_table[] = {
+    "SMFIC_ABORT", SMFIC_ABORT,
+    "SMFIC_BODY", SMFIC_BODY,
+    "SMFIC_CONNECT", SMFIC_CONNECT,
+    "SMFIC_MACRO", SMFIC_MACRO,
+    "SMFIC_BODYEOB", SMFIC_BODYEOB,
+    "SMFIC_HELO", SMFIC_HELO,
+    "SMFIC_HEADER", SMFIC_HEADER,
+    "SMFIC_MAIL", SMFIC_MAIL,
+    "SMFIC_EOH", SMFIC_EOH,
+    "SMFIC_OPTNEG", SMFIC_OPTNEG,
+    "SMFIC_QUIT", SMFIC_QUIT,
+    "SMFIC_RCPT", SMFIC_RCPT,
+#ifdef SMFIC_DATA
+    "SMFIC_DATA", SMFIC_DATA,
+#endif
+#ifdef SMFIC_UNKNOWN
+    "SMFIC_UNKNOWN", SMFIC_UNKNOWN,
+#endif
+    0, 0,
+};
+
+static NAME_CODE smfir_table[] = {
+    "SMFIR_ADDRCPT", SMFIR_ADDRCPT,
+    "SMFIR_DELRCPT", SMFIR_DELRCPT,
+    "SMFIR_ACCEPT", SMFIR_ACCEPT,
+    "SMFIR_REPLBODY", SMFIR_REPLBODY,
+    "SMFIR_CONTINUE", SMFIR_CONTINUE,
+    "SMFIR_DISCARD", SMFIR_DISCARD,
+#ifdef SMFIR_CONN_FAIL
+    "SMFIR_CONN_FAIL", SMFIR_CONN_FAIL,
+#endif
+#ifdef SMFIR_CHGHEADER
+    "SMFIR_CHGHEADER", SMFIR_CHGHEADER,
+#endif
+    "SMFIR_PROGRESS", SMFIR_PROGRESS,
+    "SMFIR_REJECT", SMFIR_REJECT,
+    "SMFIR_TEMPFAIL", SMFIR_TEMPFAIL,
+#ifdef SMFIR_SHUTDOWN
+    "SMFIR_SHUTDOWN", SMFIR_SHUTDOWN,
+#endif
+    "SMFIR_ADDHEADER", SMFIR_ADDHEADER,
+#ifdef SMFIR_INSHEADER
+    "SMFIR_INSHEADER", SMFIR_INSHEADER,
+#endif
+    "SMFIR_REPLYCODE", SMFIR_REPLYCODE,
+#ifdef SMFIR_QUARANTINE
+    "SMFIR_QUARANTINE", SMFIR_QUARANTINE,
+#endif
+    0, 0,
+};
+
+static NAME_MASK smfip_table[] = {
+    "SMFIP_NOCONNECT", SMFIP_NOCONNECT,
+    "SMFIP_NOHELO", SMFIP_NOHELO,
+    "SMFIP_NOMAIL", SMFIP_NOMAIL,
+    "SMFIP_NORCPT", SMFIP_NORCPT,
+    "SMFIP_NOBODY", SMFIP_NOBODY,
+    "SMFIP_NOHDRS", SMFIP_NOHDRS,
+    "SMFIP_NOEOH", SMFIP_NOEOH,
+#ifdef SMFIP_NOHREPL
+    "SMFIP_NOHREPL", SMFIP_NOHREPL,
+#endif
+#ifdef SMFIP_NOUNKNOWN
+    "SMFIP_NOUNKNOWN", SMFIP_NOUNKNOWN,
+#endif
+#ifdef SMFIP_NODATA
+    "SMFIP_NODATA", SMFIP_NODATA,
+#endif
+    0, 0,
+};
+
+static NAME_MASK smfif_table[] = {
+    "SMFIF_ADDHDRS", SMFIF_ADDHDRS,
+    "SMFIF_CHGBODY", SMFIF_CHGBODY,
+    "SMFIF_ADDRCPT", SMFIF_ADDRCPT,
+    "SMFIF_DELRCPT", SMFIF_DELRCPT,
+    "SMFIF_CHGHDRS", SMFIF_CHGHDRS,
+#ifdef SMFIF_QUARANTINE
+    "SMFIF_QUARANTINE", SMFIF_QUARANTINE,
+#endif
+    0, 0,
+};
+
+/* SLMs. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* milter8_def_reply - set persistent response */
+
+static const char *milter8_def_reply(MILTER8 *milter, const char *reply)
+{
+    if (milter->def_reply)
+       myfree(milter->def_reply);
+    milter->def_reply = reply ? mystrdup(reply) : 0;
+    return (milter->def_reply);
+}
+
+/* milter8_conf_error - local/remote configuration error */
+
+static int milter8_conf_error(MILTER8 *milter)
+{
+    const char *reply;
+
+    if (milter->fp != 0) {
+       (void) vstream_fclose(milter->fp);
+       milter->fp = 0;
+    }
+    if (strcasecmp(milter->def_action, "accept") == 0) {
+       reply = 0;
+    } else {
+       reply = "451 4.3.5 Server configuration problem - try again later";
+    }
+    milter8_def_reply(milter, reply);
+    return (milter->state = MILTER8_STAT_ERROR);
+}
+
+/* milter8_comm_error - read/write/format communication error */
+
+static int milter8_comm_error(MILTER8 *milter)
+{
+    const char *reply;
+
+    if (milter->fp != 0) {
+       (void) vstream_fclose(milter->fp);
+       milter->fp = 0;
+    }
+    if (strcasecmp(milter->def_action, "accept") == 0) {
+       reply = 0;
+    } else if (strcasecmp(milter->def_action, "reject") == 0) {
+       reply = "550 5.5.0 Service unavailable";
+    } else if (strcasecmp(milter->def_action, "tempfail") == 0) {
+       reply = "451 4.7.1 Service unavailable - try again later";
+    } else {
+       msg_warn("milter %s: unrecognized default action: %s",
+                milter->m.name, milter->def_action);
+       reply = "451 4.3.5 Server configuration problem - try again later";
+    }
+    milter8_def_reply(milter, reply);
+    return (milter->state = MILTER8_STAT_ERROR);
+}
+
+/* milter8_close_stream - close stream to milter application */
+
+static void milter8_close_stream(MILTER8 *milter)
+{
+    if (milter->fp != 0) {
+       (void) vstream_fclose(milter->fp);
+       milter->fp = 0;
+    }
+    milter->state = MILTER8_STAT_CLOSED;
+}
+
+/* milter8_read_cmd - receive command code now, receive data later */
+
+static int milter8_read_cmd(MILTER8 *milter, unsigned char *command,
+                                   ssize_t *data_len)
+{
+    UINT32_TYPE len;
+    ssize_t pkt_len;
+    int     cmd;
+
+    /*
+     * Receive the packet length.
+     */
+    if ((vstream_fread(milter->fp, (char *) &len, UINT32_SIZE))
+       != UINT32_SIZE) {
+       msg_warn("milter %s: can't read packet header: %m", milter->m.name);
+       return (milter8_comm_error(milter));
+    } else if ((pkt_len = ntohl(len)) < 1) {
+       msg_warn("milter %s: bad packet length: %ld",
+                milter->m.name, (long) pkt_len);
+       return (milter8_comm_error(milter));
+    } else if (pkt_len > XXX_MAX_DATA) {
+       msg_warn("milter %s: unreasonable packet length: %ld",
+                milter->m.name, (long) pkt_len);
+       return (milter8_comm_error(milter));
+    }
+
+    /*
+     * Receive the command code.
+     */
+    else if ((cmd = VSTREAM_GETC(milter->fp)) == VSTREAM_EOF) {
+       msg_warn("milter %s: EOF while reading command code: %m",
+                milter->m.name);
+       return (milter8_comm_error(milter));
+    }
+
+    /*
+     * All is well.
+     */
+    else {
+       *command = cmd;
+       *data_len = pkt_len - 1;
+       return (0);
+    }
+}
+
+/* vmilter8_read_data - read command data */
+
+static int vmilter8_read_data(MILTER8 *milter, ssize_t data_len, va_list ap)
+{
+    const char *myname = "milter8_read_data";
+    int     arg_type;
+    int     data_left;
+    UINT32_TYPE net_long;
+    UINT32_TYPE *host_long_ptr;
+    VSTRING *buf;
+    int     ch;
+
+    for (data_left = data_len; (arg_type = va_arg(ap, int)) > 0; /* void */ ) {
+       switch (arg_type) {
+
+           /*
+            * Host order long.
+            */
+       case MILTER8_DATA_HLONG:
+           if (data_left < UINT32_SIZE) {
+               msg_warn("milter %s: input packet too short for network long",
+                        milter->m.name);
+               return (milter8_comm_error(milter));
+           }
+           host_long_ptr = va_arg(ap, UINT32_TYPE *);
+           if (vstream_fread(milter->fp, (char *) &net_long, UINT32_SIZE)
+               != UINT32_SIZE) {
+               msg_warn("milter %s: EOF while reading network long: %m",
+                        milter->m.name);
+               return (milter8_comm_error(milter));
+           }
+           data_left -= UINT32_SIZE;
+           *host_long_ptr = ntohl(net_long);
+           break;
+
+           /*
+            * Raw on-the-wire format.
+            */
+       case MILTER8_DATA_BUFFER:
+           if (data_left < 1) {
+               msg_warn("milter %s: no data in input packet", milter->m.name);
+               return (milter8_comm_error(milter));
+           }
+           buf = va_arg(ap, VSTRING *);
+           VSTRING_RESET(buf);
+           VSTRING_SPACE(buf, data_left);
+           if (vstream_fread(milter->fp, (char *) STR(buf), data_left)
+               != data_left) {
+               msg_warn("milter %s: EOF while reading data: %m", milter->m.name);
+               return (milter8_comm_error(milter));
+           }
+           VSTRING_AT_OFFSET(buf, data_left);
+           data_left = 0;
+           break;
+
+           /*
+            * Pointer to null-terminated string.
+            */
+       case MILTER8_DATA_STRING:
+           if (data_left < 1) {
+               msg_warn("milter %s: packet too short for string",
+                        milter->m.name);
+               return (milter8_comm_error(milter));
+           }
+           buf = va_arg(ap, VSTRING *);
+           VSTRING_RESET(buf);
+           for (;;) {
+               if ((ch = VSTREAM_GETC(milter->fp)) == VSTREAM_EOF) {
+                   msg_warn("%s: milter %s: EOF while reading string: %m",
+                            myname, milter->m.name);
+                   return (milter8_comm_error(milter));
+               }
+               data_left -= 1;
+               if (ch == 0)
+                   break;
+               VSTRING_ADDCH(buf, ch);
+               if (data_left <= 0) {
+                   msg_warn("%s: milter %s: missing string null termimator",
+                            myname, milter->m.name);
+                   return (milter8_comm_error(milter));
+               }
+           }
+           VSTRING_TERMINATE(buf);
+           break;
+
+           /*
+            * Error.
+            */
+       default:
+           msg_panic("%s: unknown argument type: %d", myname, arg_type);
+       }
+    }
+
+    /*
+     * Sanity checks. We may have excess data when the sender is confused. We
+     * may have a negative count when we're confused outselves.
+     */
+    if (data_left > 0) {
+       msg_warn("%s: left-over data %ld bytes", myname, (long) data_left);
+       return (milter8_comm_error(milter));
+    }
+    if (data_left < 0)
+       msg_panic("%s: bad left-over data count %ld",
+                 myname, (long) data_left);
+    return (0);
+}
+
+/* milter8_read_data - read command data */
+
+static int milter8_read_data(MILTER8 *milter, ssize_t data_len,...)
+{
+    va_list ap;
+    int     ret;
+
+    va_start(ap, data_len);
+    ret = vmilter8_read_data(milter, data_len, ap);
+    va_end(ap);
+    return (ret);
+}
+
+/* vmilter8_size_data - compute command data length */
+
+static ssize_t vmilter8_size_data(va_list ap)
+{
+    const char *myname = "vmilter8_size_data";
+    ssize_t data_len;
+    int     arg_type;
+    VSTRING *buf;
+    const char *str;
+    const char **cpp;
+
+    /*
+     * Compute data size.
+     */
+    for (data_len = 0; (arg_type = va_arg(ap, int)) > 0; /* void */ ) {
+       switch (arg_type) {
+
+           /*
+            * Host order long.
+            */
+       case MILTER8_DATA_HLONG:
+           (void) va_arg(ap, UINT32_TYPE);
+           data_len += UINT32_SIZE;
+           break;
+
+           /*
+            * Raw on-the-wire format.
+            */
+       case MILTER8_DATA_BUFFER:
+           buf = va_arg(ap, VSTRING *);
+           data_len += LEN(buf);
+           break;
+
+           /*
+            * Pointer to null-terminated string.
+            */
+       case MILTER8_DATA_STRING:
+           str = va_arg(ap, char *);
+           data_len += strlen(str) + 1;
+           break;
+
+           /*
+            * Array of pointers to null-terminated strings.
+            */
+       case MILTER8_DATA_ARGV:
+           for (cpp = va_arg(ap, const char **); *cpp; cpp++)
+               data_len += strlen(*cpp) + 1;
+           break;
+
+           /*
+            * Network order short, promoted to int.
+            */
+       case MILTER8_DATA_NSHORT:
+           (void) va_arg(ap, unsigned);
+           data_len += UINT16_SIZE;
+           break;
+
+           /*
+            * Octet, promoted to int.
+            */
+       case MILTER8_DATA_OCTET:
+           (void) va_arg(ap, unsigned);
+           data_len += 1;
+           break;
+
+           /*
+            * Error.
+            */
+       default:
+           msg_panic("%s: bad argument type: %d", myname, arg_type);
+       }
+    }
+    va_end(ap);
+    return (data_len);
+}
+
+/* vmilter8_write_cmd - write command to Sendmail 8 Milter */
+
+static int vmilter8_write_cmd(MILTER8 *milter, int command, ssize_t data_len,
+                                     va_list ap)
+{
+    const char *myname = "vmilter8_write_cmd";
+    int     arg_type;
+    UINT32_TYPE pkt_len;
+    UINT32_TYPE host_long;
+    UINT32_TYPE net_long;
+    UINT16_TYPE net_short;
+    VSTRING *buf;
+    const char *str;
+    const char **cpp;
+    unsigned char ch;
+
+    /*
+     * Deliver the packet.
+     */
+    if ((pkt_len = 1 + data_len) < 1)
+       msg_panic("%s: bad packet length %d", myname, pkt_len);
+    pkt_len = htonl(pkt_len);
+    (void) vstream_fwrite(milter->fp, (char *) &pkt_len, UINT32_SIZE);
+    (void) VSTREAM_PUTC(command, milter->fp);
+    while ((arg_type = va_arg(ap, int)) > 0) {
+       switch (arg_type) {
+
+           /*
+            * Network long.
+            */
+       case MILTER8_DATA_HLONG:
+           host_long = va_arg(ap, UINT32_TYPE);
+           net_long = htonl(host_long);
+           (void) vstream_fwrite(milter->fp, (char *) &net_long, UINT32_SIZE);
+           break;
+
+           /*
+            * Raw on-the-wire format.
+            */
+       case MILTER8_DATA_BUFFER:
+           buf = va_arg(ap, VSTRING *);
+           (void) vstream_fwrite(milter->fp, STR(buf), LEN(buf));
+           break;
+
+           /*
+            * Pointer to null-terminated string.
+            */
+       case MILTER8_DATA_STRING:
+           str = va_arg(ap, char *);
+           (void) vstream_fwrite(milter->fp, str, strlen(str) + 1);
+           break;
+
+           /*
+            * Octet, promoted to int.
+            */
+       case MILTER8_DATA_OCTET:
+           ch = va_arg(ap, unsigned);
+           (void) vstream_fwrite(milter->fp, &ch, 1);
+           break;
+
+           /*
+            * Array of pointers to null-terminated strings.
+            */
+       case MILTER8_DATA_ARGV:
+           for (cpp = va_arg(ap, const char **); *cpp; cpp++)
+               (void) vstream_fwrite(milter->fp, *cpp, strlen(*cpp) + 1);
+           break;
+
+           /*
+            * Network order short, promoted to int.
+            */
+       case MILTER8_DATA_NSHORT:
+           net_short = va_arg(ap, unsigned);
+           (void) vstream_fwrite(milter->fp, (char *) &net_short, UINT16_SIZE);
+           break;
+
+           /*
+            * Error.
+            */
+       default:
+           msg_panic("%s: bad argument type: %d", myname, arg_type);
+       }
+
+       /*
+        * Report errors immediately.
+        */
+       if (vstream_ferror(milter->fp)) {
+           msg_warn("milter %s: error writing command: %m", milter->m.name);
+           milter8_comm_error(milter);
+           break;
+       }
+    }
+    va_end(ap);
+    return (milter->state == MILTER8_STAT_ERROR);
+}
+
+/* milter8_write_cmd - write command to Sendmail 8 Milter */
+
+static int milter8_write_cmd(MILTER8 *milter, int command,...)
+{
+    va_list ap;
+    ssize_t data_len;
+    int     err;
+
+    /*
+     * Size the command data.
+     */
+    va_start(ap, command);
+    data_len = vmilter8_size_data(ap);
+    va_end(ap);
+
+    /*
+     * Send the command and data.
+     */
+    va_start(ap, command);
+    err = vmilter8_write_cmd(milter, command, data_len, ap);
+    va_end(ap);
+
+    return (err);
+}
+
+/* milter8_event - report event and receive reply */
+
+static const char *milter8_event(MILTER8 *milter, int event,
+                                        int skip_event_flag,
+                                        int skip_reply,
+                                        ARGV *macros,...)
+{
+    va_list ap;
+    ssize_t data_len;
+    int     err;
+    unsigned char cmd;
+    ssize_t data_size;
+    const char *smfic_name;
+    const char *smfir_name;
+    MILTERS *parent;
+    UINT32_TYPE index;
+
+#define DONT_SKIP_REPLY        0
+
+    /*
+     * Skip this event if it is not defined for my protocol version.
+     */
+#ifndef USE_LIBMILTER_INCLUDES
+    if ((skip_event_flag & milter->np_mask) != 0) {
+       if (msg_verbose)
+           msg_info("skipping non-protocol event %s for milter %s",
+                    (smfic_name = str_name_code(smfic_table, event)) != 0 ?
+                    smfic_name : "(unknown MTA event)", milter->m.name);
+       return (milter->def_reply);
+    }
+#endif
+
+    /*
+     * Send the macros even when the corresponding list is empty. This is not
+     * a problem because we're sending macros and event parameters in one
+     * transaction.
+     */
+    if (msg_verbose) {
+       VSTRING *buf = vstring_alloc(100);
+
+       if (macros) {
+           if (macros->argc > 0) {
+               char  **cpp;
+
+               for (cpp = macros->argv; *cpp && cpp[1]; cpp += 2)
+                   vstring_sprintf_append(buf, " %s=%s", *cpp, cpp[1]);
+           }
+       }
+       msg_info("event: %s; macros:%s",
+                (smfic_name = str_name_code(smfic_table, event)) != 0 ?
+                smfic_name : "(unknown MTA event)", *STR(buf) ?
+                STR(buf) : " (none)");
+       vstring_free(buf);
+    }
+    if (macros) {
+       if (milter8_write_cmd(milter, SMFIC_MACRO,
+                             MILTER8_DATA_OCTET, event,
+                             MILTER8_DATA_ARGV, macros->argv,
+                             MILTER8_DATA_END) != 0)
+           return (milter->def_reply);
+    }
+
+    /*
+     * Skip this event if the Milter told us not to send it.
+     */
+    if ((skip_event_flag & milter->ev_mask) != 0) {
+       if (msg_verbose)
+           msg_info("skipping event %s for milter %s",
+                    (smfic_name = str_name_code(smfic_table, event)) != 0 ?
+                    smfic_name : "(unknown MTA event)", milter->m.name);
+       return (milter->def_reply);
+    }
+
+    /*
+     * Size the command data.
+     */
+    va_start(ap, macros);
+    data_len = vmilter8_size_data(ap);
+    va_end(ap);
+
+    /*
+     * Send the command and data.
+     */
+    va_start(ap, macros);
+    err = vmilter8_write_cmd(milter, event, data_len, ap);
+    va_end(ap);
+    if (err != 0)
+       return (milter->def_reply);
+
+    /*
+     * Special feature: don't wait for one reply per header. This allows us
+     * to send multiple headers in one transaction, and improves over-all
+     * performance.
+     */
+    if (skip_reply) {
+       if (msg_verbose)
+           msg_info("skipping reply %s for milter %s",
+                    (smfic_name = str_name_code(smfic_table, event)) != 0 ?
+                    smfic_name : "(unknown MTA event)", milter->m.name);
+       return (milter->def_reply);
+    }
+
+    /*
+     * Receive the reply or replies.
+     * 
+     * XXX Bound the loop iteration count.
+     */
+#define IN_CONNECT_EVENT(e) ((e) == SMFIC_CONNECT || (e) == SMFIC_HELO)
+
+    for (;;) {
+       if (milter8_read_cmd(milter, &cmd, &data_size) != 0)
+           return (milter->def_reply);
+       if (msg_verbose)
+           msg_info("reply: %s %d",
+                    (smfir_name = str_name_code(smfir_table, cmd)) != 0 ?
+                    smfir_name : "unknown", data_size);
+       switch (cmd) {
+
+           /*
+            * Still working on it.
+            */
+       case SMFIR_PROGRESS:
+           if (data_size != 0)
+               break;
+           continue;
+
+           /*
+            * Decision: continue processing.
+            */
+       case SMFIR_CONTINUE:
+           if (data_size != 0)
+               break;
+           return (milter->def_reply);
+
+           /*
+            * Decision: accept this message, or accept all further commands
+            * in this SMTP connection. This decision is final (i.e. Sendmail
+            * 8 changes receiver state).
+            */
+       case SMFIR_ACCEPT:
+           if (data_size != 0)
+               break;
+           if (IN_CONNECT_EVENT(event)) {
+#ifdef LIBMILTER_AUTO_DISCONNECT
+               milter8_close_stream(milter);
+#endif
+               /* No more events for this SMTP connection. */
+               milter->state = MILTER8_STAT_ACCEPT_CON;
+           } else {
+               /* No more events for this message. */
+               milter->state = MILTER8_STAT_ACCEPT_MSG;
+           }
+           return (milter->def_reply);
+
+           /*
+            * Decision: accept and silently discard this message. According
+            * to the milter API documentation there will be no action when
+            * this is requested by a connection-level function. This
+            * decision is final (i.e. Sendmail 8 changes receiver state).
+            */
+       case SMFIR_DISCARD:
+           if (data_size != 0)
+               break;
+           if (IN_CONNECT_EVENT(event)) {
+               msg_warn("milter %s: DISCARD action is not allowed "
+                        "for connect or helo", milter->m.name);
+               milter8_conf_error(milter);
+               return (milter->def_reply);
+           } else {
+               /* No more events for this message. */
+               milter->state = MILTER8_STAT_ACCEPT_MSG;
+               return ("D");
+           }
+
+           /*
+            * Decision: reject connection, message or recipient. This
+            * decision is final (i.e. Sendmail 8 changes receiver state).
+            */
+       case SMFIR_REJECT:
+           if (data_size != 0)
+               break;
+           if (IN_CONNECT_EVENT(event)) {
+#ifdef LIBMILTER_AUTO_DISCONNECT
+               milter8_close_stream(milter);
+#endif
+               milter->state = MILTER8_STAT_REJECT_CON;
+               return (milter8_def_reply(milter, "550 5.7.1 Command rejected"));
+           } else {
+               return ("550 5.7.1 Command rejected");
+           }
+
+           /*
+            * Decision: tempfail. This decision is final (i.e. Sendmail 8
+            * changes receiver state).
+            */
+       case SMFIR_TEMPFAIL:
+           if (data_size != 0)
+               break;
+           if (IN_CONNECT_EVENT(event)) {
+#ifdef LIBMILTER_AUTO_DISCONNECT
+               milter8_close_stream(milter);
+#endif
+               milter->state = MILTER8_STAT_REJECT_CON;
+               return (milter8_def_reply(milter,
+                      "451 4.7.1 Service unavailable - try again later"));
+           } else {
+               return ("451 4.7.1 Service unavailable - try again later");
+           }
+
+           /*
+            * Decision: disconnect. This decision is final (i.e. Sendmail 8
+            * changes receiver state).
+            */
+#ifdef SMFIR_SHUTDOWN
+       case SMFIR_SHUTDOWN:
+           if (data_size != 0)
+               break;
+#ifdef LIBMILTER_AUTO_DISCONNECT
+           milter8_close_stream(milter);
+#endif
+           milter->state = MILTER8_STAT_REJECT_CON;
+           return (milter8_def_reply(milter, "S"));
+#endif
+
+           /*
+            * Decision: "ddd d.d+.d+ text". This decision is final (i.e.
+            * Sendmail 8 changes receiver state). Note: the reply may be in
+            * multi-line SMTP format.
+            */
+       case SMFIR_REPLYCODE:
+           if (milter8_read_data(milter, data_size,
+                                 MILTER8_DATA_BUFFER, milter->buf,
+                                 MILTER8_DATA_END) != 0)
+               return (milter->def_reply);
+           if ((STR(milter->buf)[0] != '4' && STR(milter->buf)[0] != '5')
+               || !ISDIGIT(STR(milter->buf)[1])
+               || !ISDIGIT(STR(milter->buf)[2])
+               || (STR(milter->buf)[3] != ' ' && STR(milter->buf)[3] != '-')
+               || STR(milter->buf)[4] != STR(milter->buf)[0]) {
+               msg_warn("milter %s: malformed reply: %s",
+                        milter->m.name, STR(milter->buf));
+               milter8_conf_error(milter);
+               return (milter->def_reply);
+           }
+           if (IN_CONNECT_EVENT(event)) {
+#ifdef LIBMILTER_AUTO_DISCONNECT
+               milter8_close_stream(milter);
+#endif
+               milter->state = MILTER8_STAT_REJECT_CON;
+               return (milter8_def_reply(milter, STR(milter->buf)));
+           } else {
+               return (STR(milter->buf));
+           }
+
+           /*
+            * Decision: quarantine. In Sendmail 8.13 this does not imply a
+            * transition in the receiver state (reply, reject, tempfail,
+            * accept, discard).
+            */
+#ifdef SMFIR_QUARANTINE
+       case SMFIR_QUARANTINE:
+           /* XXX What to do with the "reason" text? */
+           if (milter8_read_data(milter, data_size,
+                                 MILTER8_DATA_BUFFER, milter->buf,
+                                 MILTER8_DATA_END) != 0)
+               return (milter->def_reply);
+           return ("H");
+#endif
+
+           /*
+            * Modification request or error.
+            */
+       default:
+           if (event == SMFIC_BODYEOB) {
+               switch (cmd) {
+
+                   /*
+                    * Modification request: replace, insert or delete
+                    * header. Index 1 means the first instance.
+                    */
+#ifdef SMFIR_CHGHEADER
+               case SMFIR_CHGHEADER:
+                   if (milter8_read_data(milter, data_size,
+                                         MILTER8_DATA_HLONG, &index,
+                                         MILTER8_DATA_STRING, milter->buf,
+                                         MILTER8_DATA_STRING, milter->body,
+                                         MILTER8_DATA_END) != 0)
+                       return (milter->def_reply);
+                   parent = milter->m.parent;
+                   if ((ssize_t) index < 1) {
+                       msg_warn("milter %s: bad change header index: %ld",
+                                milter->m.name, (long) index);
+                       milter8_conf_error(milter);
+                       return (milter->def_reply);
+                   }
+                   if (LEN(milter->buf) == 0) {
+                       msg_warn("milter %s: null change header name",
+                                milter->m.name);
+                       milter8_conf_error(milter);
+                       return (milter->def_reply);
+                   }
+                   if (STR(milter->body)[0])
+                       parent->upd_header(parent->chg_context, (ssize_t) index,
+                                          STR(milter->buf),
+                                          STR(milter->body));
+                   else
+                       parent->del_header(parent->chg_context, (ssize_t) index,
+                                          STR(milter->buf));
+                   continue;
+#endif
+
+                   /*
+                    * Modification request: append header.
+                    */
+               case SMFIR_ADDHEADER:
+                   if (milter8_read_data(milter, data_size,
+                                         MILTER8_DATA_STRING, milter->buf,
+                                         MILTER8_DATA_STRING, milter->body,
+                                         MILTER8_DATA_END) != 0)
+                       return (milter->def_reply);
+                   parent = milter->m.parent;
+                   parent->add_header(parent->chg_context, STR(milter->buf),
+                                      STR(milter->body));
+                   continue;
+
+                   /*
+                    * Modification request: insert header. With Sendmail 8,
+                    * index 0 means the top-most header. We use 1-based
+                    * indexing for consistency with header change
+                    * operations.
+                    */
+#ifdef SMFIR_INSHEADER
+               case SMFIR_INSHEADER:
+                   if (milter8_read_data(milter, data_size,
+                                         MILTER8_DATA_HLONG, &index,
+                                         MILTER8_DATA_STRING, milter->buf,
+                                         MILTER8_DATA_STRING, milter->body,
+                                         MILTER8_DATA_END) != 0)
+                       return (milter->def_reply);
+                   if ((ssize_t) index + 1 < 1) {
+                       msg_warn("milter %s: bad insert header index: %ld",
+                                milter->m.name, (long) index);
+                       milter8_conf_error(milter);
+                       return (milter->def_reply);
+                   }
+                   parent = milter->m.parent;
+                   parent->ins_header(parent->chg_context, (ssize_t) index + 1,
+                                      STR(milter->buf), STR(milter->body));
+                   continue;
+#endif
+
+                   /*
+                    * Modification request: append recipient.
+                    */
+               case SMFIR_ADDRCPT:
+                   if (milter8_read_data(milter, data_size,
+                                         MILTER8_DATA_STRING, milter->buf,
+                                         MILTER8_DATA_END) != 0)
+                       return (milter->def_reply);
+                   parent = milter->m.parent;
+                   parent->add_rcpt(parent->chg_context, STR(milter->buf));
+                   continue;
+
+                   /*
+                    * Modification request: delete (expansion of) recipient.
+                    */
+               case SMFIR_DELRCPT:
+                   if (milter8_read_data(milter, data_size,
+                                         MILTER8_DATA_STRING, milter->buf,
+                                         MILTER8_DATA_END) != 0)
+                       return (milter->def_reply);
+                   parent = milter->m.parent;
+                   parent->del_rcpt(parent->chg_context, STR(milter->buf));
+                   continue;
+
+                   /*
+                    * Modification request: replace the message body, and
+                    * update the message size.
+                    */
+#if 0
+               case SMFIR_REPLBODY:
+                   if (milter8_read_data(milter, data_size,
+                                         MILTER8_DATA_BUFFER, milter->body,
+                                         MILTER8_DATA_END) != 0)
+                       return (milter->def_reply);
+                   parent = milter->m.parent;
+                   parent->repl_body(parent->chg_context, milter->body);
+                   continue;
+#endif
+               }
+           }
+           msg_warn("milter %s: unexpected filter response %s after event %s",
+                    milter->m.name,
+                    (smfir_name = str_name_code(smfir_table, cmd)) != 0 ?
+                    smfir_name : "(unknown filter reply)",
+                    (smfic_name = str_name_code(smfic_table, event)) != 0 ?
+                    smfic_name : "(unknown MTA event)");
+           milter8_comm_error(milter);
+           return (milter->def_reply);
+       }
+
+       /*
+        * Get here when the reply was followed by data bytes that weren't
+        * supposed to be there.
+        */
+       msg_warn("milter %s: reply %s was followed by %d data bytes",
+       milter->m.name, (smfir_name = str_name_code(smfir_table, cmd)) != 0 ?
+                smfir_name : "unknown", data_len);
+       milter8_comm_error(milter);
+       return (milter->def_reply);
+    }
+}
+
+/* milter8_connect - connect to filter */
+
+static void milter8_connect(MILTER8 *milter)
+{
+    const char *myname = "milter8_connect";
+    ssize_t data_len;
+    unsigned char cmd;
+    char   *transport;
+    char   *endpoint;
+    int     (*connect_fn) (const char *, int, int);
+    int     fd;
+    const UINT32_TYPE my_actions = (SMFIF_ADDHDRS | SMFIF_ADDRCPT
+                                   | SMFIF_DELRCPT | SMFIF_CHGHDRS
+    /* Not yet: | SMFIF_CHGBODY */
+#ifdef SMFIF_QUARANTINE
+                                   | SMFIF_QUARANTINE
+#endif
+    );
+
+#ifdef USE_LIBMILTER_INCLUDES
+    const UINT32_TYPE my_version = SMFI_VERSION;
+    const UINT32_TYPE my_events = (SMFIP_NOCONNECT | SMFIP_NOHELO
+                                  | SMFIP_NOMAIL | SMFIP_NORCPT
+                                  | SMFIP_NOBODY | SMFIP_NOHDRS
+                                  | SMFIP_NOEOH
+#ifdef SMFIP_NOHREPL
+                                  | SMFIP_NOHREPL
+#endif
+#ifdef SMFIP_NOUNKNOWN
+                                  | SMFIP_NOUNKNOWN
+#endif
+#ifdef SMFIP_NODATA
+                                  | SMFIP_NODATA
+#endif
+    );
+
+#else
+    UINT32_TYPE my_version = 0;
+    UINT32_TYPE my_events = 0;
+    char   *saved_version;
+    char   *cp;
+    char   *name;
+
+#endif
+
+    /*
+     * Sanity check.
+     */
+    if (milter->fp != 0)
+       msg_panic("%s: milter %s: socket is not closed",
+                 myname, milter->m.name);
+
+#ifndef USE_LIBMILTER_INCLUDES
+
+    /*
+     * For user friendliness reasons the milter_protocol configuration
+     * parameter can specify both the protocol version and protocol
+     * extensions (e.g., don't reply for each individual message header).
+     * 
+     * The protocol version is sent as is to the milter application.
+     * 
+     * The version and extensions determine what events we can send to the
+     * milter application.
+     * 
+     * We don't announce support for events that aren't defined for my protocol
+     * version. Today's libmilter implementations don't seem to care, but we
+     * don't want to take the risk that a future version will be more picky.
+     */
+    cp = saved_version = mystrdup(milter->protocol);
+    while ((name = mystrtok(&cp, " ,\t\r\n")) != 0) {
+       int     mask;
+       int     vers;
+
+       if ((mask = name_code(milter8_event_masks,
+                             NAME_CODE_FLAG_NONE, name)) == -1
+           || (vers = name_code(milter8_versions,
+                                NAME_CODE_FLAG_NONE, name)) == -1
+           || (vers != 0 && my_version != 0)) {
+           msg_warn("milter %s: bad protocol information: %s",
+                    milter->m.name, name);
+           milter8_conf_error(milter);
+           return;
+       }
+       if (vers != 0)
+           my_version = vers;
+       my_events |= mask;
+    }
+    myfree(saved_version);
+    if (my_events == 0 || my_version == 0) {
+       msg_warn("milter %s: no protocol version information", milter->m.name);
+       milter8_conf_error(milter);
+       return;
+    }
+
+    /*
+     * Don't send events that aren't defined for my protocol version. This is
+     * somewhat tricky: unlike the other SMFIP_mumble bits, the SMFIP_NOHREPL
+     * bit specifies something that the milter application won't send. We
+     * must not inadvertently turn this bit on, because the MTA would get out
+     * of sync with the application.
+     */
+    milter->np_mask = ~(my_events | SMFIP_NOHREPL);
+    if (msg_verbose)
+       msg_info("%s: non-protocol events for protocol version %d: %s",
+                myname, my_version,
+                str_name_mask_opt(milter->buf, "non-protocol event mask",
+                          smfip_table, milter->np_mask, NAME_MASK_NUMBER));
+#endif
+
+    /*
+     * Parse the Milter application endpoint.
+     */
+#define FREE_TRANSPORT_AND_BAIL_OUT(milter, milter_error) do { \
+       myfree(transport); \
+       milter_error(milter); \
+       return; \
+    } while (0);
+
+    transport = mystrdup(milter->m.name);
+    if ((endpoint = split_at(transport, ':')) == 0
+       || *endpoint == 0 || *transport == 0) {
+       msg_warn("Milter service needs transport:endpoint instead of \"%s\"",
+                milter->m.name);
+       FREE_TRANSPORT_AND_BAIL_OUT(milter, milter8_conf_error);
+    }
+    if (msg_verbose)
+       msg_info("%s: transport=%s endpoint=%s", myname, transport, endpoint);
+    if (strcmp(transport, "inet") == 0) {
+       connect_fn = inet_connect;
+    } else if (strcmp(transport, "unix") == 0) {
+       connect_fn = unix_connect;
+    } else if (strcmp(transport, "local") == 0) {
+       connect_fn = LOCAL_CONNECT;
+    } else {
+       msg_warn("invalid transport name: %s in Milter service: %s",
+                transport, milter->m.name);
+       FREE_TRANSPORT_AND_BAIL_OUT(milter, milter8_conf_error);
+    }
+
+    /*
+     * Connect to the Milter application.
+     */
+    if ((fd = connect_fn(endpoint, BLOCKING, milter->conn_timeout)) < 0) {
+       msg_warn("connect to Milter service %s: %m", milter->m.name);
+       FREE_TRANSPORT_AND_BAIL_OUT(milter, milter8_comm_error);
+    }
+    myfree(transport);
+    milter->fp = vstream_fdopen(fd, O_RDWR);
+    vstream_control(milter->fp,
+                   VSTREAM_CTL_DOUBLE,
+                   VSTREAM_CTL_TIMEOUT, milter->cmd_timeout,
+                   VSTREAM_CTL_END);
+
+    /*
+     * Open the negotiations by sending what actions the Milter may request
+     * and what events the Milter can receive.
+     */
+    if (msg_verbose) {
+       msg_info("%s: my_version=0x%lx", myname, (long) my_version);
+       msg_info("%s: my_actions=0x%lx %s", myname, (long) my_actions,
+                str_name_mask_opt(milter->buf, "request mask",
+                               smfif_table, my_actions, NAME_MASK_NUMBER));
+       msg_info("%s: my_events=0x%lx %s", myname, (long) my_events,
+                str_name_mask_opt(milter->buf, "event mask",
+                                smfip_table, my_events, NAME_MASK_NUMBER));
+    }
+    errno = 0;
+    if (milter8_write_cmd(milter, SMFIC_OPTNEG,
+                         MILTER8_DATA_HLONG, my_version,
+                         MILTER8_DATA_HLONG, my_actions,
+                         MILTER8_DATA_HLONG, my_events,
+                         MILTER8_DATA_END) != 0) {
+       msg_warn("milter %s: write error in initial handshake",
+                milter->m.name);
+    }
+
+    /*
+     * Receive the filter's response and verify that we are compatible.
+     */
+    else if (milter8_read_cmd(milter, &cmd, &data_len) != 0) {
+       msg_warn("milter %s: read error in initial handshake", milter->m.name);
+       /* milter8_read_cmd() called milter8_comm_error() */
+    } else if (cmd != SMFIC_OPTNEG) {
+       msg_warn("milter %s: unexpected reply \"%c\" in initial handshake",
+                milter->m.name, cmd);
+       (void) milter8_comm_error(milter);
+    } else if (milter8_read_data(milter, data_len,
+                                MILTER8_DATA_HLONG, &milter->version,
+                                MILTER8_DATA_HLONG, &milter->rq_mask,
+                                MILTER8_DATA_HLONG, &milter->ev_mask,
+                                MILTER8_DATA_END) != 0) {
+       msg_warn("milter %s: read error in initial handshake", milter->m.name);
+       /* milter8_read_data() called milter8_comm_error() */
+    } else if (milter->version > my_version) {
+       msg_warn("milter %s: protocol version %d conflict"
+                " with MTA protocol version %d",
+                milter->m.name, milter->version, my_version);
+       (void) milter8_comm_error(milter);
+    } else if ((milter->rq_mask & my_actions) != milter->rq_mask) {
+       msg_warn("milter %s: request mask 0x%x conflict"
+                " with MTA request mask 0x%lx",
+                milter->m.name, milter->rq_mask, (long) my_actions);
+       (void) milter8_comm_error(milter);
+    }
+
+    /*
+     * Successful negotiations completed.
+     */
+    else {
+       if (msg_verbose) {
+           if ((milter->ev_mask & my_events) != milter->ev_mask)
+               msg_info("milter %s: event mask 0x%x includes features not"
+                        " offered in MTA event mask 0x%lx",
+                        milter->m.name, milter->ev_mask, (long) my_events);
+           msg_info("%s: milter %s version %d",
+                    myname, milter->m.name, milter->version);
+           msg_info("%s: events %s", myname,
+                    str_name_mask_opt(milter->buf, "event mask",
+                          smfip_table, milter->ev_mask, NAME_MASK_NUMBER));
+           msg_info("%s: requests %s", myname,
+                    str_name_mask_opt(milter->buf, "request mask",
+                          smfif_table, milter->rq_mask, NAME_MASK_NUMBER));
+       }
+       milter->state = MILTER8_STAT_READY;
+       milter8_def_reply(milter, 0);
+    }
+}
+
+/* milter8_conn_event - report connect event to Sendmail 8 milter */
+
+static const char *milter8_conn_event(MILTER *m,
+                                             const char *client_name,
+                                             const char *client_addr,
+                                             const char *client_port,
+                                             unsigned addr_family,
+                                             ARGV *macros)
+{
+    const char *myname = "milter8_conn_event";
+    MILTER8 *milter = (MILTER8 *) m;
+    int     port;
+
+    /*
+     * XXX Sendmail 8 libmilter closes the MTA-to-filter socket when it finds
+     * out that the SMTP client has disconnected. Because of this, Postfix
+     * has to open a new MTA-to-filter socket for each SMTP client.
+     */
+#ifdef LIBMILTER_AUTO_DISCONNECT
+    milter8_connect(milter);
+#endif
+
+    /*
+     * Report the event.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_READY:
+       if (msg_verbose)
+           msg_info("%s: milter %s: connect %s/%s",
+                    myname, milter->m.name, client_name, client_addr);
+       if (client_port == 0) {
+           port = 0;
+       } else if (!alldig(client_port) || (port = atoi(client_port)) < 0
+                  || port > 65535) {
+           msg_warn("milter %s: bad client port number %s",
+                    milter->m.name, client_port);
+           port = 0;
+       }
+       milter->state = MILTER8_STAT_ENVELOPE;
+       switch (addr_family) {
+       case AF_INET:
+           return (milter8_event(milter, SMFIC_CONNECT, SMFIP_NOCONNECT,
+                                 DONT_SKIP_REPLY, macros,
+                                 MILTER8_DATA_STRING, client_name,
+                                 MILTER8_DATA_OCTET, SMFIA_INET,
+                                 MILTER8_DATA_NSHORT, htons(port),
+                                 MILTER8_DATA_STRING, client_addr,
+                                 MILTER8_DATA_END));
+#ifdef HAS_IPV6
+       case AF_INET6:
+           return (milter8_event(milter, SMFIC_CONNECT, SMFIP_NOCONNECT,
+                                 DONT_SKIP_REPLY, macros,
+                                 MILTER8_DATA_STRING, client_name,
+                                 MILTER8_DATA_OCTET, SMFIA_INET6,
+                                 MILTER8_DATA_NSHORT, htons(port),
+                                 MILTER8_DATA_STRING, client_addr,
+                                 MILTER8_DATA_END));
+#endif
+       case AF_UNIX:
+           return (milter8_event(milter, SMFIC_CONNECT, SMFIP_NOCONNECT,
+                                 DONT_SKIP_REPLY, macros,
+                                 MILTER8_DATA_STRING, client_name,
+                                 MILTER8_DATA_OCTET, SMFIA_UNIX,
+                                 MILTER8_DATA_NSHORT, htons(0),
+                                 MILTER8_DATA_STRING, client_addr,
+                                 MILTER8_DATA_END));
+       default:
+           return (milter8_event(milter, SMFIC_CONNECT, SMFIP_NOCONNECT,
+                                 DONT_SKIP_REPLY, macros,
+                                 MILTER8_DATA_STRING, client_name,
+                                 MILTER8_DATA_OCTET, SMFIA_UNKNOWN,
+                                 MILTER8_DATA_END));
+       }
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+/* milter8_helo_event - report HELO/EHLO command to Sendmail 8 milter */
+
+static const char *milter8_helo_event(MILTER *m, const char *helo_name,
+                                             int unused_esmtp,
+                                             ARGV *macros)
+{
+    const char *myname = "milter8_helo_event";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    /*
+     * Report the event.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_ENVELOPE:
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: milter %s: helo %s",
+                    myname, milter->m.name, helo_name);
+       return (milter8_event(milter, SMFIC_HELO, SMFIP_NOHELO,
+                             DONT_SKIP_REPLY, macros,
+                             MILTER8_DATA_STRING, helo_name,
+                             MILTER8_DATA_END));
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+/* milter8_mail_event - report MAIL command to Sendmail 8 milter */
+
+static const char *milter8_mail_event(MILTER *m, const char **argv,
+                                             ARGV *macros)
+{
+    const char *myname = "milter8_mail_event";
+    MILTER8 *milter = (MILTER8 *) m;
+    const char **cpp;
+
+    /*
+     * Report the event.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_ENVELOPE:
+       if (msg_verbose) {
+           VSTRING *buf = vstring_alloc(100);
+
+           for (cpp = argv; *cpp; cpp++)
+               vstring_sprintf_append(buf, " %s", *cpp);
+           msg_info("%s: milter %s: mail%s",
+                    myname, milter->m.name, STR(buf));
+           vstring_free(buf);
+       }
+       return (milter8_event(milter, SMFIC_MAIL, SMFIP_NOMAIL,
+                             DONT_SKIP_REPLY, macros,
+                             MILTER8_DATA_ARGV, argv,
+                             MILTER8_DATA_END));
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+/* milter8_rcpt_event - report RCPT command to Sendmail 8 milter */
+
+static const char *milter8_rcpt_event(MILTER *m, const char **argv,
+                                             ARGV *macros)
+{
+    const char *myname = "milter8_rcpt_event";
+    MILTER8 *milter = (MILTER8 *) m;
+    const char **cpp;
+
+    /*
+     * Report the event.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_ENVELOPE:
+       if (msg_verbose) {
+           VSTRING *buf = vstring_alloc(100);
+
+           for (cpp = argv; *cpp; cpp++)
+               vstring_sprintf_append(buf, " %s", *cpp);
+           msg_info("%s: milter %s: rcpt%s",
+                    myname, milter->m.name, STR(buf));
+           vstring_free(buf);
+       }
+       return (milter8_event(milter, SMFIC_RCPT, SMFIP_NORCPT,
+                             DONT_SKIP_REPLY, macros,
+                             MILTER8_DATA_ARGV, argv,
+                             MILTER8_DATA_END));
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+/* milter8_data_event - report DATA command to Sendmail 8 milter */
+
+#ifdef SMFIC_DATA
+
+static const char *milter8_data_event(MILTER *m, ARGV *macros)
+{
+    const char *myname = "milter8_data_event";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    /*
+     * Report the event.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_ENVELOPE:
+       if (msg_verbose)
+           msg_info("%s: milter %s: data command", myname, milter->m.name);
+       return (milter8_event(milter, SMFIC_DATA, SMFIP_NODATA,
+                             DONT_SKIP_REPLY, macros,
+                             MILTER8_DATA_END));
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+#else
+#define milter8_data_event     0
+#endif
+
+/* milter8_unknown_event - report unknown SMTP command to Sendmail 8 milter */
+
+#ifdef SMFIC_UNKNOWN
+
+static const char *milter8_unknown_event(MILTER *m, const char *command,
+                                                ARGV *macros)
+{
+    const char *myname = "milter8_unknown_event";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    /*
+     * Report the event.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_ENVELOPE:
+       if (msg_verbose)
+           msg_info("%s: milter %s: unknown command: %s",
+                    myname, milter->m.name, command);
+       /* XXX Sendmail doesn't send macros (checked with 8.6.13). */
+       return (milter8_event(milter, SMFIC_UNKNOWN, SMFIP_NOUNKNOWN,
+                             DONT_SKIP_REPLY, macros,
+                             MILTER8_DATA_STRING, command,
+                             MILTER8_DATA_END));
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+#else
+#define milter8_unknown_event  0
+#endif
+
+/* milter8_other_event - reply for other event */
+
+static const char *milter8_other_event(MILTER *m)
+{
+    const char *myname = "milter8_other_event";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    /*
+     * Return the default reply.
+     */
+    if (msg_verbose)
+       msg_info("%s: milter %s", myname, milter->m.name);
+    return (milter->def_reply);
+}
+
+/* milter8_abort - cancel one milter's message receiving state */
+
+static void milter8_abort(MILTER *m)
+{
+    const char *myname = "milter8_abort";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    /*
+     * XXX Sendmail 8 libmilter closes the MTA-to-filter socket when it finds
+     * out that the SMTP client has disconnected. Because of this, Postfix
+     * has to open a new MTA-to-filter socket for each SMTP client.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+       if (msg_verbose)
+           msg_info("%s: skip milter %s", myname, milter->m.name);
+       break;
+    case MILTER8_STAT_ENVELOPE:
+    case MILTER8_STAT_MESSAGE:
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: abort milter %s", myname, milter->m.name);
+       (void) milter8_write_cmd(milter, SMFIC_ABORT, MILTER8_DATA_END);
+       if (milter->state != MILTER8_STAT_ERROR)
+           milter->state = MILTER8_STAT_ENVELOPE;
+       break;
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+/* milter8_disc_event - report client disconnect event */
+
+static void milter8_disc_event(MILTER *m)
+{
+    const char *myname = "milter8_disc_event";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    /*
+     * XXX Sendmail 8 libmilter closes the MTA-to-filter socket when it finds
+     * out that the SMTP client has disconnected. Because of this, Postfix
+     * has to open a new MTA-to-filter socket for each SMTP client.
+     */
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+#ifdef LIBMILTER_AUTO_DISCONNECT
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+#endif
+       if (msg_verbose)
+           msg_info("%s: skip quit milter %s", myname, milter->m.name);
+       break;
+    case MILTER8_STAT_ENVELOPE:
+    case MILTER8_STAT_MESSAGE:
+#ifndef LIBMILTER_AUTO_DISCONNECT
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+#endif
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: quit milter %s", myname, milter->m.name);
+       (void) milter8_write_cmd(milter, SMFIC_QUIT, MILTER8_DATA_END);
+       break;
+    }
+#ifdef LIBMILTER_AUTO_DISCONNECT
+    milter8_close_stream(milter);
+#else
+    if (milter->state != MILTER8_STAT_ERROR)
+       milter->state = MILTER8_STAT_READY;
+#endif
+    milter8_def_reply(milter, 0);
+}
+
+ /*
+  * Structure to ship context across the MIME_STATE engine.
+  */
+typedef struct {
+    MILTER8 *milter;                   /* milter client */
+    ARGV   *macros;                    /* end-of-body macros */
+    const char *resp;                  /* milter application response */
+} MILTER_MSG_CONTEXT;
+
+/* milter8_header - milter8_message call-back for message header */
+
+static void milter8_header(void *ptr, int unused_header_class,
+                                  HEADER_OPTS *header_info,
+                                  VSTRING *buf, off_t unused_offset)
+{
+    const char *myname = "milter8_header";
+    MILTER_MSG_CONTEXT *msg_ctx = (MILTER_MSG_CONTEXT *) ptr;
+    MILTER8 *milter = msg_ctx->milter;
+    char   *cp;
+    int     skip_reply;
+
+    /*
+     * Sendmail 8 sends multi-line headers as text separated by newline.
+     * 
+     * We destroy the header buffer to split it into label and value. Changing
+     * the buffer is explicitly allowed by the mime_state(3) interface.
+     */
+    if (msg_verbose > 1)
+       msg_info("%s: header milter %s: %.100s",
+                myname, milter->m.name, STR(buf));
+    cp = STR(buf) + (header_info ? strlen(header_info->name) :
+                    is_header(STR(buf)));
+    /* XXX Following matches is_header.c */
+    while (*cp == ' ' || *cp == '\t')
+       *cp++ = 0;
+    if (*cp != ':')
+       msg_panic("%s: header label not followed by ':'", myname);
+    *cp++ = 0;
+    /* XXX Following matches mime_state.c */
+    while (*cp == ' ' || *cp == '\t')
+       cp++;
+#ifdef SMFIP_NOHREPL
+    skip_reply = ((milter->ev_mask & SMFIP_NOHREPL) != 0);
+#else
+    skip_reply = DONT_SKIP_REPLY;
+#endif
+    msg_ctx->resp =
+       milter8_event(milter, SMFIC_HEADER, SMFIP_NOHDRS,
+                     skip_reply, msg_ctx->macros,
+                     MILTER8_DATA_STRING, STR(buf),
+                     MILTER8_DATA_STRING, cp,
+                     MILTER8_DATA_END);
+}
+
+/* milter8_eoh - milter8_message call-back for end-of-header */
+
+static void milter8_eoh(void *ptr)
+{
+    const char *myname = "milter8_eoh";
+    MILTER_MSG_CONTEXT *msg_ctx = (MILTER_MSG_CONTEXT *) ptr;
+    MILTER8 *milter = msg_ctx->milter;
+
+    if (msg_verbose)
+       msg_info("%s: eoh milter %s", myname, milter->m.name);
+    msg_ctx->resp =
+       milter8_event(milter, SMFIC_EOH, SMFIP_NOEOH,
+                     DONT_SKIP_REPLY, msg_ctx->macros,
+                     MILTER8_DATA_END);
+}
+
+/* milter8_body - milter8_message call-back for body content */
+
+static void milter8_body(void *ptr, int rec_type,
+                                const char *buf, ssize_t len,
+                                off_t offset)
+{
+    const char *myname = "milter8_body";
+    MILTER_MSG_CONTEXT *msg_ctx = (MILTER_MSG_CONTEXT *) ptr;
+    MILTER8 *milter = msg_ctx->milter;
+    ssize_t todo = len;
+    const char *bp = buf;
+    ssize_t space;
+    ssize_t count;
+
+    /*
+     * XXX I thought I was going to delegate all the on-the-wire formatting
+     * to a common lower layer, but unfortunately it's not practical. If we
+     * were to do MILTER_CHUNK_SIZE buffering in a common lower layer, then
+     * we would have to pass along call-backs and state, so that the
+     * call-back can invoke milter8_event() with the right arguments when the
+     * MILTER_CHUNK_SIZE buffer reaches capacity. That's just too ugly.
+     * 
+     * To recover the cost of making an extra copy of body content from Milter
+     * buffer to VSTREAM buffer, we could make vstream_fwrite() a little
+     * smarter so that it does large transfers directly from the user buffer
+     * instead of copying the data one block at a time into a VSTREAM buffer.
+     */
+    if (msg_verbose > 1)
+       msg_info("%s: body milter %s: %.100s", myname, milter->m.name, buf);
+    while (todo > 0) {
+       /* Append one REC_TYPE_NORM or REC_TYPE_CONT to body chunk buffer. */
+       space = MILTER_CHUNK_SIZE - LEN(milter->body);
+       if (space <= 0)
+           msg_panic("%s: bad buffer size: %ld",
+                     myname, (long) LEN(milter->body));
+       count = (todo > space ? space : todo);
+       vstring_memcat(milter->body, bp, count);
+       bp += count;
+       todo -= count;
+       /* Flush body chunk buffer when full. */
+       if (LEN(milter->body) == MILTER_CHUNK_SIZE) {
+           msg_ctx->resp =
+               milter8_event(milter, SMFIC_BODY, SMFIP_NOBODY,
+                             DONT_SKIP_REPLY, msg_ctx->macros,
+                             MILTER8_DATA_BUFFER, milter->body,
+                             MILTER8_DATA_END);
+           if (msg_ctx->resp != 0 || milter->state != MILTER8_STAT_MESSAGE)
+               break;
+           VSTRING_RESET(milter->body);
+       }
+       /* To append \r\n, simply redirect input to another buffer. */
+       if (rec_type == REC_TYPE_NORM && todo == 0) {
+           bp = "\r\n";
+           todo = 2;
+           rec_type = REC_TYPE_EOF;
+       }
+    }
+}
+
+/* milter8_eob - milter8_message call-back for end-of-body */
+
+static void milter8_eob(void *ptr)
+{
+    const char *myname = "milter8_eob";
+    MILTER_MSG_CONTEXT *msg_ctx = (MILTER_MSG_CONTEXT *) ptr;
+    MILTER8 *milter = msg_ctx->milter;
+
+    if (msg_verbose)
+       msg_info("%s: eob milter %s", myname, milter->m.name);
+    msg_ctx->resp =
+       milter8_event(msg_ctx->milter, SMFIC_BODYEOB, 0,
+                     DONT_SKIP_REPLY, msg_ctx->macros,
+                     MILTER8_DATA_BUFFER, milter->body,
+                     MILTER8_DATA_END);
+}
+
+/* milter8_message - send message content and receive reply */
+
+static const char *milter8_message(MILTER *m, VSTREAM *qfile,
+                                          off_t data_offset,
+                                          ARGV *macros)
+{
+    const char *myname = "milter8_message";
+    MILTER8 *milter = (MILTER8 *) m;
+    MIME_STATE *mime_state;
+    int     rec_type;
+    MIME_STATE_DETAIL *detail;
+    int     mime_errs = 0;
+    MILTER_MSG_CONTEXT msg_ctx;
+    VSTRING *buf;
+
+    switch (milter->state) {
+    case MILTER8_STAT_ERROR:
+    case MILTER8_STAT_ACCEPT_CON:
+    case MILTER8_STAT_REJECT_CON:
+    case MILTER8_STAT_ACCEPT_MSG:
+       if (msg_verbose)
+           msg_info("%s: skip message to milter %s", myname, milter->m.name);
+       return (milter->def_reply);
+    case MILTER8_STAT_ENVELOPE:
+       if (msg_verbose)
+           msg_info("%s: message to milter %s", myname, milter->m.name);
+       if (vstream_fseek(qfile, data_offset, SEEK_SET) < 0) {
+           msg_warn("%s: vstream_fseek %s: %m", myname, VSTREAM_PATH(qfile));
+           return ("450 4.3.0 Queue file write error");
+       }
+       msg_ctx.milter = milter;
+       msg_ctx.macros = macros;
+       msg_ctx.resp = 0;
+       mime_state =
+           mime_state_alloc(MIME_OPT_DISABLE_MIME,
+                            (milter->ev_mask & SMFIP_NOHDRS) ?
+                            (MIME_STATE_HEAD_OUT) 0 : milter8_header,
+                            (milter->ev_mask & SMFIP_NOEOH) ?
+                            (MIME_STATE_ANY_END) 0 : milter8_eoh,
+                            (milter->ev_mask & SMFIP_NOBODY) ?
+                            (MIME_STATE_BODY_OUT) 0 : milter8_body,
+                            milter8_eob,
+                            (MIME_STATE_ERR_PRINT) 0,
+                            (void *) &msg_ctx);
+       buf = vstring_alloc(100);
+       milter->state = MILTER8_STAT_MESSAGE;
+       VSTRING_RESET(milter->body);
+       vstream_control(milter->fp,
+                       VSTREAM_CTL_DOUBLE,
+                       VSTREAM_CTL_TIMEOUT, milter->msg_timeout,
+                       VSTREAM_CTL_END);
+
+       /*
+        * XXX When the message (not MIME body part) does not end in CRLF
+        * (i.e. the last record was REC_TYPE_CONT), do we send CRLF
+        * terminator before triggering the end-of-body condition?
+        */
+       for (;;) {
+           if ((rec_type = rec_get(qfile, buf, 0)) < 0) {
+               msg_warn("%s: error reading %s: %m",
+                        myname, VSTREAM_PATH(qfile));
+               msg_ctx.resp = "450 4.3.0 Queue file write error";
+               break;
+           }
+           /* Invoke the appropriate call-back routine. */
+           mime_errs = mime_state_update(mime_state, rec_type,
+                                         STR(buf), LEN(buf));
+           if (mime_errs) {
+               detail = mime_state_detail(mime_errs);
+               msg_warn("%s: MIME problem %s in %s",
+                        myname, detail->text, VSTREAM_PATH(qfile));
+               msg_ctx.resp = "450 4.3.0 Queue file write error";
+               break;
+           }
+           if (msg_ctx.resp != 0)
+               break;
+           if (milter->state != MILTER8_STAT_MESSAGE)
+               break;
+           if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
+               break;
+       }
+       mime_state_free(mime_state);
+       vstring_free(buf);
+       if (milter->fp)
+           vstream_control(milter->fp,
+                           VSTREAM_CTL_DOUBLE,
+                           VSTREAM_CTL_TIMEOUT, milter->cmd_timeout,
+                           VSTREAM_CTL_END);
+       if (milter->state == MILTER8_STAT_MESSAGE
+           || milter->state == MILTER8_STAT_ACCEPT_MSG)
+           milter->state = MILTER8_STAT_ENVELOPE;
+       return (msg_ctx.resp);
+    default:
+       msg_panic("%s: milter %s: bad state %d",
+                 myname, milter->m.name, milter->state);
+    }
+}
+
+ /*
+  * Preliminary protocol to send/receive milter instances. This needs to be
+  * extended with type information once we support multiple milter protocols.
+  */
+#define MAIL_ATTR_MILT_NAME    "milter_name"
+#define MAIL_ATTR_MILT_VERS    "milter_version"
+#define MAIL_ATTR_MILT_ACTS    "milter_actions"
+#define MAIL_ATTR_MILT_EVTS    "milter_events"
+#define MAIL_ATTR_MILT_NPTS    "milter_non_events"
+#define MAIL_ATTR_MILT_STAT    "milter_state"
+#define MAIL_ATTR_MILT_CONN    "milter_conn_timeout"
+#define MAIL_ATTR_MILT_CMD     "milter_cmd_timeout"
+#define MAIL_ATTR_MILT_MSG     "milter_msg_timeout"
+#define MAIL_ATTR_MILT_ACT     "milter_action"
+
+/* milter8_active - report if this milter still wants events */
+
+static int milter8_active(MILTER *m)
+{
+    MILTER8 *milter = (MILTER8 *) m;
+
+    return (milter->fp != 0
+           && (milter->state == MILTER8_STAT_ENVELOPE
+               || milter->state == MILTER8_STAT_READY));
+}
+
+/* milter8_send - send milter instance */
+
+static int milter8_send(MILTER *m, VSTREAM *stream)
+{
+    const char *myname = "milter8_send";
+    MILTER8 *milter = (MILTER8 *) m;
+
+    if (msg_verbose)
+       msg_info("%s: milter %s", myname, milter->m.name);
+
+    if (attr_print(stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_NAME, milter->m.name,
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_VERS, milter->version,
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_ACTS, milter->rq_mask,
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_EVTS, milter->ev_mask,
+#ifndef USE_LIBMILTER_INCLUDES
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_NPTS, milter->np_mask,
+#endif
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_STAT, milter->state,
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_CONN, milter->conn_timeout,
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_CMD, milter->cmd_timeout,
+                  ATTR_TYPE_INT, MAIL_ATTR_MILT_MSG, milter->msg_timeout,
+                  ATTR_TYPE_STR, MAIL_ATTR_MILT_ACT, milter->def_action,
+                  ATTR_TYPE_END) != 0
+       || vstream_fflush(stream) != 0) {
+       return (-1);
+#ifdef CANT_WRITE_BEFORE_SENDING_FD
+    } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_STR, MAIL_ATTR_DUMMY, milter->buf,
+                        ATTR_TYPE_END) != 1) {
+       return (-1);
+#endif
+    } else if (LOCAL_SEND_FD(vstream_fileno(stream),
+                            vstream_fileno(milter->fp)) < 0) {
+       return (-1);
+#ifdef MUST_READ_AFTER_SENDING_FD
+    } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_STR, MAIL_ATTR_DUMMY, milter->buf,
+                        ATTR_TYPE_END) != 1) {
+       return (-1);
+#endif
+    } else {
+       return (0);
+    }
+}
+
+static MILTER8 *milter8_alloc(const char *, int, int, int, const char *,
+                                     const char *, MILTERS *);
+
+/* milter8_receive - receive milter instance */
+
+MILTER *milter8_receive(VSTREAM *stream, MILTERS *parent)
+{
+    const char *myname = "milter8_receive";
+    static VSTRING *name_buf;
+    static VSTRING *act_buf;
+    MILTER8 *milter;
+    int     version;
+    int     rq_mask;
+    int     ev_mask;
+    int     np_mask;
+    int     state;
+    int     conn_timeout;
+    int     cmd_timeout;
+    int     msg_timeout;
+    int     fd;
+
+    if (name_buf == 0) {
+       name_buf = vstring_alloc(10);
+       act_buf = vstring_alloc(10);
+    }
+    if (attr_scan(stream, ATTR_FLAG_STRICT,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_NAME, name_buf,
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_VERS, &version,
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_ACTS, &rq_mask,
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_EVTS, &ev_mask,
+#ifndef USE_LIBMILTER_INCLUDES
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_NPTS, &np_mask,
+#endif
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_STAT, &state,
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_CONN, &conn_timeout,
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_CMD, &cmd_timeout,
+                 ATTR_TYPE_INT, MAIL_ATTR_MILT_MSG, &msg_timeout,
+                 ATTR_TYPE_STR, MAIL_ATTR_MILT_ACT, act_buf,
+                 ATTR_TYPE_END) < 9) {
+       return (0);
+#ifdef CANT_WRITE_BEFORE_SENDING_FD
+    } else if (attr_print(stream, ATTR_FLAG_NONE,
+                         ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "",
+                         ATTR_TYPE_END) != 0
+              || vstream_fflush(stream) != 0) {
+       return (0);
+#endif
+    } else if ((fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) {
+       return (0);
+#ifdef MUST_READ_AFTER_SENDING_FD
+    } else if (attr_print(stream, ATTR_FLAG_NONE,
+                         ATTR_TYPE_STR, MAIL_ATTR_DUMMY, "",
+                         ATTR_TYPE_END) != 0) {
+       return (0);
+#endif
+    } else {
+#define NO_PROTOCOL    ((char *) 0)
+
+       if (msg_verbose)
+           msg_info("%s: milter %s", myname, STR(name_buf));
+
+       milter = milter8_alloc(STR(name_buf), conn_timeout, cmd_timeout,
+                           msg_timeout, NO_PROTOCOL, STR(act_buf), parent);
+       milter->fp = vstream_fdopen(fd, O_RDWR);
+       vstream_control(milter->fp, VSTREAM_CTL_DOUBLE, VSTREAM_CTL_END);
+       milter->version = version;
+       milter->rq_mask = rq_mask;
+       milter->ev_mask = ev_mask;
+#ifndef USE_LIBMILTER_INCLUDES
+       milter->np_mask = np_mask;
+#endif
+       milter->state = state;
+       return (&milter->m);
+    }
+}
+
+/* milter8_free - destroy Milter instance */
+
+static void milter8_free(MILTER *m)
+{
+    MILTER8 *milter = (MILTER8 *) m;
+
+    if (msg_verbose)
+       msg_info("free milter %s", milter->m.name);
+    if (milter->fp)
+       (void) vstream_fclose(milter->fp);
+    myfree(milter->m.name);
+    vstring_free(milter->buf);
+    vstring_free(milter->body);
+    if (milter->protocol)
+       myfree(milter->protocol);
+    myfree(milter->def_action);
+    if (milter->def_reply)
+       myfree(milter->def_reply);
+    myfree((char *) milter);
+}
+
+/* milter8_alloc - create MTA-side Sendmail 8 Milter instance */
+
+static MILTER8 *milter8_alloc(const char *name, int conn_timeout,
+                                     int cmd_timeout, int msg_timeout,
+                                     const char *protocol,
+                                     const char *def_action,
+                                     MILTERS *parent)
+{
+    MILTER8 *milter;
+
+    /*
+     * Fill in the structure.
+     */
+    milter = (MILTER8 *) mymalloc(sizeof(*milter));
+    milter->m.name = mystrdup(name);
+    milter->m.next = 0;
+    milter->m.parent = parent;
+    milter->m.conn_event = milter8_conn_event;
+    milter->m.helo_event = milter8_helo_event;
+    milter->m.mail_event = milter8_mail_event;
+    milter->m.rcpt_event = milter8_rcpt_event;
+    milter->m.data_event = milter8_data_event; /* may be null */
+    milter->m.message = milter8_message;
+    milter->m.unknown_event = milter8_unknown_event;   /* may be null */
+    milter->m.other_event = milter8_other_event;
+    milter->m.abort = milter8_abort;
+    milter->m.disc_event = milter8_disc_event;
+    milter->m.active = milter8_active;
+    milter->m.send = milter8_send;
+    milter->m.free = milter8_free;
+    milter->fp = 0;
+    milter->buf = vstring_alloc(100);
+    milter->body = vstring_alloc(100);
+    milter->version = 0;
+    milter->rq_mask = 0;
+    milter->ev_mask = 0;
+    milter->state = MILTER8_STAT_CLOSED;
+    milter->conn_timeout = conn_timeout;
+    milter->cmd_timeout = cmd_timeout;
+    milter->msg_timeout = msg_timeout;
+    milter->protocol = (protocol ? mystrdup(protocol) : 0);
+    milter->def_action = mystrdup(def_action);
+    milter->def_reply = 0;
+
+    return (milter);
+}
+
+/* milter8_create - create MTA-side Sendmail 8 Milter instance */
+
+MILTER *milter8_create(const char *name, int conn_timeout, int cmd_timeout,
+                              int msg_timeout, const char *protocol,
+                              const char *def_action, MILTERS *parent)
+{
+    MILTER8 *milter;
+
+    /*
+     * Fill in the structure.
+     */
+    milter = milter8_alloc(name, conn_timeout, cmd_timeout, msg_timeout,
+                          protocol, def_action, parent);
+
+    /*
+     * XXX Sendmail 8 libmilter closes the MTA-to-filter socket when it finds
+     * out that the SMTP client has disconnected. Because of this, Postfix
+     * has to open a new MTA-to-filter socket for each SMTP client.
+     */
+#ifndef LIBMILTER_AUTO_DISCONNECT
+    milter8_connect(milter);
+#endif
+    return (&milter->m);
+}
diff --git a/postfix/src/milter/test-list b/postfix/src/milter/test-list
new file mode 100644 (file)
index 0000000..f66a0ac
--- /dev/null
@@ -0,0 +1,39 @@
+# Tempfail tests
+./test-milter -C 1 -a tempfail -c connect -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c helo -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c mail -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c rcpt -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c header -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c eoh -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c body -p inet:9999@127.0.0.1
+./test-milter -C 1 -a tempfail -c eom -p inet:9999@127.0.0.1
+
+# Reject tests
+./test-milter -C 1 -a reject -c connect -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c helo -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c mail -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c rcpt -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c header -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c eoh -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c body -p inet:9999@127.0.0.1
+./test-milter -C 1 -a reject -c eom -p inet:9999@127.0.0.1
+
+# Accept tests
+./test-milter -C 1 -a accept -c connect -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c helo -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c rcpt -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c mail -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c header -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c eoh -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c body -p inet:9999@127.0.0.1
+./test-milter -C 1 -a accept -c eom -p inet:9999@127.0.0.1
+
+# discard tests
+./test-milter -C 1 -a discard -c connect -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c helo -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c rcpt -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c mail -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c header -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c eoh -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c body -p inet:9999@127.0.0.1
+./test-milter -C 1 -a discard -c eom -p inet:9999@127.0.0.1
diff --git a/postfix/src/milter/test-milter.c b/postfix/src/milter/test-milter.c
new file mode 100644 (file)
index 0000000..60df98a
--- /dev/null
@@ -0,0 +1,356 @@
+ /*
+  * Simple test mail filter program.
+  * 
+  * Options:
+  * 
+  * -a accept|tempfail|reject|discard|ddd x.y.z text
+  * 
+  * Specifies a non-default reply. The default is to always continue.
+  * 
+  * -c connect|helo|mail|rcpt|data|header|eoh|body|eom|unknown|close|abort
+  * 
+  * When to send the non-default reply. The default is "connect".
+  * 
+  * -p inet:port@host|unix:/path/name
+  * 
+  * The mail filter listen endpoint.
+  * 
+  * -C count
+  * 
+  * Terminate after count connections.
+  */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "libmilter/mfapi.h"
+#include "libmilter/mfdef.h"
+
+static int conn_count;
+static int verbose;
+
+static int test_connect_reply = SMFIS_CONTINUE;
+static int test_helo_reply = SMFIS_CONTINUE;
+static int test_mail_reply = SMFIS_CONTINUE;
+static int test_rcpt_reply = SMFIS_CONTINUE;
+
+#if SMFI_VERSION > 3
+static int test_data_reply = SMFIS_CONTINUE;
+
+#endif
+static int test_header_reply = SMFIS_CONTINUE;
+static int test_eoh_reply = SMFIS_CONTINUE;
+static int test_body_reply = SMFIS_CONTINUE;
+static int test_eom_reply = SMFIS_CONTINUE;
+
+#if SMFI_VERSION > 2
+static int test_unknown_reply = SMFIS_CONTINUE;
+
+#endif
+static int test_close_reply = SMFIS_CONTINUE;
+static int test_abort_reply = SMFIS_CONTINUE;
+
+struct command_map {
+    const char *name;
+    int    *reply;
+};
+
+static struct command_map command_map[] = {
+    "connect", &test_connect_reply,
+    "helo", &test_helo_reply,
+    "mail", &test_mail_reply,
+    "rcpt", &test_rcpt_reply,
+#if SMFI_VERSION > 3
+    "data", &test_data_reply,
+#endif
+    "header", &test_header_reply,
+    "eoh", &test_eoh_reply,
+    "body", &test_body_reply,
+    "eom", &test_eom_reply,
+#if SMFI_VERSION > 2
+    "unknown", &test_unknown_reply,
+#endif
+    "close", &test_close_reply,
+    "abort", &test_abort_reply,
+    0, 0,
+};
+
+static char *reply_code;
+static char *reply_dsn;
+static char *reply_message;
+
+static int test_reply(SMFICTX *ctx, int code)
+{
+    if (code == SMFIR_REPLYCODE) {
+       if (smfi_setreply(ctx, reply_code, reply_dsn, reply_message) != MI_SUCCESS)
+           fprintf(stderr, "smfi_setreply failed\n");
+       return (reply_code[0] == '4' ? SMFIS_TEMPFAIL : SMFIS_REJECT);
+    } else {
+       return (code);
+    }
+}
+
+static sfsistat test_connect(SMFICTX *ctx, char *name, struct sockaddr * sa)
+{
+    const char *print_addr;
+    char    buf[BUFSIZ];
+
+    printf("test_connect %s ", name);
+    switch (sa->sa_family) {
+    case AF_INET:
+       {
+           struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+
+           print_addr = inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
+           if (print_addr == 0)
+               print_addr = strerror(errno);
+           printf("AF_INET (%s)\n", print_addr);
+       }
+       break;
+#ifdef HAS_IPV6
+    case AF_INET6:
+       {
+           struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
+
+           print_addr = inet_ntop(AF_INET, &sin6->sin6_addr, buf, sizeof(buf));
+           if (print_addr == 0)
+               print_addr = strerror(errno);
+           printf("AF_INET6 (%s)\n", print_addr);
+       }
+       break;
+#endif
+    case AF_UNIX:
+       {
+#undef sun
+           struct sockaddr_un *sun = (struct sockaddr_un *) sa;
+
+           printf("AF_UNIX (%s)\n", sun->sun_path);
+       }
+       break;
+    default:
+       printf(" [unknown address family]\n");
+       break;
+    }
+    return (test_reply(ctx, test_connect_reply));
+}
+
+static sfsistat test_helo(SMFICTX *ctx, char *arg)
+{
+    printf("test_helo \"%s\"\n", arg ? arg : "NULL");
+    return (test_reply(ctx, test_helo_reply));
+}
+
+static sfsistat test_mail(SMFICTX *ctx, char **argv)
+{
+    char  **cpp;
+
+    printf("test_mail");
+    for (cpp = argv; *cpp; cpp++)
+       printf(" \"%s\"", *cpp);
+    printf("\n");
+    return (test_reply(ctx, test_mail_reply));
+}
+
+static sfsistat test_rcpt(SMFICTX *ctx, char **argv)
+{
+    char  **cpp;
+
+    printf("test_rcpt");
+    for (cpp = argv; *cpp; cpp++)
+       printf(" \"%s\"", *cpp);
+    printf("\n");
+    return (test_reply(ctx, test_rcpt_reply));
+}
+
+
+sfsistat test_header(SMFICTX *ctx, char *name, char *value)
+{
+    printf("test_header \"%s\" \"%s\"\n", name, value);
+    return (test_reply(ctx, test_header_reply));
+}
+
+static sfsistat test_eoh(SMFICTX *ctx)
+{
+    printf("test_eoh\n");
+    return (test_reply(ctx, test_eoh_reply));
+}
+
+static sfsistat test_body(SMFICTX *ctx, unsigned char *data, size_t data_len)
+{
+    printf("test_body %ld bytes\n", (long) data_len);
+    return (test_reply(ctx, test_body_reply));
+}
+
+static sfsistat test_eom(SMFICTX *ctx)
+{
+    printf("test_eom\n");
+    return (test_reply(ctx, test_eom_reply));
+}
+
+static sfsistat test_abort(SMFICTX *ctx)
+{
+    printf("test_abort\n");
+    return (test_reply(ctx, test_abort_reply));
+}
+
+static sfsistat test_close(SMFICTX *ctx)
+{
+    printf("test_close\n");
+    if (verbose)
+       printf("conn_count %d\n", conn_count);
+    if (conn_count > 0 && --conn_count == 0)
+       exit(0);
+    return (test_reply(ctx, test_close_reply));
+}
+
+#if SMFI_VERSION > 3
+
+static sfsistat test_data(SMFICTX *ctx)
+{
+    printf("test_data\n");
+    return (test_reply(ctx, test_data_reply));
+}
+
+#endif
+
+#if SMFI_VERSION > 2
+
+static sfsistat test_unknown(SMFICTX *ctx)
+{
+    printf("test_unknown\n");
+    return (test_reply(ctx, test_unknown_reply));
+}
+
+#endif
+
+static struct smfiDesc smfilter =
+{
+    "test-milter",
+    SMFI_VERSION,
+    SMFIF_ADDRCPT | SMFIF_DELRCPT | SMFIF_CHGHDRS,
+    test_connect,
+    test_helo,
+    test_mail,
+    test_rcpt,
+    test_header,
+    test_eoh,
+    test_body,
+    test_eom,
+    test_abort,
+    test_close,
+#if SMFI_VERSION > 2
+    test_unknown,
+#endif
+#if SMFI_VERSION > 3
+    test_data,
+#endif
+};
+
+int     main(int argc, char **argv)
+{
+    char   *action = 0;
+    char   *command = 0;
+    struct command_map *cp;
+    int     ch;
+    int     code;
+
+    while ((ch = getopt(argc, argv, "a:c:d:p:vC:")) > 0) {
+       switch (ch) {
+       case 'a':
+           action = optarg;
+           break;
+       case 'c':
+           command = optarg;
+           break;
+       case 'd':
+           if (smfi_setdbg(atoi(optarg)) == MI_FAILURE) {
+               fprintf(stderr, "smfi_setdbg failed\n");
+               exit(1);
+           }
+           break;
+       case 'p':
+           if (smfi_setconn(optarg) == MI_FAILURE) {
+               fprintf(stderr, "smfi_setconn failed\n");
+               exit(1);
+           }
+           break;
+       case 'v':
+           verbose++;
+           break;
+       case 'C':
+           conn_count = atoi(optarg);
+           break;
+       default:
+           fprintf(stderr,
+                   "usage: %s [-a action] [-c command] [-C conn_count] [-d debug] -p port [-v]\n",
+                   argv[0]);
+           exit(1);
+       }
+    }
+    if (smfi_register(smfilter) == MI_FAILURE) {
+       fprintf(stderr, "smfi_register failed\n");
+       exit(1);
+    }
+    if (command) {
+       for (cp = command_map; /* see below */ ; cp++) {
+           if (cp->name == 0) {
+               fprintf(stderr, "bad -c argument: %s\n", command);
+               exit(1);
+           }
+           if (strcmp(command, cp->name) == 0)
+               break;
+       }
+    }
+    if (action) {
+       if (command == 0)
+           cp = command_map;
+       if (strcmp(action, "tempfail") == 0) {
+           cp->reply[0] = SMFIS_TEMPFAIL;
+       } else if (strcmp(action, "reject") == 0) {
+           cp->reply[0] = SMFIS_REJECT;
+       } else if (strcmp(action, "accept") == 0) {
+           cp->reply[0] = SMFIS_ACCEPT;
+       } else if (strcmp(action, "discard") == 0) {
+           cp->reply[0] = SMFIS_DISCARD;
+       } else if ((code = atoi(action)) >= 400
+                  && code <= 599
+                  && action[3] == ' ') {
+           cp->reply[0] = SMFIR_REPLYCODE;
+           reply_code = action;
+           reply_dsn = action + 3;
+           if (*reply_dsn != 0) {
+               *reply_dsn++ = 0;
+               reply_dsn += strspn(reply_dsn, " ");
+           }
+           if (*reply_dsn == 0) {
+               reply_dsn = reply_message = 0;
+           } else {
+               reply_message = reply_dsn + strcspn(reply_dsn, " ");
+               if (*reply_message != 0) {
+                   *reply_message++ = 0;
+                   reply_message += strspn(reply_message, " ");
+               }
+               if (*reply_message == 0)
+                   reply_message = 0;
+           }
+       } else {
+           fprintf(stderr, "bad -a argument: %s\n", action);
+           exit(1);
+       }
+       if (verbose) {
+           printf("command %s action %d\n", cp->name, cp->reply[0]);
+           if (reply_code)
+               printf("reply code %s dsn %s message %s\n",
+                      reply_code, reply_dsn ? reply_dsn : "(null)",
+                      reply_message ? reply_message : "(null)");
+       }
+    }
+    return (smfi_main());
+}
index 66fb73e3fbedab9661f3df5b4bbd1d85e0393e65..f3937eb566563640324d4374ff7ca919e43cd77c 100644 (file)
@@ -155,6 +155,7 @@ qmgr_deliver.o: ../../include/mail_proto.h
 qmgr_deliver.o: ../../include/mail_queue.h
 qmgr_deliver.o: ../../include/msg.h
 qmgr_deliver.o: ../../include/msg_stats.h
+qmgr_deliver.o: ../../include/mymalloc.h
 qmgr_deliver.o: ../../include/rcpt_print.h
 qmgr_deliver.o: ../../include/recipient_list.h
 qmgr_deliver.o: ../../include/scan_dir.h
index 67320c5705461dae8a3eaf6f04bd19d8486a9c6b..46a9cfdcd7f8ed2176e5a4088e2c038a61884fa5 100644 (file)
@@ -124,7 +124,7 @@ static void qmgr_active_done_3_generic(QMGR_MESSAGE *);
 
 static void qmgr_active_corrupt(const char *queue_id)
 {
-    char   *myname = "qmgr_active_corrupt";
+    const char *myname = "qmgr_active_corrupt";
 
     if (mail_queue_rename(queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT)) {
        if (errno != ENOENT)
@@ -143,7 +143,7 @@ static void qmgr_active_corrupt(const char *queue_id)
 static void qmgr_active_defer(const char *queue_name, const char *queue_id,
                                      const char *dest_queue, int delay)
 {
-    char   *myname = "qmgr_active_defer";
+    const char *myname = "qmgr_active_defer";
     const char *path;
     struct utimbuf tbuf;
 
@@ -169,7 +169,7 @@ static void qmgr_active_defer(const char *queue_name, const char *queue_id,
 
 int     qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
 {
-    char   *myname = "qmgr_active_feed";
+    const char *myname = "qmgr_active_feed";
     QMGR_MESSAGE *message;
     struct stat st;
     const char *path;
@@ -249,7 +249,7 @@ int     qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
 
 void    qmgr_active_done(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_active_done";
+    const char *myname = "qmgr_active_done";
     struct stat st;
 
     if (msg_verbose)
@@ -331,7 +331,7 @@ static void qmgr_active_done_2_bounce_flush(int status, char *context)
 
 static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_active_done_2_generic";
+    const char *myname = "qmgr_active_done_2_generic";
     const char *path;
     struct stat st;
     int     status;
@@ -483,7 +483,7 @@ static void qmgr_active_done_3_defer_flush(int status, char *context)
 
 static void qmgr_active_done_3_generic(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_active_done_3_generic";
+    const char *myname = "qmgr_active_done_3_generic";
     int     delay;
 
     /*
index c9f1f5e1ea651996be1dcc0d2c62dbbd0449c9d5..352939c931c61381446c110d73e4c07bdcb2a9d3 100644 (file)
@@ -165,10 +165,10 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
               ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
               ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, message->dsn_ret,
               ATTR_TYPE_FUNC, msg_stats_print, (void *) &stats,
-              ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
-              ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
-              ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, message->client_proto,
-              ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, message->client_helo,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, message->client_name,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, message->client_addr,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, message->client_proto,
+              ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, message->client_helo,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, message->sasl_method,
             ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
@@ -216,7 +216,6 @@ static void qmgr_deliver_update(int unused_event, char *context)
     int     status;
     RECIPIENT *recipient;
     int     nrcpt;
-    char   *whatsup;
 
     if (dsb == 0)
        dsb = dsb_create();
@@ -334,7 +333,6 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
     QMGR_QUEUE *queue;
     QMGR_ENTRY *entry;
     DSN     dsn;
-    char   *whatsup;
 
     /*
      * Find out if this delivery process is really available. Once elected,
index ec85209dfc49d42c178a8f2e7c3bb7d1b1423f7d..3783a61f1b6429979c3069929ccd100016188b3c 100644 (file)
@@ -94,7 +94,7 @@
 
 QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *queue)
 {
-    char   *myname = "qmgr_entry_select";
+    const char *myname = "qmgr_entry_select";
     QMGR_ENTRY *entry;
 
     if ((entry = queue->todo.prev) != 0) {
index 53c89cbb6acf11afa461595f2d5cae9a0cf1c777..33706b6d0319d553fa942f39a8e609a6493fe0fc 100644 (file)
@@ -440,7 +440,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            }
            continue;
        }
-       if (rec_type == REC_TYPE_DONE) {
+       if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_DRCP) {
            if (message->rcpt_offset == 0) {
                if (dsn_orcpt) {
                    myfree(dsn_orcpt);
@@ -590,20 +590,42 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
                    myfree(message->encoding);
                message->encoding = mystrdup(value);
            }
+
+           /*
+            * Backwards compatibility. Before Postfix 2.3, the logging
+            * attributes were called client_name, etc. Now they are called
+            * log_client_name. etc., and client_name is used for the actual
+            * client information. To support old queue files, we accept both
+            * names for the purpose of logging; the new name overrides the
+            * old one.
+            */
+           else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_NAME) == 0) {
+               if (message->client_name == 0)
+                   message->client_name = mystrdup(value);
+           } else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_ADDR) == 0) {
+               if (message->client_addr == 0)
+                   message->client_addr = mystrdup(value);
+           } else if (strcmp(name, MAIL_ATTR_ACT_PROTO_NAME) == 0) {
+               if (message->client_proto == 0)
+                   message->client_proto = mystrdup(value);
+           } else if (strcmp(name, MAIL_ATTR_ACT_HELO_NAME) == 0) {
+               if (message->client_helo == 0)
+                   message->client_helo = mystrdup(value);
+           }
            /* Original client attributes. */
-           else if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
+           else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_NAME) == 0) {
                if (message->client_name != 0)
                    myfree(message->client_name);
                message->client_name = mystrdup(value);
-           } else if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_ADDR) == 0) {
                if (message->client_addr != 0)
                    myfree(message->client_addr);
                message->client_addr = mystrdup(value);
-           } else if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_LOG_PROTO_NAME) == 0) {
                if (message->client_proto != 0)
                    myfree(message->client_proto);
                message->client_proto = mystrdup(value);
-           } else if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_LOG_HELO_NAME) == 0) {
                if (message->client_helo != 0)
                    myfree(message->client_helo);
                message->client_helo = mystrdup(value);
@@ -1212,7 +1234,7 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
 QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
                                         int qflags)
 {
-    char   *myname = "qmgr_message_alloc";
+    const char *myname = "qmgr_message_alloc";
     QMGR_MESSAGE *message;
 
     if (msg_verbose)
@@ -1272,7 +1294,7 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
 
 QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_message_realloc";
+    const char *myname = "qmgr_message_realloc";
 
     /*
      * Sanity checks.
index a4e55f0585f3a351277574b9474e97e7d867883e..e68f80348c861047f72109a85610a682db773501 100644 (file)
@@ -57,7 +57,7 @@
 void    qmgr_move(const char *src_queue, const char *dst_queue,
                          time_t time_stamp)
 {
-    char   *myname = "qmgr_move";
+    const char *myname = "qmgr_move";
     SCAN_DIR *queue_dir;
     char   *queue_id;
     struct utimbuf tbuf;
index 5571ae24eed89c5c7727497b6d41e4c460309ec7..438c5130ffaa28187a0d7d535b009c02cf0deb26 100644 (file)
@@ -122,7 +122,7 @@ static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
 
 void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 {
-    char   *myname = "qmgr_queue_unthrottle";
+    const char *myname = "qmgr_queue_unthrottle";
     QMGR_TRANSPORT *transport = queue->transport;
 
     if (msg_verbose)
@@ -156,7 +156,7 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 
 void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
 {
-    char   *myname = "qmgr_queue_throttle";
+    const char *myname = "qmgr_queue_throttle";
 
     /*
      * Sanity checks.
@@ -213,7 +213,7 @@ QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *transport)
 
 void    qmgr_queue_done(QMGR_QUEUE *queue)
 {
-    char   *myname = "qmgr_queue_done";
+    const char *myname = "qmgr_queue_done";
     QMGR_TRANSPORT *transport = queue->transport;
 
     /*
index e0d9fc079abfe478df28c5a686680546156ecb34..37a3ce8ded3edc7abfebe4d62bdecc56c0ddc879 100644 (file)
@@ -76,7 +76,7 @@
 
 static void qmgr_scan_start(QMGR_SCAN *scan_info)
 {
-    char   *myname = "qmgr_scan_start";
+    const char *myname = "qmgr_scan_start";
 
     /*
      * Sanity check.
index d31f7bd390b0cdab552d5787ec1af7bac1cfe1d0..df522f37606fd45d4ce45c4eaadea4bc36609de6 100644 (file)
@@ -116,7 +116,7 @@ static void qmgr_transport_unthrottle_wrapper(int unused_event, char *context)
 
 void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
 {
-    char   *myname = "qmgr_transport_unthrottle";
+    const char *myname = "qmgr_transport_unthrottle";
 
     /*
      * This routine runs after expiration of the timer set by
@@ -142,7 +142,7 @@ void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
 
 void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, DSN *dsn)
 {
-    char   *myname = "qmgr_transport_throttle";
+    const char *myname = "qmgr_transport_throttle";
 
     /*
      * We are unable to connect to a deliver process for this type of message
index 750215b6dba8643abdfc21e8a7bf5b38af22d6fb..bb1704e4f3099bf364038dca95f0d4ccb0687c8a 100644 (file)
@@ -184,15 +184,25 @@ static int file_read_error(PICKUP_INFO *info, int type)
     return (REMOVE_MESSAGE_FILE);
 }
 
-/* cleanup_service_error - handle error writing to cleanup service. */
+/* cleanup_service_error_reason - handle error writing to cleanup service. */
 
-static int cleanup_service_error(PICKUP_INFO *info, int status)
+static int cleanup_service_error_reason(PICKUP_INFO *info, int status,
+                                               const char *reason)
 {
-    msg_warn("%s: %s", info->path, cleanup_strerror(status));
+
+    /*
+     * XXX If the cleanup server gave a reason, then it was already logged.
+     * Don't bother logging it another time.
+     */
+    if (reason == 0)
+       msg_warn("%s: %s", info->path, cleanup_strerror(status));
     return ((status & CLEANUP_STAT_BAD) ?
            REMOVE_MESSAGE_FILE : KEEP_MESSAGE_FILE);
 }
 
+#define cleanup_service_error(info, status) \
+       cleanup_service_error_reason((info), (status), (char *) 0)
+
 /* copy_segment - copy a record group */
 
 static int copy_segment(VSTREAM *qfile, VSTREAM *cleanup, PICKUP_INFO *info,
@@ -214,6 +224,8 @@ static int copy_segment(VSTREAM *qfile, VSTREAM *cleanup, PICKUP_INFO *info,
      * Allow attribute records if the queue file is owned by the mail system
      * (postsuper -r) or if the attribute specifies the MIME body type
      * (sendmail -B).
+     * 
+     * We must allow PTR records here because of "postsuper -r".
      */
     for (;;) {
        if ((type = rec_get(qfile, buf, var_line_limit)) < 0
@@ -378,7 +390,8 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
     rec_fputs(cleanup, REC_TYPE_END, "");
     if (attr_scan(cleanup, ATTR_FLAG_MISSING,
                  ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
-                 ATTR_TYPE_END) != 1)
+                 ATTR_TYPE_STR, MAIL_ATTR_WHY, buf,
+                 ATTR_TYPE_END) != 2)
        return (cleanup_service_error(info, CLEANUP_STAT_WRITE));
 
     /*
@@ -388,7 +401,7 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
      * now and then.
      */
     if (status) {
-       return (cleanup_service_error(info, status));
+       return (cleanup_service_error_reason(info, status, vstring_str(buf)));
     } else {
        return (REMOVE_MESSAGE_FILE);
     }
index 09bc803609ba4e9cf898b9c691326221d8dfae1c..086370263a2cdad949ebec9bddc2af3ef3b17c39 100644 (file)
@@ -708,7 +708,7 @@ static ARGV *expand_argv(const char *service, char **argv,
 
 static void get_service_params(PIPE_PARAMS *config, char *service)
 {
-    char   *myname = "get_service_params";
+    const char *myname = "get_service_params";
 
     /*
      * Figure out the command time limit for this transport.
@@ -727,7 +727,7 @@ static void get_service_params(PIPE_PARAMS *config, char *service)
 
 static void get_service_attr(PIPE_ATTR *attr, char **argv)
 {
-    char   *myname = "get_service_attr";
+    const char *myname = "get_service_attr";
     struct passwd *pwd;
     struct group *grp;
     char   *user;                      /* user name */
@@ -966,7 +966,7 @@ static int eval_command_status(int command_status, char *service,
 
 static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
 {
-    char   *myname = "deliver_message";
+    const char *myname = "deliver_message";
     static PIPE_PARAMS conf;
     static PIPE_ATTR attr;
     RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
index 03dacc69dfcc468b95b233e817038380bd3c4234..7f043f4c31e475f9cd8717b1a6f9e8cd271afd7b 100644 (file)
@@ -113,6 +113,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
     const char *error_text;
     char   *attr_name;
     char   *attr_value;
+    int     rec_flags = (msg_verbose ? REC_FLAG_NONE : REC_FLAG_DEFAULT);
 
 #define TEXT_RECORD(rec_type) \
            (rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM)
@@ -131,10 +132,10 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
     /*
      * Now look at the rest.
      */
-    for (;;) {
+    do {
        if (flags & PC_FLAG_OFFSET)
            offset = vstream_ftell(fp);
-       rec_type = rec_get(fp, buffer, 0);
+       rec_type = rec_get_raw(fp, buffer, 0, rec_flags);
        if (rec_type == REC_TYPE_ERROR)
            msg_fatal("record read error");
        if (rec_type == REC_TYPE_EOF)
@@ -159,6 +160,13 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
            vstream_printf("%s: %s", rec_type_name(rec_type),
                           asctime(localtime(&time)));
            break;
+       case REC_TYPE_PTR:                      /* pointer */
+           vstream_printf("%s: ", rec_type_name(rec_type));
+           vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
+           VSTREAM_PUTCHAR('\n');
+           if (rec_goto(fp, STR(buffer)) == REC_TYPE_ERROR)
+               msg_fatal("bad pointer record, or input is not seekable");
+           break;
        case REC_TYPE_CONT:                     /* REC_TYPE_FILT collision */
            if (!in_message)
                vstream_printf("%s: ", rec_type_name(rec_type));
@@ -176,6 +184,13 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
            vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
            VSTREAM_PUTCHAR('\n');
            break;
+       case REC_TYPE_DTXT:
+           if (msg_verbose) {
+               vstream_printf("%s: ", rec_type_name(rec_type));
+               vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer));
+               VSTREAM_PUTCHAR('\n');
+           }
+           break;
        case REC_TYPE_MESG:
            vstream_printf("*** MESSAGE CONTENTS %s ***\n", VSTREAM_PATH(fp));
            in_message = 1;
@@ -213,7 +228,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
         * In case the next record is broken.
         */
        vstream_fflush(VSTREAM_OUT);
-    }
+    } while (rec_type != REC_TYPE_END);
 }
 
 /* usage - explain and terminate */
index cdf25b91b2f042932950b8df695ec021e3aa8678..25069f439c2eff6d9e17f4f075d07f15e0bd7ba9 100644 (file)
@@ -1,33 +1,56 @@
 # Extract initialization tables from actual source code.
 
+# XXX: Associated variable aliasing:
+#
+# Some parameters bind to different variables in different contexts,
+# And other parameters map to associated variables in a many-to-1
+# fashion. This is mostly the result of the SMTP+LMTP integration
+# and the overloading of parameters that have identical semantics,
+# for the corresponding context.
+#
+# The "++table[...]" below ignores the associated variable name
+# when doing duplicate elimination. Differences in the default value
+# or lower/upper bounds still result in "postconf -d" duplicates,
+# which are a sign of an error somewhere...
+
 /^(static| )*CONFIG_INT_TABLE .*\{/,/\};/ { 
     if ($1 ~ /VAR/) {
        print "int " substr($3,2,length($3)-2) ";" > "int_vars.h"
-       print | "sed 's/[       ][      ]*/ /g' | sort -u >int_table.h" 
+       if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+           print |"sed 's/[    ][      ]*/ /g' > int_table.h"
+       }
     }
 }
 /^(static| )*CONFIG_STR_TABLE .*\{/,/\};/ { 
     if ($1 ~ /VAR/) {
        print "char *" substr($3,2,length($3)-2) ";" > "str_vars.h"
-       print | "sed 's/[       ][      ]*/ /g' | sort -u >str_table.h" 
+       if (++stab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+           print |"sed 's/[    ][      ]*/ /g' > str_table.h"
+       }
     }
 }
 /^(static| )*CONFIG_RAW_TABLE .*\{/,/\};/ { 
     if ($1 ~ /VAR/) {
        print "char *" substr($3,2,length($3)-2) ";" > "raw_vars.h"
-       print | "sed 's/[       ][      ]*/ /g' | sort -u >raw_table.h" 
+       if (++rtab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+           print |"sed 's/[    ][      ]*/ /g' > raw_table.h"
+       }
     }
 }
 /^(static| )*CONFIG_BOOL_TABLE .*\{/,/\};/ { 
     if ($1 ~ /VAR/) {
        print "int " substr($3,2,length($3)-2) ";" > "bool_vars.h"
-       print | "sed 's/[       ][      ]*/ /g' | sort -u >bool_table.h" 
+       if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+           print |"sed 's/[    ][      ]*/ /g' > bool_table.h"
+       }
     }
 }
 /^(static| )*CONFIG_TIME_TABLE .*\{/,/\};/ { 
     if ($1 ~ /VAR/) {
        print "int " substr($3,2,length($3)-2) ";" > "time_vars.h"
-       print | "sed 's/[       ][      ]*/ /g' | sort -u >time_table.h" 
+       if (++ttab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+           print |"sed 's/[    ][      ]*/ /g' > time_table.h" 
+       }
     }
 }
 
index ee43bcd024072477a286f274c0f1da0c42ab12eb..aa064c8287ad25851e4c1fa244155df8cd599cda 100644 (file)
@@ -375,7 +375,8 @@ int     main(int argc, char **argv)
     rec_fprintf(dst->stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
                REC_TYPE_TIME_ARG(start));
     for (;;) {
-       rec_type = rec_get(VSTREAM_IN, buf, var_line_limit);
+       /* Don't allow PTR records. */
+       rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE);
        if (rec_type == REC_TYPE_EOF) {         /* request cancelled */
            mail_stream_cleanup(dst);
            if (remove(postdrop_path))
index 67320c5705461dae8a3eaf6f04bd19d8486a9c6b..46a9cfdcd7f8ed2176e5a4088e2c038a61884fa5 100644 (file)
@@ -124,7 +124,7 @@ static void qmgr_active_done_3_generic(QMGR_MESSAGE *);
 
 static void qmgr_active_corrupt(const char *queue_id)
 {
-    char   *myname = "qmgr_active_corrupt";
+    const char *myname = "qmgr_active_corrupt";
 
     if (mail_queue_rename(queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT)) {
        if (errno != ENOENT)
@@ -143,7 +143,7 @@ static void qmgr_active_corrupt(const char *queue_id)
 static void qmgr_active_defer(const char *queue_name, const char *queue_id,
                                      const char *dest_queue, int delay)
 {
-    char   *myname = "qmgr_active_defer";
+    const char *myname = "qmgr_active_defer";
     const char *path;
     struct utimbuf tbuf;
 
@@ -169,7 +169,7 @@ static void qmgr_active_defer(const char *queue_name, const char *queue_id,
 
 int     qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
 {
-    char   *myname = "qmgr_active_feed";
+    const char *myname = "qmgr_active_feed";
     QMGR_MESSAGE *message;
     struct stat st;
     const char *path;
@@ -249,7 +249,7 @@ int     qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
 
 void    qmgr_active_done(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_active_done";
+    const char *myname = "qmgr_active_done";
     struct stat st;
 
     if (msg_verbose)
@@ -331,7 +331,7 @@ static void qmgr_active_done_2_bounce_flush(int status, char *context)
 
 static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_active_done_2_generic";
+    const char *myname = "qmgr_active_done_2_generic";
     const char *path;
     struct stat st;
     int     status;
@@ -483,7 +483,7 @@ static void qmgr_active_done_3_defer_flush(int status, char *context)
 
 static void qmgr_active_done_3_generic(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_active_done_3_generic";
+    const char *myname = "qmgr_active_done_3_generic";
     int     delay;
 
     /*
index ed16d8691ae30b65bd4266c5801adb5605a6eaa7..19dce9614787fa460d29673dd3c53e5e5e62e874 100644 (file)
@@ -170,10 +170,10 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
               ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
               ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, message->dsn_ret,
               ATTR_TYPE_FUNC, msg_stats_print, (void *) &stats,
-              ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
-              ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
-              ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, message->client_proto,
-              ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, message->client_helo,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, message->client_name,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, message->client_addr,
+            ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, message->client_proto,
+              ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, message->client_helo,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, message->sasl_method,
             ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
@@ -221,7 +221,6 @@ static void qmgr_deliver_update(int unused_event, char *context)
     int     status;
     RECIPIENT *recipient;
     int     nrcpt;
-    char   *whatsup;
 
     if (dsb == 0)
        dsb = dsb_create();
@@ -338,7 +337,6 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
 {
     QMGR_ENTRY *entry;
     DSN     dsn;
-    char   *whatsup;
 
     /*
      * Find out if this delivery process is really available. Once elected,
index 3b1603ef56cc2beade53f376391283027ec3fc90..58c278bfd0cec94bbdd3ad0e6e363486e1307594 100644 (file)
 
 QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *peer)
 {
-    char   *myname = "qmgr_entry_select";
+    const char *myname = "qmgr_entry_select";
     QMGR_ENTRY *entry;
     QMGR_QUEUE *queue;
 
index 094838b77e2dcc8efc6f70f4bf755b66c9949e95..30365f6a8192d8508ec91bb9133c6ffa7a2297c3 100644 (file)
@@ -329,7 +329,7 @@ static void qmgr_job_parent_gone(QMGR_JOB *job, QMGR_JOB *parent)
 
 static void qmgr_job_unlink(QMGR_JOB *job)
 {
-    char   *myname = "qmgr_job_unlink";
+    const char *myname = "qmgr_job_unlink";
     QMGR_TRANSPORT *transport = job->transport;
 
     /*
@@ -402,7 +402,7 @@ static void qmgr_job_retire(QMGR_JOB *job)
 
 void    qmgr_job_free(QMGR_JOB *job)
 {
-    char   *myname = "qmgr_job_free";
+    const char *myname = "qmgr_job_free";
     QMGR_MESSAGE *message = job->message;
     QMGR_TRANSPORT *transport = job->transport;
 
@@ -566,7 +566,7 @@ static QMGR_JOB *qmgr_job_candidate(QMGR_JOB *current)
 
 static QMGR_JOB *qmgr_job_preempt(QMGR_JOB *current)
 {
-    char   *myname = "qmgr_job_preempt";
+    const char *myname = "qmgr_job_preempt";
     QMGR_TRANSPORT *transport = current->transport;
     QMGR_JOB *job,
            *prev;
@@ -683,7 +683,7 @@ static QMGR_JOB *qmgr_job_preempt(QMGR_JOB *current)
 
 static void qmgr_job_pop(QMGR_JOB *job)
 {
-    char   *myname = "qmgr_job_pop";
+    const char *myname = "qmgr_job_pop";
     QMGR_TRANSPORT *transport = job->transport;
     QMGR_JOB *parent;
 
index 2ff6ee88a4637dc70bebdd465363ff08f26f2744..aa71e190772c694088145a43d164ceb82581da44 100644 (file)
@@ -274,7 +274,9 @@ static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message)
            msg_info("old-style scan record %c %s", rec_type, start);
        if (rec_type == REC_TYPE_END)
            break;
-       if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_RCPT) {
+       if (rec_type == REC_TYPE_DONE
+           || rec_type == REC_TYPE_RCPT
+           || rec_type == REC_TYPE_DRCP) {
            message->rcpt_unread++;
            continue;
        }
@@ -472,7 +474,7 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            }
            continue;
        }
-       if (rec_type == REC_TYPE_DONE) {
+       if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_DRCP) {
            if (message->rcpt_offset == 0) {
                message->rcpt_unread--;
                if (dsn_orcpt) {
@@ -623,20 +625,42 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
                    myfree(message->encoding);
                message->encoding = mystrdup(value);
            }
+
+           /*
+            * Backwards compatibility. Before Postfix 2.3, the logging
+            * attributes were called client_name, etc. Now they are called
+            * log_client_name. etc., and client_name is used for the actual
+            * client information. To support old queue files we accept both
+            * names for the purpose of logging; the new name overrides the
+            * old one.
+            */
+           else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_NAME) == 0) {
+               if (message->client_name == 0)
+                   message->client_name = mystrdup(value);
+           } else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_ADDR) == 0) {
+               if (message->client_addr == 0)
+                   message->client_addr = mystrdup(value);
+           } else if (strcmp(name, MAIL_ATTR_ACT_PROTO_NAME) == 0) {
+               if (message->client_proto == 0)
+                   message->client_proto = mystrdup(value);
+           } else if (strcmp(name, MAIL_ATTR_ACT_HELO_NAME) == 0) {
+               if (message->client_helo == 0)
+                   message->client_helo = mystrdup(value);
+           }
            /* Original client attributes. */
-           else if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
+           else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_NAME) == 0) {
                if (message->client_name != 0)
                    myfree(message->client_name);
                message->client_name = mystrdup(value);
-           } else if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_ADDR) == 0) {
                if (message->client_addr != 0)
                    myfree(message->client_addr);
                message->client_addr = mystrdup(value);
-           } else if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_LOG_PROTO_NAME) == 0) {
                if (message->client_proto != 0)
                    myfree(message->client_proto);
                message->client_proto = mystrdup(value);
-           } else if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_LOG_HELO_NAME) == 0) {
                if (message->client_helo != 0)
                    myfree(message->client_helo);
                message->client_helo = mystrdup(value);
@@ -1316,7 +1340,7 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
 QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
                                         int qflags)
 {
-    char   *myname = "qmgr_message_alloc";
+    const char *myname = "qmgr_message_alloc";
     QMGR_MESSAGE *message;
 
     if (msg_verbose)
@@ -1378,7 +1402,7 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
 
 QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
 {
-    char   *myname = "qmgr_message_realloc";
+    const char *myname = "qmgr_message_realloc";
 
     /*
      * Sanity checks.
index a4e55f0585f3a351277574b9474e97e7d867883e..e68f80348c861047f72109a85610a682db773501 100644 (file)
@@ -57,7 +57,7 @@
 void    qmgr_move(const char *src_queue, const char *dst_queue,
                          time_t time_stamp)
 {
-    char   *myname = "qmgr_move";
+    const char *myname = "qmgr_move";
     SCAN_DIR *queue_dir;
     char   *queue_id;
     struct utimbuf tbuf;
index 9b61e64e521d4c904d5f679c92fde13d5855ce89..75ee14d5bd315427d585ac65d4c067a1f4c7fb78 100644 (file)
@@ -86,7 +86,7 @@ QMGR_PEER *qmgr_peer_create(QMGR_JOB *job, QMGR_QUEUE *queue)
 
 void    qmgr_peer_free(QMGR_PEER *peer)
 {
-    char   *myname = "qmgr_peer_free";
+    const char *myname = "qmgr_peer_free";
     QMGR_JOB *job = peer->job;
     QMGR_QUEUE *queue = peer->queue;
 
index eeb886ae8c960a051515fb0c7366f6af8d4626f1..414de5836533c938947c40ec7dfa109c4454bbd2 100644 (file)
@@ -120,7 +120,7 @@ static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
 
 void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 {
-    char   *myname = "qmgr_queue_unthrottle";
+    const char *myname = "qmgr_queue_unthrottle";
     QMGR_TRANSPORT *transport = queue->transport;
 
     if (msg_verbose)
@@ -154,7 +154,7 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 
 void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
 {
-    char   *myname = "qmgr_queue_throttle";
+    const char *myname = "qmgr_queue_throttle";
 
     /*
      * Sanity checks.
@@ -190,7 +190,7 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
 
 void    qmgr_queue_done(QMGR_QUEUE *queue)
 {
-    char   *myname = "qmgr_queue_done";
+    const char *myname = "qmgr_queue_done";
     QMGR_TRANSPORT *transport = queue->transport;
 
     /*
index e0d9fc079abfe478df28c5a686680546156ecb34..37a3ce8ded3edc7abfebe4d62bdecc56c0ddc879 100644 (file)
@@ -76,7 +76,7 @@
 
 static void qmgr_scan_start(QMGR_SCAN *scan_info)
 {
-    char   *myname = "qmgr_scan_start";
+    const char *myname = "qmgr_scan_start";
 
     /*
      * Sanity check.
index 14a4593ac238bcdfb855357bc219fc146cb8c45a..ce4de744f534899bad1817fa1287b0d64762bb9e 100644 (file)
@@ -121,7 +121,7 @@ static void qmgr_transport_unthrottle_wrapper(int unused_event, char *context)
 
 void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
 {
-    char   *myname = "qmgr_transport_unthrottle";
+    const char *myname = "qmgr_transport_unthrottle";
 
     /*
      * This routine runs after expiration of the timer set by
@@ -147,7 +147,7 @@ void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
 
 void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, DSN *dsn)
 {
-    char   *myname = "qmgr_transport_throttle";
+    const char *myname = "qmgr_transport_throttle";
 
     /*
      * We are unable to connect to a deliver process for this type of message
index 5eacb571675e2f1b4f2661848d249fb301ecde70..0254a38756b8a57f9d251b1a368c3af363ce4ed7 100644 (file)
@@ -311,17 +311,31 @@ static void qmqpd_copy_sender(QMQPD_STATE *state)
 
 static void qmqpd_write_attributes(QMQPD_STATE *state)
 {
+
+    /*
+     * Logging attributes, also used for XFORWARD.
+     */
     if (IS_AVAIL_CLIENT_NAME(state->name))
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_CLIENT_NAME, state->name);
+                   MAIL_ATTR_LOG_CLIENT_NAME, state->name);
     if (IS_AVAIL_CLIENT_ADDR(state->addr))
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_CLIENT_ADDR, state->rfc_addr);
+                   MAIL_ATTR_LOG_CLIENT_ADDR, state->rfc_addr);
     if (IS_AVAIL_CLIENT_NAMADDR(state->namaddr))
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_ORIGIN, state->namaddr);
+                   MAIL_ATTR_LOG_ORIGIN, state->namaddr);
+    rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+               MAIL_ATTR_LOG_PROTO_NAME, state->protocol);
+
+    /*
+     * Provenance attributes for Milter policy etc.
+     */
+    rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+               MAIL_ATTR_ACT_CLIENT_NAME, state->name);
     rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-               MAIL_ATTR_PROTO_NAME, state->protocol);
+               MAIL_ATTR_ACT_CLIENT_ADDR, state->rfc_addr);
+    rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u",
+               MAIL_ATTR_ACT_CLIENT_AF, state->addr_family);
 }
 
 /* qmqpd_copy_recipients - copy message recipients */
@@ -521,6 +535,9 @@ static void qmqpd_send_status(QMQPD_STATE *state)
     if (state->err == CLEANUP_STAT_OK) {
        qmqpd_reply(state, DONT_LOG, QMQPD_STAT_OK,
                    "Ok: queued as %s", state->queue_id);
+    } else if ((state->err & CLEANUP_STAT_DEFER) != 0) {
+       qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
+                   "Error: %s", STR(state->why_rejected));
     } else if ((state->err & CLEANUP_STAT_BAD) != 0) {
        qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
                    "Error: internal error %d", state->err);
index 01d89f710fecd228886a453a460c5f28ce13c868..40fde59b4c1797d4404a2de0ab7e2f6c32eb6138 100644 (file)
@@ -37,6 +37,7 @@ typedef struct {
     char   *addr;                      /* client IP address */
     char   *namaddr;                   /* name[addr] */
     char   *rfc_addr;                  /* RFC 2821 client IP address */
+    int     addr_family;               /* address family */
     char   *queue_id;                  /* queue file ID */
     VSTREAM *cleanup;                  /* cleanup server */
     MAIL_STREAM *dest;                 /* cleanup server */
index a42e184d2083e4b9cfcc5d131cf4f14160fa6560..9d084e56dbd06a203b48a1cca852cb363065d88c 100644 (file)
@@ -72,7 +72,7 @@
 
 void    qmqpd_peer_init(QMQPD_STATE *state)
 {
-    char   *myname = "qmqpd_peer_init";
+    const char *myname = "qmqpd_peer_init";
     struct sockaddr_storage ss;
     struct sockaddr *sa;
     SOCKADDR_SIZE sa_length;
@@ -95,6 +95,7 @@ void    qmqpd_peer_init(QMQPD_STATE *state)
        state->name = mystrdup(CLIENT_NAME_UNKNOWN);
        state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
        state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+       state->addr_family = AF_UNSPEC;
     }
 
     /*
@@ -145,6 +146,7 @@ void    qmqpd_peer_init(QMQPD_STATE *state)
 
                state->addr = mystrdup(colonp + 1);
                state->rfc_addr = mystrdup(colonp + 1);
+               state->addr_family = AF_INET;
                aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0);
                if (aierr)
                    msg_fatal("%s: cannot convert %s from string to binary: %s",
@@ -168,6 +170,7 @@ void    qmqpd_peer_init(QMQPD_STATE *state)
                state->addr = mystrdup(client_addr.buf);
                state->rfc_addr =
                    concatenate(IPV6_COL, client_addr.buf, (char *) 0);
+               state->addr_family = sa->sa_family;
            }
        }
 
@@ -179,6 +182,7 @@ void    qmqpd_peer_init(QMQPD_STATE *state)
        {
            state->addr = mystrdup(client_addr.buf);
            state->rfc_addr = mystrdup(client_addr.buf);
+           state->addr_family = sa->sa_family;
        }
 
        /*
@@ -241,6 +245,7 @@ void    qmqpd_peer_init(QMQPD_STATE *state)
        state->name = mystrdup("localhost");
        state->addr = mystrdup("127.0.0.1");    /* XXX bogus. */
        state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */
+       state->addr_family = AF_UNSPEC;
     }
 
     /*
index 3bf1a67e443c7d5bdeee7f1d5c1455b917bf6761..f7cc4b7ae476c1550028afee16c62912958b516c 100644 (file)
@@ -904,7 +904,7 @@ int     main(int argc, char **argv)
     char   *qtime = 0;
     const char *errstr;
     uid_t   uid;
-    char   *rewrite_context = MAIL_ATTR_RWR_LOCAL;
+    const char *rewrite_context = MAIL_ATTR_RWR_LOCAL;
     int     dsn_notify = 0;
     const char *dsn_envid = 0;
     int     saved_optind;
index a248fe5b7ae91cb7de0c9dd255047fe99e2e0981..8b781b4a175c10f4cac9cdebecaba9dc1a8b2971 100644 (file)
@@ -91,6 +91,41 @@ depend: $(MAKES)
        @$(EXPORT) make -f Makefile.in Makefile 1>&2
 
 # do not edit below this line - it is generated by 'make depend'
+legacy.o: ../../include/msg.h
+legacy.o: ../../include/stringops.h
+legacy.o: ../../include/sys_defs.h
+legacy.o: ../../include/vbuf.h
+legacy.o: ../../include/vstream.h
+legacy.o: ../../include/vstring.h
+legacy.o: ../../include/vstring_vstream.h
+legacy.o: legacy.c
+levels.o: ../../include/argv.h
+levels.o: ../../include/attr.h
+levels.o: ../../include/deliver_request.h
+levels.o: ../../include/dict.h
+levels.o: ../../include/dsn.h
+levels.o: ../../include/dsn_buf.h
+levels.o: ../../include/htable.h
+levels.o: ../../include/maps.h
+levels.o: ../../include/match_list.h
+levels.o: ../../include/match_ops.h
+levels.o: ../../include/msg.h
+levels.o: ../../include/msg_stats.h
+levels.o: ../../include/name_mask.h
+levels.o: ../../include/recipient_list.h
+levels.o: ../../include/resolve_clnt.h
+levels.o: ../../include/scache.h
+levels.o: ../../include/string_list.h
+levels.o: ../../include/stringops.h
+levels.o: ../../include/sys_defs.h
+levels.o: ../../include/tls.h
+levels.o: ../../include/tok822.h
+levels.o: ../../include/vbuf.h
+levels.o: ../../include/vstream.h
+levels.o: ../../include/vstring.h
+levels.o: ../../include/vstring_vstream.h
+levels.o: levels.c
+levels.o: smtp.h
 lmtp_params.o: lmtp_params.c
 smtp.o: ../../include/argv.h
 smtp.o: ../../include/attr.h
@@ -111,6 +146,7 @@ smtp.o: ../../include/match_ops.h
 smtp.o: ../../include/msg.h
 smtp.o: ../../include/msg_stats.h
 smtp.o: ../../include/mymalloc.h
+smtp.o: ../../include/name_code.h
 smtp.o: ../../include/name_mask.h
 smtp.o: ../../include/recipient_list.h
 smtp.o: ../../include/resolve_clnt.h
@@ -146,6 +182,7 @@ smtp_addr.o: ../../include/msg.h
 smtp_addr.o: ../../include/msg_stats.h
 smtp_addr.o: ../../include/myaddrinfo.h
 smtp_addr.o: ../../include/mymalloc.h
+smtp_addr.o: ../../include/name_mask.h
 smtp_addr.o: ../../include/own_inet_addr.h
 smtp_addr.o: ../../include/recipient_list.h
 smtp_addr.o: ../../include/resolve_clnt.h
@@ -253,6 +290,7 @@ smtp_map11.o: ../../include/match_list.h
 smtp_map11.o: ../../include/match_ops.h
 smtp_map11.o: ../../include/msg.h
 smtp_map11.o: ../../include/msg_stats.h
+smtp_map11.o: ../../include/name_mask.h
 smtp_map11.o: ../../include/quote_822_local.h
 smtp_map11.o: ../../include/quote_flags.h
 smtp_map11.o: ../../include/recipient_list.h
@@ -297,6 +335,7 @@ smtp_proto.o: ../../include/msg.h
 smtp_proto.o: ../../include/msg_stats.h
 smtp_proto.o: ../../include/mymalloc.h
 smtp_proto.o: ../../include/name_code.h
+smtp_proto.o: ../../include/name_mask.h
 smtp_proto.o: ../../include/off_cvt.h
 smtp_proto.o: ../../include/quote_821_local.h
 smtp_proto.o: ../../include/quote_822_local.h
@@ -337,6 +376,7 @@ smtp_rcpt.o: ../../include/match_ops.h
 smtp_rcpt.o: ../../include/msg.h
 smtp_rcpt.o: ../../include/msg_stats.h
 smtp_rcpt.o: ../../include/mymalloc.h
+smtp_rcpt.o: ../../include/name_mask.h
 smtp_rcpt.o: ../../include/recipient_list.h
 smtp_rcpt.o: ../../include/resolve_clnt.h
 smtp_rcpt.o: ../../include/scache.h
@@ -365,6 +405,7 @@ smtp_reuse.o: ../../include/match_ops.h
 smtp_reuse.o: ../../include/msg.h
 smtp_reuse.o: ../../include/msg_stats.h
 smtp_reuse.o: ../../include/mymalloc.h
+smtp_reuse.o: ../../include/name_mask.h
 smtp_reuse.o: ../../include/recipient_list.h
 smtp_reuse.o: ../../include/resolve_clnt.h
 smtp_reuse.o: ../../include/scache.h
@@ -394,6 +435,7 @@ smtp_sasl_glue.o: ../../include/match_ops.h
 smtp_sasl_glue.o: ../../include/msg.h
 smtp_sasl_glue.o: ../../include/msg_stats.h
 smtp_sasl_glue.o: ../../include/mymalloc.h
+smtp_sasl_glue.o: ../../include/name_mask.h
 smtp_sasl_glue.o: ../../include/recipient_list.h
 smtp_sasl_glue.o: ../../include/resolve_clnt.h
 smtp_sasl_glue.o: ../../include/scache.h
@@ -424,6 +466,7 @@ smtp_sasl_proto.o: ../../include/match_ops.h
 smtp_sasl_proto.o: ../../include/msg.h
 smtp_sasl_proto.o: ../../include/msg_stats.h
 smtp_sasl_proto.o: ../../include/mymalloc.h
+smtp_sasl_proto.o: ../../include/name_mask.h
 smtp_sasl_proto.o: ../../include/recipient_list.h
 smtp_sasl_proto.o: ../../include/resolve_clnt.h
 smtp_sasl_proto.o: ../../include/scache.h
@@ -455,6 +498,8 @@ smtp_session.o: ../../include/mime_state.h
 smtp_session.o: ../../include/msg.h
 smtp_session.o: ../../include/msg_stats.h
 smtp_session.o: ../../include/mymalloc.h
+smtp_session.o: ../../include/name_code.h
+smtp_session.o: ../../include/name_mask.h
 smtp_session.o: ../../include/recipient_list.h
 smtp_session.o: ../../include/resolve_clnt.h
 smtp_session.o: ../../include/scache.h
@@ -463,6 +508,7 @@ smtp_session.o: ../../include/stringops.h
 smtp_session.o: ../../include/sys_defs.h
 smtp_session.o: ../../include/tls.h
 smtp_session.o: ../../include/tok822.h
+smtp_session.o: ../../include/valid_hostname.h
 smtp_session.o: ../../include/vbuf.h
 smtp_session.o: ../../include/vstream.h
 smtp_session.o: ../../include/vstring.h
@@ -483,6 +529,7 @@ smtp_state.o: ../../include/match_ops.h
 smtp_state.o: ../../include/msg.h
 smtp_state.o: ../../include/msg_stats.h
 smtp_state.o: ../../include/mymalloc.h
+smtp_state.o: ../../include/name_mask.h
 smtp_state.o: ../../include/recipient_list.h
 smtp_state.o: ../../include/resolve_clnt.h
 smtp_state.o: ../../include/scache.h
@@ -541,6 +588,7 @@ smtp_unalias.o: ../../include/match_ops.h
 smtp_unalias.o: ../../include/msg.h
 smtp_unalias.o: ../../include/msg_stats.h
 smtp_unalias.o: ../../include/myaddrinfo.h
+smtp_unalias.o: ../../include/name_mask.h
 smtp_unalias.o: ../../include/recipient_list.h
 smtp_unalias.o: ../../include/resolve_clnt.h
 smtp_unalias.o: ../../include/scache.h
index e0baa460bbd468236454ade210075a3bbd97ec5d..7ca90e6052e66fb6ea824d23dcd9b5b52296fa67 100644 (file)
 #include <vstring_vstream.h>
 #include <stringops.h>
 
+ /*
+  * TLS levels
+  */
+#include <tls.h>
+
  /*
   * Application-specific.
   */
@@ -51,18 +56,18 @@ static void smtp_tls_policy_lookup(int *site_level, const char *lookup)
     if (strcasecmp(lookup, "-")) {
        if (!strcasecmp(lookup, "NONE")) {
            /* NONE overrides MAY or NOTFOUND. */
-           if (*site_level <= SMTP_TLS_LEV_MAY)
-               *site_level = SMTP_TLS_LEV_NONE;
+           if (*site_level <= TLS_LEV_MAY)
+               *site_level = TLS_LEV_NONE;
        } else if (!strcasecmp(lookup, "MAY")) {
            /* MAY overrides NOTFOUND but not NONE. */
-           if (*site_level < SMTP_TLS_LEV_NONE)
-               *site_level = SMTP_TLS_LEV_MAY;
+           if (*site_level < TLS_LEV_NONE)
+               *site_level = TLS_LEV_MAY;
        } else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) {
-           if (*site_level < SMTP_TLS_LEV_ENCRYPT)
-               *site_level = SMTP_TLS_LEV_ENCRYPT;
+           if (*site_level < TLS_LEV_ENCRYPT)
+               *site_level = TLS_LEV_ENCRYPT;
        } else if (!strcasecmp(lookup, "MUST")) {
-           if (*site_level < SMTP_TLS_LEV_VERIFY)
-               *site_level = SMTP_TLS_LEV_VERIFY;
+           if (*site_level < TLS_LEV_VERIFY)
+               *site_level = TLS_LEV_VERIFY;
        } else {
            msg_fatal("unknown TLS policy '%s'", lookup);
        }
@@ -82,17 +87,17 @@ static int policy(const char *host, const char *dest)
      */
     if (var_smtp_enforce_tls)
        global_level = var_smtp_tls_enforce_peername ?
-           SMTP_TLS_LEV_VERIFY : SMTP_TLS_LEV_ENCRYPT;
+           TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
     else
        global_level = var_smtp_use_tls ?
-           SMTP_TLS_LEV_MAY : SMTP_TLS_LEV_NONE;
+           TLS_LEV_MAY : TLS_LEV_NONE;
 
     /*
      * Compute the per-site TLS enforcement level. For compatibility with the
      * original TLS patch, this algorithm is gives equal precedence to host
      * and next-hop policies.
      */
-    site_level = SMTP_TLS_LEV_NOTFOUND;
+    site_level = TLS_LEV_NOTFOUND;
 
     smtp_tls_policy_lookup(&site_level, dest);
     smtp_tls_policy_lookup(&site_level, host);
@@ -112,9 +117,9 @@ static int policy(const char *host, const char *dest)
      * consistently override wildcard policies, and (non-wildcard) per-site
      * policies consistently override global policies.
      */
-    if (site_level == SMTP_TLS_LEV_NOTFOUND
-       || (site_level == SMTP_TLS_LEV_MAY
-           && global_level > SMTP_TLS_LEV_MAY))
+    if (site_level == TLS_LEV_NOTFOUND
+       || (site_level == TLS_LEV_MAY
+           && global_level > TLS_LEV_MAY))
        tls_level = global_level;
     else
        tls_level = site_level;
@@ -140,13 +145,13 @@ static void set_global_policy(const char *global)
 
 static const char *print_policy(int level)
 {
-    if (level == SMTP_TLS_LEV_VERIFY)
+    if (level == TLS_LEV_VERIFY)
        return ("must");
-    if (level == SMTP_TLS_LEV_ENCRYPT)
+    if (level == TLS_LEV_ENCRYPT)
        return ("must_nopeermatch");
-    if (level == SMTP_TLS_LEV_MAY)
+    if (level == TLS_LEV_MAY)
        return ("may");
-    if (level == SMTP_TLS_LEV_NONE)
+    if (level == TLS_LEV_NONE)
        return ("none");
     msg_panic("unknown policy level %d", level);
 }
index 980b4fd0eba8694d4ec5cb561345c157945fbc95..b6e04f66dc11f9e13f8a8e62cd15ceae73f50ec9 100644 (file)
@@ -7,7 +7,26 @@
        VAR_LMTP_SASL_PATH, DEF_LMTP_SASL_PATH, &var_smtp_sasl_path, 0, 0,
 #ifdef USE_TLS
        VAR_LMTP_SASL_TLS_OPTS, DEF_LMTP_SASL_TLS_OPTS, &var_smtp_sasl_tls_opts, 0, 0,
+#ifdef SNAPSHOT                                /* XXX: Not yet */
        VAR_LMTP_SASL_TLSV_OPTS, DEF_LMTP_SASL_TLSV_OPTS, &var_smtp_sasl_tlsv_opts, 0, 0,
+#endif
+       VAR_LMTP_TLS_CERT_FILE, DEF_LMTP_TLS_CERT_FILE, &var_smtp_tls_cert_file, 0, 0,
+       VAR_LMTP_TLS_KEY_FILE, DEF_LMTP_TLS_KEY_FILE, &var_smtp_tls_key_file, 0, 0,
+       VAR_LMTP_TLS_DCERT_FILE, DEF_LMTP_TLS_DCERT_FILE, &var_smtp_tls_dcert_file, 0, 0,
+       VAR_LMTP_TLS_DKEY_FILE, DEF_LMTP_TLS_DKEY_FILE, &var_smtp_tls_dkey_file, 0, 0,
+       VAR_LMTP_TLS_CA_FILE, DEF_LMTP_TLS_CA_FILE, &var_smtp_tls_CAfile, 0, 0,
+       VAR_LMTP_TLS_CA_PATH, DEF_LMTP_TLS_CA_PATH, &var_smtp_tls_CApath, 0, 0,
+       VAR_LMTP_TLS_CIPHERS, DEF_LMTP_TLS_CIPHERS, &var_smtp_tls_ciphers, 1, 0,
+       VAR_LMTP_TLS_EXCL_CIPH, DEF_LMTP_TLS_EXCL_CIPH, &var_smtp_tls_excl_ciph, 0, 0,
+       VAR_LMTP_TLS_MAND_EXCL, DEF_LMTP_TLS_MAND_EXCL, &var_smtp_tls_mand_excl, 0, 0,
+       VAR_TLS_HIGH_CLIST, DEF_TLS_HIGH_CLIST, &var_tls_high_clist, 1, 0,
+       VAR_TLS_MEDIUM_CLIST, DEF_TLS_MEDIUM_CLIST, &var_tls_medium_clist, 1, 0,
+       VAR_TLS_LOW_CLIST, DEF_TLS_LOW_CLIST, &var_tls_low_clist, 1, 0,
+       VAR_TLS_EXPORT_CLIST, DEF_TLS_EXPORT_CLIST, &var_tls_export_clist, 1, 0,
+       VAR_TLS_NULL_CLIST, DEF_TLS_NULL_CLIST, &var_tls_null_clist, 1, 0,
+       VAR_LMTP_TLS_PROTO, DEF_LMTP_TLS_PROTO, &var_smtp_tls_protocols, 0, 0,
+       VAR_LMTP_TLS_VFY_CMATCH, DEF_LMTP_TLS_VFY_CMATCH, &var_smtp_tls_vfy_cmatch, 1, 0,
+       VAR_LMTP_TLS_SEC_CMATCH, DEF_LMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
 #endif
        VAR_LMTP_SASL_MECHS, DEF_LMTP_SASL_MECHS, &var_smtp_sasl_mechs, 0, 0,
        VAR_LMTP_SASL_TYPE, DEF_LMTP_SASL_TYPE, &var_smtp_sasl_type, 1, 0,
@@ -20,6 +39,8 @@
        VAR_LMTP_EHLO_DIS_WORDS, DEF_LMTP_EHLO_DIS_WORDS, &var_smtp_ehlo_dis_words, 0, 0,
        VAR_LMTP_EHLO_DIS_MAPS, DEF_LMTP_EHLO_DIS_MAPS, &var_smtp_ehlo_dis_maps, 0, 0,
        VAR_LMTP_TLS_PER_SITE, DEF_LMTP_TLS_PER_SITE, &var_smtp_tls_per_site, 0, 0,
+       VAR_LMTP_TLS_LEVEL, DEF_LMTP_TLS_LEVEL, &var_smtp_tls_level, 0, 0,
+       VAR_LMTP_TLS_POLICY, DEF_LMTP_TLS_POLICY, &var_smtp_tls_policy, 0, 0,
        VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
        VAR_LMTP_GENERIC_MAPS, DEF_LMTP_GENERIC_MAPS, &var_smtp_generic_maps, 0, 0,
        VAR_LMTP_TCP_PORT, DEF_LMTP_TCP_PORT, &var_lmtp_tcp_port, 0, 0,
@@ -52,6 +73,8 @@
        VAR_LMTP_MXSESS_LIMIT, DEF_LMTP_MXSESS_LIMIT, &var_smtp_mxsess_limit, 0, 0,
 #ifdef USE_TLS
        VAR_LMTP_TLS_SCERT_VD, DEF_LMTP_TLS_SCERT_VD, &var_smtp_tls_scert_vd, 0, 0,
+       VAR_LMTP_TLS_LOGLEVEL, DEF_LMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0,
+       VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 1, 0,
 #endif
        0,
     };
index cb50be0122b0e47eebfede5a3837da2960262839..22a5cd3f5b4043678d9138fe6f2c2e6ff7f8942d 100644 (file)
 /* .fi
 /*     Detailed information about STARTTLS configuration may be found
 /*     in the TLS_README document.
-/* .IP "\fBsmtp_use_tls (no)\fR"
-/*     Opportunistic mode: use TLS when a remote SMTP server announces
-/*     STARTTLS support, otherwise send the mail in the clear.
-/* .IP "\fBsmtp_enforce_tls (no)\fR"
-/*     Enforcement mode: require that remote SMTP servers use TLS
-/*     encryption, and never send mail in the clear.
+/* .IP "\fBsmtp_tls_security_level (empty)\fR"
+/*     The default SMTP TLS security level for all destinations; when
+/*     a non-empty value is specified, this overrides the obsolete parameters
+/*     smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
 /* .IP "\fBsmtp_sasl_tls_security_options ($smtp_sasl_security_options)\fR"
 /*     The SASL authentication security options that the Postfix SMTP
 /*     client uses for TLS encrypted SMTP sessions.
 /*     certificate.
 /* .IP "\fBsmtp_tls_cert_file (empty)\fR"
 /*     File with the Postfix SMTP client RSA certificate in PEM format.
-/* .IP "\fBsmtp_tls_cipherlist (empty)\fR"
-/*     Controls the Postfix SMTP client TLS cipher selection scheme.
+/* .IP "\fBsmtp_tls_mandatory_ciphers (medium)\fR"
+/*     The minimum SMTP client TLS cipher grade that is strong enough to
+/*     be used with the "encrypt" security level and higher.
+/* .IP "\fBsmtp_tls_exclude_ciphers (empty)\fR"
+/*     List of ciphers or cipher types to exclude from the SMTP client cipher
+/*     list at all security levels.
+/* .IP "\fBsmtp_tls_mandatory_exclude_ciphers (empty)\fR"
+/*     List of ciphers or cipher types to exclude from the SMTP client
+/*     cipher list at the mandatory TLS security levels: "encrypt", "verify"
+/*     and "secure".
 /* .IP "\fBsmtp_tls_dcert_file (empty)\fR"
 /*     File with the Postfix SMTP client DSA certificate in PEM format.
 /* .IP "\fBsmtp_tls_dkey_file ($smtp_tls_dcert_file)\fR"
 /*     File with the Postfix SMTP client DSA private key in PEM format.
-/* .IP "\fBsmtp_tls_enforce_peername (yes)\fR"
-/*     When TLS encryption is enforced, require that the remote SMTP
-/*     server hostname matches the information in the remote SMTP server
-/*     certificate.
 /* .IP "\fBsmtp_tls_key_file ($smtp_tls_cert_file)\fR"
 /*     File with the Postfix SMTP client RSA private key in PEM format.
 /* .IP "\fBsmtp_tls_loglevel (0)\fR"
 /* .IP "\fBsmtp_tls_note_starttls_offer (no)\fR"
 /*     Log the hostname of a remote SMTP server that offers STARTTLS,
 /*     when TLS is not already enabled for that server.
-/* .IP "\fBsmtp_tls_per_site (empty)\fR"
-/*     Optional lookup tables with the Postfix SMTP client TLS usage
-/*     policy by next-hop destination and by remote SMTP server hostname.
+/* .IP "\fBsmtp_tls_policy_maps (empty)\fR"
+/*     Optional lookup tables with the Postfix SMTP client TLS security
+/*     policy by next-hop destination; when a non-empty value is specified,
+/*     this overrides the obsolete smtp_tls_per_site parameter.
+/* .IP "\fBsmtp_tls_mandatory_protocols (SSLv3, TLSv1)\fR"
+/*     List of TLS protocol versions that are secure enough to be used
+/*     with the "encrypt" security level and higher.
 /* .IP "\fBsmtp_tls_scert_verifydepth (5)\fR"
 /*     The verification depth for remote SMTP server certificates.
+/* .IP "\fBsmtp_tls_secure_cert_match (nexthop, dot-nexthop)\fR"
+/*     The server certificate peername verification method for the
+/*     "secure" TLS security level.
 /* .IP "\fBsmtp_tls_session_cache_database (empty)\fR"
 /*     Name of the file containing the optional Postfix SMTP client
 /*     TLS session cache.
 /* .IP "\fBsmtp_tls_session_cache_timeout (3600s)\fR"
 /*     The expiration time of Postfix SMTP client TLS session cache
 /*     information.
+/* .IP "\fBsmtp_tls_verify_cert_match (hostname)\fR"
+/*     The server certificate peername verification method for the
+/*     "verify" TLS security level.
 /* .IP "\fBtls_daemon_random_bytes (32)\fR"
 /*     The number of pseudo-random bytes that an \fBsmtp\fR(8) or \fBsmtpd\fR(8)
 /*     process requests from the \fBtlsmgr\fR(8) server in order to seed its
 /*     internal pseudo random number generator (PRNG).
+/* .IP "\fBtls_high_cipherlist (!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "HIGH" grade ciphers.
+/* .IP "\fBtls_medium_cipherlist (!EXPORT:!LOW:ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers.
+/* .IP "\fBtls_low_cipherlist (!EXPORT:ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "LOW" or higher grade ciphers.
+/* .IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
+/* .IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+/*     The OpenSSL cipherlist for "NULL" grade ciphers that provide
+/*     authentication without encryption.
 /* .PP
-/*     Available in Postfix version 2.3 and later:
+/*     Available in Postfix version 2.4 and later:
 /* .IP "\fBsmtp_sasl_tls_verified_security_options ($smtp_sasl_tls_security_options)\fR"
 /*     The SASL authentication security options that the Postfix SMTP
 /*     client uses for TLS encrypted SMTP sessions with a verified server
 /*     certificate.
+/* OBSOLETE STARTTLS CONTROLS
+/* .ad
+/* .fi
+/*     The following configuration parameters exist for compatibility
+/*     with Postfix versions before 2.3. Support for these will
+/*     be removed in a future release.
+/* .IP "\fBsmtp_use_tls (no)\fR"
+/*     Opportunistic mode: use TLS when a remote SMTP server announces
+/*     STARTTLS support, otherwise send the mail in the clear.
+/* .IP "\fBsmtp_enforce_tls (no)\fR"
+/*     Enforcement mode: require that remote SMTP servers use TLS
+/*     encryption, and never send mail in the clear.
+/* .IP "\fBsmtp_tls_enforce_peername (yes)\fR"
+/*     When TLS encryption is enforced, require that the remote SMTP
+/*     server hostname matches the information in the remote SMTP server
+/*     certificate.
+/* .IP "\fBsmtp_tls_per_site (empty)\fR"
+/*     Optional lookup tables with the Postfix SMTP client TLS usage
+/*     policy by next-hop destination and by remote SMTP server hostname.
 /* RESOURCE AND RATE CONTROLS
 /* .ad
 /* .fi
 #include <msg.h>
 #include <mymalloc.h>
 #include <name_mask.h>
+#include <name_code.h>
 
 /* Global library. */
 
@@ -612,17 +655,33 @@ bool    var_smtp_cache_demand;
 char   *var_smtp_ehlo_dis_words;
 char   *var_smtp_ehlo_dis_maps;
 
+char   *var_smtp_tls_level;
 bool    var_smtp_use_tls;
 bool    var_smtp_enforce_tls;
 char   *var_smtp_tls_per_site;
+char   *var_smtp_tls_policy;
 
 #ifdef USE_TLS
-int     var_smtp_starttls_tmout;
 char   *var_smtp_sasl_tls_opts;
 char   *var_smtp_sasl_tlsv_opts;
+int     var_smtp_starttls_tmout;
+char   *var_smtp_tls_CAfile;
+char   *var_smtp_tls_CApath;
+char   *var_smtp_tls_cert_file;
+char   *var_smtp_tls_ciphers;
+char   *var_smtp_tls_excl_ciph;
+char   *var_smtp_tls_mand_excl;
+char   *var_smtp_tls_dcert_file;
+char   *var_smtp_tls_dkey_file;
 bool    var_smtp_tls_enforce_peername;
-int     var_smtp_tls_scert_vd;
+char   *var_smtp_tls_key_file;
+int     var_smtp_tls_loglevel;
 bool    var_smtp_tls_note_starttls_offer;
+char   *var_smtp_tls_protocols;
+char   *var_smtp_tls_sec_cmatch;
+int     var_smtp_tls_scert_vd;
+char   *var_smtp_tls_vfy_cmatch;
+int     var_tls_daemon_rand_bytes;
 
 #endif
 
@@ -652,6 +711,8 @@ SSL_CTX *smtp_tls_ctx;
 
 #endif
 
+extern NAME_CODE smtp_tls_levels[];    /* smtp_session.c name_code table */
+
 /* deliver_message - deliver message with extreme prejudice */
 
 static int deliver_message(const char *service, DELIVER_REQUEST *request)
@@ -787,9 +848,29 @@ static void pre_init(char *unused_name, char **unused_argv)
     /*
      * Initialize the TLS data before entering the chroot jail
      */
-    if (var_smtp_use_tls || var_smtp_enforce_tls || var_smtp_tls_per_site[0]) {
+    if (name_code(smtp_tls_levels, NAME_CODE_FLAG_NONE,
+                 var_smtp_tls_level) > TLS_LEV_NONE ||
+       var_smtp_use_tls || var_smtp_enforce_tls ||
+       var_smtp_tls_per_site[0] || var_smtp_tls_policy[0]) {
 #ifdef USE_TLS
-       smtp_tls_ctx = tls_client_init(var_smtp_tls_scert_vd);
+       tls_client_init_props props;
+
+       /*
+        * We get stronger type safety and a cleaner interface by combining
+        * the various parameters into a single tls_client_props structure.
+        */
+       props.log_level = var_smtp_tls_loglevel;
+       props.verifydepth = var_smtp_tls_scert_vd;
+       props.cache_type = strcmp(var_procname, "smtp") == 0 ?
+           TLS_MGR_SCACHE_SMTP : TLS_MGR_SCACHE_LMTP;
+       props.cert_file = var_smtp_tls_cert_file;
+       props.key_file = var_smtp_tls_key_file;
+       props.dcert_file = var_smtp_tls_dcert_file;
+       props.dkey_file = var_smtp_tls_dkey_file;
+       props.CAfile = var_smtp_tls_CAfile;
+       props.CApath = var_smtp_tls_CApath;
+
+       smtp_tls_ctx = tls_client_init(&props);
        smtp_tls_list_init();
 #else
        msg_warn("TLS has been selected, but TLS support is not compiled in");
index 4ec7dc55a68908bfb9aa3b56006cd9cc11985a37..648737377f9d3f41526a6dd22f69a3255c506a97 100644 (file)
@@ -34,9 +34,7 @@
  /*
   * Postfix TLS library.
   */
-#ifdef USE_TLS
 #include <tls.h>
-#endif
 
  /*
   * State information associated with each SMTP delivery request.
@@ -142,22 +140,6 @@ typedef struct SMTP_STATE {
 #define SMTP_MISC_FLAG_FINAL_SERVER    (1<<5)
 #define SMTP_MISC_FLAG_CONN_CACHE      (1<<6)
 
- /*
-  * TLS enforcement level. Actual TLS policies will be NONE or higher.
-  * 
-  * There are two pseudo levels: NOTFOUND is a sentinel value for the ease of
-  * implementation; MAY is a wild-card that indicates "anything goes".
-  * 
-  * Non pseudo levels can also be used to indicate the actual security level of
-  * a session.
-  */
-#define SMTP_TLS_LEV_NOTFOUND          (-1)    /* sentinel */
-#define SMTP_TLS_LEV_NONE              0       /* plain-text only */
-#define SMTP_TLS_LEV_MAY               1       /* wildcard */
-#define SMTP_TLS_LEV_ENCRYPT           2       /* encrypted connection */
-#define SMTP_TLS_LEV_VERIFY            3       /* certificate verified */
-#define SMTP_TLS_LEV_STRICT            4       /* "secure" verification */
-
  /*
   * smtp.c
   */
@@ -218,6 +200,7 @@ typedef struct SMTP_SESSION {
 
     time_t  expire_time;               /* session reuse expiration time */
     int     reuse_count;               /* # of times reused (for logging) */
+    int     dead;                      /* No further I/O allowed */
 
 #ifdef USE_SASL_AUTH
     char   *sasl_mechanism_list;       /* server mechanism list */
@@ -228,11 +211,16 @@ typedef struct SMTP_SESSION {
 #endif
 
     /*
-     * TLS related state.
+     * TLS related state, don't forget to initialize in session_tls_init()!
      */
 #ifdef USE_TLS
-    int     tls_level;                 /* TLS enforcement level */
     TLScontext_t *tls_context;         /* TLS session state */
+    char   *tls_nexthop;               /* Nexthop domain for cert checks */
+    int     tls_level;                 /* TLS enforcement level */
+    int     tls_retry_plain;           /* Try plain when TLS handshake fails */
+    int     tls_protocols;             /* Acceptable SSL protocols (mask) */
+    char   *tls_cipherlist;            /* Acceptable SSL ciphers */
+    char   *tls_certmatch;             /* Certificate match patterns */
 #endif
 
     SMTP_STATE *state;                 /* back link */
@@ -311,14 +299,19 @@ extern int smtp_quit(SMTP_STATE *);
   * connections and other reasons why connections cannot be cached.
   */
 #define THIS_SESSION_IS_CACHED \
-       (session->expire_time > 0)
+       (!THIS_SESSION_IS_DEAD && session->expire_time > 0)
 
 #define THIS_SESSION_IS_EXPIRED \
        (THIS_SESSION_IS_CACHED \
            && session->expire_time < vstream_ftime(session->stream))
 
 #define THIS_SESSION_IS_BAD \
-       (session->expire_time < 0)
+       (!THIS_SESSION_IS_DEAD && session->expire_time < 0)
+
+#define THIS_SESSION_IS_DEAD \
+       (session->dead != 0)
+
+ /* Bring the bad news. */
 
 #define DONT_CACHE_THIS_SESSION \
        (session->expire_time = 0)
@@ -326,6 +319,14 @@ extern int smtp_quit(SMTP_STATE *);
 #define DONT_CACHE_BAD_SESSION \
        (session->expire_time = -1)
 
+#define DONT_USE_DEAD_SESSION \
+       (session->dead = 1)
+
+ /* Initialization. */
+
+#define USE_NEWBORN_SESSION \
+       (session->dead = 0)
+
 #define CACHE_THIS_SESSION_UNTIL(when) \
        (session->expire_time = (when))
 
index 03dfb0f8650e155c8e520dd3987490c68ed0726f..ea4244dc2a6fd6048b79e6b8954aaf62b1db3c50 100644 (file)
@@ -123,7 +123,7 @@ static void smtp_print_addr(char *what, DNS_RR *addr_list)
 static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
                                     DSN_BUF *why)
 {
-    char   *myname = "smtp_addr_one";
+    const char *myname = "smtp_addr_one";
     DNS_RR *addr = 0;
     DNS_RR *rr;
     int     aierr;
@@ -270,7 +270,7 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
 
 static DNS_RR *smtp_find_self(DNS_RR *addr_list)
 {
-    char   *myname = "smtp_find_self";
+    const char *myname = "smtp_find_self";
     INET_ADDR_LIST *self;
     INET_ADDR_LIST *proxy;
     DNS_RR *addr;
index 7b68b157cbab9788c30035308909b6413004fd99..e5ce591853ee10ff72eeeba19ec9a521c2c1968a 100644 (file)
@@ -380,7 +380,7 @@ static void print_line(const char *str, int len, int indent, char *context)
 
 void    smtp_chat_notify(SMTP_SESSION *session)
 {
-    char   *myname = "smtp_chat_notify";
+    const char *myname = "smtp_chat_notify";
     VSTREAM *notice;
     char  **cpp;
 
index add5c570234a5e96a6e9a86814a15e5a1f1a0601..41880cffb622990ab336cef7e8ec4e47eb8a4825 100644 (file)
@@ -121,7 +121,7 @@ static SMTP_SESSION *smtp_connect_unix(const char *addr,
                                               DSN_BUF *why,
                                               int sess_flags)
 {
-    char   *myname = "smtp_connect_unix";
+    const char *myname = "smtp_connect_unix";
     struct sockaddr_un sock_un;
     int     len = strlen(addr);
     int     sock;
@@ -170,7 +170,7 @@ static SMTP_SESSION *smtp_connect_addr(const char *destination, DNS_RR *addr,
                                               unsigned port, DSN_BUF *why,
                                               int sess_flags)
 {
-    char   *myname = "smtp_connect_addr";
+    const char *myname = "smtp_connect_addr";
     struct sockaddr_storage ss;                /* remote */
     struct sockaddr *sa = (struct sockaddr *) & ss;
     SOCKADDR_SIZE salen = sizeof(ss);
@@ -426,6 +426,7 @@ static void smtp_cleanup_session(SMTP_STATE *state)
 
 static void smtp_connect_local(SMTP_STATE *state, const char *path)
 {
+    const char *myname = "smtp_connect_local";
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session;
     DSN_BUF *why = state->why;
@@ -457,21 +458,54 @@ static void smtp_connect_local(SMTP_STATE *state, const char *path)
      */
 #define NO_PORT        0
 
+    /*
+     * Opportunistic TLS for unix domain sockets does not make much sense,
+     * since the channel is private, mere encryption without authentication
+     * is just wasted cycles and opportunity for breakage. Since we are not
+     * willing to retry after TLS handshake failures here, we downgrade "may"
+     * no "none". Nothing is lost, and much waste is avoided.
+     * 
+     * We don't know who is authenticating whom, so if a client cert is
+     * available, "encrypt" may be a sensible policy. Otherwise, we also
+     * downgrade "encrypt" to "none", this time just to avoid waste.
+     */
     if ((state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE) == 0
        || (session = smtp_reuse_addr(state, path, NO_PORT)) == 0)
        session = smtp_connect_unix(path, why, state->misc_flags);
     if ((state->session = session) != 0) {
        session->state = state;
+#ifdef USE_TLS
+       session->tls_nexthop = var_myhostname;  /* for TLS_LEV_SECURE */
+       if (session->tls_level == TLS_LEV_MAY) {
+           msg_warn("%s: opportunistic TLS encryption is not appropriate "
+                    "for unix-domain destinations.", myname);
+           session->tls_level = TLS_LEV_NONE;
+       }
+#endif
        /* All delivery errors bounce or defer. */
        state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
+
+       /*
+        * When a TLS handshake fails, the stream is marked "dead" to avoid
+        * further I/O over a broken channel.
+        */
        if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0
            && smtp_helo(state) != 0) {
-           if (vstream_ferror(session->stream) == 0
+           if (!THIS_SESSION_IS_DEAD
+               && vstream_ferror(session->stream) == 0
                && vstream_feof(session->stream) == 0)
                smtp_quit(state);
        } else {
            smtp_xfer(state);
        }
+
+       /*
+        * With opportunistic TLS disabled we don't expect to be asked to
+        * retry connections without TLS, and so we expect the final server
+        * flag to stay on.
+        */
+       if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0)
+           msg_panic("%s: unix-domain destination not final!", myname);
        smtp_cleanup_session(state);
     }
 }
@@ -618,6 +652,7 @@ static void smtp_connect_remote(SMTP_STATE *state, const char *nexthop,
     char   *dest;
     char  **cpp;
     int     non_fallback_sites;
+    int     retry_plain = 0;
     DSN_BUF *why = state->why;
 
     /*
@@ -805,9 +840,36 @@ static void smtp_connect_remote(SMTP_STATE *state, const char *nexthop,
                if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
                    && next == 0)
                    state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
+#ifdef USE_TLS
+               /* Disable TLS when retrying after a handshake failure */
+               if (retry_plain) {
+                   if (session->tls_level >= TLS_LEV_ENCRYPT)
+                       msg_panic("Plain-text retry wrong for mandatory TLS");
+                   session->tls_level = TLS_LEV_NONE;
+                   retry_plain = 0;
+               }
+               session->tls_nexthop = domain;  /* for TLS_LEV_SECURE */
+#endif
                if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0
                    && smtp_helo(state) != 0) {
-                   if (vstream_ferror(session->stream) == 0
+#ifdef USE_TLS
+
+                   /*
+                    * When an opportunistic TLS handshake fails, try the
+                    * same address again, with TLS disabled.
+                    */
+                   if ((retry_plain = session->tls_retry_plain) != 0) {
+                       --addr_count;
+                       next = addr;
+                   }
+#endif
+
+                   /*
+                    * When a TLS handshake fails, the stream is marked
+                    * "dead" to avoid further I/O over a broken channel.
+                    */
+                   if (!THIS_SESSION_IS_DEAD
+                       && vstream_ferror(session->stream) == 0
                        && vstream_feof(session->stream) == 0)
                        smtp_quit(state);
                } else {
index 2aed9efa14208e496ff68688d083f51dca01488e..c8e756e2f99537bbbb444841f519a4ae78a119b7 100644 (file)
@@ -8,7 +8,26 @@
        VAR_SMTP_SASL_PATH, DEF_SMTP_SASL_PATH, &var_smtp_sasl_path, 0, 0,
 #ifdef USE_TLS
        VAR_SMTP_SASL_TLS_OPTS, DEF_SMTP_SASL_TLS_OPTS, &var_smtp_sasl_tls_opts, 0, 0,
+#ifdef SNAPSHOT                                /* XXX: Not yet */
        VAR_SMTP_SASL_TLSV_OPTS, DEF_SMTP_SASL_TLSV_OPTS, &var_smtp_sasl_tlsv_opts, 0, 0,
+#endif
+       VAR_SMTP_TLS_CERT_FILE, DEF_SMTP_TLS_CERT_FILE, &var_smtp_tls_cert_file, 0, 0,
+       VAR_SMTP_TLS_KEY_FILE, DEF_SMTP_TLS_KEY_FILE, &var_smtp_tls_key_file, 0, 0,
+       VAR_SMTP_TLS_DCERT_FILE, DEF_SMTP_TLS_DCERT_FILE, &var_smtp_tls_dcert_file, 0, 0,
+       VAR_SMTP_TLS_DKEY_FILE, DEF_SMTP_TLS_DKEY_FILE, &var_smtp_tls_dkey_file, 0, 0,
+       VAR_SMTP_TLS_CA_FILE, DEF_SMTP_TLS_CA_FILE, &var_smtp_tls_CAfile, 0, 0,
+       VAR_SMTP_TLS_CA_PATH, DEF_SMTP_TLS_CA_PATH, &var_smtp_tls_CApath, 0, 0,
+       VAR_SMTP_TLS_CIPHERS, DEF_SMTP_TLS_CIPHERS, &var_smtp_tls_ciphers, 1, 0,
+       VAR_SMTP_TLS_EXCL_CIPH, DEF_SMTP_TLS_EXCL_CIPH, &var_smtp_tls_excl_ciph, 0, 0,
+       VAR_SMTP_TLS_MAND_EXCL, DEF_SMTP_TLS_MAND_EXCL, &var_smtp_tls_mand_excl, 0, 0,
+       VAR_TLS_HIGH_CLIST, DEF_TLS_HIGH_CLIST, &var_tls_high_clist, 1, 0,
+       VAR_TLS_MEDIUM_CLIST, DEF_TLS_MEDIUM_CLIST, &var_tls_medium_clist, 1, 0,
+       VAR_TLS_LOW_CLIST, DEF_TLS_LOW_CLIST, &var_tls_low_clist, 1, 0,
+       VAR_TLS_EXPORT_CLIST, DEF_TLS_EXPORT_CLIST, &var_tls_export_clist, 1, 0,
+       VAR_TLS_NULL_CLIST, DEF_TLS_NULL_CLIST, &var_tls_null_clist, 1, 0,
+       VAR_SMTP_TLS_PROTO, DEF_SMTP_TLS_PROTO, &var_smtp_tls_protocols, 0, 0,
+       VAR_SMTP_TLS_VFY_CMATCH, DEF_SMTP_TLS_VFY_CMATCH, &var_smtp_tls_vfy_cmatch, 1, 0,
+       VAR_SMTP_TLS_SEC_CMATCH, DEF_SMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
 #endif
        VAR_SMTP_SASL_MECHS, DEF_SMTP_SASL_MECHS, &var_smtp_sasl_mechs, 0, 0,
        VAR_SMTP_SASL_TYPE, DEF_SMTP_SASL_TYPE, &var_smtp_sasl_type, 1, 0,
@@ -21,6 +40,8 @@
        VAR_SMTP_EHLO_DIS_WORDS, DEF_SMTP_EHLO_DIS_WORDS, &var_smtp_ehlo_dis_words, 0, 0,
        VAR_SMTP_EHLO_DIS_MAPS, DEF_SMTP_EHLO_DIS_MAPS, &var_smtp_ehlo_dis_maps, 0, 0,
        VAR_SMTP_TLS_PER_SITE, DEF_SMTP_TLS_PER_SITE, &var_smtp_tls_per_site, 0, 0,
+       VAR_SMTP_TLS_LEVEL, DEF_SMTP_TLS_LEVEL, &var_smtp_tls_level, 0, 0,
+       VAR_SMTP_TLS_POLICY, DEF_SMTP_TLS_POLICY, &var_smtp_tls_policy, 0, 0,
        VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
        VAR_SMTP_GENERIC_MAPS, DEF_SMTP_GENERIC_MAPS, &var_smtp_generic_maps, 0, 0,
        VAR_LMTP_TCP_PORT, DEF_LMTP_TCP_PORT, &var_lmtp_tcp_port, 0, 0,
@@ -53,6 +74,8 @@
        VAR_SMTP_MXSESS_LIMIT, DEF_SMTP_MXSESS_LIMIT, &var_smtp_mxsess_limit, 0, 0,
 #ifdef USE_TLS
        VAR_SMTP_TLS_SCERT_VD, DEF_SMTP_TLS_SCERT_VD, &var_smtp_tls_scert_vd, 0, 0,
+       VAR_SMTP_TLS_LOGLEVEL, DEF_SMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0,
+       VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 1, 0,
 #endif
        0,
     };
index c0325d093f16b91aa7fd1681ec3da7d38eab4c81..e42825164fd8ccdd1f7c676255c68f9a1139e969 100644 (file)
@@ -236,7 +236,7 @@ static int smtp_start_tls(SMTP_STATE *);
 
 int     smtp_helo(SMTP_STATE *state)
 {
-    char   *myname = "smtp_helo";
+    const char *myname = "smtp_helo";
     SMTP_SESSION *session = state->session;
     DELIVER_REQUEST *request = state->request;
     SMTP_RESP *resp;
@@ -398,7 +398,14 @@ int     smtp_helo(SMTP_STATE *state)
            if (n == 0) {
                if (session->helo != 0)
                    myfree(session->helo);
-               session->helo = lowercase(mystrdup(word));
+
+               /*
+                * XXX: Keep the original case: we don't expect a single SMTP
+                * server to randomly change the case of its helo response.
+                * If different capitalization is detected, we should assume
+                * disjoint TLS caches.
+                */
+               session->helo = mystrdup(word);
                if (strcasecmp(word, var_myhostname) == 0
                 && (state->misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) != 0) {
                    msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
@@ -508,14 +515,14 @@ int     smtp_helo(SMTP_STATE *state)
         */
        if ((session->features & SMTP_FEATURE_STARTTLS) &&
            var_smtp_tls_note_starttls_offer &&
-           session->tls_level <= SMTP_TLS_LEV_NONE)
+           session->tls_level <= TLS_LEV_NONE)
            msg_info("Host offered STARTTLS: [%s]", session->host);
 
        /*
         * Decide whether or not to send STARTTLS.
         */
        if ((session->features & SMTP_FEATURE_STARTTLS) != 0
-           && smtp_tls_ctx != 0 && session->tls_level >= SMTP_TLS_LEV_MAY) {
+           && smtp_tls_ctx != 0 && session->tls_level >= TLS_LEV_MAY) {
 
            /*
             * Prepare for disaster.
@@ -555,7 +562,7 @@ int     smtp_helo(SMTP_STATE *state)
             * although support for it was announced in the EHLO response.
             */
            session->features &= ~SMTP_FEATURE_STARTTLS;
-           if (session->tls_level >= SMTP_TLS_LEV_ENCRYPT)
+           if (session->tls_level >= TLS_LEV_ENCRYPT)
                return (smtp_site_fail(state, session->host, resp,
                    "TLS is required, but host %s refused to start TLS: %s",
                                       session->namaddr,
@@ -570,7 +577,7 @@ int     smtp_helo(SMTP_STATE *state)
         * block. When TLS is required we must never, ever, end up in
         * plain-text mode.
         */
-       if (session->tls_level >= SMTP_TLS_LEV_ENCRYPT) {
+       if (session->tls_level >= TLS_LEV_ENCRYPT) {
            if (!(session->features & SMTP_FEATURE_STARTTLS)) {
                return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                       SMTP_RESP_FAKE(&fake, "4.7.4"),
@@ -605,6 +612,7 @@ int     smtp_helo(SMTP_STATE *state)
 static int smtp_start_tls(SMTP_STATE *state)
 {
     SMTP_SESSION *session = state->session;
+    tls_client_start_props tls_props;
     VSTRING *serverid;
     SMTP_RESP fake;
 
@@ -638,20 +646,106 @@ static int smtp_start_tls(SMTP_STATE *state)
      * multiple hosts per hostname, or even multiple hosts per IP address.
      * All this without a shared TLS session cache, and they still want to
      * use TLS session caching???
+     * 
+     * The TLS session cache records the trust chain verification status of
+     * cached sessions. Different transports may have different CAfile or
+     * CApath settings, perhaps to allow authenticated connections to sites
+     * with private CA certs without trusting said private certs for other
+     * sites. So we cannot assume that a trust chain valid for one transport
+     * is valid for another. Therefore the client session id must include
+     * either the transport name or the values of CAfile and CApath. We use
+     * the transport name.
      */
     serverid = vstring_alloc(10);
-    vstring_sprintf(serverid, "%s:%u:%s", session->addr,
+    vstring_sprintf(serverid, "%s:%s:%u:%s", state->service, session->addr,
                  ntohs(session->port), session->helo ? session->helo : "");
-    session->tls_context =
-       tls_client_start(smtp_tls_ctx, session->stream,
-                        var_smtp_starttls_tmout,
-                        session->tls_level >= SMTP_TLS_LEV_VERIFY,
-                        session->host, lowercase(vstring_str(serverid)));
+
+    /*
+     * XXX: We store only one session per lookup key. Ideally the the key
+     * maps 1-to-1 to a server TLS session cache. We use the IP address, port
+     * and ehlo response name to build a lookup key that works for split
+     * caches (that announce distinct names) behind a load balancer.
+     * 
+     * Starting with Postfix 2.3 we may have incompatible security requirements
+     * for different domains hosted on the same server (peer cache). This
+     * requires multiple sessions to be negotiated with the same peer. It
+     * would be bad to store just one session and repeatedly discard it when
+     * we encounter incompatible requirements.
+     * 
+     * This drives us to separate lookup keys for each combination of cipher and
+     * protocol requirements. While at times a stronger session may not get
+     * re-used for a delivery with weaker requirements, a multi-session cache
+     * is prohibitively complex at this time.
+     * 
+     * - Expiration code would need to selectively delete sessions from a list -
+     * Re-use code would need to decode many sessions and choose the best -
+     * Store code would needs to choose between replace and append.
+     * 
+     * Note: checking the compatibility of re-activated sessions against the
+     * cipher requirements of the session under construction requires us to
+     * store the cipher name in the session cache with the passivated session
+     * object, the name is not available when the session is revived until
+     * the handshake is complete, which is too late.
+     * 
+     * XXX: When cached ciphers are reloaded, their cipher is not available via
+     * documented APIs until the handshake completes. We need to filter out
+     * sessions that use the wrong ciphers, but may not peek at the
+     * undocumented session->cipher_id and cipher->id structure members.
+     * 
+     * Since cipherlists are typically shared by many domains, we include the
+     * cipherlist in the session cache lookup key. This avoids false
+     * positives results from the session cache.
+     * 
+     * To support mutually incompatible protocol/cipher combinations, our
+     * session key must include both the protocol and the cipherlist.
+     * 
+     * XXX: the cipherlist is case sensitive, "aDH" != "ADH". So we don't
+     * lowercase() the serverid.
+     */
+    if (session->tls_level >= TLS_LEV_ENCRYPT
+       && session->tls_protocols != 0
+       && session->tls_protocols != TLS_ALL_PROTOCOLS)
+       vstring_sprintf_append(serverid, "&p=%s",
+                              tls_protocol_names(VAR_SMTP_TLS_PROTO,
+                                                 session->tls_protocols));
+    if (session->tls_level >= TLS_LEV_ENCRYPT && session->tls_cipherlist)
+       vstring_sprintf_append(serverid, "&c=%s", session->tls_cipherlist);
+
+    tls_props.ctx = smtp_tls_ctx;
+    tls_props.stream = session->stream;
+    tls_props.log_level = var_smtp_tls_loglevel;
+    tls_props.timeout = var_smtp_starttls_tmout;
+    tls_props.tls_level = session->tls_level;
+    tls_props.nexthop = session->tls_nexthop;
+    tls_props.host = session->host;
+    tls_props.serverid = vstring_str(serverid);
+    tls_props.protocols = session->tls_protocols;
+    tls_props.cipherlist = session->tls_cipherlist;
+    tls_props.certmatch = session->tls_certmatch;
+
+    session->tls_context = tls_client_start(&tls_props);
     vstring_free(serverid);
-    if (session->tls_context == 0)
+    if (session->tls_context == 0) {
+
+       /*
+        * We must avoid further I/O, the peer is in an undefined state.
+        */
+       (void) vstream_fpurge(session->stream);
+       DONT_USE_DEAD_SESSION;
+
+       /*
+        * If TLS is optional, try again, this time without TLS.
+        * Specifically, this session is not final, don't defer any
+        * recipients yet.
+        */
+       if (session->tls_level == TLS_LEV_MAY) {
+           session->tls_retry_plain = 1;
+           state->misc_flags &= ~SMTP_MISC_FLAG_FINAL_SERVER;
+       }
        return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                               SMTP_RESP_FAKE(&fake, "4.7.5"),
                               "Cannot start TLS: handshake failure"));
+    }
 
     /*
      * At this point we have to re-negotiate the "EHLO" to reget the
@@ -852,7 +946,7 @@ static void smtp_mime_fail(SMTP_STATE *state, int mime_errs)
 static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                             NOCLOBBER int recv_state)
 {
-    char   *myname = "smtp_loop";
+    const char *myname = "smtp_loop";
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
     SMTP_RESP *resp;
index 3cb09749562adbe2da65c907ad2b06372b474675..8c0f75c68142ba519caf58bfe6ef0a63764343ac 100644 (file)
@@ -35,6 +35,8 @@
 /*
 /*     smtp_reuse_addr() looks up a cached session by its server
 /*     address, and verifies that the session is still alive.
+/*     This operation is disabled when the legacy tls_per_site
+/*     or smtp_sasl_password_maps features are enabled.
 /*     The result is null in case of failure.
 /*
 /*     Arguments:
@@ -174,6 +176,22 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
     }
     state->session = session;
 
+#ifdef USE_TLS
+
+    /*
+     * Cached sessions are never TLS encrypted, so they must not be reused
+     * when TLS encryption is required.
+     */
+    if (session->tls_level >= TLS_LEV_ENCRYPT) {
+       if (msg_verbose)
+           msg_info("%s: skipping plain-text cached session to %s",
+                    myname, label);
+       smtp_quit(state);                       /* Close politely */
+       smtp_session_free(session);             /* And avoid leaks */
+       return (state->session = 0);
+    }
+#endif
+
     /*
      * Send an RSET probe to verify that the session is still good.
      */
@@ -227,6 +245,17 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, const char *addr,
     SMTP_SESSION *session;
     int     fd;
 
+    /*
+     * XXX Disable connection cache lookup by server IP address when the
+     * tls_per_site policy or smtp_sasl_password_maps features are enabled.
+     * This connection may have been created under a different hostname that
+     * resolves to the same IP address. We don't want to use the wrong SASL
+     * credentials or the wrong TLS policy.
+     */
+    if ((var_smtp_tls_per_site && *var_smtp_tls_per_site)
+       || (var_smtp_sasl_passwd && *var_smtp_sasl_passwd))
+       return (0);
+
     /*
      * Look up the session by its IP address. This means that we have no
      * destination-to-address binding properties.
index facf547e396e1980d03e524170f693d6208e1a36..433258dc75cd3969e844a38e1729148f3d63e1f5 100644 (file)
@@ -146,7 +146,7 @@ static XSASL_CLIENT_IMPL *smtp_sasl_impl;
 
 int     smtp_sasl_passwd_lookup(SMTP_SESSION *session)
 {
-    char   *myname = "smtp_sasl_passwd_lookup";
+    const char *myname = "smtp_sasl_passwd_lookup";
     SMTP_STATE *state = session->state;
     const char *value;
     char   *passwd;
@@ -259,7 +259,7 @@ void    smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
 
 int     smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
 {
-    char   *myname = "smtp_sasl_authenticate";
+    const char *myname = "smtp_sasl_authenticate";
     SMTP_RESP *resp;
     const char *mechanism;
     int     result;
index a96bd207480bea2b6ca29dfe666a5c2f498fdfda..a76e77236845aaaddf8b1358a3c5b0ca4fcb0815 100644 (file)
@@ -171,7 +171,7 @@ int     smtp_sasl_helo_login(SMTP_STATE *state)
     ret = 0;
     if (session->sasl_mechanism_list == 0) {
        dsb_simple(why, "4.7.0", "SASL authentication failed: "
-               "server %s offered no compatible authentication mechanisms for this type of connection security",
+                  "server %s offered no compatible authentication mechanisms for this type of connection security",
                   session->namaddr);
        ret = smtp_sess_fail(state);
        /* Session reuse is disabled. */
@@ -179,15 +179,17 @@ int     smtp_sasl_helo_login(SMTP_STATE *state)
 #ifdef USE_TLS
        if (session->tls_context == 0)
 #endif
-           smtp_sasl_start(session, VAR_SMTP_SASL_OPTS, 
-               var_smtp_sasl_opts);
+           smtp_sasl_start(session, VAR_SMTP_SASL_OPTS,
+                           var_smtp_sasl_opts);
 #ifdef USE_TLS
-       else if (session->tls_context->peer_verified == 0)
-           smtp_sasl_start(session, VAR_SMTP_SASL_TLS_OPTS, 
-               var_smtp_sasl_tls_opts);
+#ifdef SNAPSHOT                                        /* XXX: Not yet */
+       else if (session->tls_context->peer_verified)
+           smtp_sasl_start(session, VAR_SMTP_SASL_TLSV_OPTS,
+                           var_smtp_sasl_tlsv_opts);
        else
-           smtp_sasl_start(session, VAR_SMTP_SASL_TLSV_OPTS, 
-               var_smtp_sasl_tlsv_opts);
+#endif
+           smtp_sasl_start(session, VAR_SMTP_SASL_TLS_OPTS,
+                           var_smtp_sasl_tls_opts);
 #endif
        if (smtp_sasl_authenticate(session, why) <= 0) {
            ret = smtp_sess_fail(state);
index 0807675994c265bb184fce033f92fb01a67f5506..b01c99cdc847932c8f298e5e791f45ef12706a93 100644 (file)
 
 #include <msg.h>
 #include <mymalloc.h>
+#include <vstring.h>
 #include <vstream.h>
 #include <stringops.h>
+#include <valid_hostname.h>
+#include <name_code.h>
 
 /* Global library. */
 
 #include "smtp.h"
 #include "smtp_sasl.h"
 
-/*#define msg_verbose 1*/
+NAME_CODE smtp_tls_levels[] = {
+    "none", TLS_LEV_NONE,
+    "may", TLS_LEV_MAY,
+    "encrypt", TLS_LEV_ENCRYPT,
+    "verify", TLS_LEV_VERIFY,
+    "secure", TLS_LEV_SECURE,
+    0, TLS_LEV_NOTFOUND,
+};
 
 #ifdef USE_TLS
 
+static MAPS *tls_policy;               /* lookup table(s) */
 static MAPS *tls_per_site;             /* lookup table(s) */
 
 /* smtp_tls_list_init - initialize per-site policy lists */
 
 void    smtp_tls_list_init(void)
 {
-    tls_per_site = maps_create(VAR_SMTP_TLS_PER_SITE, var_smtp_tls_per_site,
-                              DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+    if (*var_smtp_tls_policy) {
+       tls_policy = maps_create(VAR_SMTP_TLS_POLICY, var_smtp_tls_policy,
+                                DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+       if (*var_smtp_tls_per_site)
+           msg_warn("%s ignored when %s is not empty.",
+                    VAR_SMTP_TLS_PER_SITE, VAR_SMTP_TLS_POLICY);
+       return;
+    }
+    if (*var_smtp_tls_per_site) {
+       tls_per_site = maps_create(VAR_SMTP_TLS_PER_SITE, var_smtp_tls_per_site,
+                                  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+    }
 }
 
-/* smtp_tls_policy_print - printpolicy level */
+/* policy_name - printable tls policy level */
 
-static void smtp_tls_policy_print(const char *name, int level)
+static const char *policy_name(int level)
 {
-    msg_info("%s TLS level: %s", name,
-            level == SMTP_TLS_LEV_VERIFY ? "verify" :
-            level == SMTP_TLS_LEV_ENCRYPT ? "encrypt" :
-            level == SMTP_TLS_LEV_MAY ? "may" :
-            level == SMTP_TLS_LEV_NONE ? "none" :
-            "unknown");
+    const char *name = str_name_code(smtp_tls_levels, level);
+
+    if (name == 0)
+       name = "unknown";
+    return name;
 }
 
-/* smtp_tls_policy_lookup - look up per-site TLS security level */
+/* tls_site_lookup - look up per-site TLS security level */
 
-static void smtp_tls_policy_lookup(int *site_level, const char *site_name,
-                                          const char *site_class)
+static void tls_site_lookup(int *site_level, const char *site_name,
+                                   const char *site_class)
 {
     const char *lookup;
 
@@ -166,18 +186,18 @@ static void smtp_tls_policy_lookup(int *site_level, const char *site_name,
     if ((lookup = maps_find(tls_per_site, site_name, 0)) != 0) {
        if (!strcasecmp(lookup, "NONE")) {
            /* NONE overrides MAY or NOTFOUND. */
-           if (*site_level <= SMTP_TLS_LEV_MAY)
-               *site_level = SMTP_TLS_LEV_NONE;
+           if (*site_level <= TLS_LEV_MAY)
+               *site_level = TLS_LEV_NONE;
        } else if (!strcasecmp(lookup, "MAY")) {
            /* MAY overrides NOTFOUND but not NONE. */
-           if (*site_level < SMTP_TLS_LEV_NONE)
-               *site_level = SMTP_TLS_LEV_MAY;
+           if (*site_level < TLS_LEV_NONE)
+               *site_level = TLS_LEV_MAY;
        } else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) {
-           if (*site_level < SMTP_TLS_LEV_ENCRYPT)
-               *site_level = SMTP_TLS_LEV_ENCRYPT;
+           if (*site_level < TLS_LEV_ENCRYPT)
+               *site_level = TLS_LEV_ENCRYPT;
        } else if (!strcasecmp(lookup, "MUST")) {
-           if (*site_level < SMTP_TLS_LEV_VERIFY)
-               *site_level = SMTP_TLS_LEV_VERIFY;
+           if (*site_level < TLS_LEV_VERIFY)
+               *site_level = TLS_LEV_VERIFY;
        } else {
            msg_warn("Table %s: ignoring unknown TLS policy '%s' for %s %s",
                     var_smtp_tls_per_site, lookup, site_class, site_name);
@@ -185,69 +205,315 @@ static void smtp_tls_policy_lookup(int *site_level, const char *site_name,
     }
 }
 
-/* smtp_tls_level_init - configure session TLS enforcement level */
+/* tls_policy_lookup_one - look up destination TLS policy */
+
+static int tls_policy_lookup_one(SMTP_SESSION *session,
+                                        int *site_level, int *cipherlev,
+                                        const char *site_name,
+                                        const char *site_class)
+{
+    const char *lookup;
+    char   *policy;
+    char   *saved_policy;
+    char   *tok;
+    const char *err;
+    char   *name;
+    char   *val;
+    static VSTRING *cbuf;
+
+#undef FREE_RETURN
+#define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0)
+
+    if ((lookup = maps_find(tls_policy, site_name, 0)) == 0)
+       return (0);
+
+    if (cbuf == 0)
+       cbuf = vstring_alloc(10);
+
+#define set_context(cbuf,  site_class, site_name) \
+    vstring_sprintf(cbuf, "TLS policy table, %s '%s'", site_class, site_name)
+#define str_context(cbuf,  site_class, site_name) \
+    vstring_str(set_context(cbuf, site_class, site_name))
+
+    saved_policy = policy = mystrdup(lookup);
+
+    if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
+       msg_warn("ignoring empty tls policy for %s", site_name);
+       FREE_RETURN(1);                         /* No further lookups */
+    }
+    *site_level = name_code(smtp_tls_levels, NAME_CODE_FLAG_NONE, tok);
+    if (*site_level == TLS_LEV_NOTFOUND) {
+       msg_warn("%s: unknown security level '%s' ignored",
+                str_context(cbuf, site_class, site_name), tok);
+       FREE_RETURN(1);                         /* No further lookups */
+    }
+    while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) {
+       if ((err = split_nameval(tok, &name, &val)) != 0) {
+           msg_warn("%s: malformed attribute/value pair '%s' ignored: %s",
+                    str_context(cbuf, site_class, site_name), tok, err);
+           continue;
+       }
+       if (!strcasecmp(name, "ciphers")) {
+           if (*site_level < TLS_LEV_ENCRYPT) {
+               msg_warn("%s: attribute '%s' ignored at security level '%s'",
+                        str_context(cbuf, site_class, site_name),
+                        name, policy_name(*site_level));
+               continue;
+           }
+           *cipherlev = tls_cipher_level(val);
+           if (*cipherlev == TLS_CIPHER_NONE) {
+               msg_warn("%s: invalid %s value '%s' ignored",
+                        str_context(cbuf, site_class, site_name),
+                        name, val);
+           }
+           continue;
+       }
+       if (!strcasecmp(name, "protocols")) {
+           if (*site_level < TLS_LEV_ENCRYPT) {
+               msg_warn("%s: attribute '%s' ignored at security level '%s'",
+                        str_context(cbuf, site_class, site_name),
+                        name, policy_name(*site_level));
+               continue;
+           }
+           if (*val == 0) {
+               msg_warn("%s: empty %s value ignored",
+                        str_context(cbuf, site_class, site_name), name);
+               continue;
+           }
+           vstring_sprintf(cbuf, "protocol in TLS policy table, %s '%s':",
+                           site_class, site_name);
+           session->tls_protocols = tls_protocol_mask(vstring_str(cbuf), val);
+           continue;
+       }
+       if (!strcasecmp(name, "match")) {
+           if (*site_level < TLS_LEV_VERIFY) {
+               msg_warn("%s: attribute '%s' ignored at security level '%s'",
+                        str_context(cbuf, site_class, site_name),
+                        name, policy_name(*site_level));
+               continue;
+           }
+           if (*val == 0) {
+               msg_warn("%s: empty %s value ignored",
+                        str_context(cbuf, site_class, site_name), name);
+               continue;
+           }
+           session->tls_certmatch = mystrdup(val);
+           continue;
+       }
+       msg_warn("%s: unknown attribute '%s' ignored",
+                str_context(cbuf, site_class, site_name), name);
+    }
+
+    FREE_RETURN(1);
+}
+
+/* tls_policy_lookup - look up destination TLS policy */
+
+static void tls_policy_lookup(SMTP_SESSION *session,
+                                     int *site_level, int *cipherlev,
+                                     const char *site_name,
+                                     const char *site_class)
+{
+
+    /*
+     * Only one lookup with [nexthop]:port, [nexthop] or nexthop:port These
+     * are never the domain part of localpart@domain, rather they are
+     * explicit nexthops from transport:nexthop, and match only the
+     * corresponding policy. Parent domain matching (below) applies only to
+     * sub-domains of the recipient domain.
+     */
+    if (!valid_hostname(site_name, DONT_GRIPE)) {
+       tls_policy_lookup_one(session, site_level, cipherlev,
+                             site_name, site_class);
+       return;
+    }
+    while (1) {
+       /* Try the given domain */
+       if (tls_policy_lookup_one(session, site_level, cipherlev,
+                                 site_name, site_class))
+           return;
+       /* Re-try with parent domain */
+       if ((site_name = strchr(site_name + 1, '.')) == 0)
+           return;
+    }
+}
+
+/* set_cipherlist - Choose cipherlist per security level and cipher suite */
 
-static int smtp_tls_level_init(const char *dest, const char *host)
+static void set_cipherlist(SMTP_SESSION *session, int level, int lmtp)
+{
+    const char *cipherlist = 0;
+    const char *exclude = var_smtp_tls_excl_ciph;
+    const char *also_exclude = "";
+    const char *mand_exclude = "";
+
+    /*
+     * Use main.cf cipher level if no per-destination value specified. With
+     * mandatory encryption at least encrypt, and with mandatory verification
+     * at least authenticate!
+     */
+    switch (session->tls_level) {
+    case TLS_LEV_NONE:
+       session->tls_cipherlist = 0;
+       return;
+
+    case TLS_LEV_MAY:
+       level = TLS_CIPHER_EXPORT;              /* Interoperate! */
+       break;
+
+    case TLS_LEV_ENCRYPT:
+       also_exclude = "eNULL";
+       if (level == TLS_CIPHER_NONE)
+           level = tls_cipher_level(var_smtp_tls_ciphers);
+       mand_exclude = var_smtp_tls_mand_excl;
+       break;
+
+    case TLS_LEV_VERIFY:
+    case TLS_LEV_SECURE:
+       also_exclude = "aNULL";
+       if (level == TLS_CIPHER_NONE)
+           level = tls_cipher_level(var_smtp_tls_ciphers);
+       mand_exclude = var_smtp_tls_mand_excl;
+       break;
+    }
+
+    cipherlist = tls_cipher_list(level, exclude, mand_exclude, also_exclude,
+                                TLS_END_EXCLUDE);
+    if (cipherlist == 0) {
+       msg_warn("unknown '%s' value '%s' ignored, using 'medium'",
+                lmtp ? VAR_LMTP_TLS_CIPHERS : VAR_SMTP_TLS_CIPHERS,
+                var_smtp_tls_ciphers);
+       cipherlist = tls_cipher_list(TLS_CIPHER_MEDIUM, exclude, mand_exclude,
+                                    also_exclude, TLS_END_EXCLUDE);
+       if (cipherlist == 0)
+           msg_panic("NULL medium cipherlist");
+    }
+    session->tls_cipherlist = mystrdup(cipherlist);
+}
+
+/* session_tls_init - session TLS parameters */
+
+static void session_tls_init(SMTP_SESSION *session, const char *dest,
+                                    const char *host, int flags)
 {
     int     global_level;
     int     site_level;
-    int     tls_level;
+    int     lmtp = flags & SMTP_MISC_FLAG_USE_LMTP;
+    int     cipherlev = TLS_CIPHER_NONE;
+
+    /*
+     * Initialize all TLS related session properties.
+     */
+    session->tls_context = 0;
+    session->tls_nexthop = 0;
+    session->tls_level = TLS_LEV_NONE;
+    session->tls_retry_plain = 0;
+    session->tls_protocols = 0;
+    session->tls_cipherlist = 0;
+    session->tls_certmatch = 0;
 
     /*
      * Compute the global TLS policy. This is the default policy level when
      * no per-site policy exists. It also is used to override a wild-card
      * per-site policy.
      */
-    if (var_smtp_enforce_tls)
+    if (*var_smtp_tls_level) {
+       global_level = name_code(smtp_tls_levels, NAME_CODE_FLAG_NONE,
+                                var_smtp_tls_level);
+       if (global_level == TLS_LEV_NOTFOUND) {
+           msg_fatal("%s: unknown TLS security level '%s'",
+                     lmtp ? VAR_LMTP_TLS_LEVEL : VAR_SMTP_TLS_LEVEL,
+                     var_smtp_tls_level);
+       }
+    } else if (var_smtp_enforce_tls) {
        global_level = var_smtp_tls_enforce_peername ?
-           SMTP_TLS_LEV_VERIFY : SMTP_TLS_LEV_ENCRYPT;
-    else
+           TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
+    } else {
        global_level = var_smtp_use_tls ?
-           SMTP_TLS_LEV_MAY : SMTP_TLS_LEV_NONE;
+           TLS_LEV_MAY : TLS_LEV_NONE;
+    }
     if (msg_verbose)
-       smtp_tls_policy_print("global", global_level);
+       msg_info("%s TLS level: %s", "global", policy_name(global_level));
 
     /*
      * Compute the per-site TLS enforcement level. For compatibility with the
      * original TLS patch, this algorithm is gives equal precedence to host
      * and next-hop policies.
      */
-    site_level = SMTP_TLS_LEV_NOTFOUND;
+    site_level = TLS_LEV_NOTFOUND;
 
-    if (tls_per_site) {
-       smtp_tls_policy_lookup(&site_level, dest, "next-hop destination");
+    if (tls_policy) {
+       tls_policy_lookup(session, &site_level, &cipherlev,
+                         dest, "next-hop destination");
+    } else if (tls_per_site) {
+       tls_site_lookup(&site_level, dest, "next-hop destination");
        if (strcasecmp(dest, host) != 0)
-           smtp_tls_policy_lookup(&site_level, host, "server hostname");
+           tls_site_lookup(&site_level, host, "server hostname");
        if (msg_verbose)
-           smtp_tls_policy_print("site", site_level);
+           msg_info("%s TLS level: %s", "site", policy_name(site_level));
+
+       /*
+        * Override a wild-card per-site policy with a more specific global
+        * policy.
+        * 
+        * With the original TLS patch, 1) a per-site ENCRYPT could not override
+        * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
+        * produced inconsistent results: it changed a global VERIFY into
+        * NONE, while producing MAY with all weaker global policy settings.
+        * 
+        * With the current implementation, a combined per-site (NONE+MAY)
+        * consistently overrides global policy with NONE, and global policy
+        * can override only a per-site MAY wildcard. That is, specific
+        * policies consistently override wildcard policies, and
+        * (non-wildcard) per-site policies consistently override global
+        * policies.
+        */
+       if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
+           site_level = global_level;
     }
+    if (site_level == TLS_LEV_NOTFOUND)
+       session->tls_level = global_level;
+    else
+       session->tls_level = site_level;
 
     /*
-     * Override a wild-card per-site policy with a more specific global
-     * policy.
-     * 
-     * With the original TLS patch, 1) a per-site ENCRYPT could not override a
-     * global VERIFY, and 2) a combined per-site (NONE+MAY) policy produced
-     * inconsistent results: it changed a global VERIFY into NONE, while
-     * producing MAY with all weaker global policy settings.
-     * 
-     * With the current implementation, a combined per-site (NONE+MAY)
-     * consistently overrides global policy with NONE, and global policy can
-     * override only a per-site MAY wildcard. That is, specific policies
-     * consistently override wildcard policies, and (non-wildcard) per-site
-     * policies consistently override global policies.
+     * Use main.cf protocols setting if not set in per-destination table
      */
-    if (site_level == SMTP_TLS_LEV_NOTFOUND
-       || (site_level == SMTP_TLS_LEV_MAY
-           && global_level > SMTP_TLS_LEV_MAY))
-       tls_level = global_level;
-    else
-       tls_level = site_level;
+    if (session->tls_level >= TLS_LEV_ENCRYPT
+       && session->tls_protocols == 0
+       && *var_smtp_tls_protocols)
+       session->tls_protocols =
+           tls_protocol_mask(VAR_SMTP_TLS_PROTO, var_smtp_tls_protocols);
 
-    if (msg_verbose && tls_per_site)
-       smtp_tls_policy_print("effective", tls_level);
+    /*
+     * Convert cipher level (if set in per-destination table, else
+     * set_cipherlist uses main.cf settings) to an OpenSSL cipherlist. The
+     * "lmtp" vs. "smtp" identity is used for error reporting.
+     */
+    set_cipherlist(session, cipherlev, lmtp);
 
-    return (tls_level);
+    /*
+     * Use main.cf cert_match setting if not set in per-destination table
+     */
+    if (session->tls_level >= TLS_LEV_ENCRYPT
+       && session->tls_certmatch == 0) {
+       switch (session->tls_level) {
+       case TLS_LEV_ENCRYPT:
+           break;
+       case TLS_LEV_VERIFY:
+           session->tls_certmatch = mystrdup(var_smtp_tls_vfy_cmatch);
+           break;
+       case TLS_LEV_SECURE:
+           session->tls_certmatch = mystrdup(var_smtp_tls_sec_cmatch);
+           break;
+       default:
+           msg_panic("unexpected mandatory TLS security level: %d",
+                     session->tls_level);
+       }
+    }
+    if (msg_verbose && (tls_policy || tls_per_site))
+       msg_info("%s TLS level: %s", "effective",
+                policy_name(session->tls_level));
 }
 
 #endif
@@ -294,6 +560,7 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
     else
        DONT_CACHE_THIS_SESSION;
     session->reuse_count = 0;
+    USE_NEWBORN_SESSION;                       /* He's not dead Jim! */
 
 #ifdef USE_SASL_AUTH
     smtp_sasl_connect(session);
@@ -305,8 +572,7 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
      * security levels are defined.
      */
 #ifdef USE_TLS
-    session->tls_context = 0;
-    session->tls_level = smtp_tls_level_init(dest, host);
+    session_tls_init(session, dest, host, flags);
 #endif
     session->state = 0;
     debug_peer_check(host, addr);
@@ -324,6 +590,10 @@ void    smtp_session_free(SMTP_SESSION *session)
            tls_client_stop(smtp_tls_ctx, session->stream,
                          var_smtp_starttls_tmout, 0, session->tls_context);
     }
+    if (session->tls_cipherlist)
+       myfree(session->tls_cipherlist);
+    if (session->tls_certmatch)
+       myfree(session->tls_certmatch);
 #endif
     if (session->stream)
        vstream_fclose(session->stream);
index 6dbe2ebf3393e5ea286addffc787f06613056c52..84d57fdb97f08a20ae8cd682bba6c9a2e73b1469 100644 (file)
@@ -1,12 +1,12 @@
 SHELL  = /bin/sh
 SRCS   = smtpd.c smtpd_token.c smtpd_check.c smtpd_chat.c smtpd_state.c \
        smtpd_peer.c smtpd_sasl_proto.c smtpd_sasl_glue.c smtpd_proxy.c \
-       smtpd_xforward.c smtpd_dsn_fix.c
+       smtpd_xforward.c smtpd_dsn_fix.c smtpd_milter.c
 OBJS   = smtpd.o smtpd_token.o smtpd_check.o smtpd_chat.o smtpd_state.o \
        smtpd_peer.o smtpd_sasl_proto.o smtpd_sasl_glue.o smtpd_proxy.o \
-       smtpd_xforward.o smtpd_dsn_fix.o
+       smtpd_xforward.o smtpd_dsn_fix.o smtpd_milter.o
 HDRS   = smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \
-       smtpd_sasl_glue.h smtpd_proxy.h smtpd_dsn_fix.h
+       smtpd_sasl_glue.h smtpd_proxy.h smtpd_dsn_fix.h smtpd_milter.h
 TESTSRC        = smtpd_token_test.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -14,7 +14,8 @@ TESTPROG= smtpd_token smtpd_check
 PROG   = smtpd
 INC_DIR        = ../../include
 LIBS   = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libdns.a \
-       ../../lib/libxsasl.a ../../lib/libglobal.a ../../lib/libutil.a
+       ../../lib/libxsasl.a ../../lib/libmilter.a ../../lib/libglobal.a \
+       ../../lib/libutil.a
 
 .c.o:; $(CC) $(CFLAGS) -c $*.c
 
@@ -154,9 +155,11 @@ smtpd.o: ../../include/mail_proto.h
 smtpd.o: ../../include/mail_queue.h
 smtpd.o: ../../include/mail_server.h
 smtpd.o: ../../include/mail_stream.h
+smtpd.o: ../../include/mail_version.h
 smtpd.o: ../../include/maps.h
 smtpd.o: ../../include/match_list.h
 smtpd.o: ../../include/match_ops.h
+smtpd.o: ../../include/milter.h
 smtpd.o: ../../include/msg.h
 smtpd.o: ../../include/myaddrinfo.h
 smtpd.o: ../../include/mymalloc.h
@@ -189,6 +192,7 @@ smtpd.o: smtpd.c
 smtpd.o: smtpd.h
 smtpd.o: smtpd_chat.h
 smtpd.o: smtpd_check.h
+smtpd.o: smtpd_milter.h
 smtpd.o: smtpd_proxy.h
 smtpd.o: smtpd_sasl_glue.h
 smtpd.o: smtpd_sasl_proto.h
@@ -203,6 +207,7 @@ smtpd_chat.o: ../../include/mail_error.h
 smtpd_chat.o: ../../include/mail_params.h
 smtpd_chat.o: ../../include/mail_proto.h
 smtpd_chat.o: ../../include/mail_stream.h
+smtpd_chat.o: ../../include/milter.h
 smtpd_chat.o: ../../include/msg.h
 smtpd_chat.o: ../../include/myaddrinfo.h
 smtpd_chat.o: ../../include/mymalloc.h
@@ -252,6 +257,7 @@ smtpd_check.o: ../../include/maps.h
 smtpd_check.o: ../../include/match_list.h
 smtpd_check.o: ../../include/match_ops.h
 smtpd_check.o: ../../include/match_parent_style.h
+smtpd_check.o: ../../include/milter.h
 smtpd_check.o: ../../include/msg.h
 smtpd_check.o: ../../include/msg_stats.h
 smtpd_check.o: ../../include/myaddrinfo.h
@@ -287,6 +293,20 @@ smtpd_dsn_fix.o: ../../include/msg.h
 smtpd_dsn_fix.o: ../../include/sys_defs.h
 smtpd_dsn_fix.o: smtpd_dsn_fix.c
 smtpd_dsn_fix.o: smtpd_dsn_fix.h
+smtpd_milter.o: ../../include/argv.h
+smtpd_milter.o: ../../include/mail_params.h
+smtpd_milter.o: ../../include/mail_stream.h
+smtpd_milter.o: ../../include/milter.h
+smtpd_milter.o: ../../include/myaddrinfo.h
+smtpd_milter.o: ../../include/name_mask.h
+smtpd_milter.o: ../../include/sys_defs.h
+smtpd_milter.o: ../../include/tls.h
+smtpd_milter.o: ../../include/vbuf.h
+smtpd_milter.o: ../../include/vstream.h
+smtpd_milter.o: ../../include/vstring.h
+smtpd_milter.o: smtpd.h
+smtpd_milter.o: smtpd_milter.c
+smtpd_milter.o: smtpd_milter.h
 smtpd_peer.o: ../../include/argv.h
 smtpd_peer.o: ../../include/attr.h
 smtpd_peer.o: ../../include/inet_proto.h
@@ -294,9 +314,11 @@ smtpd_peer.o: ../../include/iostuff.h
 smtpd_peer.o: ../../include/mail_params.h
 smtpd_peer.o: ../../include/mail_proto.h
 smtpd_peer.o: ../../include/mail_stream.h
+smtpd_peer.o: ../../include/milter.h
 smtpd_peer.o: ../../include/msg.h
 smtpd_peer.o: ../../include/myaddrinfo.h
 smtpd_peer.o: ../../include/mymalloc.h
+smtpd_peer.o: ../../include/name_mask.h
 smtpd_peer.o: ../../include/sock_addr.h
 smtpd_peer.o: ../../include/stringops.h
 smtpd_peer.o: ../../include/sys_defs.h
@@ -317,6 +339,7 @@ smtpd_proxy.o: ../../include/mail_error.h
 smtpd_proxy.o: ../../include/mail_params.h
 smtpd_proxy.o: ../../include/mail_proto.h
 smtpd_proxy.o: ../../include/mail_stream.h
+smtpd_proxy.o: ../../include/milter.h
 smtpd_proxy.o: ../../include/msg.h
 smtpd_proxy.o: ../../include/myaddrinfo.h
 smtpd_proxy.o: ../../include/name_code.h
@@ -336,9 +359,11 @@ smtpd_proxy.o: smtpd_proxy.h
 smtpd_sasl_glue.o: ../../include/argv.h
 smtpd_sasl_glue.o: ../../include/mail_params.h
 smtpd_sasl_glue.o: ../../include/mail_stream.h
+smtpd_sasl_glue.o: ../../include/milter.h
 smtpd_sasl_glue.o: ../../include/msg.h
 smtpd_sasl_glue.o: ../../include/myaddrinfo.h
 smtpd_sasl_glue.o: ../../include/mymalloc.h
+smtpd_sasl_glue.o: ../../include/name_mask.h
 smtpd_sasl_glue.o: ../../include/stringops.h
 smtpd_sasl_glue.o: ../../include/sys_defs.h
 smtpd_sasl_glue.o: ../../include/tls.h
@@ -352,11 +377,13 @@ smtpd_sasl_glue.o: smtpd_sasl_glue.c
 smtpd_sasl_glue.o: smtpd_sasl_glue.h
 smtpd_sasl_proto.o: ../../include/argv.h
 smtpd_sasl_proto.o: ../../include/attr.h
+smtpd_sasl_proto.o: ../../include/ehlo_mask.h
 smtpd_sasl_proto.o: ../../include/iostuff.h
 smtpd_sasl_proto.o: ../../include/mail_error.h
 smtpd_sasl_proto.o: ../../include/mail_params.h
 smtpd_sasl_proto.o: ../../include/mail_proto.h
 smtpd_sasl_proto.o: ../../include/mail_stream.h
+smtpd_sasl_proto.o: ../../include/milter.h
 smtpd_sasl_proto.o: ../../include/msg.h
 smtpd_sasl_proto.o: ../../include/myaddrinfo.h
 smtpd_sasl_proto.o: ../../include/mymalloc.h
@@ -382,6 +409,7 @@ smtpd_state.o: ../../include/mail_error.h
 smtpd_state.o: ../../include/mail_params.h
 smtpd_state.o: ../../include/mail_proto.h
 smtpd_state.o: ../../include/mail_stream.h
+smtpd_state.o: ../../include/milter.h
 smtpd_state.o: ../../include/msg.h
 smtpd_state.o: ../../include/myaddrinfo.h
 smtpd_state.o: ../../include/mymalloc.h
@@ -407,9 +435,11 @@ smtpd_xforward.o: ../../include/attr.h
 smtpd_xforward.o: ../../include/iostuff.h
 smtpd_xforward.o: ../../include/mail_proto.h
 smtpd_xforward.o: ../../include/mail_stream.h
+smtpd_xforward.o: ../../include/milter.h
 smtpd_xforward.o: ../../include/msg.h
 smtpd_xforward.o: ../../include/myaddrinfo.h
 smtpd_xforward.o: ../../include/mymalloc.h
+smtpd_xforward.o: ../../include/name_mask.h
 smtpd_xforward.o: ../../include/sys_defs.h
 smtpd_xforward.o: ../../include/tls.h
 smtpd_xforward.o: ../../include/vbuf.h
index 5b715cb9347bfc39df1ca51887cfe3816975540d..1e47ae720046135ce739556b061740e1edd57468 100644 (file)
 /* .IP "\fBsmtpd_proxy_timeout (100s)\fR"
 /*     The time limit for connecting to a proxy filter and for sending or
 /*     receiving information.
+/* BEFORE QUEUE MILTER CONTROLS
+/* .ad
+/* .fi
+/*     As of version 2.3, Postfix supports the Sendmail version 8
+/*     Milter (mail filter) protocol. These content filters run
+/*     outside Postfix. They can inspect the SMTP command stream
+/*     and the message content, and can request modifications before
+/*     mail is queued. For details see the MILTER_README document.
+/* .IP "\fBsmtpd_milters (empty)\fR"
+/*     A list of Milter (mail filter) applications for new mail that
+/*     arrives via the Postfix \fBsmtpd\fR(8) server.
+/* .IP "\fBmilter_protocol (2)\fR"
+/*     The mail filter protocol version and optional protocol extensions
+/*     for communication with a Milter (mail filter) application.
+/* .IP "\fBmilter_default_action (tempfail)\fR"
+/*     The default action when a Milter (mail filter) application is
+/*     unavailable or mis-configured.
+/* .IP "\fBmilter_macro_daemon_name ($myhostname)\fR"
+/*     The {daemon_name} macro value for Milter (mail filter) applications.
+/* .IP "\fBmilter_macro_v ($mail_name $mail_version)\fR"
+/*     The {v} macro value for Milter (mail filter) applications.
+/* .IP "\fBmilter_connect_timeout (30s)\fR"
+/*     The time limit for connecting to a Milter (mail filter)
+/*     application, and for negotiating protocol options.
+/* .IP "\fBmilter_command_timeout (30s)\fR"
+/*     The time limit for sending an SMTP command to a Milter (mail
+/*     filter) application, and for receiving the response.
+/* .IP "\fBmilter_content_timeout (300s)\fR"
+/*     The time limit for sending message content to a Milter (mail
+/*     filter) application, and for receiving the response.
+/* .IP "\fBmilter_connect_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after completion of an SMTP connection.
+/* .IP "\fBmilter_helo_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the SMTP HELO or EHLO command.
+/* .IP "\fBmilter_mail_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the SMTP MAIL FROM command.
+/* .IP "\fBmilter_rcpt_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the SMTP RCPT TO command.
+/* .IP "\fBmilter_data_macros (see postconf -n output)\fR"
+/*     The macros that are sent to version 4 or higher Milter (mail
+/*     filter) applications after the SMTP DATA command.
+/* .IP "\fBmilter_unknown_command_macros (see postconf -n output)\fR"
+/*     The macros that are sent to version 3 or higher Milter (mail
+/*     filter) applications after an unknown SMTP command.
+/* .IP "\fBmilter_end_of_data_macros (see postconf -n output)\fR"
+/*     The macros that are sent to Milter (mail filter) applications
+/*     after the message end-of-data.
 /* GENERAL CONTENT INSPECTION CONTROLS
 /* .ad
 /* .fi
 /*     The verification depth for remote SMTP client certificates.
 /* .IP "\fBsmtpd_tls_cert_file (empty)\fR"
 /*     File with the Postfix SMTP server RSA certificate in PEM format.
-/* .IP "\fBsmtpd_tls_cipherlist (empty)\fR"
-/*     Controls the Postfix SMTP server TLS cipher selection scheme.
+/* .IP "\fBsmtpd_tls_ciphers (export)\fR"
+/*     The minimum acceptable SMTP server TLS cipher grade.
+/* .IP "\fBsmtpd_tls_exclude_ciphers (empty)\fR"
+/*     List of ciphers or cipher types to exclude from the SMTP server
+/*     cipher list.
 /* .IP "\fBsmtpd_tls_dcert_file (empty)\fR"
 /*     File with the Postfix SMTP server DSA certificate in PEM format.
 /* .IP "\fBsmtpd_tls_dh1024_param_file (empty)\fR"
 /*     File with the Postfix SMTP server RSA private key in PEM format.
 /* .IP "\fBsmtpd_tls_loglevel (0)\fR"
 /*     Enable additional Postfix SMTP server logging of TLS activity.
+/* .IP "\fBsmtpd_tls_protocols (empty)\fR"
+/*     The list of TLS protocols supported by the server.
 /* .IP "\fBsmtpd_tls_received_header (no)\fR"
 /*     Request that the Postfix SMTP server produces Received:  message
 /*     headers that include information about the protocol and cipher used,
 /*     The number of pseudo-random bytes that an \fBsmtp\fR(8) or \fBsmtpd\fR(8)
 /*     process requests from the \fBtlsmgr\fR(8) server in order to seed its
 /*     internal pseudo random number generator (PRNG).
+/* .IP "\fBtls_high_cipherlist (!EXPORT:!LOW:!MEDIUM:ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "HIGH" grade ciphers.
+/* .IP "\fBtls_medium_cipherlist (!EXPORT:!LOW:ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "MEDIUM" or higher grade ciphers.
+/* .IP "\fBtls_low_cipherlist (!EXPORT:ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "LOW" or higher grade ciphers.
+/* .IP "\fBtls_export_cipherlist (ALL:+RC4:@STRENGTH)\fR"
+/*     The OpenSSL cipherlist for "EXPORT" or higher grade ciphers.
+/* .IP "\fBtls_null_cipherlist (!aNULL:eNULL+kRSA)\fR"
+/*     The OpenSSL cipherlist for "NULL" grade ciphers that provide
+/*     authentication without encryption.
 /* VERP SUPPORT CONTROLS
 /* .ad
 /* .fi
 /*     ADDRESS_REWRITING_README Postfix address manipulation
 /*     FILTER_README, external after-queue content filter
 /*     LOCAL_RECIPIENT_README, blocking unknown local recipients
+/*     MILTER_README, before-queue mail filter applications
 /*     SMTPD_ACCESS_README, built-in access policies
 /*     SMTPD_POLICY_README, external policy server
 /*     SMTPD_PROXY_README, external before-queue content filter
 /* Global library. */
 
 #include <mail_params.h>
+#include <mail_version.h>              /* milter_macro_v */
 #include <record.h>
 #include <rec_type.h>
 #include <mail_proto.h>
 
 #include <mail_server.h>
 
+/* Mail filter library. */
+
+#include <milter.h>
+
 /* Application-specific */
 
 #include <smtpd_token.h>
 #include <smtpd_sasl_proto.h>
 #include <smtpd_sasl_glue.h>
 #include <smtpd_proxy.h>
+#include <smtpd_milter.h>
 
  /*
   * Tunable parameters. Make sure that there is some bound on the length of
@@ -951,19 +1025,48 @@ bool    var_smtpd_tls_wrappermode;
 
 #ifdef USE_TLS
 char   *var_smtpd_relay_ccerts;
+char   *var_smtpd_sasl_tls_opts;
 int     var_smtpd_starttls_tmout;
-bool    var_smtpd_tls_auth_only;
+char   *var_smtpd_tls_CAfile;
+char   *var_smtpd_tls_CApath;
 bool    var_smtpd_tls_ask_ccert;
-bool    var_smtpd_tls_req_ccert;
+bool    var_smtpd_tls_auth_only;
 int     var_smtpd_tls_ccert_vd;
+char   *var_smtpd_tls_cert_file;
+char   *var_smtpd_tls_ciphers;
+char   *var_smtpd_tls_excl_ciph;
+char   *var_smtpd_tls_dcert_file;
+char   *var_smtpd_tls_dh1024_param_file;
+char   *var_smtpd_tls_dh512_param_file;
+char   *var_smtpd_tls_dkey_file;
+char   *var_smtpd_tls_key_file;
+int     var_smtpd_tls_loglevel;
+char   *var_smtpd_tls_protocols;
 bool    var_smtpd_tls_received_header;
-char   *var_smtpd_sasl_tls_opts;
+bool    var_smtpd_tls_req_ccert;
+int     var_smtpd_tls_scache_timeout;
+int     var_tls_daemon_rand_bytes;
 
 #endif
 
 bool    var_smtpd_peername_lookup;
 int     var_plaintext_code;
 bool    var_smtpd_delay_open;
+char   *var_smtpd_milters;
+int     var_milt_conn_time;
+int     var_milt_cmd_time;
+int     var_milt_msg_time;
+char   *var_milt_protocol;
+char   *var_milt_def_action;
+char   *var_milt_daemon_name;
+char   *var_milt_v;
+char   *var_milt_conn_macros;
+char   *var_milt_helo_macros;
+char   *var_milt_mail_macros;
+char   *var_milt_rcpt_macros;
+char   *var_milt_data_macros;
+char   *var_milt_eod_macros;
+char   *var_milt_unk_macros;
 
  /*
   * Silly little macros.
@@ -1029,6 +1132,11 @@ static void chat_reset(SMTPD_STATE *, int);
 #define REASON_LOST_CONNECTION "lost connection"
 #define REASON_ERROR_LIMIT     "too many errors"
 
+ /*
+  * Mail filter initialization status.
+  */
+MILTERS *smtpd_milters;
+
 #ifdef USE_TLS
 
  /*
@@ -1084,11 +1192,80 @@ static void collapse_args(int argc, SMTPD_TOKEN *argv)
     argv[0].strval = STR(argv[0].vstrval);
 }
 
+/* check_milter_reply - process reply from Milter */
+
+static const char *check_milter_reply(SMTPD_STATE *state, const char *reply)
+{
+    const char *queue_id = state->queue_id ? state->queue_id : "NOQUEUE";
+    VSTRING *buf = vstring_alloc(100);
+    const char *action;
+    const char *text;
+
+    /*
+     * XXX Copied from log_whatsup(). Needs to be changed into a reusable
+     * function.
+     */
+    if (state->sender)
+       vstring_sprintf_append(buf, " from=<%s>", state->sender);
+    if (state->recipient)
+       vstring_sprintf_append(buf, " to=<%s>", state->recipient);
+    if (state->protocol)
+       vstring_sprintf_append(buf, " proto=%s", state->protocol);
+    if (state->helo_name)
+       vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
+
+    /*
+     * The syntax of user-specified SMTP replies is checked by the Milter
+     * module, because the replies are also used in the cleanup server.
+     * Automatically disconnect after 421 (shutdown) reply. The Sendmail 8
+     * Milter quarantine action is not final, so it is not included in
+     * MILTER_SKIP_FLAGS.
+     */
+#define MILTER_SKIP_FLAGS (CLEANUP_FLAG_DISCARD)
+
+    switch (reply[0]) {
+    case 'H':
+       state->saved_flags |= CLEANUP_FLAG_HOLD;
+       action = "milter-hold";
+       reply = 0;
+       text = "milter triggers HOLD action";
+       break;
+    case 'D':
+       state->saved_flags |= CLEANUP_FLAG_DISCARD;
+       action = "milter-discard";
+       reply = 0;
+       text = "milter triggers DISCARD action";
+       break;
+    case 'S':
+       state->error_mask |= MAIL_ERROR_POLICY;
+       action = "milter-reject";
+       reply = "421 4.7.0 Server closing connection";
+       text = 0;
+       break;
+    case '4':
+    case '5':
+       state->error_mask |= MAIL_ERROR_POLICY;
+       action = "milter-reject";
+       text = 0;
+       break;
+    default:
+       state->error_mask |= MAIL_ERROR_SOFTWARE;
+       action = "reject";
+       reply = "421 4.3.5 Server configuration error";
+       text = 0;
+       break;
+    }
+    msg_info("%s: %s: %s from %s: %s;%s", queue_id, action, state->where,
+            state->namaddr, reply ? reply : text, STR(buf));
+    vstring_free(buf);
+    return (reply);
+}
+
 /* helo_cmd - process HELO command */
 
 static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
-    char   *err;
+    const char *err;
 
     /*
      * RFC 2034: the text part of all 2xx, 4xx, and 5xx SMTP responses other
@@ -1108,6 +1285,22 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
+
+    /*
+     * XXX Sendmail compatibility: if a Milter rejects CONNECT, EHLO, or
+     * HELO, reply with 250 except in case of 421 (disconnect). The reply
+     * persists so it will apply to MAIL FROM and to other commands such as
+     * AUTH, STARTTLS, and VRFY.
+     */
+    if (smtpd_milters != 0
+       && SMTPD_STAND_ALONE(state) == 0
+       && (state->saved_flags & MILTER_SKIP_FLAGS) == 0
+       && (err = milter_helo_event(smtpd_milters, argv[1].strval, 0)) != 0
+       && (err = check_milter_reply(state, err)) != 0
+       && strncmp(err, "421", 3) == 0) {
+       smtpd_chat_reply(state, "%s", err);
+       return (-1);
+    }
     if (state->helo_name != 0)
        helo_reset(state);
     chat_reset(state, var_smtpd_hist_thrsh);
@@ -1129,7 +1322,7 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 
 static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
-    char   *err;
+    const char *err;
     int     discard_mask;
     VSTRING *reply_buf;
 
@@ -1155,6 +1348,23 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
+
+    /*
+     * XXX Sendmail compatibility: if a Milter 5xx rejects CONNECT, EHLO, or
+     * HELO, reply with ENHANCEDSTATUSCODES except in case of immediate
+     * disconnect. The reply persists so it will apply to MAIL FROM and to
+     * other commands such as AUTH, STARTTLS, and VRFY.
+     */
+    err = 0;
+    if (smtpd_milters != 0
+       && SMTPD_STAND_ALONE(state) == 0
+       && (state->saved_flags & MILTER_SKIP_FLAGS) == 0
+       && (err = milter_helo_event(smtpd_milters, argv[1].strval, 1)) != 0
+       && (err = check_milter_reply(state, err)) != 0
+       && strncmp(err, "421", 3) == 0) {
+       smtpd_chat_reply(state, "%s", err);
+       return (-1);
+    }
     if (state->helo_name != 0)
        helo_reset(state);
     chat_reset(state, var_smtpd_hist_thrsh);
@@ -1196,9 +1406,19 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        vstring_sprintf((reply_buf), (fmt), (arg)); \
     } while (0)
 
+    /*
+     * XXX Sendmail compatibility: if a Milter 5XX rejects CONNECT, EHLO, or
+     * HELO, reply with ENHANCEDSTATUSCODES only. The reply persists so it
+     * will apply to MAIL FROM, but we currently don't have a proper
+     * mechanism to apply Milter rejects to AUTH, STARTTLS, VRFY, and other
+     * commands while still allowing HELO/EHLO.
+     */
     discard_mask = state->ehlo_discard_mask;
-    if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
-       msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
+    if (err != 0 && err[0] == '5')
+       discard_mask |= ~EHLO_MASK_ENHANCEDSTATUSCODES;
+    if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
+       if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
+           msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
 
     reply_buf = vstring_alloc(10);
     vstring_strcpy(reply_buf, var_myhostname);
@@ -1272,9 +1492,12 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 
 static void helo_reset(SMTPD_STATE *state)
 {
-    if (state->helo_name)
+    if (state->helo_name) {
        myfree(state->helo_name);
-    state->helo_name = 0;
+       state->helo_name = 0;
+       if (smtpd_milters)
+           milter_abort(smtpd_milters);
+    }
 }
 
 /* mail_open_stream - open mail queue file or IPC stream */
@@ -1348,11 +1571,18 @@ static int mail_open_stream(SMTPD_STATE *state)
      * Record the time of arrival, the SASL-related stuff if applicable, the
      * sender envelope address, some session information, and some additional
      * attributes.
+     * 
+     * XXX Send Milter information first, because this will hang when cleanup
+     * goes into "throw away" mode. Also, cleanup needs to know early on
+     * whether or not it has to do its own SMTP event emulation.
      */
     if (state->dest) {
        state->cleanup = state->dest->stream;
        state->queue_id = mystrdup(state->dest->id);
        if (SMTPD_STAND_ALONE(state) == 0) {
+           if (smtpd_milters != 0
+               && (state->saved_flags & MILTER_SKIP_FLAGS) == 0)
+               (void) milter_send(smtpd_milters, state->dest->stream);
            rec_fprintf(state->cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
                        REC_TYPE_TIME_ARG(state->arrival_time));
            if (*var_filter_xport)
@@ -1404,24 +1634,42 @@ static int mail_open_stream(SMTPD_STATE *state)
                        MAIL_ATTR_ENCODING, state->encoding);
 
        /*
-        * Store the client attributes for logging purposes.
+        * Store client attributes.
         */
        if (SMTPD_STAND_ALONE(state) == 0) {
+
+           /*
+            * Attributes for logging, also used for XFORWARD.
+            */
            if (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)))
                rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                           MAIL_ATTR_CLIENT_NAME, FORWARD_NAME(state));
+                           MAIL_ATTR_LOG_CLIENT_NAME, FORWARD_NAME(state));
            if (IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))
                rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                           MAIL_ATTR_CLIENT_ADDR, FORWARD_ADDR(state));
+                           MAIL_ATTR_LOG_CLIENT_ADDR, FORWARD_ADDR(state));
            if (IS_AVAIL_CLIENT_NAMADDR(FORWARD_NAMADDR(state)))
                rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                           MAIL_ATTR_ORIGIN, FORWARD_NAMADDR(state));
+                           MAIL_ATTR_LOG_ORIGIN, FORWARD_NAMADDR(state));
            if (IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)))
                rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                           MAIL_ATTR_HELO_NAME, FORWARD_HELO(state));
+                           MAIL_ATTR_LOG_HELO_NAME, FORWARD_HELO(state));
            if (IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)))
                rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                           MAIL_ATTR_PROTO_NAME, FORWARD_PROTO(state));
+                           MAIL_ATTR_LOG_PROTO_NAME, FORWARD_PROTO(state));
+
+           /*
+            * Attributes with actual client information. These are used by
+            * Milters in case mail is re-injected with "postsuper -R".
+            */
+           rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+                       MAIL_ATTR_ACT_CLIENT_NAME, state->name);
+           rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+                       MAIL_ATTR_ACT_CLIENT_ADDR, state->addr);
+           if (state->helo_name)
+               rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+                           MAIL_ATTR_ACT_HELO_NAME, state->helo_name);
+           rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u",
+                       MAIL_ATTR_ACT_CLIENT_AF, state->addr_family);
        }
        if (state->verp_delims)
            rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
@@ -1445,7 +1693,7 @@ static int mail_open_stream(SMTPD_STATE *state)
 static int extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
                                int allow_empty_addr, int strict_rfc821)
 {
-    char   *myname = "extract_addr";
+    const char *myname = "extract_addr";
     TOK822 *tree;
     TOK822 *tp;
     TOK822 *addr = 0;
@@ -1559,11 +1807,34 @@ static int extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
     return (err);
 }
 
+/* milter_argv - impedance adapter */
+
+static const char **milter_argv(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+{
+    int     n;
+    ssize_t len = argc + 1;
+
+    if (state->milter_argc < len) {
+       if (state->milter_argc > 0)
+           state->milter_argv = (const char **)
+               myrealloc((char *) state->milter_argv,
+                         sizeof(const char *) * len);
+       else
+           state->milter_argv = (const char **)
+               mymalloc(sizeof(const char *) * len);
+       state->milter_argc = len;
+    }
+    for (n = 0; n < argc; n++)
+       state->milter_argv[n] = argv[n].strval;
+    state->milter_argv[n] = 0;
+    return (state->milter_argv);
+}
+
 /* mail_cmd - process MAIL command */
 
 static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
-    char   *err;
+    const char *err;
     int     narg;
     char   *arg;
     char   *verp_delims = 0;
@@ -1645,8 +1916,8 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
            }
            /* Reject size overflow. */
            if ((state->msg_size = off_cvt_string(arg + 5)) < 0) {
-               smtpd_chat_reply(state, "552 5.3.4 Message size exceeds file system imposed limit");
                state->error_mask |= MAIL_ERROR_POLICY;
+               smtpd_chat_reply(state, "552 5.3.4 Message size exceeds file system imposed limit");
                return (-1);
            }
 #ifdef USE_SASL_AUTH
@@ -1718,17 +1989,33 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     if (SMTPD_STAND_ALONE(state) == 0
        && var_smtpd_delay_reject == 0
        && (err = smtpd_check_mail(state, STR(state->addr_buf))) != 0) {
-       smtpd_chat_reply(state, "%s", err);
        /* XXX Reset access map side effects. */
        mail_reset(state);
+       smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
+    if (smtpd_milters != 0
+       && SMTPD_STAND_ALONE(state) == 0
+       && (state->saved_flags & MILTER_SKIP_FLAGS) == 0) {
+       state->sender = STR(state->addr_buf);
+       err = milter_mail_event(smtpd_milters,
+                               milter_argv(state, argc - 2, argv + 2));
+       state->sender = 0;
+       if (err != 0 && (err = check_milter_reply(state, err)) != 0) {
+           /* XXX Reset access map side effects. */
+           mail_reset(state);
+           smtpd_chat_reply(state, "%s", err);
+           return (-1);
+       }
+    }
 
     /*
      * Check the queue file space, if applicable.
      */
     if (!USE_SMTPD_PROXY(state)) {
        if ((err = smtpd_check_queue(state)) != 0) {
+           /* XXX Reset access map side effects. */
+           mail_reset(state);
            smtpd_chat_reply(state, "%s", err);
            return (-1);
        }
@@ -1748,8 +2035,11 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        state->dsn_envid = mystrdup(STR(state->dsn_buf));
     if (USE_SMTPD_PROXY(state))
        state->proxy_mail = mystrdup(STR(state->buffer));
-    if (var_smtpd_delay_open == 0 && mail_open_stream(state) < 0)
+    if (var_smtpd_delay_open == 0 && mail_open_stream(state) < 0) {
+       /* XXX Reset access map side effects. */
+       mail_reset(state);
        return (-1);
+    }
     smtpd_chat_reply(state, "250 2.1.0 Ok");
     return (0);
 }
@@ -1777,6 +2067,8 @@ static void mail_reset(SMTPD_STATE *state)
        state->queue_id = 0;
     }
     if (state->sender) {
+       if (SMTPD_STAND_ALONE(state) == 0 && smtpd_milters != 0)
+           milter_abort(smtpd_milters);
        myfree(state->sender);
        state->sender = 0;
     }
@@ -1824,13 +2116,18 @@ static void mail_reset(SMTPD_STATE *state)
        myfree(state->dsn_envid);
        state->dsn_envid = 0;
     }
+    if (state->milter_argv) {
+       myfree((char *) state->milter_argv);
+       state->milter_argv = 0;
+       state->milter_argc = 0;
+    }
 }
 
 /* rcpt_cmd - process RCPT TO command */
 
 static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
-    char   *err;
+    const char *err;
     int     narg;
     char   *arg;
     int     rate;
@@ -1875,10 +2172,10 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
                           &rate) == ANVIL_STAT_OK
        && rate > var_smtpd_crcpt_limit) {
        state->error_mask |= MAIL_ERROR_POLICY;
-       smtpd_chat_reply(state, "450 4.7.1 Error: too many recipients from %s",
-                        state->addr);
        msg_warn("Recipient address rate limit exceeded: %d from %s for service %s",
                 rate, state->namaddr, state->service);
+       smtpd_chat_reply(state, "450 4.7.1 Error: too many recipients from %s",
+                        state->addr);
        return (-1);
     }
     if (argv[2].tokval == SMTPD_TOK_ERROR) {
@@ -1942,6 +2239,17 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
            smtpd_chat_reply(state, "%s", err);
            return (-1);
        }
+       if (smtpd_milters != 0
+           && (state->saved_flags & MILTER_SKIP_FLAGS) == 0) {
+           state->recipient = STR(state->addr_buf);
+           err = milter_rcpt_event(smtpd_milters,
+                                   milter_argv(state, argc - 2, argv + 2));
+           state->recipient = 0;
+           if (err != 0 && (err = check_milter_reply(state, err)) != 0) {
+               smtpd_chat_reply(state, "%s", err);
+               return (-1);
+           }
+       }
     }
 
     /*
@@ -2079,7 +2387,7 @@ static void comment_sanitize(VSTRING *comment_string)
 
 static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
 {
-    char   *err;
+    const char *err;
     char   *start;
     int     len;
     int     curr_rec_type;
@@ -2093,7 +2401,6 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
     int     out_error;
     char  **cpp;
     CLEANUP_STAT_DETAIL *detail;
-    int     smtp_code;
 
 #ifdef USE_TLS
     VSTRING *peer_CN;
@@ -2128,6 +2435,14 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
+    if (smtpd_milters != 0
+       && SMTPD_STAND_ALONE(state) == 0
+       && (state->saved_flags & MILTER_SKIP_FLAGS) == 0
+       && (err = milter_data_event(smtpd_milters)) != 0
+       && (err = check_milter_reply(state, err)) != 0) {
+       smtpd_chat_reply(state, "%s", err);
+       return (-1);
+    }
     if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_WANT_MORE,
                                        "%s", STR(state->buffer)) != 0) {
        smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
@@ -2161,8 +2476,11 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      * recipient, list the recipient address.
      */
     if (state->cleanup) {
-       if (state->saved_flags)
-           rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d", state->saved_flags);
+       if (SMTPD_STAND_ALONE(state) == 0) {
+           if (state->saved_flags)
+               rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
+                           state->saved_flags);
+       }
        rec_fputs(state->cleanup, REC_TYPE_MESG, "");
     }
 
@@ -2386,6 +2704,16 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
                             "250 2.0.0 Ok: queued as %s", state->queue_id);
        else
            smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+    } else if ((state->err & CLEANUP_STAT_DEFER) != 0) {
+       state->error_mask |= MAIL_ERROR_POLICY;
+       detail = cleanup_stat_detail(CLEANUP_STAT_DEFER);
+       if (why && LEN(why) > 0) {
+           /* Allow address-specific DSN status in header/body_checks. */
+           smtpd_chat_reply(state, "%d %s", detail->smtp, STR(why));
+       } else {
+           smtpd_chat_reply(state, "%d %s Error: %s",
+                            detail->smtp, detail->dsn, detail->text);
+       }
     } else if ((state->err & CLEANUP_STAT_BAD) != 0) {
        state->error_mask |= MAIL_ERROR_SOFTWARE;
        detail = cleanup_stat_detail(CLEANUP_STAT_BAD);
@@ -2408,10 +2736,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
            smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
        } else if (why && LEN(why) > 0) {
            /* Allow address-specific DSN status in header/body_checks. */
-           smtp_code = (STR(why)[0] == '5' ? (detail->smtp % 100) + 500 :
-                        STR(why)[0] == '4' ? (detail->smtp % 100) + 400 :
-                        detail->smtp);
-           smtpd_chat_reply(state, "%d %s", smtp_code, STR(why));
+           smtpd_chat_reply(state, "%d %s", detail->smtp, STR(why));
        } else {
            smtpd_chat_reply(state, "%d %s Error: %s",
                             detail->smtp, detail->dsn, detail->text);
@@ -2498,7 +2823,7 @@ static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
 
 static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
-    char   *err = 0;
+    const char *err = 0;
 
     /*
      * The SMTP standard (RFC 821) disallows unquoted special characters in
@@ -2529,6 +2854,12 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "502 5.5.1 VRFY command is disabled");
        return (-1);
     }
+    if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0
+       && (err[0] == '5' || err[0] == '4')) {
+       state->error_mask |= MAIL_ERROR_POLICY;
+       smtpd_chat_reply(state, "%s", err);
+       return (-1);
+    }
     if (argc < 2) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
        smtpd_chat_reply(state, "501 5.5.4 Syntax: VRFY address");
@@ -2565,7 +2896,7 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 
 static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
-    char   *err;
+    const char *err;
 
     /*
      * Sanity checks.
@@ -2575,6 +2906,12 @@ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
        return (-1);
     }
+    if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0
+       && (err[0] == '5' || err[0] == '4')) {
+       state->error_mask |= MAIL_ERROR_POLICY;
+       smtpd_chat_reply(state, "%s", err);
+       return (-1);
+    }
     if (IN_MAIL_TRANSACTION(state)) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
        smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
@@ -2809,6 +3146,13 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
            }
            UPDATE_STR(state->addr, bare_value);
            UPDATE_STR(state->rfc_addr, attr_value);
+#ifdef HAS_IPV6
+           if (strncasecmp(attr_value, INET_PROTO_NAME_IPV6 ":",
+                           sizeof(INET_PROTO_NAME_IPV6 ":") - 1) == 0)
+               state->addr_family = AF_INET6;
+           else
+#endif
+               state->addr_family = AF_INET;
            update_namaddr = 1;
        }
 
@@ -2891,7 +3235,7 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        helo_reset(state);
     if (got_proto == 0 && strcasecmp(state->protocol, MAIL_PROTO_SMTP) != 0) {
        myfree(state->protocol);
-        state->protocol = mystrdup(MAIL_PROTO_SMTP);
+       state->protocol = mystrdup(MAIL_PROTO_SMTP);
     }
 #ifdef USE_SASL_AUTH
     if (var_smtpd_sasl_enable)
@@ -2900,6 +3244,8 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     chat_reset(state, 0);
     mail_reset(state);
     rcpt_reset(state);
+    if (smtpd_milters)
+       milter_disc_event(smtpd_milters);
     vstream_longjmp(state->client, SMTP_ERR_NONE);
     return (0);
 }
@@ -2922,7 +3268,7 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        XFORWARD_DOMAIN, SMTPD_STATE_XFORWARD_DOMAIN,
        0, 0,
     };
-    static char *context_name[] = {
+    static const char *context_name[] = {
        MAIL_ATTR_RWR_LOCAL,            /* Postfix internal form */
        MAIL_ATTR_RWR_REMOTE,           /* Postfix internal form */
     };
@@ -3157,7 +3503,8 @@ static void smtpd_start_tls(SMTPD_STATE *state)
      */
     state->tls_context =
        tls_server_start(smtpd_tls_ctx, state->client,
-                        var_smtpd_starttls_tmout, state->name, state->addr,
+                        var_smtpd_starttls_tmout, var_smtpd_tls_loglevel,
+                        state->name, state->addr,
                       (var_smtpd_tls_req_ccert && state->tls_enforce_tls));
 
     /*
@@ -3176,11 +3523,11 @@ static void smtpd_start_tls(SMTPD_STATE *state)
                             &rate) == ANVIL_STAT_OK
        && rate > var_smtpd_cntls_limit) {
        state->error_mask |= MAIL_ERROR_POLICY;
+       msg_warn("New TLS session rate limit exceeded: %d from %s for service %s",
+                rate, state->namaddr, state->service);
        smtpd_chat_reply(state,
                    "421 4.7.0 %s Error: too many new TLS sessions from %s",
                         var_myhostname, state->namaddr);
-       msg_warn("New TLS session rate limit exceeded: %d from %s for service %s",
-                rate, state->namaddr, state->service);
        /* XXX Use regular return to signal end of session. */
        vstream_longjmp(state->client, SMTP_ERR_QUIET);
     }
@@ -3211,6 +3558,7 @@ static void smtpd_start_tls(SMTPD_STATE *state)
 
 static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
 {
+    const char *err;
     int     rate;
 
     if (argc != 1) {
@@ -3218,6 +3566,19 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        smtpd_chat_reply(state, "501 5.5.4 Syntax: STARTTLS");
        return (-1);
     }
+    if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0) {
+       if (err[0] == '5') {
+           state->error_mask |= MAIL_ERROR_POLICY;
+           smtpd_chat_reply(state, "%s", err);
+           return (-1);
+       }
+       /* Sendmail compatibility: map 4xx into 454. */
+       else if (err[0] == '4') {
+           state->error_mask |= MAIL_ERROR_POLICY;
+           smtpd_chat_reply(state, "454 4.3.0 Try again later");
+           return (-1);
+       }
+    }
     if (state->tls_context != 0) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
        smtpd_chat_reply(state, "554 5.5.1 Error: TLS already active");
@@ -3230,6 +3591,7 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        return (-1);
     }
     if (smtpd_tls_ctx == 0) {
+       state->error_mask |= MAIL_ERROR_SOFTWARE;
        smtpd_chat_reply(state, "454 4.3.0 TLS not available due to local problem");
        return (-1);
     }
@@ -3248,11 +3610,11 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
                                  &rate) == ANVIL_STAT_OK
        && rate > var_smtpd_cntls_limit) {
        state->error_mask |= MAIL_ERROR_POLICY;
+       msg_warn("Refusing STARTTLS request from %s for service %s",
+                state->namaddr, state->service);
        smtpd_chat_reply(state,
                       "454 4.7.0 Error: too many new TLS sessions from %s",
                         state->namaddr);
-       msg_warn("Refusing STARTTLS request from %s for service %s",
-                state->namaddr, state->service);
        return (-1);
     }
     smtpd_chat_reply(state, "220 2.0.0 Ready to start TLS");
@@ -3344,9 +3706,9 @@ static void smtpd_proto(SMTPD_STATE *state)
     int     argc;
     SMTPD_TOKEN *argv;
     SMTPD_CMD *cmdp;
-    int     count;
-    int     crate;
+    int     tls_rate;
     const char *ehlo_words;
+    const char *err;
     int     status;
 
     /*
@@ -3409,7 +3771,7 @@ static void smtpd_proto(SMTPD_STATE *state)
        if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) {
            if (smtpd_tls_ctx == 0) {
                msg_warn("Wrapper-mode request dropped from %s for service %s."
-                        " TLS context initialization failed. For details see"
+                      " TLS context initialization failed. For details see"
                         " earlier warnings in your logs.",
                         state->namaddr, state->service);
                break;
@@ -3419,8 +3781,8 @@ static void smtpd_proto(SMTPD_STATE *state)
                && anvil_clnt
                && !namadr_list_match(hogger_list, state->name, state->addr)
                && anvil_clnt_newtls_stat(anvil_clnt, state->service,
-                                      state->addr, &crate) == ANVIL_STAT_OK
-               && crate > var_smtpd_cntls_limit) {
+                                   state->addr, &tls_rate) == ANVIL_STAT_OK
+               && tls_rate > var_smtpd_cntls_limit) {
                state->error_mask |= MAIL_ERROR_POLICY;
                msg_warn("Refusing TLS service request from %s for service %s",
                         state->namaddr, state->service);
@@ -3446,27 +3808,32 @@ static void smtpd_proto(SMTPD_STATE *state)
            && anvil_clnt
            && !namadr_list_match(hogger_list, state->name, state->addr)
            && anvil_clnt_connect(anvil_clnt, state->service, state->addr,
-                                 &count, &crate) == ANVIL_STAT_OK) {
-           if (var_smtpd_cconn_limit > 0 && count > var_smtpd_cconn_limit) {
+                                 &state->conn_count, &state->conn_rate)
+           == ANVIL_STAT_OK) {
+           if (var_smtpd_cconn_limit > 0
+               && state->conn_count > var_smtpd_cconn_limit) {
                state->error_mask |= MAIL_ERROR_POLICY;
+               msg_warn("Connection concurrency limit exceeded: %d from %s for service %s",
+                        state->conn_count, state->namaddr, state->service);
                smtpd_chat_reply(state, "421 4.7.0 %s Error: too many connections from %s",
                                 var_myhostname, state->addr);
-               msg_warn("Connection concurrency limit exceeded: %d from %s for service %s",
-                        count, state->namaddr, state->service);
                break;
            }
-           if (var_smtpd_crate_limit > 0 && crate > var_smtpd_crate_limit) {
+           if (var_smtpd_crate_limit > 0
+               && state->conn_rate > var_smtpd_crate_limit) {
+               msg_warn("Connection rate limit exceeded: %d from %s for service %s",
+                        state->conn_rate, state->namaddr, state->service);
                smtpd_chat_reply(state, "421 4.7.0 %s Error: too many connections from %s",
                                 var_myhostname, state->addr);
-               msg_warn("Connection rate limit exceeded: %d from %s for service %s",
-                        crate, state->namaddr, state->service);
                break;
            }
        }
        /* XXX We use the real client for connect access control. */
        if (SMTPD_STAND_ALONE(state) == 0
            && var_smtpd_delay_reject == 0
-           && (state->access_denied = smtpd_check_client(state)) != 0) {
+           && (err = smtpd_check_client(state)) != 0) {
+           state->error_mask |= MAIL_ERROR_POLICY;
+           state->access_denied = mystrdup(err);
            smtpd_chat_reply(state, "%s", state->access_denied);
            state->error_count++;
        }
@@ -3476,8 +3843,39 @@ static void smtpd_proto(SMTPD_STATE *state)
         * other than the initial greeting and any response to HELO or EHLO
         * are prefaced with a status code as defined in RFC 3463.
         */
+
+       /*
+        * XXX If a Milter rejects CONNECT, reply with 220 except in case of
+        * hard reject or 421 (disconnect). The reply persists so it will
+        * apply to MAIL FROM and to other commands such as AUTH, STARTTLS,
+        * and VRFY. Note: after a Milter CONNECT reject, we must not reject
+        * HELO or EHLO, but we do change the feature list that is announced
+        * in the EHLO response.
+        */
+#define XXX_NO_PORT    "0"
        else {
-           smtpd_chat_reply(state, "220 %s", var_smtpd_banner);
+           err = 0;
+           if (smtpd_milters != 0 && SMTPD_STAND_ALONE(state) == 0) {
+               milter_macro_callback(smtpd_milters, smtpd_milter_eval,
+                                     (void *) state);
+               if ((err = milter_conn_event(smtpd_milters, state->reverse_name,
+                                            state->addr, XXX_NO_PORT,
+                                            state->addr_family)) != 0)
+                   err = check_milter_reply(state, err);
+           }
+           if (err && err[0] == '5') {
+               state->error_mask |= MAIL_ERROR_POLICY;
+               smtpd_chat_reply(state, "554 %s ESMTP not accepting connections",
+                                var_myhostname);
+               state->error_count++;
+           } else if (err && strncmp(err, "421", 3) == 0) {
+               state->error_mask |= MAIL_ERROR_POLICY;
+               smtpd_chat_reply(state, "421 %s Service unavailable - try again later",
+                                var_myhostname);
+               /* Not: state->error_count++; */
+           } else {
+               smtpd_chat_reply(state, "220 %s", var_smtpd_banner);
+           }
        }
 
        /*
@@ -3490,6 +3888,8 @@ static void smtpd_proto(SMTPD_STATE *state)
        state->ehlo_discard_mask = ehlo_mask(ehlo_words);
 
        for (;;) {
+           if (state->flags & SMTPD_FLAG_HANGUP)
+               break;
            if (state->error_count >= var_smtpd_hard_erlim) {
                state->reason = REASON_ERROR_LIMIT;
                state->error_mask |= MAIL_ERROR_PROTOCOL;
@@ -3516,6 +3916,7 @@ static void smtpd_proto(SMTPD_STATE *state)
                if (strcasecmp(argv[0].strval, cmdp->name) == 0)
                    break;
            if (cmdp->name == 0) {
+               state->where = SMTPD_CMD_UNKNOWN;
                if (is_header(argv[0].strval)
                    || (*var_smtpd_forbid_cmds
                 && string_list_match(smtpd_forbid_cmds, argv[0].strval))) {
@@ -3524,7 +3925,14 @@ static void smtpd_proto(SMTPD_STATE *state)
                    smtpd_chat_reply(state, "221 2.7.0 Error: I can break rules, too. Goodbye.");
                    break;
                }
-               smtpd_chat_reply(state, "502 5.5.2 Error: command not recognized");
+               if (smtpd_milters != 0
+                   && SMTPD_STAND_ALONE(state) == 0
+                   && (err = milter_unknown_event(smtpd_milters,
+                                                  argv[0].strval)) != 0
+                   && (err = check_milter_reply(state, err)) != 0) {
+                   smtpd_chat_reply(state, err);
+               } else
+                   smtpd_chat_reply(state, "502 5.5.2 Error: command not recognized");
                state->error_mask |= MAIL_ERROR_PROTOCOL;
                state->error_count++;
                continue;
@@ -3604,6 +4012,8 @@ static void smtpd_proto(SMTPD_STATE *state)
     chat_reset(state, 0);
     mail_reset(state);
     rcpt_reset(state);
+    if (smtpd_milters)
+       milter_disc_event(smtpd_milters);
 }
 
 /* smtpd_service - service one client */
@@ -3699,6 +4109,8 @@ static void pre_accept(char *unused_name, char **unused_argv)
 
 static void pre_jail_init(char *unused_name, char **unused_argv)
 {
+    int     enforce_tls = var_smtpd_tls_wrappermode || var_smtpd_enforce_tls;
+    int     use_tls = var_smtpd_use_tls || enforce_tls;
 
     /*
      * Initialize blacklist/etc. patterns before entering the chroot jail, in
@@ -3733,15 +4145,79 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
      * used in this scenario anyhow.
      */
     if (getuid() == 0 || getuid() == var_owner_uid) {
-       if (var_smtpd_use_tls || var_smtpd_enforce_tls
-           || var_smtpd_tls_wrappermode)
+       if (use_tls) {
 #ifdef USE_TLS
-           smtpd_tls_ctx =
-               tls_server_init(var_smtpd_tls_ccert_vd,
-                               var_smtpd_tls_ask_ccert);
+           tls_server_props props;
+           int     havecert;
+           int     oknocert;
+           int     wantcert;
+
+           /*
+            * We get stronger type safety and a cleaner interface by
+            * combining the various parameters into a single
+            * tls_server_props structure.
+            */
+           props.log_level = var_smtpd_tls_loglevel;
+           props.verifydepth = var_smtpd_tls_ccert_vd;
+           props.cache_type = TLS_MGR_SCACHE_SMTPD;
+           props.scache_timeout = var_smtpd_tls_scache_timeout;
+           props.cert_file = var_smtpd_tls_cert_file;
+           props.key_file = var_smtpd_tls_key_file;
+           props.dcert_file = var_smtpd_tls_dcert_file;
+           props.dkey_file = var_smtpd_tls_dkey_file;
+           props.CAfile = var_smtpd_tls_CAfile;
+           props.CApath = var_smtpd_tls_CApath;
+           props.dh1024_param_file = var_smtpd_tls_dh1024_param_file;
+           props.dh512_param_file = var_smtpd_tls_dh512_param_file;
+           props.protocols = *var_smtpd_tls_protocols ?
+               tls_protocol_mask(VAR_SMTPD_TLS_PROTO,
+                                 var_smtpd_tls_protocols) : 0;
+           props.ask_ccert = var_smtpd_tls_ask_ccert;
+
+           /*
+            * Can't use anonymous ciphers if we want client certificates.
+            * Must use anonymous ciphers if we have no certificates.
+            * 
+            * XXX: Ugh! Too many booleans!
+            */
+           wantcert = props.ask_ccert;
+           wantcert |= enforce_tls && var_smtpd_tls_req_ccert;
+           oknocert = strcasecmp(props.cert_file, "none") == 0;
+           if (oknocert)
+               props.cert_file = "";
+           havecert = *props.cert_file || *props.dcert_file;
+
+           if (!havecert && wantcert)
+               msg_warn("Need a server cert to request client certs");
+           if (!enforce_tls && var_smtpd_tls_req_ccert)
+               msg_warn("Can't require client certs unless TLS is required");
+
+           props.cipherlist =
+               tls_cipher_list(tls_cipher_level(var_smtpd_tls_ciphers),
+                               var_smtpd_tls_excl_ciph,
+                               havecert ? "" : "aRSA aDSS",
+                               wantcert ? "aNULL" : "",
+                               TLS_END_EXCLUDE);
+           if (props.cipherlist == 0) {
+               msg_warn("unknown '%s' value '%s' ignored, using 'export'",
+                        VAR_SMTPD_TLS_CIPHERS, var_smtpd_tls_ciphers);
+               props.cipherlist =
+                   tls_cipher_list(TLS_CIPHER_EXPORT,
+                                   var_smtpd_tls_excl_ciph,
+                                   havecert ? "" : "aRSA aDSS",
+                                   wantcert ? "aNULL" : "",
+                                   TLS_END_EXCLUDE);
+           }
+           if (havecert || oknocert)
+               smtpd_tls_ctx = tls_server_init(&props);
+           else if (enforce_tls)
+               msg_fatal("No server certs available. TLS can't be enabled");
+           else
+               msg_warn("No server certs available. TLS won't be enabled");
 #else
            msg_warn("TLS has been selected, but TLS support is not compiled in");
 #endif
+       }
     }
 
     /*
@@ -3770,6 +4246,33 @@ static void post_jail_init(char *unused_name, char **unused_argv)
     smtpd_input_transp_mask =
     input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
 
+    /*
+     * Sendmail mail filters.
+     * 
+     * XXX Should not do this when running in stand-alone mode. But that test
+     * looks at VSTREAM_IN which is not available at this point.
+     * 
+     * XXX Disable non_smtpd_milters when not sending our own mail filter list.
+     */
+    if ((smtpd_input_transp_mask & INPUT_TRANSP_MILTER) == 0) {
+       if (*var_smtpd_milters)
+           smtpd_milters = milter_create(var_smtpd_milters,
+                                         var_milt_conn_time,
+                                         var_milt_cmd_time,
+                                         var_milt_msg_time,
+                                         var_milt_protocol,
+                                         var_milt_def_action,
+                                         var_milt_conn_macros,
+                                         var_milt_helo_macros,
+                                         var_milt_mail_macros,
+                                         var_milt_rcpt_macros,
+                                         var_milt_data_macros,
+                                         var_milt_eod_macros,
+                                         var_milt_unk_macros);
+       else
+           smtpd_input_transp_mask |= INPUT_TRANSP_MILTER;
+    }
+
     /*
      * Sanity checks. The queue_minfree value should be at least as large as
      * (process_limit * message_size_limit) but that is unpractical, so we
@@ -3830,6 +4333,8 @@ int     main(int argc, char **argv)
        VAR_SMTPD_CNTLS_LIMIT, DEF_SMTPD_CNTLS_LIMIT, &var_smtpd_cntls_limit, 0, 0,
 #ifdef USE_TLS
        VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
+       VAR_SMTPD_TLS_LOGLEVEL, DEF_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, 0, 0,
+       VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 1, 0,
 #endif
        0,
     };
@@ -3843,7 +4348,11 @@ int     main(int argc, char **argv)
        VAR_SMTPD_POLICY_TTL, DEF_SMTPD_POLICY_TTL, &var_smtpd_policy_ttl, 1, 0,
 #ifdef USE_TLS
        VAR_SMTPD_STARTTLS_TMOUT, DEF_SMTPD_STARTTLS_TMOUT, &var_smtpd_starttls_tmout, 1, 0,
+       VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0,
 #endif
+       VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, &var_milt_conn_time, 1, 0,
+       VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, &var_milt_cmd_time, 1, 0,
+       VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, &var_milt_msg_time, 1, 0,
        0,
     };
     static CONFIG_BOOL_TABLE bool_table[] = {
@@ -3916,8 +4425,36 @@ int     main(int argc, char **argv)
 #ifdef USE_TLS
        VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_smtpd_relay_ccerts, 0, 0,
        VAR_SMTPD_SASL_TLS_OPTS, DEF_SMTPD_SASL_TLS_OPTS, &var_smtpd_sasl_tls_opts, 0, 0,
+       VAR_SMTPD_TLS_CERT_FILE, DEF_SMTPD_TLS_CERT_FILE, &var_smtpd_tls_cert_file, 0, 0,
+       VAR_SMTPD_TLS_KEY_FILE, DEF_SMTPD_TLS_KEY_FILE, &var_smtpd_tls_key_file, 0, 0,
+       VAR_SMTPD_TLS_DCERT_FILE, DEF_SMTPD_TLS_DCERT_FILE, &var_smtpd_tls_dcert_file, 0, 0,
+       VAR_SMTPD_TLS_DKEY_FILE, DEF_SMTPD_TLS_DKEY_FILE, &var_smtpd_tls_dkey_file, 0, 0,
+       VAR_SMTPD_TLS_CA_FILE, DEF_SMTPD_TLS_CA_FILE, &var_smtpd_tls_CAfile, 0, 0,
+       VAR_SMTPD_TLS_CA_PATH, DEF_SMTPD_TLS_CA_PATH, &var_smtpd_tls_CApath, 0, 0,
+       VAR_SMTPD_TLS_CIPHERS, DEF_SMTPD_TLS_CIPHERS, &var_smtpd_tls_ciphers, 1, 0,
+       VAR_SMTPD_TLS_EXCL_CIPH, DEF_SMTPD_TLS_EXCL_CIPH, &var_smtpd_tls_excl_ciph, 0, 0,
+       VAR_TLS_HIGH_CLIST, DEF_TLS_HIGH_CLIST, &var_tls_high_clist, 1, 0,
+       VAR_TLS_MEDIUM_CLIST, DEF_TLS_MEDIUM_CLIST, &var_tls_medium_clist, 1, 0,
+       VAR_TLS_LOW_CLIST, DEF_TLS_LOW_CLIST, &var_tls_low_clist, 1, 0,
+       VAR_TLS_EXPORT_CLIST, DEF_TLS_EXPORT_CLIST, &var_tls_export_clist, 1, 0,
+       VAR_TLS_NULL_CLIST, DEF_TLS_NULL_CLIST, &var_tls_null_clist, 1, 0,
+       VAR_SMTPD_TLS_PROTO, DEF_SMTPD_TLS_PROTO, &var_smtpd_tls_protocols, 0, 0,
+       VAR_SMTPD_TLS_512_FILE, DEF_SMTPD_TLS_512_FILE, &var_smtpd_tls_dh512_param_file, 0, 0,
+       VAR_SMTPD_TLS_1024_FILE, DEF_SMTPD_TLS_1024_FILE, &var_smtpd_tls_dh1024_param_file, 0, 0,
 #endif
        VAR_SMTPD_SASL_TYPE, DEF_SMTPD_SASL_TYPE, &var_smtpd_sasl_type, 1, 0,
+       VAR_SMTPD_MILTERS, DEF_SMTPD_MILTERS, &var_smtpd_milters, 0, 0,
+       VAR_MILT_CONN_MACROS, DEF_MILT_CONN_MACROS, &var_milt_conn_macros, 0, 0,
+       VAR_MILT_HELO_MACROS, DEF_MILT_HELO_MACROS, &var_milt_helo_macros, 0, 0,
+       VAR_MILT_MAIL_MACROS, DEF_MILT_MAIL_MACROS, &var_milt_mail_macros, 0, 0,
+       VAR_MILT_RCPT_MACROS, DEF_MILT_RCPT_MACROS, &var_milt_rcpt_macros, 0, 0,
+       VAR_MILT_DATA_MACROS, DEF_MILT_DATA_MACROS, &var_milt_data_macros, 0, 0,
+       VAR_MILT_EOD_MACROS, DEF_MILT_EOD_MACROS, &var_milt_eod_macros, 0, 0,
+       VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0,
+       VAR_MILT_PROTOCOL, DEF_MILT_PROTOCOL, &var_milt_protocol, 1, 0,
+       VAR_MILT_DEF_ACTION, DEF_MILT_DEF_ACTION, &var_milt_def_action, 1, 0,
+       VAR_MILT_DAEMON_NAME, DEF_MILT_DAEMON_NAME, &var_milt_daemon_name, 1, 0,
+       VAR_MILT_V, DEF_MILT_V, &var_milt_v, 1, 0,
        0,
     };
     static CONFIG_RAW_TABLE raw_table[] = {
index ebde037d023cb8a345a0fe4d405592a31b51d393..262f73819630ec9c0f594a713f67700b122f09e2 100644 (file)
 #include <tls.h>
 #endif
 
+ /*
+  * Milter library.
+  */
+#include <milter.h>
+
  /*
   * Variables that keep track of conversation state. There is only one SMTP
   * conversation at a time, so the state variables can be made global. And
@@ -61,6 +66,7 @@ typedef struct {
 } SMTPD_XFORWARD_ATTR;
 
 typedef struct SMTPD_STATE {
+    int     flags;                     /* see below */
     int     err;                       /* cleanup server/queue file errors */
     VSTREAM *client;                   /* SMTP client handle */
     VSTRING *buffer;                   /* SMTP client buffer */
@@ -72,9 +78,12 @@ typedef struct SMTPD_STATE {
     char   *addr;                      /* client host address string */
     char   *namaddr;                   /* combined name and address */
     char   *rfc_addr;                  /* address for RFC 2821 */
+    int     addr_family;               /* address family */
     struct sockaddr_storage sockaddr;  /* binary client endpoint */
-    int     name_status;               /* 2=ok, 4=soft, 5=hard */
-    int     reverse_name_status;       /* 2=ok, 4=soft, 5=hard */
+    int     name_status;               /* 2=ok 4=soft 5=hard 6=forged */
+    int     reverse_name_status;       /* 2=ok 4=soft 5=hard */
+    int     conn_count;                        /* connections from this client */
+    int     conn_rate;                 /* connection rate for this client */
     int     error_count;               /* reset after DOT */
     int     error_mask;                        /* client errors */
     int     notify_mask;               /* what to report to postmaster */
@@ -162,8 +171,15 @@ typedef struct SMTPD_STATE {
     TLScontext_t *tls_context;         /* TLS session state */
 #endif
 
+    /*
+     * Milter support.
+     */
+    const char **milter_argv;
+    ssize_t milter_argc;
 } SMTPD_STATE;
 
+#define SMTPD_FLAG_HANGUP      (1<<0)  /* disconnect */
+
 #define SMTPD_STATE_XFORWARD_INIT  (1<<0)      /* xforward preset done */
 #define SMTPD_STATE_XFORWARD_NAME  (1<<1)      /* client name received */
 #define SMTPD_STATE_XFORWARD_ADDR  (1<<2)      /* client address received */
@@ -205,6 +221,7 @@ extern void smtpd_state_reset(SMTPD_STATE *);
 #define SMTPD_CMD_QUIT         "QUIT"
 #define SMTPD_CMD_XCLIENT      "XCLIENT"
 #define SMTPD_CMD_XFORWARD     "XFORWARD"
+#define SMTPD_CMD_UNKNOWN      "UNKNOWN"
 
  /*
   * Representation of unknown client information within smtpd processes. This
@@ -254,6 +271,7 @@ extern void smtpd_peer_reset(SMTPD_STATE *state);
 #define        SMTPD_PEER_CODE_OK      2
 #define SMTPD_PEER_CODE_TEMP   4
 #define SMTPD_PEER_CODE_PERM   5
+#define SMTPD_PEER_CODE_FORGED 6
 
  /*
   * Choose between normal or forwarded attributes.
@@ -300,6 +318,11 @@ extern void smtpd_xforward_reset(SMTPD_STATE *);
   */
 extern int smtpd_input_transp_mask;
 
+ /*
+  * More Milter support.
+  */
+extern MILTERS *smtpd_milters;
+
 /* LICENSE
 /* .ad
 /* .fi
index d91b8131a306af00e4aaf8ecb22da577d07a5673..6239d50b1c732d0c7ca0a97afc786256bf627eeb 100644 (file)
@@ -29,7 +29,8 @@
 /*     smtpd_chat_reply() formats a server reply, sends it to the
 /*     client, and appends a copy to the SMTP transaction log.
 /*     When soft_bounce is enabled, all 5xx (reject) reponses are
-/*     replaced by 4xx (try again).
+/*     replaced by 4xx (try again). In case of a 421 reply the
+/*     SMTPD_FLAG_HANGUP flag is set for orderly disconnect.
 /*
 /*     smtpd_chat_notify() sends a copy of the SMTP transaction log
 /*     to the postmaster for review. The postmaster notice is sent only
@@ -136,7 +137,7 @@ void    smtpd_chat_query(SMTPD_STATE *state)
 
 /* smtpd_chat_reply - format, send and record an SMTP response */
 
-void    smtpd_chat_reply(SMTPD_STATE *state, char *format,...)
+void    smtpd_chat_reply(SMTPD_STATE *state, const char *format,...)
 {
     va_list ap;
     int     delay = 0;
@@ -179,6 +180,12 @@ void    smtpd_chat_reply(SMTPD_STATE *state, char *format,...)
        vstream_longjmp(state->client, SMTP_ERR_TIME);
     if (vstream_ferror(state->client))
        vstream_longjmp(state->client, SMTP_ERR_EOF);
+
+    /*
+     * Orderly disconnect in case of 421 reply.
+     */
+    if (strncmp(STR(state->buffer), "421", 3) == 0)
+       state->flags |= SMTPD_FLAG_HANGUP;
 }
 
 /* print_line - line_wrap callback */
@@ -194,7 +201,7 @@ static void print_line(const char *str, int len, int indent, char *context)
 
 void    smtpd_chat_notify(SMTPD_STATE *state)
 {
-    char   *myname = "smtpd_chat_notify";
+    const char *myname = "smtpd_chat_notify";
     VSTREAM *notice;
     char  **cpp;
 
index a2dab070d03df61d807809801b90a1952193814a..132314b67cdbf3f3202889b89efadd43b97c3f58 100644 (file)
@@ -14,7 +14,7 @@
   */
 extern void smtpd_chat_reset(SMTPD_STATE *);
 extern void smtpd_chat_query(SMTPD_STATE *);
-extern void PRINTFLIKE(2, 3) smtpd_chat_reply(SMTPD_STATE *, char *, ...);
+extern void PRINTFLIKE(2, 3) smtpd_chat_reply(SMTPD_STATE *, const char *, ...);
 extern void smtpd_chat_notify(SMTPD_STATE *);
 
 /* LICENSE
index 83a8c313eb999db6a2bd09114316a999a7674696..a07c3a847f78eab466be6b3b5a6c781ec7620599 100644 (file)
@@ -525,10 +525,10 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
 
 /* has_required - make sure required restriction is present */
 
-static int has_required(ARGV *restrictions, char **required)
+static int has_required(ARGV *restrictions, const char **required)
 {
     char  **rest;
-    char  **reqd;
+    const char **reqd;
     ARGV   *expansion;
 
     /*
@@ -551,10 +551,10 @@ static int has_required(ARGV *restrictions, char **required)
 
 /* fail_required - handle failure to use required restriction */
 
-static void fail_required(char *name, char **required)
+static void fail_required(const char *name, const char **required)
 {
-    char   *myname = "fail_required";
-    char  **reqd;
+    const char *myname = "fail_required";
+    const char **reqd;
     VSTRING *example;
 
     /*
@@ -582,7 +582,7 @@ void    smtpd_check_init(void)
     const char *name;
     const char *value;
     char   *cp;
-    static char *rcpt_required[] = {
+    static const char *rcpt_required[] = {
        CHECK_RELAY_DOMAINS,
        REJECT_UNAUTH_DEST,
        REJECT_ALL,
@@ -920,7 +920,7 @@ static const char *check_mail_addr_find(SMTPD_STATE *state,
 
 static int reject_unknown_reverse_name(SMTPD_STATE *state)
 {
-    char   *myname = "reject_unknown_reverse_name";
+    const char *myname = "reject_unknown_reverse_name";
 
     if (msg_verbose)
        msg_info("%s: %s", myname, state->reverse_name);
@@ -938,14 +938,14 @@ static int reject_unknown_reverse_name(SMTPD_STATE *state)
 
 static int reject_unknown_client(SMTPD_STATE *state)
 {
-    char   *myname = "reject_unknown_client";
+    const char *myname = "reject_unknown_client";
 
     if (msg_verbose)
        msg_info("%s: %s %s", myname, state->name, state->addr);
 
     if (state->name_status != SMTPD_PEER_CODE_OK)
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
-                               state->name_status == SMTPD_PEER_CODE_PERM ?
+                               state->name_status >= SMTPD_PEER_CODE_PERM ?
                                   var_unk_client_code : 450, "4.7.1",
                    "Client host rejected: cannot find your hostname, [%s]",
                                   state->addr));
@@ -956,7 +956,7 @@ static int reject_unknown_client(SMTPD_STATE *state)
 
 static int reject_plaintext_session(SMTPD_STATE *state)
 {
-    char   *myname = "reject_plaintext_session";
+    const char *myname = "reject_plaintext_session";
 
     if (msg_verbose)
        msg_info("%s: %s %s", myname, state->name, state->addr);
@@ -974,7 +974,7 @@ static int reject_plaintext_session(SMTPD_STATE *state)
 
 static int permit_inet_interfaces(SMTPD_STATE *state)
 {
-    char   *myname = "permit_inet_interfaces";
+    const char *myname = "permit_inet_interfaces";
 
     if (msg_verbose)
        msg_info("%s: %s %s", myname, state->name, state->addr);
@@ -988,7 +988,7 @@ static int permit_inet_interfaces(SMTPD_STATE *state)
 
 static int permit_mynetworks(SMTPD_STATE *state)
 {
-    char   *myname = "permit_mynetworks";
+    const char *myname = "permit_mynetworks";
 
     if (msg_verbose)
        msg_info("%s: %s %s", myname, state->name, state->addr);
@@ -1026,7 +1026,7 @@ static char *dup_if_truncate(char *name)
 static int reject_invalid_hostaddr(SMTPD_STATE *state, char *addr,
                                        char *reply_name, char *reply_class)
 {
-    char   *myname = "reject_invalid_hostaddr";
+    const char *myname = "reject_invalid_hostaddr";
     ssize_t len;
     char   *test_addr;
     int     stat;
@@ -1064,7 +1064,7 @@ static int reject_invalid_hostaddr(SMTPD_STATE *state, char *addr,
 static int reject_invalid_hostname(SMTPD_STATE *state, char *name,
                                        char *reply_name, char *reply_class)
 {
-    char   *myname = "reject_invalid_hostname";
+    const char *myname = "reject_invalid_hostname";
     char   *test_name;
     int     stat;
 
@@ -1102,7 +1102,7 @@ static int reject_invalid_hostname(SMTPD_STATE *state, char *name,
 static int reject_non_fqdn_hostname(SMTPD_STATE *state, char *name,
                                        char *reply_name, char *reply_class)
 {
-    char   *myname = "reject_non_fqdn_hostname";
+    const char *myname = "reject_non_fqdn_hostname";
     char   *test_name;
     int     stat;
 
@@ -1139,7 +1139,7 @@ static int reject_non_fqdn_hostname(SMTPD_STATE *state, char *name,
 static int reject_unknown_hostname(SMTPD_STATE *state, char *name,
                                        char *reply_name, char *reply_class)
 {
-    char   *myname = "reject_unknown_hostname";
+    const char *myname = "reject_unknown_hostname";
     int     dns_status;
 
     if (msg_verbose)
@@ -1174,7 +1174,7 @@ static int reject_unknown_hostname(SMTPD_STATE *state, char *name,
 static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name,
                            const char *reply_name, const char *reply_class)
 {
-    char   *myname = "reject_unknown_mailhost";
+    const char *myname = "reject_unknown_mailhost";
     int     dns_status;
 
     if (msg_verbose)
@@ -1241,7 +1241,7 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs)
 static int check_relay_domains(SMTPD_STATE *state, char *recipient,
                                       char *reply_name, char *reply_class)
 {
-    char   *myname = "check_relay_domains";
+    const char *myname = "check_relay_domains";
 
 #if 1
     static int once;
@@ -1282,7 +1282,7 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
 
 static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
 {
-    char   *myname = "permit_auth_destination";
+    const char *myname = "permit_auth_destination";
     const RESOLVE_REPLY *reply;
 
     if (msg_verbose)
@@ -1331,7 +1331,7 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
 
 static int reject_unauth_destination(SMTPD_STATE *state, char *recipient)
 {
-    char   *myname = "reject_unauth_destination";
+    const char *myname = "reject_unauth_destination";
 
     if (msg_verbose)
        msg_info("%s: %s", myname, recipient);
@@ -1356,7 +1356,7 @@ static int reject_unauth_destination(SMTPD_STATE *state, char *recipient)
 static int reject_unauth_pipelining(SMTPD_STATE *state,
                            const char *reply_name, const char *reply_class)
 {
-    char   *myname = "reject_unauth_pipelining";
+    const char *myname = "reject_unauth_pipelining";
 
     if (msg_verbose)
        msg_info("%s: %s", myname, state->where);
@@ -1380,7 +1380,7 @@ static int reject_unauth_pipelining(SMTPD_STATE *state,
 static int all_auth_mx_addr(SMTPD_STATE *state, char *host,
                            const char *reply_name, const char *reply_class)
 {
-    char   *myname = "all_auth_mx_addr";
+    const char *myname = "all_auth_mx_addr";
     MAI_HOSTADDR_STR hostaddr;
     DNS_RR *rr;
     DNS_RR *addr_list;
@@ -1438,7 +1438,7 @@ static int all_auth_mx_addr(SMTPD_STATE *state, char *host,
 static int has_my_addr(SMTPD_STATE *state, const char *host,
                            const char *reply_name, const char *reply_class)
 {
-    char   *myname = "has_my_addr";
+    const char *myname = "has_my_addr";
     struct addrinfo *res;
     struct addrinfo *res0;
     int     aierr;
@@ -1556,7 +1556,7 @@ static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list,
 static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
                            const char *reply_name, const char *reply_class)
 {
-    char   *myname = "permit_mx_backup";
+    const char *myname = "permit_mx_backup";
     const RESOLVE_REPLY *reply;
     const char *domain;
     DNS_RR *mx_list;
@@ -1677,7 +1677,7 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
 static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr,
                                        char *reply_name, char *reply_class)
 {
-    char   *myname = "reject_non_fqdn_address";
+    const char *myname = "reject_non_fqdn_address";
     char   *domain;
     char   *test_dom;
     int     stat;
@@ -1729,7 +1729,7 @@ static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr,
 static int reject_unknown_address(SMTPD_STATE *state, const char *addr,
                            const char *reply_name, const char *reply_class)
 {
-    char   *myname = "reject_unknown_address";
+    const char *myname = "reject_unknown_address";
     const RESOLVE_REPLY *reply;
     const char *domain;
 
@@ -1766,7 +1766,7 @@ static int reject_unverified_address(SMTPD_STATE *state, const char *addr,
                            const char *reply_name, const char *reply_class,
                                             int unv_addr_code)
 {
-    char   *myname = "reject_unverified_address";
+    const char *myname = "reject_unverified_address";
     VSTRING *why = vstring_alloc(10);
     int     rqst_status;
     int     rcpt_status;
@@ -1901,7 +1901,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
                                      const char *reply_class,
                                      const char *def_acl)
 {
-    char   *myname = "check_table_result";
+    const char *myname = "check_table_result";
     int     code;
     ARGV   *restrictions;
     jmp_buf savebuf;
@@ -2237,7 +2237,7 @@ static int check_access(SMTPD_STATE *state, const char *table, const char *name,
                              int flags, int *found, const char *reply_name,
                                const char *reply_class, const char *def_acl)
 {
-    char   *myname = "check_access";
+    const char *myname = "check_access";
     const char *value;
     DICT   *dict;
 
@@ -2260,7 +2260,7 @@ static int check_access(SMTPD_STATE *state, const char *table, const char *name,
                                                 def_acl), FOUND);
        if (dict_errno != 0) {
            msg_warn("%s: table lookup problem", table);
-           value = "450 4.3.0 Server configuration error";
+           value = "451 4.3.5 Server configuration error";
            CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
                                                 reply_name, reply_class,
                                                 def_acl), FOUND);
@@ -2277,7 +2277,7 @@ static int check_domain_access(SMTPD_STATE *state, const char *table,
                                       const char *reply_class,
                                       const char *def_acl)
 {
-    char   *myname = "check_domain_access";
+    const char *myname = "check_domain_access";
     const char *name;
     const char *next;
     const char *value;
@@ -2306,7 +2306,7 @@ static int check_domain_access(SMTPD_STATE *state, const char *table,
                                                     def_acl), FOUND);
            if (dict_errno != 0) {
                msg_warn("%s: table lookup problem", table);
-               value = "450 4.3.0 Server configuration error";
+               value = "451 4.3.5 Server configuration error";
                CHK_DOMAIN_RETURN(check_table_result(state, table, value,
                                            domain, reply_name, reply_class,
                                                     def_acl), FOUND);
@@ -2333,7 +2333,7 @@ static int check_addr_access(SMTPD_STATE *state, const char *table,
                                     const char *reply_class,
                                     const char *def_acl)
 {
-    char   *myname = "check_addr_access";
+    const char *myname = "check_addr_access";
     char   *addr;
     const char *value;
     DICT   *dict;
@@ -2365,7 +2365,7 @@ static int check_addr_access(SMTPD_STATE *state, const char *table,
                                                   def_acl), FOUND);
            if (dict_errno != 0) {
                msg_warn("%s: table lookup problem", table);
-               value = "450 4.3.0 Server configuration error";
+               value = "451 4.3.5 Server configuration error";
                CHK_ADDR_RETURN(check_table_result(state, table, value, address,
                                                   reply_name, reply_class,
                                                   def_acl), FOUND);
@@ -2386,7 +2386,7 @@ static int check_namadr_access(SMTPD_STATE *state, const char *table,
                                       const char *reply_class,
                                       const char *def_acl)
 {
-    char   *myname = "check_namadr_access";
+    const char *myname = "check_namadr_access";
     int     status;
 
     if (msg_verbose)
@@ -2571,7 +2571,7 @@ static int check_ccert_access(SMTPD_STATE *state, const char *table,
                                      const char *def_acl)
 {
 #ifdef USE_TLS
-    char   *myname = "check_ccert_access";
+    const char *myname = "check_ccert_access";
     int     found;
 
     if (!state->tls_context)
@@ -2610,7 +2610,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table,
                                     const char *reply_class,
                                     const char *def_acl)
 {
-    char   *myname = "check_mail_access";
+    const char *myname = "check_mail_access";
     const RESOLVE_REPLY *reply;
     const char *domain;
     int     status;
@@ -2825,15 +2825,15 @@ static const char *smtpd_expand_lookup(const char *name, int unused_mode,
      * 
      * Return NULL only for non-existent names.
      */
-    if (STREQ(name, MAIL_ATTR_CLIENT)) {
+    if (STREQ(name, MAIL_ATTR_ACT_CLIENT)) {
        return (state->namaddr);
-    } else if (STREQ(name, MAIL_ATTR_CLIENT_ADDR)) {
+    } else if (STREQ(name, MAIL_ATTR_ACT_CLIENT_ADDR)) {
        return (state->addr);
-    } else if (STREQ(name, MAIL_ATTR_CLIENT_NAME)) {
+    } else if (STREQ(name, MAIL_ATTR_ACT_CLIENT_NAME)) {
        return (state->name);
-    } else if (STREQ(name, MAIL_ATTR_REVERSE_CLIENT_NAME)) {
+    } else if (STREQ(name, MAIL_ATTR_ACT_REVERSE_CLIENT_NAME)) {
        return (state->reverse_name);
-    } else if (STREQ(name, MAIL_ATTR_HELO_NAME)) {
+    } else if (STREQ(name, MAIL_ATTR_ACT_HELO_NAME)) {
        return (state->helo_name ? state->helo_name : "");
     } else if (STREQN(name, MAIL_ATTR_SENDER, CONST_LEN(MAIL_ATTR_SENDER))) {
        return (smtpd_expand_addr(state->expand_buf, state->sender,
@@ -3057,7 +3057,7 @@ static int rbl_match_addr(SMTPD_RBL_STATE *rbl, const char *addr)
 static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain,
                                   const char *addr, const char *reply_class)
 {
-    char   *myname = "reject_rbl";
+    const char *myname = "reject_rbl";
     ARGV   *octets;
     VSTRING *query;
     int     i;
@@ -3105,7 +3105,7 @@ static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain,
 static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain,
                                  const char *what, const char *reply_class)
 {
-    char   *myname = "reject_rbl_domain";
+    const char *myname = "reject_rbl_domain";
     VSTRING *query;
     SMTPD_RBL_STATE *rbl;
     const char *domain;
@@ -3148,7 +3148,7 @@ static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain,
 
 static int reject_maps_rbl(SMTPD_STATE *state)
 {
-    char   *myname = "reject_maps_rbl";
+    const char *myname = "reject_maps_rbl";
     char   *saved_domains = mystrdup(var_maps_rbl_domains);
     char   *bp = saved_domains;
     char   *rbl_domain;
@@ -3269,12 +3269,12 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
                          ATTR_FLAG_NONE,       /* Query attributes. */
                        ATTR_TYPE_STR, MAIL_ATTR_REQ, "smtpd_access_policy",
                          ATTR_TYPE_STR, MAIL_ATTR_PROTO_STATE, state->where,
-                      ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, state->protocol,
-                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, state->addr,
-                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, state->name,
-                         ATTR_TYPE_STR, MAIL_ATTR_REVERSE_CLIENT_NAME,
+                  ATTR_TYPE_STR, MAIL_ATTR_ACT_PROTO_NAME, state->protocol,
+                     ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, state->addr,
+                     ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_NAME, state->name,
+                         ATTR_TYPE_STR, MAIL_ATTR_ACT_REVERSE_CLIENT_NAME,
                          state->reverse_name,
-                         ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME,
+                         ATTR_TYPE_STR, MAIL_ATTR_ACT_HELO_NAME,
                          state->helo_name ? state->helo_name : "",
                          ATTR_TYPE_STR, MAIL_ATTR_SENDER,
                          state->sender ? state->sender : "",
@@ -3389,7 +3389,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                                  const char *reply_class,
                                  const char *def_acl)
 {
-    char   *myname = "generic_checks";
+    const char *myname = "generic_checks";
     char  **cpp;
     const char *name;
     int     status = 0;
@@ -3821,7 +3821,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
 int     smtpd_check_addr(const char *addr)
 {
     const RESOLVE_REPLY *resolve_reply;
-    char   *myname = "smtpd_check_addr";
+    const char *myname = "smtpd_check_addr";
 
     if (msg_verbose)
        msg_info("%s: addr=%s", myname, addr);
@@ -4412,7 +4412,7 @@ char   *smtpd_check_size(SMTPD_STATE *state, off_t size)
 
 char   *smtpd_check_queue(SMTPD_STATE *state)
 {
-    char   *myname = "smtpd_check_queue";
+    const char *myname = "smtpd_check_queue";
     struct fsspace fsbuf;
     int     status;
 
@@ -5019,7 +5019,7 @@ int     main(int argc, char **argv)
        case 4:
        case 3:
            if (strcasecmp(args->argv[0], "client") == 0) {
-               state.where = "CONNECT";
+               state.where = SMTPD_AFTER_CONNECT;
                UPDATE_STRING(state.name, args->argv[1]);
                UPDATE_STRING(state.reverse_name, args->argv[1]);
                UPDATE_STRING(state.addr, args->argv[2]);
diff --git a/postfix/src/smtpd/smtpd_milter.c b/postfix/src/smtpd/smtpd_milter.c
new file mode 100644 (file)
index 0000000..bf322c9
--- /dev/null
@@ -0,0 +1,152 @@
+/*++
+/* NAME
+/*     smtpd_milter 3
+/* SUMMARY
+/*     SMTP server milter glue
+/* SYNOPSIS
+/*     #include <smtpd.h>
+/*     #include <smtpd_milter.h>
+/*
+/*     const char *smtpd_milter_eval(name, context)
+/*     const char *name;
+/*     void    *context;
+/* DESCRIPTION
+/*     smtpd_milter_eval() is a milter(3) call-back routine to
+/*     expand Sendmail macros before they are sent to filters.
+/* DIAGNOSTICS
+/*     Panic: interface violations. Fatal errors: out of memory.
+/*     internal protocol errors.
+/* 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>
+
+/* Utility library. */
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Milter library. */
+
+#include <milter.h>
+
+/* Application-specific. */
+
+#include <smtpd.h>
+#include <smtpd_milter.h>
+
+ /*
+  * SLMs.
+  */
+#define STR(x) vstring_str(x)
+
+/* smtpd_milter_eval - evaluate milter macro */
+
+const char *smtpd_milter_eval(const char *name, void *ptr)
+{
+    SMTPD_STATE *state = (SMTPD_STATE *) ptr;
+
+    /*
+     * Canonicalize the name.
+     */
+    if (*name != '{') {                                /* } */
+       if (state->expand_buf == 0)
+           state->expand_buf = vstring_alloc(10);
+       vstring_sprintf(state->expand_buf, "{%s}", name);
+       name = STR(state->expand_buf);
+    }
+
+    /*
+     * System macros.
+     */
+    if (strcmp(name, S8_MAC_DAEMON_NAME) == 0)
+       return (var_milt_daemon_name);
+    if (strcmp(name, S8_MAC_V) == 0)
+       return (var_milt_v);
+
+    /*
+     * Connect macros.
+     */
+    if (strcmp(name, S8_MAC_J) == 0)
+       return (var_myhostname);
+    if (strcmp(name, S8_MAC_CLIENT_ADDR) == 0)
+       return (state->rfc_addr);
+    if (strcmp(name, S8_MAC_CLIENT_CONN) == 0) {
+       if (state->expand_buf == 0)
+           state->expand_buf = vstring_alloc(10);
+       vstring_sprintf(state->expand_buf, "%d", state->conn_count);
+       return (STR(state->expand_buf));
+    }
+    if (strcmp(name, S8_MAC_CLIENT_NAME) == 0)
+       return (state->name);
+    if (strcmp(name, S8_MAC_CLIENT_PTR) == 0)
+       return (state->reverse_name);
+    if (strcmp(name, S8_MAC_CLIENT_RES) == 0)
+       return (state->name_status == SMTPD_PEER_CODE_OK ? "OK" :
+               state->name_status == SMTPD_PEER_CODE_FORGED ? "FORGED" :
+             state->name_status == SMTPD_PEER_CODE_TEMP ? "TEMP" : "FAIL");
+
+    /*
+     * HELO macros.
+     */
+#ifdef USE_TLS
+#define IF_ENCRYPTED(x) (state->tls_context ? (x) : 0)
+#define IF_VERIFIED(x) \
+    (state->tls_context && state->tls_context->peer_verified ? (x) : 0)
+
+    if (strcmp(name, S8_MAC_TLS_VERSION) == 0)
+       return (IF_ENCRYPTED(state->tls_context->protocol));
+    if (strcmp(name, S8_MAC_CIPHER) == 0)
+       return (IF_ENCRYPTED(state->tls_context->cipher_name));
+    if (strcmp(name, S8_MAC_CIPHER_BITS) == 0) {
+       if (state->tls_context == 0)
+           return (0);
+       if (state->expand_buf == 0)
+           state->expand_buf = vstring_alloc(10);
+       vstring_sprintf(state->expand_buf, "%d",
+                       IF_ENCRYPTED(state->tls_context->cipher_usebits));
+       return (STR(state->expand_buf));
+    }
+    if (strcmp(name, S8_MAC_CERT_SUBJECT) == 0)
+       return (IF_VERIFIED(state->tls_context->peer_CN));
+    if (strcmp(name, S8_MAC_CERT_ISSUER) == 0)
+       return (IF_VERIFIED(state->tls_context->issuer_CN));
+#endif
+
+    /*
+     * MAIL FROM macros.
+     */
+#define IF_SASL_ENABLED(s) (var_smtpd_sasl_enable && (s) ? (s) : 0)
+
+    if (strcmp(name, S8_MAC_I) == 0)
+       return (state->queue_id);
+#ifdef USE_SASL_AUTH
+    if (strcmp(name, S8_MAC_AUTH_TYPE) == 0)
+       return (IF_SASL_ENABLED(state->sasl_method));
+    if (strcmp(name, S8_MAC_AUTH_AUTHEN) == 0)
+       return (IF_SASL_ENABLED(state->sasl_username));
+    if (strcmp(name, S8_MAC_AUTH_AUTHOR) == 0)
+       return (IF_SASL_ENABLED(state->sasl_sender));
+#endif
+    if (strcmp(name, S8_MAC_MAIL_ADDR) == 0)
+       return (state->sender);
+
+    /*
+     * RCPT TO macros.
+     */
+    if (strcmp(name, S8_MAC_RCPT_ADDR) == 0)
+       return (state->recipient);
+
+    return (0);
+}
diff --git a/postfix/src/smtpd/smtpd_milter.h b/postfix/src/smtpd/smtpd_milter.h
new file mode 100644 (file)
index 0000000..4006bde
--- /dev/null
@@ -0,0 +1,27 @@
+/*++
+/* NAME
+/*     smtpd_milter 3h
+/* SUMMARY
+/*     SMTP server milter glue
+/* SYNOPSIS
+/*     #include <smtpd.h>
+/*     #include <smtpd_milter.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * External interface.
+  */
+extern const char *smtpd_milter_eval(const char *, void *);
+
+/* 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
+/*--*/
+
index 7d46f3285f1e731986cb253a634103d8d9d93c79..269d0565bf7305b893b86845af75a7746d3b6410 100644 (file)
 
 void    smtpd_peer_init(SMTPD_STATE *state)
 {
-    char   *myname = "smtpd_peer_init";
+    const char *myname = "smtpd_peer_init";
     SOCKADDR_SIZE sa_length;
     struct sockaddr *sa;
     INET_PROTO_INFO *proto_info = inet_proto_info();
@@ -151,6 +151,7 @@ void    smtpd_peer_init(SMTPD_STATE *state)
        state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
        state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
        state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+       state->addr_family = AF_UNSPEC;
        state->name_status = SMTPD_PEER_CODE_PERM;
        state->reverse_name_status = SMTPD_PEER_CODE_PERM;
     }
@@ -203,6 +204,7 @@ void    smtpd_peer_init(SMTPD_STATE *state)
 
                state->addr = mystrdup(colonp + 1);
                state->rfc_addr = mystrdup(colonp + 1);
+               state->addr_family = AF_INET;
                aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0);
                if (aierr)
                    msg_fatal("%s: cannot convert %s from string to binary: %s",
@@ -226,6 +228,7 @@ void    smtpd_peer_init(SMTPD_STATE *state)
                state->addr = mystrdup(client_addr.buf);
                state->rfc_addr =
                    concatenate(IPV6_COL, client_addr.buf, (char *) 0);
+               state->addr_family = sa->sa_family;
            }
        }
 
@@ -237,6 +240,7 @@ void    smtpd_peer_init(SMTPD_STATE *state)
        {
            state->addr = mystrdup(client_addr.buf);
            state->rfc_addr = mystrdup(client_addr.buf);
+           state->addr_family = sa->sa_family;
        }
 
        /*
@@ -290,13 +294,13 @@ void    smtpd_peer_init(SMTPD_STATE *state)
                msg_warn("%s: hostname %s verification failed: %s",
                         state->addr, state->name, MAI_STRERROR(aierr));
                REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ?
-                             SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM));
+                           SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_FORGED));
            } else {
                for (res = res0; /* void */ ; res = res->ai_next) {
                    if (res == 0) {
                        msg_warn("%s: address not listed for hostname %s",
                                 state->addr, state->name);
-                       REJECT_PEER_NAME(state, SMTPD_PEER_CODE_PERM);
+                       REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED);
                        break;
                    }
                    if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
@@ -321,6 +325,7 @@ void    smtpd_peer_init(SMTPD_STATE *state)
        state->reverse_name = mystrdup("localhost");
        state->addr = mystrdup("127.0.0.1");    /* XXX bogus. */
        state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */
+       state->addr_family = AF_UNSPEC;
        state->name_status = SMTPD_PEER_CODE_OK;
        state->reverse_name_status = SMTPD_PEER_CODE_OK;
     }
index e7c2edd4a8d221dfd7bc8ab13d0d851e1ea6515c..cef154027ad1f57094cd4a3e243b5e3eb10814c2 100644 (file)
 #include <mail_params.h>
 #include <mail_proto.h>
 #include <mail_error.h>
+#include <ehlo_mask.h>
 
 /* Application-specific. */
 
@@ -138,17 +139,32 @@ int     smtpd_sasl_auth_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
     char   *auth_mechanism;
     char   *initial_response;
+    const char *err;
 
     if (var_helo_required && state->helo_name == 0) {
        state->error_mask |= MAIL_ERROR_POLICY;
        smtpd_chat_reply(state, "503 5.5.1 Error: send HELO/EHLO first");
        return (-1);
     }
-    if (SMTPD_STAND_ALONE(state) || !var_smtpd_sasl_enable) {
+    if (SMTPD_STAND_ALONE(state) || !var_smtpd_sasl_enable
+       || (state->ehlo_discard_mask & EHLO_MASK_AUTH)) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
        smtpd_chat_reply(state, "503 5.5.1 Error: authentication not enabled");
        return (-1);
     }
+    if (smtpd_milters != 0 && (err = milter_other_event(smtpd_milters)) != 0) {
+        if (err[0] == '5') {
+            state->error_mask |= MAIL_ERROR_POLICY;
+            smtpd_chat_reply(state, "%s", err);
+            return (-1);
+        }
+        /* Sendmail compatibility: map 4xx into 454. */
+        else if (err[0] == '4') {
+            state->error_mask |= MAIL_ERROR_POLICY;
+            smtpd_chat_reply(state, "454 4.3.0 Try again later");
+            return (-1);
+        }
+    }
 #ifdef USE_TLS
     if (state->tls_auth_only && !state->tls_context) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
index 184dafc27b5277b41c8e3666adc3f9e3607619dc..fd8fb56406e3dd8ba195b9bb8350fd149998e727 100644 (file)
@@ -78,6 +78,7 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
      * Initialize the state information for this connection, and fill in the
      * connection-specific fields.
      */
+    state->flags = 0;
     state->err = CLEANUP_STAT_OK;
     state->client = stream;
     state->service = mystrdup(service);
@@ -150,6 +151,9 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
        smtpd_sasl_connect(state, VAR_SMTPD_SASL_OPTS, var_smtpd_sasl_opts);
 #endif
 
+    state->milter_argv = 0;
+    state->milter_argc = 0;
+
     /*
      * Initialize peer information.
      */
@@ -182,6 +186,8 @@ void    smtpd_state_reset(SMTPD_STATE *state)
        vstring_free(state->buffer);
     if (state->addr_buf)
        vstring_free(state->addr_buf);
+    if (state->access_denied)
+       myfree(state->access_denied);
     if (state->protocol)
        myfree(state->protocol);
     smtpd_peer_reset(state);
index ec84e595399e4e654944d5d9f8bf3d0692679dbd..289c2f1a388890d75278f062f112f55799d037e6 100644 (file)
@@ -426,7 +426,7 @@ static int data_read(SINK_STATE *state)
   * The table of all SMTP commands that we can handle.
   */
 typedef struct SINK_COMMAND {
-    char   *name;
+    const char *name;
     void    (*response) (SINK_STATE *);
     void    (*hard_response) (SINK_STATE *);
     void    (*soft_response) (SINK_STATE *);
index f02f809acd25f729c0f35b7ed696b64479469c49..5f84e43320189ea553129ceb33661519f1af98ad 100644 (file)
@@ -504,7 +504,7 @@ static void read_banner(int unused_event, char *context)
 static void send_helo(SESSION *session)
 {
     int     except;
-    char   *NOCLOBBER protocol = (talk_lmtp ? "LHLO" : "HELO");
+    const char *NOCLOBBER protocol = (talk_lmtp ? "LHLO" : "HELO");
 
     /*
      * Send the standard greeting with our hostname
index 9f07e009482be305ca94141b4f9b0ac588fc236f..403f6da71b1627f5d7e60f1d777b43e5fe34263d 100644 (file)
@@ -173,7 +173,7 @@ typedef struct {
 
 static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv)
 {
-    char   *myname = "get_service_attr";
+    const char *myname = "get_service_attr";
     struct passwd *pwd;
     struct group *grp;
     char   *user;                      /* user name */
@@ -265,7 +265,7 @@ static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv)
 
 static void spawn_service(VSTREAM *client_stream, char *service, char **argv)
 {
-    char   *myname = "spawn_service";
+    const char *myname = "spawn_service";
     static SPAWN_ATTR attr;
     WAIT_STATUS_T status;
     ARGV   *export_env;
index 15cfbc3140094e64e9338520394b63d95995912a..0a1695f60f77c551629fd17ba8320190b41612e6 100644 (file)
@@ -89,6 +89,7 @@ depend: $(MAKES)
 # do not edit below this line - it is generated by 'make depend'
 tls_bio_ops.o: ../../include/iostuff.h
 tls_bio_ops.o: ../../include/msg.h
+tls_bio_ops.o: ../../include/name_mask.h
 tls_bio_ops.o: ../../include/sys_defs.h
 tls_bio_ops.o: ../../include/vbuf.h
 tls_bio_ops.o: ../../include/vstream.h
@@ -96,15 +97,18 @@ tls_bio_ops.o: ../../include/vstring.h
 tls_bio_ops.o: tls.h
 tls_bio_ops.o: tls_bio_ops.c
 tls_certkey.o: ../../include/msg.h
+tls_certkey.o: ../../include/name_mask.h
 tls_certkey.o: ../../include/sys_defs.h
 tls_certkey.o: ../../include/vbuf.h
 tls_certkey.o: ../../include/vstream.h
 tls_certkey.o: ../../include/vstring.h
 tls_certkey.o: tls.h
 tls_certkey.o: tls_certkey.c
+tls_client.o: ../../include/argv.h
 tls_client.o: ../../include/mail_params.h
 tls_client.o: ../../include/msg.h
 tls_client.o: ../../include/mymalloc.h
+tls_client.o: ../../include/name_mask.h
 tls_client.o: ../../include/stringops.h
 tls_client.o: ../../include/sys_defs.h
 tls_client.o: ../../include/vbuf.h
@@ -114,6 +118,7 @@ tls_client.o: tls.h
 tls_client.o: tls_client.c
 tls_client.o: tls_mgr.h
 tls_dh.o: ../../include/msg.h
+tls_dh.o: ../../include/name_mask.h
 tls_dh.o: ../../include/sys_defs.h
 tls_dh.o: ../../include/vbuf.h
 tls_dh.o: ../../include/vstream.h
@@ -134,6 +139,7 @@ tls_mgr.o: tls_mgr.c
 tls_mgr.o: tls_mgr.h
 tls_misc.o: ../../include/msg.h
 tls_misc.o: ../../include/mymalloc.h
+tls_misc.o: ../../include/name_mask.h
 tls_misc.o: ../../include/stringops.h
 tls_misc.o: ../../include/sys_defs.h
 tls_misc.o: ../../include/vbuf.h
@@ -169,6 +175,7 @@ tls_prng_file.o: ../../include/mymalloc.h
 tls_prng_file.o: ../../include/sys_defs.h
 tls_prng_file.o: tls_prng.h
 tls_prng_file.o: tls_prng_file.c
+tls_rsa.o: ../../include/name_mask.h
 tls_rsa.o: ../../include/sys_defs.h
 tls_rsa.o: ../../include/vbuf.h
 tls_rsa.o: ../../include/vstream.h
@@ -189,6 +196,7 @@ tls_scache.o: ../../include/vstring.h
 tls_scache.o: tls_scache.c
 tls_scache.o: tls_scache.h
 tls_seed.o: ../../include/msg.h
+tls_seed.o: ../../include/name_mask.h
 tls_seed.o: ../../include/sys_defs.h
 tls_seed.o: ../../include/vbuf.h
 tls_seed.o: ../../include/vstream.h
@@ -202,6 +210,7 @@ tls_server.o: ../../include/hex_code.h
 tls_server.o: ../../include/mail_params.h
 tls_server.o: ../../include/msg.h
 tls_server.o: ../../include/mymalloc.h
+tls_server.o: ../../include/name_mask.h
 tls_server.o: ../../include/stringops.h
 tls_server.o: ../../include/sys_defs.h
 tls_server.o: ../../include/vbuf.h
@@ -212,6 +221,7 @@ tls_server.o: tls_mgr.h
 tls_server.o: tls_server.c
 tls_session.o: ../../include/msg.h
 tls_session.o: ../../include/mymalloc.h
+tls_session.o: ../../include/name_mask.h
 tls_session.o: ../../include/sys_defs.h
 tls_session.o: ../../include/vbuf.h
 tls_session.o: ../../include/vstream.h
@@ -220,6 +230,7 @@ tls_session.o: tls.h
 tls_session.o: tls_session.c
 tls_stream.o: ../../include/iostuff.h
 tls_stream.o: ../../include/msg.h
+tls_stream.o: ../../include/name_mask.h
 tls_stream.o: ../../include/sys_defs.h
 tls_stream.o: ../../include/vbuf.h
 tls_stream.o: ../../include/vstream.h
@@ -228,6 +239,7 @@ tls_stream.o: tls.h
 tls_stream.o: tls_stream.c
 tls_verify.o: ../../include/msg.h
 tls_verify.o: ../../include/mymalloc.h
+tls_verify.o: ../../include/name_mask.h
 tls_verify.o: ../../include/sys_defs.h
 tls_verify.o: ../../include/vbuf.h
 tls_verify.o: ../../include/vstream.h
index 9db5ca6fd80abd97cdd8c0ce504d689464722844..d69e3479bf99d41de2b0381558bbd6555fd5c670 100644 (file)
 /* DESCRIPTION
 /* .nf
 
+ /*
+  * TLS enforcement levels. Non-sentinel values also be used to indicate
+  * the actual security level of a session.
+  */
+#define TLS_LEV_NOTFOUND       -1      /* sentinel */
+#define TLS_LEV_NONE           0       /* plain-text only */
+#define TLS_LEV_MAY            1       /* wildcard */
+#define TLS_LEV_ENCRYPT                2       /* encrypted connection */
+#define TLS_LEV_VERIFY         3       /* certificate verified */
+#define TLS_LEV_SECURE         4       /* "secure" verification */
+
+#ifdef USE_TLS
+
  /*
   * OpenSSL library.
   */
   * Utility library.
   */
 #include <vstream.h>
+#include <name_mask.h>
+#include <name_code.h>
+
+#define TLS_BIO_BUFSIZE        8192
+
+ /*
+  * Names of valid tlsmgr(8) session caches.
+  */
+#define TLS_MGR_SCACHE_SMTPD   "smtpd"
+#define TLS_MGR_SCACHE_SMTP    "smtp"
+#define TLS_MGR_SCACHE_LMTP    "lmtp"
 
  /*
   * TLS session context, also used by the VSTREAM call-back routines for SMTP
   * input/output, and by OpenSSL call-back routines for key verification.
   */
 #define CCERT_BUFSIZ   256
-#define HOST_BUFSIZ  255               /* RFC 1035 */
 
 typedef struct {
     SSL    *con;
     BIO    *internal_bio;              /* postfix/TLS side of pair */
     BIO    *network_bio;               /* network side of pair */
+    char   *cache_type;                        /* tlsmgr(8) cache type if enabled */
     char   *serverid;                  /* unique server identifier */
     char   *peer_CN;                   /* Peer Common Name */
     char   *issuer_CN;                 /* Issuer Common Name */
@@ -60,14 +84,74 @@ typedef struct {
     int     session_reused;            /* this session was reused */
 } TLScontext_t;
 
-#define TLS_BIO_BUFSIZE        8192
+ /*
+  * Client protocol selection bitmask
+  */
+#define TLS_PROTOCOL_SSLv2     (1<<0)  /* SSLv2 */
+#define TLS_PROTOCOL_SSLv3     (1<<1)  /* SSLv3 */
+#define TLS_PROTOCOL_TLSv1     (1<<2)  /* TLSv1 */
+#define TLS_ALL_PROTOCOLS      \
+       ( TLS_PROTOCOL_SSLv2 | TLS_PROTOCOL_SSLv3 | TLS_PROTOCOL_TLSv1 )
+
+ /*
+  * tls_misc.c
+  */
+#define TLS_CIPHER_NONE                0
+#define TLS_CIPHER_NULL                1
+#define TLS_CIPHER_EXPORT      2
+#define TLS_CIPHER_LOW         3
+#define TLS_CIPHER_MEDIUM      4
+#define TLS_CIPHER_HIGH                5
+
+extern NAME_MASK tls_protocol_table[];
+extern NAME_CODE tls_cipher_level_table[];
+
+#define tls_protocol_mask(tag, protocols) \
+    name_mask_delim_opt((tag), tls_protocol_table, (protocols), \
+                       ":" NAME_MASK_DEFAULT_DELIM, \
+                       NAME_MASK_ANY_CASE | NAME_MASK_RETURN)
+
+#define tls_protocol_names(tag, mask) \
+    str_name_mask_opt((VSTRING *)0, (tag), tls_protocol_table, (mask), \
+                     NAME_MASK_FATAL|NAME_MASK_COMMA)
+
+#define tls_cipher_level(str) \
+    name_code(tls_cipher_level_table, NAME_CODE_FLAG_NONE, (str))
+
+#define TLS_END_EXCLUDE ((char *)0)
+extern char *tls_cipher_list(int,...);
 
  /*
   * tls_client.c
   */
-extern SSL_CTX *tls_client_init(int);
-extern TLScontext_t *tls_client_start(SSL_CTX *, VSTREAM *, int, int,
-                                             const char *, const char *);
+typedef struct {
+    int     log_level;
+    int     verifydepth;
+    const char *cache_type;
+    const char *cert_file;
+    const char *key_file;
+    const char *dcert_file;
+    const char *dkey_file;
+    const char *CAfile;
+    const char *CApath;
+} tls_client_init_props;
+
+typedef struct {
+    SSL_CTX *ctx;
+    VSTREAM *stream;
+    int     log_level;
+    int     timeout;
+    int     tls_level;                 /* Security level */
+    char   *nexthop;                   /* destination domain */
+    char   *host;                      /* MX hostname */
+    char   *serverid;                  /* Session cache key */
+    int     protocols;                 /* Encrypt level protocols, 0 => all */
+    char   *cipherlist;                        /* Encrypt level ciphers */
+    char   *certmatch;                 /* Verify level match patterns */
+} tls_client_start_props;
+
+extern SSL_CTX *tls_client_init(const tls_client_init_props *);
+extern TLScontext_t *tls_client_start(const tls_client_start_props *);
 
 #define tls_client_stop(ctx , stream, timeout, failure, TLScontext) \
        tls_session_stop((ctx), (stream), (timeout), (failure), (TLScontext))
@@ -75,8 +159,26 @@ extern TLScontext_t *tls_client_start(SSL_CTX *, VSTREAM *, int, int,
  /*
   * tls_server.c
   */
-extern SSL_CTX *tls_server_init(int, int);
-extern TLScontext_t *tls_server_start(SSL_CTX *, VSTREAM *, int,
+typedef struct {
+    int     log_level;
+    int     verifydepth;
+    const char *cache_type;
+    long    scache_timeout;
+    const char *cert_file;
+    const char *key_file;
+    const char *dcert_file;
+    const char *dkey_file;
+    const char *CAfile;
+    const char *CApath;
+    const char *cipherlist;
+    int     protocols;                 /* protocols, 0 => all */
+    const char *dh1024_param_file;
+    const char *dh512_param_file;
+    int     ask_ccert;
+} tls_server_props;
+
+extern SSL_CTX *tls_server_init(const tls_server_props *);
+extern TLScontext_t *tls_server_start(SSL_CTX *, VSTREAM *, int, int,
                                           const char *, const char *, int);
 
 #define tls_server_stop(ctx , stream, timeout, failure, TLScontext) \
@@ -161,6 +263,7 @@ extern int tls_set_my_certificate_key_info(SSL_CTX *, const char *,
   * tls_misc.c
   */
 extern int TLScontext_index;
+extern int TLSscache_index;
 
 extern TLScontext_t *tls_alloc_context(int, const char *);
 extern void tls_free_context(TLScontext_t *);
@@ -176,9 +279,6 @@ extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long);
 extern void tls_int_seed(void);
 extern int tls_ext_seed(int);
 
- /*
-  * tls_temp.c, code that is going away.
-  */
 #endif                                 /* TLS_INTERNAL */
 
 /* LICENSE
@@ -192,4 +292,5 @@ extern int tls_ext_seed(int);
 /*      Yorktown Heights, NY 10598, USA
 /*--*/
 
+#endif                                 /* USE_TLS */
 #endif                                 /* _TLS_H_INCLUDED_ */
index d97521dc9fd46c2f914ac588e0749eb1744c994e..05deba332e6b2af2a71a8267a1915e0c4db50c04 100644 (file)
@@ -136,10 +136,17 @@ int     tls_set_my_certificate_key_info(SSL_CTX *ctx,
                                const char *cert_file, const char *key_file,
                              const char *dcert_file, const char *dkey_file)
 {
+
+    /*
+     * Lack of certificates is fine so long as we are prepared to use
+     * anonymous ciphers.
+     */
+#if 0
     if (*cert_file == 0 && *dcert_file == 0) {
        msg_warn("need an RSA or DSA certificate/key pair");
        return (-1);
     }
+#endif
     if (*cert_file) {
        if (!set_cert_stuff(ctx, cert_file, *key_file ? key_file : cert_file)) {
            msg_info("cannot load RSA certificate and key data");
index 79f251fb0fbfac4020ae1f0696bf90d22d22ee01..4b9f9942e0733878625b7ef4a674dd258689e90b 100644 (file)
@@ -6,18 +6,11 @@
 /* SYNOPSIS
 /*     #include <tls.h>
 /*
-/*     SSL_CTX *tls_client_init(verifydepth)
-/*     int     verifydepth; /* unused */
+/*     SSL_CTX *tls_client_init(props)
+/*     const tls_client_init_props *props;
 /*
-/*     TLScontext_t *tls_client_start(client_ctx, stream, timeout,
-/*                                     enforce_peername, peername,
-/*                                     serverid)
-/*     SSL_CTX *client_ctx;
-/*     VSTREAM *stream;
-/*     int     timeout;
-/*     int     enforce_peername;
-/*     const char *peername;
-/*     const char *serverid;
+/*     TLScontext_t *tls_client_start(props)
+/*     const tls_client_start_props *props;
 /*
 /*     void    tls_client_stop(client_ctx, stream, failure, TLScontext)
 /*     SSL_CTX *client_ctx;
 /*     Certificate details are also decided during this phase,
 /*     so that peer-specific behavior is not possible.
 /*
-/*     tls_client_start() activates the TLS feature for the VSTREAM
-/*     passed as argument. We expect that network buffers are flushed and
-/*     the TLS handshake can begin immediately. The serverid argument
-/*     specifies a string that hopefully uniquely identifies a server.
-/*     It is used as the client session cache lookup key.
+/*     tls_client_start() activates the TLS session over an established
+/*     stream. We expect that network buffers are flushed and
+/*     the TLS handshake can begin immediately.
 /*
 /*     tls_client_stop() sends the "close notify" alert via
 /*     SSL_shutdown() to the peer and resets all connection specific
 
 /* Utility library. */
 
+#include <argv.h>
 #include <mymalloc.h>
 #include <vstring.h>
 #include <vstream.h>
 #define STR    vstring_str
 #define LEN    VSTRING_LEN
 
- /*
-  * Do or don't we cache client sessions?
-  */
-static int tls_client_cache = 0;
-
 /* load_clnt_session - load session from client cache (non-callback) */
 
-static SSL_SESSION *load_clnt_session(const char *cache_id)
+static SSL_SESSION *load_clnt_session(TLScontext_t *TLScontext)
 {
     SSL_SESSION *session = 0;
     VSTRING *session_data = vstring_alloc(2048);
@@ -170,19 +157,29 @@ static SSL_SESSION *load_clnt_session(const char *cache_id)
     /*
      * Prepare the query.
      */
-    if (var_smtp_tls_loglevel >= 3)
-       msg_info("looking for session %s in client cache", cache_id);
+    if (TLScontext->log_level >= 2)
+       msg_info("looking for session %s in %s cache",
+                TLScontext->serverid, TLScontext->cache_type);
+
+    /*
+     * We only get here if the cache_type is not empty. This code is not
+     * called unless caching is enabled and the cache_type is stored in the
+     * server SSL context.
+     */
+    if (TLScontext->cache_type == 0)
+       msg_panic("null client session cache type in session lookup");
 
     /*
      * Look up and activate the SSL_SESSION object. Errors are non-fatal,
      * since caching is only an optimization.
      */
-    if (tls_mgr_lookup(tls_client_cache, cache_id,
+    if (tls_mgr_lookup(TLScontext->cache_type, TLScontext->serverid,
                       session_data) == TLS_MGR_STAT_OK) {
        session = tls_session_activate(STR(session_data), LEN(session_data));
        if (session) {
-           if (var_smtp_tls_loglevel >= 3)
-               msg_info("reloaded session %s from client cache", cache_id);
+           if (TLScontext->log_level >= 2)
+               msg_info("reloaded session %s from %s cache",
+                        TLScontext->serverid, TLScontext->cache_type);
        }
     }
 
@@ -200,16 +197,26 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
 {
     TLScontext_t *TLScontext;
     VSTRING *session_data;
-    const char *cache_id;
 
     /*
-     * Look up the cache ID string for this session object.
+     * The cache name (if caching is enabled in tlsmgr(8)) and the cache ID
+     * string for this session are stored in the TLScontext. It cannot be
+     * null at this point.
      */
-    TLScontext = SSL_get_ex_data(ssl, TLScontext_index);
-    cache_id = TLScontext->serverid;
+    if ((TLScontext = SSL_get_ex_data(ssl, TLScontext_index)) == 0)
+       msg_panic("null TLScontext in new session callback");
 
-    if (var_smtp_tls_loglevel >= 3)
-       msg_info("save session %s to client cache", cache_id);
+    /*
+     * We only get here if the cache_type is not empty. This callback is not
+     * set unless caching is enabled and the cache_type is stored in the
+     * server SSL context.
+     */
+    if (TLScontext->cache_type == 0)
+       msg_panic("null session cache type in new session callback");
+
+    if (TLScontext->log_level >= 2)
+       msg_info("save session %s to %s cache",
+                TLScontext->serverid, TLScontext->cache_type);
 
 #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
 
@@ -230,16 +237,15 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
      * Passivate and save the session object. Errors are non-fatal, since
      * caching is only an optimization.
      */
-    session_data = tls_session_passivate(session);
-    if (session_data)
-       tls_mgr_update(tls_client_cache, cache_id,
+    if ((session_data = tls_session_passivate(session)) != 0) {
+       tls_mgr_update(TLScontext->cache_type, TLScontext->serverid,
                       STR(session_data), LEN(session_data));
+       vstring_free(session_data);
+    }
 
     /*
      * Clean up.
      */
-    if (session_data)
-       vstring_free(session_data);
     SSL_SESSION_free(session);                 /* 200502 */
 
     return (1);
@@ -249,26 +255,26 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
 
 static void uncache_session(TLScontext_t *TLScontext)
 {
-    if (TLScontext->serverid == 0)
+    if (TLScontext->cache_type == 0 || TLScontext->serverid == 0)
        return;
 
-    if (var_smtp_tls_loglevel >= 3)
+    if (TLScontext->log_level >= 2)
        msg_info("remove session %s from client cache", TLScontext->serverid);
 
-    tls_mgr_delete(tls_client_cache, TLScontext->serverid);
+    tls_mgr_delete(TLScontext->cache_type, TLScontext->serverid);
 }
 
 /* tls_client_init - initialize client-side TLS engine */
 
-SSL_CTX *tls_client_init(int unused_verifydepth)
+SSL_CTX *tls_client_init(const tls_client_init_props *props)
 {
-    int     off = 0;
+    long    off = 0;
     SSL_CTX *client_ctx;
-    int     cache_types;
+    int     cachable;
 
     /* See skeleton in OpenSSL apps/s_client.c. */
 
-    if (var_smtp_tls_loglevel >= 2)
+    if (props->log_level >= 2)
        msg_info("initializing the client-side TLS engine");
 
     /*
@@ -310,10 +316,8 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
     }
 
     /*
-     * Here we might set SSL_OP_NO_SSLv2, SSL_OP_NO_SSLv3, SSL_OP_NO_TLSv1.
-     * Of course, the last one would not make sense, since RFC2487 is only
-     * defined for TLS, but we don't know what is out there. So leave things
-     * completely open, as of today.
+     * Protocol selection is destination dependent, so we delay the protocol
+     * selection options to the per-session SSL object.
      */
     off |= tls_bug_bits();
     SSL_CTX_set_options(client_ctx, off);
@@ -321,19 +325,9 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
     /*
      * Set the call-back routine for verbose logging.
      */
-    if (var_smtp_tls_loglevel >= 2)
+    if (props->log_level >= 2)
        SSL_CTX_set_info_callback(client_ctx, tls_info_callback);
 
-    /*
-     * Override the default cipher list with our own list.
-     */
-    if (*var_smtp_tls_cipherlist != 0)
-       if (SSL_CTX_set_cipher_list(client_ctx, var_smtp_tls_cipherlist) == 0) {
-           tls_print_errors();
-           SSL_CTX_free(client_ctx);           /* 200411 */
-           return (0);
-       }
-
     /*
      * Load the CA public key certificates for both the client cert and for
      * the verification of server certificates. As provided by OpenSSL we
@@ -345,8 +339,8 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
      * at startup time, so that you don't have the hassle to maintain another
      * copy of the CApath directory for chroot-jail.
      */
-    if (tls_set_ca_certificate_info(client_ctx, var_smtp_tls_CAfile,
-                                   var_smtp_tls_CApath) < 0) {
+    if (tls_set_ca_certificate_info(client_ctx,
+                                   props->CAfile, props->CApath) < 0) {
        SSL_CTX_free(client_ctx);               /* 200411 */
        return (0);
     }
@@ -369,12 +363,10 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
      * RSA certificate only. A client with openssl-library will use RSA first
      * if not especially changed in the cipher setup.
      */
-    if ((*var_smtp_tls_cert_file != 0 || *var_smtp_tls_dcert_file != 0)
-       && tls_set_my_certificate_key_info(client_ctx,
-                                          var_smtp_tls_cert_file,
-                                          var_smtp_tls_key_file,
-                                          var_smtp_tls_dcert_file,
-                                          var_smtp_tls_dkey_file) < 0) {
+    if ((*props->cert_file != 0 || *props->dcert_file != 0)
+       && tls_set_my_certificate_key_info(client_ctx, props->cert_file,
+                                        props->key_file, props->dcert_file,
+                                          props->dkey_file) < 0) {
        SSL_CTX_free(client_ctx);               /* 200411 */
        return (0);
     }
@@ -404,13 +396,26 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
      * sessions from the external cache, so we must delete them directly (not
      * via a callback).
      */
-    SSL_CTX_set_timeout(client_ctx, var_smtp_tls_scache_timeout);
+
+    if (TLSscache_index < 0)
+       TLSscache_index =
+           SSL_CTX_get_ex_new_index(0, "TLScontext ex_data index",
+                                    NULL, NULL, NULL);
+
+    if (tls_mgr_policy(props->cache_type, &cachable) != TLS_MGR_STAT_OK)
+       cachable = 0;
+
+    if (!SSL_CTX_set_ex_data(client_ctx, TLSscache_index,
+                            (void *) props->cache_type)) {
+       msg_warn("Session cache off: error saving cache type in SSL context.");
+       tls_print_errors();
+       cachable = 0;
+    }
 
     /*
-     * The session cache is implemented by the tlsmgr(8) process.
+     * The external session cache is implemented by the tlsmgr(8) process.
      */
-    if (tls_mgr_policy(&cache_types) == TLS_MGR_STAT_OK
-       && (tls_client_cache = (cache_types & TLS_MGR_SCACHE_CLIENT)) != 0) {
+    if (cachable) {
 
        /*
         * OpenSSL does not use callbacks to load sessions from a client
@@ -450,19 +455,61 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
 
 /* match_hostname -  match hostname against pattern */
 
-static int match_hostname(const char *pattern, const char *hostname)
+static int match_hostname(const char *peerid, ARGV *cmatch_argv,
+                                 const char *nexthop, const char *hname)
 {
-    char   *peername_left;
+    const char *pattern;
+    const char *pattern_left;
+    int     sub;
+    int     i;
+    int     idlen;
+    int     patlen;
+
+    for (i = 0; i < cmatch_argv->argc; ++i) {
+       sub = 0;
+       if (!strcasecmp(cmatch_argv->argv[i], "nexthop"))
+           pattern = nexthop;
+       else if (!strcasecmp(cmatch_argv->argv[i], "hostname"))
+           pattern = hname;
+       else if (!strcasecmp(cmatch_argv->argv[i], "dot-nexthop")) {
+           pattern = nexthop;
+           sub = 1;
+       } else {
+           pattern = cmatch_argv->argv[i];
+           if (*pattern == '.' && pattern[1] != '\0') {
+               ++pattern;
+               sub = 1;
+           }
+       }
 
-    return (strcasecmp(hostname, pattern) == 0
-           || (pattern[0] == '*' && pattern[1] == '.' && pattern[2] != 0
-               && (peername_left = strchr(hostname, '.')) != 0
-               && strcasecmp(peername_left + 1, pattern + 2) == 0));
+       /*
+        * Sub-domain match, peerid is any sub-domain of pattern.
+        */
+       if (sub)
+           if ((idlen = strlen(peerid)) > (patlen = strlen(pattern)) + 1
+               && peerid[idlen - patlen - 1] == '.'
+               && !strcasecmp(peerid + (idlen - patlen), pattern))
+               return (1);
+           else
+               continue;
+
+       /*
+        * NOT sub-domain match, but "*.domain.tld" in peerid matches any
+        * host.domain.tld in the pattern.
+        */
+       if (!strcasecmp(peerid, pattern)
+           || (peerid[0] == '*' && peerid[1] == '.' && peerid[2] != 0
+               && (pattern_left = strchr(pattern, '.')) != 0
+               && strcasecmp(pattern_left + 1, peerid + 2) == 0))
+           return (1);
+    }
+    return (0);
 }
 
 /* verify_extract_peer - verify peer name and extract peer information */
 
-static void verify_extract_peer(const char *peername, X509 *peercert,
+static void verify_extract_peer(const char *nexthop, const char *hname,
+                                       char *certmatch, X509 *peercert,
                                        TLScontext_t *TLScontext)
 {
     int     i;
@@ -470,6 +517,7 @@ static void verify_extract_peer(const char *peername, X509 *peercert,
     int     hostname_matched = 0;
     int     dNSName_found = 0;
     int     verify_peername;
+    ARGV   *cmatch_argv = 0;
 
     STACK_OF(GENERAL_NAME) * gens;
 
@@ -480,10 +528,20 @@ static void verify_extract_peer(const char *peername, X509 *peercert,
        (TLScontext->enforce_CN != 0 && TLScontext->peer_verified != 0);
 
     if (verify_peername) {
+       cmatch_argv = argv_split(certmatch, "\t\n\r, :");
 
        /*
         * Verify the dNSName(s) in the peer certificate against the
         * peername.
+        * 
+        * XXX: The nexthop and host name may both be the same network address
+        * rather than a DNS name. In this case we really should be looking
+        * for GEN_IPADD entries, not GEN_DNS entries.
+        * 
+        * XXX: In ideal world the caller who used the address to build the
+        * connection would tell us that the nexthop is the connection
+        * address, but if that is not practical, we can parse the nexthop
+        * again here.
         */
        gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
        if (gens) {
@@ -493,7 +551,8 @@ static void verify_extract_peer(const char *peername, X509 *peercert,
                if (gn->type == GEN_DNS) {
                    dNSName_found++;
                    if ((hostname_matched =
-                        match_hostname((char *) gn->d.ia5->data, peername)))
+                      match_hostname((char *) gn->d.ia5->data, cmatch_argv,
+                                     nexthop, hname)) != 0)
                        break;
                }
            }
@@ -504,7 +563,7 @@ static void verify_extract_peer(const char *peername, X509 *peercert,
        if (!hostname_matched)
            msg_info("certificate peer name verification failed for "
                     "%s: %d dNSNames in certificate found, "
-                    "but none match", peername, dNSName_found);
+                    "but none match", hname, dNSName_found);
     }
     if ((TLScontext->peer_CN = tls_peer_CN(peercert)) == 0)
        TLScontext->peer_CN = mystrdup("");
@@ -519,16 +578,19 @@ static void verify_extract_peer(const char *peername, X509 *peercert,
         * peername.
         */
        if (TLScontext->peer_CN[0] != '\0') {
-           hostname_matched = match_hostname(TLScontext->peer_CN, peername);
+           hostname_matched = match_hostname(TLScontext->peer_CN, cmatch_argv,
+                                             nexthop, hname);
            if (!hostname_matched)
                msg_info("certificate peer name verification failed "
-                        "for %s: CommonName mis-match: %s",
-                        peername, TLScontext->peer_CN);
+                        "for nexthop=%s, host=%s: CommonName mis-match: %s",
+                        nexthop, hname, TLScontext->peer_CN);
        }
     }
+    if (cmatch_argv)
+       cmatch_argv = argv_free(cmatch_argv);
     TLScontext->hostname_matched = hostname_matched;
 
-    if (var_smtp_tls_loglevel >= 1) {
+    if (TLScontext->log_level >= 1) {
        if (TLScontext->peer_verified
            && (!TLScontext->enforce_CN || TLScontext->hostname_matched))
            msg_info("Verified: subject_CN=%s, issuer=%s",
@@ -544,11 +606,7 @@ static void verify_extract_peer(const char *peername, X509 *peercert,
   * buffers are flushed and the "220 Ready to start TLS" was received by us,
   * so that we can immediately start the TLS handshake process.
   */
-TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
-                                      int timeout,
-                                      int enforce_peername,
-                                      const char *peername,
-                                      const char *serverid)
+TLScontext_t *tls_client_start(const tls_client_start_props *props)
 {
     int     sts;
     SSL_SESSION *session;
@@ -556,25 +614,29 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
     X509   *peercert;
     TLScontext_t *TLScontext;
 
-    if (var_smtp_tls_loglevel >= 1)
-       msg_info("setting up TLS connection to %s", peername);
+    if (props->log_level >= 1)
+       msg_info("setting up TLS connection to %s", props->host);
 
     /*
      * Allocate a new TLScontext for the new connection and get an SSL
      * structure. Add the location of TLScontext to the SSL to later retrieve
      * the information inside the tls_verify_certificate_callback().
+     * 
+     * If session caching was enabled when TLS was initialized, the cache type
+     * is stored in the client SSL context.
      */
-    TLScontext = tls_alloc_context(var_smtp_tls_loglevel, peername);
-    TLScontext->serverid = mystrdup(serverid);
+    TLScontext = tls_alloc_context(props->log_level, props->host);
+    TLScontext->serverid = mystrdup(props->serverid);
+    TLScontext->cache_type = SSL_CTX_get_ex_data(props->ctx, TLSscache_index);
 
-    if ((TLScontext->con = (SSL *) SSL_new(client_ctx)) == NULL) {
-       msg_info("Could not allocate 'TLScontext->con' with SSL_new()");
+    if ((TLScontext->con = (SSL *) SSL_new(props->ctx)) == NULL) {
+       msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
        tls_print_errors();
        tls_free_context(TLScontext);
        return (0);
     }
     if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
-       msg_info("Could not set application data for 'TLScontext->con'");
+       msg_warn("Could not set application data for 'TLScontext->con'");
        tls_print_errors();
        tls_free_context(TLScontext);
        return (0);
@@ -584,7 +646,7 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * Set the verification parameters to be checked in
      * tls_verify_certificate_callback().
      */
-    if (enforce_peername) {
+    if (props->tls_level >= TLS_LEV_VERIFY) {
        TLScontext->enforce_verify_errors = 1;
        TLScontext->enforce_CN = 1;
        SSL_set_verify(TLScontext->con, SSL_VERIFY_PEER,
@@ -609,6 +671,51 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
        tls_free_context(TLScontext);
        return (0);
     }
+#define DISABLE_ALL (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1)
+
+    /*
+     * Per session protocol selection for sessions with mandatory encryption
+     * 
+     * OpenSSL will ignore cached sessions that use the wrong protocol. So we do
+     * not need to filter out cached sessions with the "wrong" protocol,
+     * rather OpenSSL will simply negotiate a new session.
+     * 
+     * Still, we expect the caller to salt the session lookup key with the
+     * protocol list, so that sessions found in the cache are always
+     * acceptable.
+     * 
+     * Not enabling any protocols explicitly, enables all.
+     */
+    if (props->tls_level >= TLS_LEV_ENCRYPT
+       && props->protocols != 0 && props->protocols != TLS_ALL_PROTOCOLS) {
+       long    disable = DISABLE_ALL;
+
+       if (props->protocols & TLS_PROTOCOL_TLSv1)
+           disable &= ~SSL_OP_NO_TLSv1;
+       if (props->protocols & TLS_PROTOCOL_SSLv3)
+           disable &= ~SSL_OP_NO_SSLv3;
+       if (props->protocols & TLS_PROTOCOL_SSLv2)
+           disable &= ~SSL_OP_NO_SSLv2;
+
+       SSL_set_options(TLScontext->con, disable);
+    }
+
+    /*
+     * Per session cipher selection for sessions with mandatory encryption
+     * 
+     * By the time a TLS client is negotiating ciphers it has already offered to
+     * re-use a session, it is too late to renege on the offer. So we must
+     * not attempt to re-use sessions whose ciphers are too weak. We expect
+     * the caller to salt the session lookup key with the cipher list, so
+     * that sessions found in the cache are always acceptable.
+     */
+    if (props->cipherlist != 0)
+       if (SSL_set_cipher_list(TLScontext->con, props->cipherlist) == 0) {
+           msg_warn("Could not set cipherlist: %s", props->cipherlist);
+           tls_print_errors();
+           tls_free_context(TLScontext);
+           return (0);
+       }
 
     /*
      * Try to load an existing session from the TLS session cache.
@@ -616,9 +723,17 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * XXX To avoid memory leaks we must always call SSL_SESSION_free() after
      * calling SSL_set_session(), regardless of whether or not the session
      * will be reused.
-     */
-    if (tls_client_cache) {
-       session = load_clnt_session(serverid);
+     * 
+     * XXX: We rely on the client including any non-default cipherlist in the
+     * serverid cache lookup key so as to avoid fetching sessions with
+     * incompatible ciphers. We could save the cipher name into the cache
+     * together with the serialized session, and compare it against the
+     * cipherlist here, but this is unlikely to be worth the trouble. A sane
+     * administrator should use at most a handful of cipherlists, especially
+     * when setting policy for domains served by a common MX host.
+     */
+    if (TLScontext->cache_type) {
+       session = load_clnt_session(TLScontext);
        if (session) {
            SSL_set_session(TLScontext->con, session);
            SSL_SESSION_free(session);          /* 200411 */
@@ -670,7 +785,7 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * Well there is a BIO below the SSL routines that is automatically
      * created for us, so we can use it for debugging purposes.
      */
-    if (var_smtp_tls_loglevel >= 3)
+    if (props->log_level >= 3)
        BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
 
     /*
@@ -680,9 +795,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * Error handling: If the SSL handhake fails, we print out an error message
      * and remove all TLS state concerning this session.
      */
-    sts = tls_bio_connect(vstream_fileno(stream), timeout, TLScontext);
+    sts = tls_bio_connect(vstream_fileno(props->stream), props->timeout,
+                         TLScontext);
     if (sts <= 0) {
-       msg_info("SSL_connect error to %s: %d", peername, sts);
+       msg_info("SSL_connect error to %s: %d", props->host, sts);
        tls_print_errors();
        uncache_session(TLScontext);
        tls_free_context(TLScontext);
@@ -693,13 +809,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * The TLS engine is active. Switch to the tls_timed_read/write()
      * functions and make the TLScontext available to those functions.
      */
-    tls_stream_start(stream, TLScontext);
+    tls_stream_start(props->stream, TLScontext);
 
-    if (var_smtp_tls_loglevel >= 3 && SSL_session_reused(TLScontext->con))
-       msg_info("Reusing old session");
-
-    /* Only loglevel==4 dumps everything */
-    if (var_smtp_tls_loglevel < 4)
+    /* Only log_level==4 dumps everything */
+    if (props->log_level < 4)
        BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
 
     /*
@@ -707,19 +820,23 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * session was negotiated.
      */
     TLScontext->session_reused = SSL_session_reused(TLScontext->con);
+    if (props->log_level >= 2 && TLScontext->session_reused)
+       msg_info("Reusing old session");
 
     /*
      * Do peername verification if requested and extract useful information
      * from the certificate for later use.
      */
     if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) {
-       verify_extract_peer(peername, peercert, TLScontext);
+       verify_extract_peer(props->nexthop, props->host, props->certmatch,
+                           peercert, TLScontext);
        X509_free(peercert);
     }
-    if (enforce_peername && !TLScontext->hostname_matched) {
+    if (TLScontext->enforce_CN && !TLScontext->hostname_matched) {
        msg_info("Server certificate could not be verified for %s:"
-                " hostname mismatch", peername);
-       tls_client_stop(client_ctx, stream, timeout, 0, TLScontext);
+                " hostname mismatch", props->host);
+       tls_client_stop(props->ctx, props->stream, props->timeout, 0,
+                       TLScontext);
        return (0);
     }
 
@@ -732,9 +849,9 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
     TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher,
                                             &(TLScontext->cipher_algbits));
 
-    if (var_smtp_tls_loglevel >= 1)
+    if (props->log_level >= 1)
        msg_info("TLS connection established to %s: %s with cipher %s"
-                " (%d/%d bits)", peername,
+                " (%d/%d bits)", props->host,
                 TLScontext->protocol, TLScontext->cipher_name,
                 TLScontext->cipher_usebits, TLScontext->cipher_algbits);
 
index c493775c0165becc9dbb72cd836967ae319152ee..84cd775611021713c88c518eca65fd0ce70cd023 100644 (file)
 /*     VSTRING *buf;
 /*     int     len;
 /*
-/*     int     tls_mgr_policy(cache_types)
-/*     int     *cache_types;
+/*     int     tls_mgr_policy(cache_type, cachable)
+/*     const char *cache_type;
+/*     int     *cachable;
 /*
 /*     int     tls_mgr_update(cache_type, cache_id, buf, len)
-/*     int     cache_type;
+/*     const char *cache_type;
 /*     const char *cache_id;
 /*     const char *buf;
 /*     ssize_t len;
 /*
 /*     int     tls_mgr_lookup(cache_type, cache_id, buf)
-/*     int     cache_type;
+/*     const char *cache_type;
 /*     const char *cache_id;
 /*     VSTRING *buf;
 /*
 /*     int     tls_mgr_delete(cache_type, cache_id)
-/*     int     cache_type;
+/*     const char *cache_type;
 /*     const char *cache_id;
 /* DESCRIPTION
 /*     These routines communicate with the tlsmgr(8) server for
 /*     the specified session cache.
 /*
 /*     Arguments
-/* .IP cache_types
-/*     The bit-wise OR of zero or more of the following:
-/* .RS
-/* .IP TLS_MGR_SCACHE_CLIENT
-/*     Session caching is enabled for SMTP client sessions.
-/* .IP TLS_MGR_SCACHE_SERVER
-/*     Session caching is enabled for SMTP server sessions.
-/* .RE
 /* .IP cache_type
-/*     One of TLS_MGR_SCACHE_CLIENT or TLS_MGR_SCACHE_SERVER (see above).
+/*     One of TLS_MGR_SCACHE_SMTPD, TLS_MGR_SCACHE_SMTP or
+/*     TLS_MGR_SCACHE_LMTP.
+/* .IP cachable
+/*     Pointer to int, set non-zero if the requested cache_type
+/*     is enabled.
 /* .IP cache_id
 /*     The session cache lookup key.
 /* .IP buf
@@ -176,7 +173,7 @@ int     tls_mgr_seed(VSTRING *buf, int len)
 
 /* tls_mgr_policy - request caching policy */
 
-int     tls_mgr_policy(int *policy)
+int     tls_mgr_policy(const char *cache_type, int *cachable)
 {
     int     status;
 
@@ -192,10 +189,11 @@ int     tls_mgr_policy(int *policy)
     if (attr_clnt_request(tls_mgr,
                          ATTR_FLAG_NONE,       /* Request attributes */
                        ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_POLICY,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_END,
                          ATTR_FLAG_MISSING,    /* Reply attributes */
                          ATTR_TYPE_INT, TLS_MGR_ATTR_STATUS, &status,
-                         ATTR_TYPE_INT, TLS_MGR_ATTR_POLICY, policy,
+                         ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable,
                          ATTR_TYPE_END) != 2)
        status = TLS_MGR_STAT_FAIL;
     return (status);
@@ -203,7 +201,8 @@ int     tls_mgr_policy(int *policy)
 
 /* tls_mgr_lookup - request cached session */
 
-int     tls_mgr_lookup(int cache_type, const char *cache_id, VSTRING *buf)
+int     tls_mgr_lookup(const char *cache_type, const char *cache_id,
+                              VSTRING *buf)
 {
     int     status;
 
@@ -219,7 +218,7 @@ int     tls_mgr_lookup(int cache_type, const char *cache_id, VSTRING *buf)
     if (attr_clnt_request(tls_mgr,
                          ATTR_FLAG_NONE,       /* Request */
                        ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP,
-                         ATTR_TYPE_INT, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
                          ATTR_TYPE_END,
                          ATTR_FLAG_MISSING,    /* Reply */
@@ -232,7 +231,7 @@ int     tls_mgr_lookup(int cache_type, const char *cache_id, VSTRING *buf)
 
 /* tls_mgr_update - save session to cache */
 
-int     tls_mgr_update(int cache_type, const char *cache_id,
+int     tls_mgr_update(const char *cache_type, const char *cache_id,
                               const char *buf, ssize_t len)
 {
     int     status;
@@ -249,7 +248,7 @@ int     tls_mgr_update(int cache_type, const char *cache_id,
     if (attr_clnt_request(tls_mgr,
                          ATTR_FLAG_NONE,       /* Request */
                        ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_UPDATE,
-                         ATTR_TYPE_INT, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
                          ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, len, buf,
                          ATTR_TYPE_END,
@@ -262,7 +261,7 @@ int     tls_mgr_update(int cache_type, const char *cache_id,
 
 /* tls_mgr_delete - remove cached session */
 
-int     tls_mgr_delete(int cache_type, const char *cache_id)
+int     tls_mgr_delete(const char *cache_type, const char *cache_id)
 {
     int     status;
 
@@ -278,7 +277,7 @@ int     tls_mgr_delete(int cache_type, const char *cache_id)
     if (attr_clnt_request(tls_mgr,
                          ATTR_FLAG_NONE,       /* Request */
                        ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_DELETE,
-                         ATTR_TYPE_INT, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
                          ATTR_TYPE_END,
                          ATTR_FLAG_MISSING,    /* Reply */
@@ -334,11 +333,11 @@ int     main(int unused_ac, char **av)
 #define COMMAND(argv, str, len) \
     (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len)
 
-       if (COMMAND(argv, "policy", 1)) {
-           int     cache_types;
+       if (COMMAND(argv, "policy", 2)) {
+           int     cachable;
 
-           status = tls_mgr_policy(&cache_types);
-           vstream_printf("status=%d policy=0x%x\n", status, cache_types);
+           status = tls_mgr_policy(argv[2], &cachable);
+           vstream_printf("status=%d cachable=%d\n", status, cachable);
        } else if (COMMAND(argv, "seed", 2)) {
            VSTRING *buf = vstring_alloc(10);
            VSTRING *hex = vstring_alloc(10);
@@ -351,29 +350,24 @@ int     main(int unused_ac, char **av)
            vstring_free(buf);
        } else if (COMMAND(argv, "lookup", 3)) {
            VSTRING *buf = vstring_alloc(10);
-           int     cache_type = atoi(argv->argv[1]);
 
-           status = tls_mgr_lookup(cache_type, argv->argv[2], buf);
+           status = tls_mgr_lookup(argv[1], argv->argv[2], buf);
            vstream_printf("status=%d session=%.*s\n",
                           status, LEN(buf), STR(buf));
        } else if (COMMAND(argv, "update", 4)) {
-           int     cache_type = atoi(argv->argv[1]);
-
-           status = tls_mgr_update(cache_type, argv->argv[2],
+           status = tls_mgr_update(argv[1], argv->argv[2],
                                    argv->argv[3], strlen(argv->argv[3]));
            vstream_printf("status=%d\n", status);
        } else if (COMMAND(argv, "delete", 3)) {
-           int     cache_type = atoi(argv->argv[1]);
-
-           status = tls_mgr_delete(cache_type, argv->argv[2]);
+           status = tls_mgr_delete(argv[1], argv->argv[2]);
            vstream_printf("status=%d\n", status);
        } else {
            vstream_printf("usage:\n"
                           "seed byte_count\n"
-                          "policy\n"
-                          "lookup cache_type cache_id\n"
-                          "update cache_type cache_id session\n"
-                          "delete cache_type cache_id\n");
+                          "policy smtpd|smtp|lmtp\n"
+                          "lookup smtpd|smtp|lmtp cache_id\n"
+                          "update smtpd|smtp|lmtp cache_id session\n"
+                          "delete smtpd|smtp|lmtp cache_id\n");
        }
        vstream_fflush(VSTREAM_OUT);
     }
index 068002c3a8bc34c6a5fddf84ca90c71c456af868..bc9effffc6a073516abb3d5c3c49323dc27a5e55 100644 (file)
@@ -23,7 +23,7 @@
 #define TLS_MGR_REQ_LOOKUP     "lookup"
 #define TLS_MGR_REQ_UPDATE     "update"
 #define TLS_MGR_REQ_DELETE     "delete"
-#define TLS_MGR_ATTR_POLICY    "policy"
+#define TLS_MGR_ATTR_CACHABLE  "cachable"
 #define TLS_MGR_ATTR_CACHE_TYPE        "cache_type"
 #define TLS_MGR_ATTR_SEED      "seed"
 #define TLS_MGR_ATTR_CACHE_ID  "cache_id"
 #define TLS_MGR_STAT_ERR       (-1)    /* object not found */
 #define TLS_MGR_STAT_FAIL      (-2)    /* protocol error */
 
- /*
-  * Are we talking about the client or server cache?
-  */
-#define TLS_MGR_SCACHE_CLIENT  (1<<0)
-#define TLS_MGR_SCACHE_SERVER  (1<<1)
-
  /*
   * Functional interface.
   */
 extern int tls_mgr_seed(VSTRING *, int);
-extern int tls_mgr_policy(int *);
-extern int tls_mgr_lookup(int, const char *, VSTRING *);
-extern int tls_mgr_update(int, const char *, const char *, ssize_t);
-extern int tls_mgr_delete(int, const char *);
+extern int tls_mgr_policy(const char *, int *);
+extern int tls_mgr_lookup(const char *, const char *, VSTRING *);
+extern int tls_mgr_update(const char *, const char *, const char *, ssize_t);
+extern int tls_mgr_delete(const char *, const char *);
 
 /* LICENSE
 /* .ad
index 3b45df09fe8dae8f393a591b0446205640c5dbe7..456651161c4a564916e43fd0f8b76ca1e846fa0b 100644 (file)
 /*     void    tls_free_context(TLScontext)
 /*     TLScontext_t *TLScontext;
 /*
+/*     void    tls_check_version()
+/*
+/*     long    tls_bug_bits()
+/*
 /*     void    tls_print_errors()
 /*
 /*     void    tls_info_callback(ssl, where, ret)
 /*     tls_free_context() destroys a TLScontext structure
 /*     together with OpenSSL structures that are attached to it.
 /*
+/*     tls_check_version() logs a warning when the run-time OpenSSL
+/*     library differs in its major, minor or micro number from
+/*     the compile-time OpenSSL headers.
+/*
+/*     tls_bug_bits() returns the bug compatibility mask appropriate
+/*     for the run-time library. Some of the bug work-arounds are
+/*     not appropriate for some library versions.
+/*
 /*     tls_print_errors() queries the OpenSSL error stack,
 /*     logs the error messages, and clears the error stack.
 /*
 /* Application-specific. */
 
  /*
-  * Indices to attach our own information to SSL and to SSL_SESSION objects,
-  * so that it can be accessed by call-back routines.
+  * Index to attach TLScontext pointers to SSL objects, so that they can be
+  * accessed by call-back routines.
   */
 int     TLScontext_index = -1;
 
+ /*
+  * Index to attach session cache names SSL_CTX objects.
+  */
+int     TLSscache_index = -1;
+
+ /*
+  * Protocol name <=> mask conversion.
+  */
+NAME_MASK tls_protocol_table[] = {
+    SSL_TXT_SSLV2, TLS_PROTOCOL_SSLv2,
+    SSL_TXT_SSLV3, TLS_PROTOCOL_SSLv3,
+    SSL_TXT_TLSV1, TLS_PROTOCOL_TLSv1,
+    0, 0,
+};
+
+char   *var_tls_high_clist;
+char   *var_tls_medium_clist;
+char   *var_tls_low_clist;
+char   *var_tls_export_clist;
+char   *var_tls_null_clist;
+
+ /*
+  * Ciphersuite name <=> code conversion.
+  */
+NAME_CODE tls_cipher_level_table[] = {
+    "high", TLS_CIPHER_HIGH,
+    "medium", TLS_CIPHER_MEDIUM,
+    "low", TLS_CIPHER_LOW,
+    "export", TLS_CIPHER_EXPORT,
+    "null", TLS_CIPHER_NULL,
+    0, TLS_CIPHER_NONE,
+};
+
  /*
   * Parsed OpenSSL version number.
   */
@@ -109,6 +154,77 @@ typedef struct {
     int     status;
 } TLS_VINFO;
 
+/* tls_cipher_list - Cipherlist for given grade, less exclusions */
+
+char   *tls_cipher_list(int level,...)
+{
+    const char *myname = "tls_cipher_list";
+    static VSTRING *buf;
+    va_list ap;
+    const char *exclude;
+    char   *tok;
+    char   *save;
+    char   *cp;
+
+    buf = buf ? buf : vstring_alloc(10);
+    VSTRING_RESET(buf);
+
+    switch (level) {
+    case TLS_CIPHER_HIGH:
+       vstring_strcpy(buf, var_tls_high_clist);
+       break;
+    case TLS_CIPHER_MEDIUM:
+       vstring_strcpy(buf, var_tls_medium_clist);
+       break;
+    case TLS_CIPHER_LOW:
+       vstring_strcpy(buf, var_tls_low_clist);
+       break;
+    case TLS_CIPHER_EXPORT:
+       vstring_strcpy(buf, var_tls_export_clist);
+       break;
+    case TLS_CIPHER_NULL:
+       vstring_strcpy(buf, var_tls_null_clist);
+       break;
+    case TLS_CIPHER_NONE:
+       return 0;
+    default:
+       msg_panic("%s: invalid cipher level: %d", myname, level);
+    }
+
+    if (VSTRING_LEN(buf) == 0)
+       msg_panic("%s: empty cipherlist", myname);
+
+    va_start(ap, level);
+    while ((exclude = va_arg(ap, char *)) != 0) {
+       if (*exclude == '\0')
+           continue;
+       save = cp = mystrdup(exclude);
+       while ((tok = mystrtok(&cp, "\t\n\r ,")) != 0) {
+
+           /*
+            * Can't exclude ciphers that start with modifiers, or
+            * multi-element (":" separated) ciphers.
+            */
+           if (strchr("!+-@", *tok)) {
+               msg_warn("%s: can't exclude '!+-@' modifiers, '%s' ignored",
+                        myname, tok);
+               continue;
+           }
+           if (strchr(tok, ':')) {
+               msg_warn("%s: can't exclude compound ciphers, '%s' ignored",
+                        myname, tok);
+               continue;
+           }
+           vstring_sprintf_append(buf, ":!%s", tok);
+       }
+       myfree(save);
+    }
+    va_end(ap);
+
+    return (vstring_str(buf));
+}
+
+
 /* tls_alloc_context - allocate TLScontext */
 
 TLScontext_t *tls_alloc_context(int log_level, const char *peername)
@@ -171,6 +287,8 @@ void    tls_free_context(TLScontext_t *TLScontext)
     myfree((char *) TLScontext);
 }
 
+/* tls_version_split - Split OpenSSL version number into major, minor, ... */
+
 static void tls_version_split(long version, TLS_VINFO *info)
 {
 
@@ -267,7 +385,7 @@ long    tls_bug_bits(void)
     /*
      * In OpenSSL 0.9.8[ab], enabling zlib compression breaks the padding bug
      * work-around, leading to false positives and failed connections. We may
-     * not interoperate with systems with the bug, but this better than
+     * not interoperate with systems with the bug, but this is better than
      * breaking on all 0.9.8[ab] systems that have zlib support enabled.
      */
     if (lib_version >= 0x00908000L && lib_version <= 0x0090802fL) {
index bcd2cc03f252decd9492a4d93ce903ac5a86f875..7eefd905567aad92f3e501dbe2b9f39b28678bf0 100644 (file)
@@ -6,10 +6,10 @@
 /* SYNOPSIS
 /*     #include <tls_scache.h>
 /*
-/*     TLS_SCACHE *tls_scache_open(dbname, cache_label, log_level, timeout)
+/*     TLS_SCACHE *tls_scache_open(dbname, cache_label, verbose, timeout)
 /*     const char *dbname
 /*     const char *cache_label;
-/*     int     log_level;
+/*     int     verbose;
 /*     int     timeout;
 /*
 /*     void    tls_scache_close(cache)
@@ -72,8 +72,8 @@
 /*     The base name of the session cache file.
 /* .IP cache_label
 /*     A string that is used in logging and error messages.
-/* .IP log_level
-/*     The logging level for cache operations.
+/* .IP verbose
+/*     Do verbose logging of cache operations? (zero == no)
 /* .IP timeout
 /*     The time after wich a session cache entry is considered too old.
 /* .IP first_next
@@ -186,7 +186,7 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
     /*
      * Logging.
      */
-    if (cp->log_level >= 3)
+    if (cp->verbose)
        msg_info("write %s TLS cache entry %s: time=%ld [data %ld bytes]",
                 cp->cache_label, cache_id, (long) entry->timestamp,
                 (long) session_len);
@@ -202,7 +202,7 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
 /* tls_scache_decode - decode TLS session cache entry */
 
 static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
-                                    const char *hex_data, ssize_t hex_data_len,
+                                const char *hex_data, ssize_t hex_data_len,
                                     VSTRING *out_session)
 {
     TLS_SCACHE_ENTRY *entry;
@@ -235,10 +235,10 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
     /*
      * Logging.
      */
-    if (cp->log_level >= 3)
+    if (cp->verbose)
        msg_info("read %s TLS cache entry %s: time=%ld [data %ld bytes]",
                 cp->cache_label, cache_id, (long) entry->timestamp,
-                (long) (LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session)));
+             (long) (LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session)));
 
     /*
      * Other mandatory restrictions.
@@ -269,7 +269,7 @@ int     tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
     /*
      * Logging.
      */
-    if (cp->log_level >= 3)
+    if (cp->verbose)
        msg_info("lookup %s session id=%s", cp->cache_label, cache_id);
 
     /*
@@ -306,7 +306,7 @@ int     tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
     /*
      * Logging.
      */
-    if (cp->log_level >= 3)
+    if (cp->verbose)
        msg_info("put %s session id=%s [data %ld bytes]",
                 cp->cache_label, cache_id, (long) len);
 
@@ -416,7 +416,7 @@ int     tls_scache_delete(TLS_SCACHE *cp, const char *cache_id)
     /*
      * Logging.
      */
-    if (cp->log_level >= 3)
+    if (cp->verbose)
        msg_info("delete %s session id=%s", cp->cache_label, cache_id);
 
     /*
@@ -431,7 +431,7 @@ int     tls_scache_delete(TLS_SCACHE *cp, const char *cache_id)
 /* tls_scache_open - open TLS session cache file */
 
 TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
-                                   int log_level, int timeout)
+                                   int verbose, int timeout)
 {
     TLS_SCACHE *cp;
     DICT   *dict;
@@ -439,7 +439,7 @@ TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
     /*
      * Logging.
      */
-    if (log_level >= 3)
+    if (verbose)
        msg_info("open %s TLS cache %s", cache_label, dbname);
 
     /*
@@ -479,7 +479,7 @@ TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
     cp->flags = 0;
     cp->db = dict;
     cp->cache_label = mystrdup(cache_label);
-    cp->log_level = log_level;
+    cp->verbose = verbose;
     cp->timeout = timeout;
     cp->saved_cursor = 0;
 
@@ -494,7 +494,7 @@ void    tls_scache_close(TLS_SCACHE *cp)
     /*
      * Logging.
      */
-    if (cp->log_level >= 3)
+    if (cp->verbose)
        msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name);
 
     /*
index 0eb88ab0b7a52803706efd2bbb82dd606809e2ae..78903416db0f6413c7cd3fb3b271ad8daae981e0 100644 (file)
@@ -23,8 +23,8 @@
 typedef struct {
     int     flags;                     /* see below */
     DICT   *db;                                /* database handle */
-    char   *cache_label;               /* "client" or "server" */
-    int     log_level;                 /* smtp(d)_tls_log_level */
+    char   *cache_label;               /* "smtpd", "smtp" or "lmtp" */
+    int     verbose;                   /* enable verbose logging */
     int     timeout;                   /* smtp(d)_tls_session_cache_timeout */
     char   *saved_cursor;              /* cursor cache ID */
 } TLS_SCACHE;
index e2642bf831979a230c718b0fbc88ef658ea8399f..76e90efce263e6f8b46a6f377d601dab041137c0 100644 (file)
@@ -6,15 +6,15 @@
 /* SYNOPSIS
 /*     #include <tls.h>
 /*
-/*     SSL_CTX *tls_server_init(verifydepth, askcert)
-/*     int     verifydepth;
-/*     int     askcert;
+/*     SSL_CTX *tls_server_init(props)
+/*     const tls_server_props *props;
 /*
-/*     TLScontext_t *tls_server_start(server_ctx, stream, timeout,
+/*     TLScontext_t *tls_server_start(server_ctx, stream, timeout, log_level,
 /*                                     peername, peeraddr, requirecert)
 /*     SSL_CTX *server_ctx;
 /*     VSTREAM *stream;
 /*     int     timeout;
+/*     int     log_level;
 /*     const char *peername;
 /*     const char *peeraddr;
 /*     int     requirecert;
@@ -139,34 +139,38 @@ static const char hexcodes[] = "0123456789ABCDEF";
   */
 static char server_session_id_context[] = "Postfix/TLS";
 
-static int tls_server_cache = 0;
-
 /* get_server_session_cb - callback to retrieve session from server cache */
 
-static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
-                                                 unsigned char *session_id,
+static SSL_SESSION *get_server_session_cb(SSL *ssl, unsigned char *session_id,
                                                  int session_id_length,
                                                  int *unused_copy)
 {
+    TLScontext_t *TLScontext;
     VSTRING *cache_id;
     VSTRING *session_data = vstring_alloc(2048);
     SSL_SESSION *session = 0;
 
+    if ((TLScontext = SSL_get_ex_data(ssl, TLScontext_index)) == 0)
+       msg_panic("null TLScontext in session lookup callback");
+
 #define HEX_CACHE_ID(id, len) \
     hex_encode(vstring_alloc(2 * (len) + 1), (char *) (id), (len))
 
     cache_id = HEX_CACHE_ID(session_id, session_id_length);
-    if (var_smtpd_tls_loglevel >= 3)
-       msg_info("looking up session %s in server cache", STR(cache_id));
+
+    if (TLScontext->log_level >= 2)
+       msg_info("looking up session %s in %s cache",
+                STR(cache_id), TLScontext->cache_type);
 
     /*
      * Load the session from cache and decode it.
      */
-    if (tls_mgr_lookup(tls_server_cache, STR(cache_id),
+    if (tls_mgr_lookup(TLScontext->cache_type, STR(cache_id),
                       session_data) == TLS_MGR_STAT_OK) {
        session = tls_session_activate(STR(session_data), LEN(session_data));
-       if (session && (var_smtpd_tls_loglevel >= 3))
-           msg_info("reloaded session %s from server cache", STR(cache_id));
+       if (session && (TLScontext->log_level >= 2))
+           msg_info("reloaded session %s from %s cache",
+                    STR(cache_id), TLScontext->cache_type);
     }
 
     /*
@@ -187,31 +191,41 @@ static void uncache_session(SSL_CTX *ctx, TLScontext_t *TLScontext)
 
     SSL_CTX_remove_session(ctx, session);
 
+    if (TLScontext->cache_type == 0)
+       return;
+
     cache_id = HEX_CACHE_ID(session->session_id, session->session_id_length);
-    if (var_smtpd_tls_loglevel >= 3)
-       msg_info("remove session %s from server cache", STR(cache_id));
+    if (TLScontext->log_level >= 2)
+       msg_info("remove session %s from %s cache",
+                STR(cache_id), TLScontext->cache_type);
 
-    tls_mgr_delete(tls_server_cache, STR(cache_id));
+    tls_mgr_delete(TLScontext->cache_type, STR(cache_id));
     vstring_free(cache_id);
 }
 
 /* new_server_session_cb - callback to save session to server cache */
 
-static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
+static int new_server_session_cb(SSL *ssl, SSL_SESSION *session)
 {
     VSTRING *cache_id;
+    TLScontext_t *TLScontext;
     VSTRING *session_data;
 
+    if ((TLScontext = SSL_get_ex_data(ssl, TLScontext_index)) == 0)
+       msg_panic("null TLScontext in new session callback");
+
     cache_id = HEX_CACHE_ID(session->session_id, session->session_id_length);
-    if (var_smtpd_tls_loglevel >= 3)
-       msg_info("save session %s to server cache", STR(cache_id));
+
+    if (TLScontext->log_level >= 2)
+       msg_info("save session %s to %s cache",
+                STR(cache_id), TLScontext->cache_type);
 
     /*
      * Passivate and save the session state.
      */
     session_data = tls_session_passivate(session);
     if (session_data)
-       tls_mgr_update(tls_server_cache, STR(cache_id),
+       tls_mgr_update(TLScontext->cache_type, STR(cache_id),
                       STR(session_data), LEN(session_data));
 
     /*
@@ -227,16 +241,16 @@ static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
 
 /* tls_server_init - initialize the server-side TLS engine */
 
-SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
+SSL_CTX *tls_server_init(const tls_server_props *props)
 {
-    int     off = 0;
+    long    off = 0;
     int     verify_flags = SSL_VERIFY_NONE;
     SSL_CTX *server_ctx;
-    int     cache_types;
+    int     cachable;
 
     /* See skeleton at OpenSSL apps/s_server.c. */
 
-    if (var_smtpd_tls_loglevel >= 2)
+    if (props->log_level >= 2)
        msg_info("initializing the server-side TLS engine");
 
     /*
@@ -280,25 +294,40 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
     }
 
     /*
-     * Here we might set SSL_OP_NO_SSLv2, SSL_OP_NO_SSLv3, SSL_OP_NO_TLSv1.
-     * Of course, the last one would not make sense, since RFC2487 is only
-     * defined for TLS, but we also want to accept Netscape communicator
-     * requests, and it only supports SSLv3.
+     * Protocol work-arounds, OpenSSL version dependent.
      */
     off |= tls_bug_bits();
     SSL_CTX_set_options(server_ctx, off);
 
+#define DISABLE_ALL (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1)
+
+    /*
+     * Global protocol selection. Not enabling any explicitly, enables all.
+     */
+    if (props->protocols != 0 && props->protocols != TLS_ALL_PROTOCOLS) {
+       long    disable = DISABLE_ALL;
+
+       if (props->protocols & TLS_PROTOCOL_TLSv1)
+           disable &= ~SSL_OP_NO_TLSv1;
+       if (props->protocols & TLS_PROTOCOL_SSLv3)
+           disable &= ~SSL_OP_NO_SSLv3;
+       if (props->protocols & TLS_PROTOCOL_SSLv2)
+           disable &= ~SSL_OP_NO_SSLv2;
+
+       SSL_CTX_set_options(server_ctx, disable);
+    }
+
     /*
      * Set the call-back routine for verbose logging.
      */
-    if (var_smtpd_tls_loglevel >= 2)
+    if (props->log_level >= 2)
        SSL_CTX_set_info_callback(server_ctx, tls_info_callback);
 
     /*
      * Override the default cipher list with our own list.
      */
-    if (*var_smtpd_tls_cipherlist != 0)
-       if (SSL_CTX_set_cipher_list(server_ctx, var_smtpd_tls_cipherlist) == 0) {
+    if (*props->cipherlist != 0)
+       if (SSL_CTX_set_cipher_list(server_ctx, props->cipherlist) == 0) {
            tls_print_errors();
            SSL_CTX_free(server_ctx);           /* 200411 */
            return (0);
@@ -315,8 +344,8 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
      * at startup time, so that you don't have the hassle to maintain another
      * copy of the CApath directory for chroot-jail.
      */
-    if (tls_set_ca_certificate_info(server_ctx, var_smtpd_tls_CAfile,
-                                   var_smtpd_tls_CApath) < 0) {
+    if (tls_set_ca_certificate_info(server_ctx,
+                                   props->CAfile, props->CApath) < 0) {
        SSL_CTX_free(server_ctx);               /* 200411 */
        return (0);
     }
@@ -334,10 +363,9 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
      * openssl-library will use RSA first if not especially changed in the
      * cipher setup.
      */
-    if (tls_set_my_certificate_key_info(server_ctx, var_smtpd_tls_cert_file,
-                                       var_smtpd_tls_key_file,
-                                       var_smtpd_tls_dcert_file,
-                                       var_smtpd_tls_dkey_file) < 0) {
+    if (tls_set_my_certificate_key_info(server_ctx, props->cert_file,
+                                       props->key_file, props->dcert_file,
+                                       props->dkey_file) < 0) {
        SSL_CTX_free(server_ctx);               /* 200411 */
        return (0);
     }
@@ -358,10 +386,10 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
      * will not abort but just log the error message.
      */
     SSL_CTX_set_tmp_dh_callback(server_ctx, tls_tmp_dh_cb);
-    if (*var_smtpd_tls_dh1024_param_file != 0)
-       tls_set_dh_1024_from_file(var_smtpd_tls_dh1024_param_file);
-    if (*var_smtpd_tls_dh512_param_file != 0)
-       tls_set_dh_512_from_file(var_smtpd_tls_dh512_param_file);
+    if (*props->dh1024_param_file != 0)
+       tls_set_dh_1024_from_file(props->dh1024_param_file);
+    if (*props->dh512_param_file != 0)
+       tls_set_dh_512_from_file(props->dh512_param_file);
 
     /*
      * If we want to check client certificates, we have to indicate it in
@@ -383,37 +411,13 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
      * reasons, but this would involve severe changes in the internal postfix
      * logic, so we have to live with it the way it is.
      */
-    if (askcert)
+    if (props->ask_ccert)
        verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
     SSL_CTX_set_verify(server_ctx, verify_flags,
                       tls_verify_certificate_callback);
-    if (*var_smtpd_tls_CAfile)
+    if (*props->CAfile)
        SSL_CTX_set_client_CA_list(server_ctx,
-                            SSL_load_client_CA_file(var_smtpd_tls_CAfile));
-
-    /*
-     * Initialize the session cache.
-     * 
-     * With a large number of concurrent smtpd(8) processes, it is not a good
-     * idea to cache multiple large session objects in each process. We set
-     * the internal cache size to 1, and don't register a "remove_cb" so as
-     * to avoid deleting good sessions from the external cache prematurely
-     * (when the internal cache is full, OpenSSL removes sessions from the
-     * external cache also)!
-     * 
-     * This makes SSL_CTX_remove_session() not useful for flushing broken
-     * sessions from the external cache, so we must delete them directly (not
-     * via a callback).
-     * 
-     * Set a session id context to identify to what type of server process
-     * created a session. In our case, the context is simply the name of the
-     * mail system: "Postfix/TLS".
-     */
-    SSL_CTX_sess_set_cache_size(server_ctx, 1);
-    SSL_CTX_set_timeout(server_ctx, var_smtpd_tls_scache_timeout);
-    SSL_CTX_set_session_id_context(server_ctx,
-                                  (void *) &server_session_id_context,
-                                  sizeof(server_session_id_context));
+                                  SSL_load_client_CA_file(props->CAfile));
 
     /*
      * The session cache is implemented by the tlsmgr(8) server.
@@ -425,13 +429,66 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
      * entries, and leave it up to the tlsmgr process instead. Found by
      * Victor Duchovni.
      */
-    if (tls_mgr_policy(&cache_types) == TLS_MGR_STAT_OK
-       && (tls_server_cache = (cache_types & TLS_MGR_SCACHE_SERVER)) != 0) {
+
+    if (TLSscache_index < 0)
+       TLSscache_index =
+           SSL_CTX_get_ex_new_index(0, "TLScontext ex_data index",
+                                    NULL, NULL, NULL);
+
+    if (tls_mgr_policy(props->cache_type, &cachable) != TLS_MGR_STAT_OK)
+       cachable = 0;
+
+    if (cachable &&
+       !SSL_CTX_set_ex_data(server_ctx, TLSscache_index,
+                            (void *) props->cache_type)) {
+       msg_warn("Session cache off: error saving cache type in SSL context.");
+       tls_print_errors();
+       cachable = 0;
+    }
+    if (cachable) {
+
+       /*
+        * Initialize the session cache.
+        * 
+        * With a large number of concurrent smtpd(8) processes, it is not a
+        * good idea to cache multiple large session objects in each process.
+        * We set the internal cache size to 1, and don't register a
+        * "remove_cb" so as to avoid deleting good sessions from the
+        * external cache prematurely (when the internal cache is full,
+        * OpenSSL removes sessions from the external cache also)!
+        * 
+        * This makes SSL_CTX_remove_session() not useful for flushing broken
+        * sessions from the external cache, so we must delete them directly
+        * (not via a callback).
+        * 
+        * Set a session id context to identify to what type of server process
+        * created a session. In our case, the context is simply the name of
+        * the mail system: "Postfix/TLS".
+        */
+       SSL_CTX_sess_set_cache_size(server_ctx, 1);
+       SSL_CTX_set_session_id_context(server_ctx,
+                                      (void *) &server_session_id_context,
+                                      sizeof(server_session_id_context));
        SSL_CTX_set_session_cache_mode(server_ctx,
                                       SSL_SESS_CACHE_SERVER |
                                       SSL_SESS_CACHE_NO_AUTO_CLEAR);
        SSL_CTX_sess_set_get_cb(server_ctx, get_server_session_cb);
        SSL_CTX_sess_set_new_cb(server_ctx, new_server_session_cb);
+
+       /*
+        * OpenSSL ignores timed-out sessions, we need to set the internal
+        * cache timeut at least as high as the external cache timeout. This
+        * applies even if no internal cache is used.
+        */
+       SSL_CTX_set_timeout(server_ctx, props->scache_timeout);
+    } else {
+
+       /*
+        * If we have no external cache, disable all caching, no use wasting
+        * client memory resources with sessions they are unlikely to be able
+        * to reuse.
+        */
+       SSL_CTX_set_session_cache_mode(server_ctx, SSL_SESS_CACHE_OFF);
     }
 
     /*
@@ -452,7 +509,8 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
   * the client, so that we can immediately start the TLS handshake process.
   */
 TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
-                                      int timeout, const char *peername,
+                                      int timeout, int log_level,
+                                      const char *peername,
                                       const char *peeraddr, int requirecert)
 {
     int     sts;
@@ -465,7 +523,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
     unsigned char md[EVP_MAX_MD_SIZE];
     char    buf[CCERT_BUFSIZ];
 
-    if (var_smtpd_tls_loglevel >= 1)
+    if (log_level >= 1)
        msg_info("setting up TLS connection from %s[%s]", peername, peeraddr);
 
     /*
@@ -473,7 +531,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
      * structure. Add the location of TLScontext to the SSL to later retrieve
      * the information inside the tls_verify_certificate_callback().
      */
-    TLScontext = tls_alloc_context(var_smtpd_tls_loglevel, peername);
+    TLScontext = tls_alloc_context(log_level, peername);
+    TLScontext->cache_type = SSL_CTX_get_ex_data(server_ctx, TLSscache_index);
 
     if ((TLScontext->con = (SSL *) SSL_new(server_ctx)) == NULL) {
        msg_info("Could not allocate 'TLScontext->con' with SSL_new()");
@@ -548,7 +607,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
      * Well there is a BIO below the SSL routines that is automatically
      * created for us, so we can use it for debugging purposes.
      */
-    if (var_smtpd_tls_loglevel >= 3)
+    if (log_level >= 3)
        BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
 
     /*
@@ -566,7 +625,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
        return (0);
     }
     /* Only loglevel==4 dumps everything */
-    if (var_smtpd_tls_loglevel < 4)
+    if (log_level < 4)
        BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
 
     /*
@@ -574,6 +633,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
      * session was negotiated.
      */
     TLScontext->session_reused = SSL_session_reused(TLScontext->con);
+    if (log_level >= 2 && TLScontext->session_reused)
+       msg_info("Reusing old session");
 
     /*
      * Let's see whether a peer certificate is available and what is the
@@ -584,7 +645,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
        if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
            TLScontext->peer_verified = 1;
 
-       if (var_smtpd_tls_loglevel >= 2) {
+       if (log_level >= 2) {
            X509_NAME_oneline(X509_get_subject_name(peer),
                              buf, sizeof(buf));
            msg_info("subject=%s", buf);
@@ -604,7 +665,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
                else
                    TLScontext->peer_fingerprint[(j * 3) + 2] = '\0';
            }
-           if (var_smtpd_tls_loglevel >= 1)
+           if (log_level >= 1)
                msg_info("fingerprint=%s", TLScontext->peer_fingerprint);
        }
        if ((TLScontext->peer_CN = tls_peer_CN(peer)) == 0)
@@ -612,7 +673,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
        if ((TLScontext->issuer_CN = tls_issuer_CN(peer)) == 0)
            TLScontext->issuer_CN = mystrdup("");
 
-       if (var_smtpd_tls_loglevel >= 1) {
+       if (log_level >= 1) {
            if (TLScontext->peer_verified)
                msg_info("Verified: subject_CN=%s, issuer=%s",
                         TLScontext->peer_CN, TLScontext->issuer_CN);
@@ -653,7 +714,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
      */
     tls_stream_start(stream, TLScontext);
 
-    if (var_smtpd_tls_loglevel >= 1)
+    if (log_level >= 1)
        msg_info("TLS connection established from %s[%s]: %s with cipher %s (%d/%d bits)",
                 peername, peeraddr,
                 TLScontext->protocol, TLScontext->cipher_name,
index 212edb56ba4de6106a961b2ade2013d373d559b4..19531407e8c65b85ed257b53ff8f326ac725b69b 100644 (file)
@@ -70,7 +70,7 @@
 static ssize_t tls_timed_read(int fd, void *buf, size_t len, int timeout,
                                      void *context)
 {
-    char   *myname = "tls_timed_read";
+    const char *myname = "tls_timed_read";
     ssize_t ret;
     TLScontext_t *TLScontext;
 
index 6a23205ee9cc419769bb116ba3b559297f952dd4..4ee4ddd88a3ff1e8227cb155d80c400e53c236b3 100644 (file)
@@ -71,8 +71,10 @@ tlsmgr.o: ../../include/mail_server.h
 tlsmgr.o: ../../include/master_proto.h
 tlsmgr.o: ../../include/msg.h
 tlsmgr.o: ../../include/mymalloc.h
+tlsmgr.o: ../../include/name_mask.h
 tlsmgr.o: ../../include/stringops.h
 tlsmgr.o: ../../include/sys_defs.h
+tlsmgr.o: ../../include/tls.h
 tlsmgr.o: ../../include/tls_mgr.h
 tlsmgr.o: ../../include/tls_prng.h
 tlsmgr.o: ../../include/tls_scache.h
index e086e2c311bcc59e7fbde76c640409665f6c4b67..dd2d6e8bc75b81bd282684dfde9c1ab886e0af25 100644 (file)
 /* TLS SESSION CACHE
 /* .ad
 /* .fi
-/* .IP "\fBsmtpd_tls_session_cache_database (empty)\fR"
-/*     Name of the file containing the optional Postfix SMTP server
-/*     TLS session cache.
-/* .IP "\fBsmtpd_tls_session_cache_timeout (3600s)\fR"
-/*     The expiration time of Postfix SMTP server TLS session cache
-/*     information.
+/* .IP "\fBlmtp_tls_loglevel (0)\fR"
+/*     The LMTP-specific version of the smtp_tls_loglevel
+/*     configuration parameter.
+/* .IP "\fBlmtp_tls_session_cache_database (empty)\fR"
+/*     The LMTP-specific version of the smtp_tls_session_cache_database
+/*     configuration parameter.
+/* .IP "\fBlmtp_tls_session_cache_timeout (3600s)\fR"
+/*     The LMTP-specific version of the smtp_tls_session_cache_timeout
+/*     configuration parameter.
+/* .IP "\fBsmtp_tls_loglevel (0)\fR"
+/*     Enable additional Postfix SMTP client logging of TLS activity.
 /* .IP "\fBsmtp_tls_session_cache_database (empty)\fR"
 /*     Name of the file containing the optional Postfix SMTP client
 /*     TLS session cache.
 /* .IP "\fBsmtp_tls_session_cache_timeout (3600s)\fR"
 /*     The expiration time of Postfix SMTP client TLS session cache
 /*     information.
+/* .IP "\fBsmtpd_tls_loglevel (0)\fR"
+/*     Enable additional Postfix SMTP server logging of TLS activity.
+/* .IP "\fBsmtpd_tls_session_cache_database (empty)\fR"
+/*     Name of the file containing the optional Postfix SMTP server
+/*     TLS session cache.
+/* .IP "\fBsmtpd_tls_session_cache_timeout (3600s)\fR"
+/*     The expiration time of Postfix SMTP server TLS session cache
+/*     information.
 /* PSEUDO RANDOM NUMBER GENERATOR
 /* .ad
 /* .fi
 /* TLS library. */
 
 #ifdef USE_TLS
+#include <tls.h>                       /* TLS_MGR_SCACHE_<type> */
 #include <tls_prng.h>
 #include <tls_scache.h>
 
@@ -202,6 +216,16 @@ char   *var_tls_rand_source;
 int     var_tls_rand_bytes;
 int     var_tls_reseed_period;
 int     var_tls_prng_exch_period;
+int     var_smtpd_tls_loglevel;
+char   *var_smtpd_tls_scache_db;
+int     var_smtpd_tls_scache_timeout;
+int     var_smtp_tls_loglevel;
+char   *var_smtp_tls_scache_db;
+int     var_smtp_tls_scache_timeout;
+int     var_lmtp_tls_loglevel;
+char   *var_lmtp_tls_scache_db;
+int     var_lmtp_tls_scache_timeout;
+char   *var_tls_rand_exch_name;
 
  /*
   * Bound the time that we are willing to wait for an I/O operation. This
@@ -236,17 +260,26 @@ static TLS_PRNG_SRC *rand_source_file;
 #define EGD_PATH(egd) ((egd) + EGD_PREF_LEN)
 
  /*
-  * State for TLS client and server session caches.
+  * State for TLS session caches.
   */
-static TLS_SCACHE *clnt_scache_info;   /* cache handle */
-static int clnt_scache_active;         /* scan in progress */
-
-static TLS_SCACHE *srvr_scache_info;   /* cache handle */
-static int srvr_scache_active;         /* scan in progress */
-
-#define WHICH_CACHE_INFO(type) \
-    (type == TLS_MGR_SCACHE_CLIENT ? clnt_scache_info : \
-       type == TLS_MGR_SCACHE_SERVER ? srvr_scache_info : 0)
+typedef struct {
+    char   *cache_label;
+    TLS_SCACHE *cache_info;
+    int     cache_active;
+    char  **cache_db;
+    int    *cache_loglevel;
+    int    *cache_timeout;
+} TLSMGR_SCACHE;
+
+TLSMGR_SCACHE cache_table[] = {
+    TLS_MGR_SCACHE_SMTPD, 0, 0, &var_smtpd_tls_scache_db,
+    &var_smtpd_tls_loglevel, &var_smtpd_tls_scache_timeout,
+    TLS_MGR_SCACHE_SMTP, 0, 0, &var_smtp_tls_scache_db,
+    &var_smtp_tls_loglevel, &var_smtp_tls_scache_timeout,
+    TLS_MGR_SCACHE_LMTP, 0, 0, &var_lmtp_tls_scache_db,
+    &var_lmtp_tls_loglevel, &var_lmtp_tls_scache_timeout,
+    0,
+};
 
  /*
   * SLMs.
@@ -374,11 +407,12 @@ static void tlsmgr_reseed_event(int unused_event, char *dummy)
     event_request_timer(tlsmgr_reseed_event, dummy, next_period);
 }
 
-/* tlsmgr_clnt_cache_run_event - start SMTP client TLS session cache scan */
+/* tlsmgr_cache_run_event - start TLS session cache scan */
 
-static void tlsmgr_clnt_cache_run_event(int unused_event, char *dummy)
+static void tlsmgr_cache_run_event(int unused_event, char *ctx)
 {
-    const char *myname = "tlsmgr_clnt_cache_run_event";
+    const char *myname = "tlsmgr_cache_run_event";
+    TLSMGR_SCACHE *cache = (TLSMGR_SCACHE *) ctx;
 
     /*
      * This routine runs when it is time for another TLS session cache scan.
@@ -387,41 +421,17 @@ static void tlsmgr_clnt_cache_run_event(int unused_event, char *dummy)
      * Don't start a new scan when the timer goes off while cache cleanup is
      * still in progress.
      */
-    if (var_smtp_tls_loglevel >= 3)
-       msg_info("%s: start TLS client session cache cleanup", myname);
+    if (cache->cache_info->verbose)
+       msg_info("%s: start TLS %s session cache cleanup",
+                myname, cache->cache_label);
 
-    if (clnt_scache_active == 0)
-       clnt_scache_active =
-           tls_scache_sequence(clnt_scache_info, DICT_SEQ_FUN_FIRST,
+    if (cache->cache_active == 0)
+       cache->cache_active =
+           tls_scache_sequence(cache->cache_info, DICT_SEQ_FUN_FIRST,
                                TLS_SCACHE_SEQUENCE_NOTHING);
 
-    event_request_timer(tlsmgr_clnt_cache_run_event, dummy,
-                       var_smtp_tls_scache_timeout);
-}
-
-/* tlsmgr_srvr_cache_run_event - start SMTP server TLS session cache scan */
-
-static void tlsmgr_srvr_cache_run_event(int unused_event, char *dummy)
-{
-    const char *myname = "tlsmgr_srvr_cache_run_event";
-
-    /*
-     * This routine runs when it is time for another TLS session cache scan.
-     * Make sure this routine gets called again in the future.
-     * 
-     * Don't start a new scan when the timer goes off while cache cleanup is
-     * still in progress.
-     */
-    if (var_smtpd_tls_loglevel >= 3)
-       msg_info("%s: start TLS server session cache cleanup", myname);
-
-    if (srvr_scache_active == 0)
-       srvr_scache_active =
-           tls_scache_sequence(srvr_scache_info, DICT_SEQ_FUN_FIRST,
-                               TLS_SCACHE_SEQUENCE_NOTHING);
-
-    event_request_timer(tlsmgr_srvr_cache_run_event, dummy,
-                       var_smtpd_tls_scache_timeout);
+    event_request_timer(tlsmgr_cache_run_event, (char *) cache,
+                       cache->cache_info->timeout);
 }
 
 /* tlsmgr_loop - TLS manager main loop */
@@ -429,6 +439,8 @@ static void tlsmgr_srvr_cache_run_event(int unused_event, char *dummy)
 static int tlsmgr_loop(char *unused_name, char **unused_argv)
 {
     struct timeval tv;
+    int     active = 0;
+    TLSMGR_SCACHE *ent;
 
     /*
      * Update the PRNG pool with the time of day. We do it here after every
@@ -452,18 +464,14 @@ static int tlsmgr_loop(char *unused_name, char **unused_argv)
 #define DONT_WAIT      0
 #define WAIT_FOR_EVENT (-1)
 
-    if (clnt_scache_active)
-       clnt_scache_active =
-           tls_scache_sequence(clnt_scache_info, DICT_SEQ_FUN_NEXT,
-                               TLS_SCACHE_SEQUENCE_NOTHING);
-    if (srvr_scache_active)
-       srvr_scache_active =
-           tls_scache_sequence(srvr_scache_info, DICT_SEQ_FUN_NEXT,
-                               TLS_SCACHE_SEQUENCE_NOTHING);
+    for (ent = cache_table; ent->cache_label; ++ent) {
+       if (ent->cache_info && ent->cache_active)
+           active |= ent->cache_active =
+               tls_scache_sequence(ent->cache_info, DICT_SEQ_FUN_NEXT,
+                                   TLS_SCACHE_SEQUENCE_NOTHING);
+    }
 
-    if (clnt_scache_active || srvr_scache_active)
-       return (DONT_WAIT);
-    return (WAIT_FOR_EVENT);
+    return (active ? DONT_WAIT : WAIT_FOR_EVENT);
 }
 
 /* tlsmgr_request_receive - receive request */
@@ -517,15 +525,15 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
                                   char **argv)
 {
     static VSTRING *request = 0;
+    static VSTRING *cache_type = 0;
     static VSTRING *cache_id = 0;
     static VSTRING *buffer = 0;
-    int     cache_type;
     int     len;
     static char wakeup[] = {           /* master wakeup request */
        TRIGGER_REQ_WAKEUP,
        0,
     };
-    TLS_SCACHE *cache;
+    TLSMGR_SCACHE *ent;
     int     status = TLS_MGR_STAT_FAIL;
 
     /*
@@ -539,6 +547,7 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
      */
     if (request == 0) {
        request = vstring_alloc(10);
+       cache_type = vstring_alloc(10);
        cache_id = vstring_alloc(10);
        buffer = vstring_alloc(10);
     }
@@ -556,15 +565,25 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
         */
        if (STREQ(STR(request), TLS_MGR_REQ_LOOKUP)) {
            if (attr_scan(client_stream, ATTR_FLAG_STRICT,
-                       ATTR_TYPE_INT, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
                          ATTR_TYPE_END) == 2) {
-               if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
-                   msg_warn("bogus cache type \"%d\" in \"%s\" request",
-                            cache_type, TLS_MGR_REQ_LOOKUP);
+               for (ent = cache_table; ent->cache_label; ++ent)
+                   if (strcmp(ent->cache_label, STR(cache_type)) == 0)
+                       break;
+               if (ent->cache_label == 0) {
+                   msg_warn("bogus cache type \"%s\" in \"%s\" request",
+                            STR(cache_type), TLS_MGR_REQ_LOOKUP);
+                   VSTRING_RESET(buffer);
+               } else if (ent->cache_info == 0) {
+
+                   /*
+                    * Cache type valid, but not enabled
+                    */
                    VSTRING_RESET(buffer);
                } else {
-                   status = tls_scache_lookup(cache, STR(cache_id), buffer) ?
+                   status = tls_scache_lookup(ent->cache_info,
+                                              STR(cache_id), buffer) ?
                        TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
                }
            }
@@ -580,16 +599,19 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
         */
        else if (STREQ(STR(request), TLS_MGR_REQ_UPDATE)) {
            if (attr_scan(client_stream, ATTR_FLAG_STRICT,
-                       ATTR_TYPE_INT, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
                          ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer,
                          ATTR_TYPE_END) == 3) {
-               if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
-                   msg_warn("bogus cache type \"%d\" in \"%s\" request",
-                            cache_type, TLS_MGR_REQ_UPDATE);
-               } else {
+               for (ent = cache_table; ent->cache_label; ++ent)
+                   if (strcmp(ent->cache_label, STR(cache_type)) == 0)
+                       break;
+               if (ent->cache_label == 0) {
+                   msg_warn("bogus cache type \"%s\" in \"%s\" request",
+                            STR(cache_type), TLS_MGR_REQ_UPDATE);
+               } else if (ent->cache_info != 0) {
                    status =
-                       tls_scache_update(cache, STR(cache_id),
+                       tls_scache_update(ent->cache_info, STR(cache_id),
                                          STR(buffer), LEN(buffer)) ?
                        TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
                }
@@ -604,14 +626,18 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
         */
        else if (STREQ(STR(request), TLS_MGR_REQ_DELETE)) {
            if (attr_scan(client_stream, ATTR_FLAG_STRICT,
-                       ATTR_TYPE_INT, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
                          ATTR_TYPE_END) == 2) {
-               if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
-                   msg_warn("bogus cache type \"%d\" in \"%s\" request",
-                            cache_type, TLS_MGR_REQ_DELETE);
-               } else {
-                   status = tls_scache_delete(cache, STR(cache_id)) ?
+               for (ent = cache_table; ent->cache_label; ++ent)
+                   if (strcmp(ent->cache_label, STR(cache_type)) == 0)
+                       break;
+               if (ent->cache_label == 0) {
+                   msg_warn("bogus cache type \"%s\" in \"%s\" request",
+                            STR(cache_type), TLS_MGR_REQ_DELETE);
+               } else if (ent->cache_info != 0) {
+                   status = tls_scache_delete(ent->cache_info,
+                                              STR(cache_id)) ?
                        TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
                }
            }
@@ -650,19 +676,25 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
         * Caching policy request.
         */
        else if (STREQ(STR(request), TLS_MGR_REQ_POLICY)) {
-           int     cache_types = 0;
+           int     cachable = 0;
 
            if (attr_scan(client_stream, ATTR_FLAG_STRICT,
-                         ATTR_TYPE_END) == 0) {
-               if (clnt_scache_info)
-                   cache_types |= TLS_MGR_SCACHE_CLIENT;
-               if (srvr_scache_info)
-                   cache_types |= TLS_MGR_SCACHE_SERVER;
-               status = TLS_MGR_STAT_OK;
+                         ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
+                         ATTR_TYPE_END) == 1) {
+               for (ent = cache_table; ent->cache_label; ++ent)
+                   if (strcmp(ent->cache_label, STR(cache_type)) == 0)
+                       break;
+               if (ent->cache_label == 0) {
+                   msg_warn("bogus cache type \"%s\" in \"%s\" request",
+                            STR(cache_type), TLS_MGR_REQ_POLICY);
+               } else {
+                   cachable = (ent->cache_info != 0) ? 1 : 0;
+                   status = TLS_MGR_STAT_OK;
+               }
            }
            attr_print(client_stream, ATTR_FLAG_NONE,
                       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
-                      ATTR_TYPE_INT, TLS_MGR_ATTR_POLICY, cache_types,
+                      ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable,
                       ATTR_TYPE_END);
        }
 
@@ -702,6 +734,7 @@ static void tlsmgr_pre_init(char *unused_name, char **unused_argv)
 {
     char   *path;
     struct timeval tv;
+    TLSMGR_SCACHE *ent;
 
     /*
      * If nothing else works then at least this will get us a few bits of
@@ -773,22 +806,19 @@ static void tlsmgr_pre_init(char *unused_name, char **unused_argv)
      * privileged. Start the cache maintenance pseudo threads after dropping
      * privileges.
      */
-    if (*var_smtp_tls_scache_db)
-       clnt_scache_info =
-           tls_scache_open(var_smtp_tls_scache_db, "client",
-                           var_smtp_tls_loglevel,
-                           var_smtp_tls_scache_timeout);
-    if (*var_smtpd_tls_scache_db)
-       srvr_scache_info =
-           tls_scache_open(var_smtpd_tls_scache_db, "server",
-                           var_smtpd_tls_loglevel,
-                           var_smtpd_tls_scache_timeout);
+    for (ent = cache_table; ent->cache_label; ++ent)
+       if (**ent->cache_db)
+           ent->cache_info =
+               tls_scache_open(*ent->cache_db, ent->cache_label,
+                           *ent->cache_loglevel >= 2, *ent->cache_timeout);
 }
 
 /* tlsmgr_post_init - post-jail initialization */
 
 static void tlsmgr_post_init(char *unused_name, char **unused_argv)
 {
+    TLSMGR_SCACHE *ent;
+
 #define NULL_EVENT     (0)
 #define NULL_CONTEXT   ((char *) 0)
 
@@ -825,10 +855,9 @@ static void tlsmgr_post_init(char *unused_name, char **unused_argv)
      * length, but early cleanup makes verbose logging more informative (we
      * get positive confirmation that the cleanup threads are running).
      */
-    if (*var_smtp_tls_scache_db)
-       tlsmgr_clnt_cache_run_event(NULL_EVENT, NULL_CONTEXT);
-    if (*var_smtpd_tls_scache_db)
-       tlsmgr_srvr_cache_run_event(NULL_EVENT, NULL_CONTEXT);
+    for (ent = cache_table; ent->cache_label; ++ent)
+       if (ent->cache_info)
+           tlsmgr_cache_run_event(NULL_EVENT, (char *) ent);
 }
 
 /* tlsmgr_before_exit - save PRNG state before exit */
@@ -849,15 +878,25 @@ int     main(int argc, char **argv)
 {
     static CONFIG_STR_TABLE str_table[] = {
        VAR_TLS_RAND_SOURCE, DEF_TLS_RAND_SOURCE, &var_tls_rand_source, 0, 0,
+       VAR_TLS_RAND_EXCH_NAME, DEF_TLS_RAND_EXCH_NAME, &var_tls_rand_exch_name, 0, 0,
+       VAR_SMTPD_TLS_SCACHE_DB, DEF_SMTPD_TLS_SCACHE_DB, &var_smtpd_tls_scache_db, 0, 0,
+       VAR_SMTP_TLS_SCACHE_DB, DEF_SMTP_TLS_SCACHE_DB, &var_smtp_tls_scache_db, 0, 0,
+       VAR_LMTP_TLS_SCACHE_DB, DEF_LMTP_TLS_SCACHE_DB, &var_lmtp_tls_scache_db, 0, 0,
        0,
     };
     static CONFIG_TIME_TABLE time_table[] = {
        VAR_TLS_RESEED_PERIOD, DEF_TLS_RESEED_PERIOD, &var_tls_reseed_period, 1, 0,
        VAR_TLS_PRNG_UPD_PERIOD, DEF_TLS_PRNG_UPD_PERIOD, &var_tls_prng_exch_period, 1, 0,
+       VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0,
+       VAR_SMTP_TLS_SCACHTIME, DEF_SMTP_TLS_SCACHTIME, &var_smtp_tls_scache_timeout, 0, 0,
+       VAR_LMTP_TLS_SCACHTIME, DEF_LMTP_TLS_SCACHTIME, &var_lmtp_tls_scache_timeout, 0, 0,
        0,
     };
     static CONFIG_INT_TABLE int_table[] = {
        VAR_TLS_RAND_BYTES, DEF_TLS_RAND_BYTES, &var_tls_rand_bytes, 1, 0,
+       VAR_SMTPD_TLS_LOGLEVEL, DEF_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, 0, 0,
+       VAR_SMTP_TLS_LOGLEVEL, DEF_SMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0,
+       VAR_LMTP_TLS_LOGLEVEL, DEF_LMTP_TLS_LOGLEVEL, &var_lmtp_tls_loglevel, 0, 0,
        0,
     };
 
index c03f019be7e17299a3f839df1fac34bb1be72490..c3dd39193523e9de367f7a048fb04735d9d695ce 100644 (file)
@@ -138,7 +138,7 @@ static void resolve_addr(RES_CONTEXT *rp, char *sender, char *addr,
                                 VSTRING *channel, VSTRING *nexthop,
                                 VSTRING *nextrcpt, int *flags)
 {
-    char   *myname = "resolve_addr";
+    const char *myname = "resolve_addr";
     VSTRING *addr_buf = vstring_alloc(100);
     TOK822 *tree = 0;
     TOK822 *saved_domain = 0;
index ea601b704b8f6e67f167ce8b37214325763add4a..8210993e3288c34be3b8b9c84924c2920e1ed270 100644 (file)
@@ -203,7 +203,7 @@ int     attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
 
 void    attr_clnt_control(ATTR_CLNT *client, int name,...)
 {
-    char   *myname = "attr_clnt_control";
+    const char *myname = "attr_clnt_control";
     va_list ap;
 
     for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
index 4e6d18f5e45502f9973ad285310a4366ace86885..cd323921c00bfe0e3e492633ea13ba9f39712f36 100644 (file)
@@ -104,7 +104,7 @@ CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create,
                              CTABLE_DELETE_FN delete, void *context)
 {
     CTABLE *cache = (CTABLE *) mymalloc(sizeof(CTABLE));
-    char   *myname = "ctable_create";
+    const char *myname = "ctable_create";
 
     if (limit < 1)
        msg_panic("%s: bad cache limit: %d", myname, limit);
@@ -123,7 +123,7 @@ CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create,
 
 const void *ctable_locate(CTABLE *cache, const char *key)
 {
-    char   *myname = "ctable_locate";
+    const char *myname = "ctable_locate";
     CTABLE_ENTRY *entry;
 
     /*
index bb55c5a2fb9eebb2a05c338686654efade80ff3f..18a3716be2463dc43c70ff5974542a9ffbfcfc85 100644 (file)
@@ -225,7 +225,7 @@ typedef struct {
 
 void    dict_register(const char *dict_name, DICT *dict_info)
 {
-    char   *myname = "dict_register";
+    const char *myname = "dict_register";
     DICT_NODE *node;
 
     if (dict_table == 0)
@@ -267,7 +267,7 @@ static void dict_node_free(char *ptr)
 
 void    dict_unregister(const char *dict_name)
 {
-    char   *myname = "dict_unregister";
+    const char *myname = "dict_unregister";
     DICT_NODE *node;
 
     if ((node = dict_node(dict_name)) == 0)
@@ -282,7 +282,7 @@ void    dict_unregister(const char *dict_name)
 
 void    dict_update(const char *dict_name, const char *member, const char *value)
 {
-    char   *myname = "dict_update";
+    const char *myname = "dict_update";
     DICT_NODE *node;
     DICT   *dict;
 
@@ -302,7 +302,7 @@ void    dict_update(const char *dict_name, const char *member, const char *value
 
 const char *dict_lookup(const char *dict_name, const char *member)
 {
-    char   *myname = "dict_lookup";
+    const char *myname = "dict_lookup";
     DICT_NODE *node;
     DICT   *dict;
     const char *ret = 0;
@@ -325,7 +325,7 @@ const char *dict_lookup(const char *dict_name, const char *member)
 
 int     dict_delete(const char *dict_name, const char *member)
 {
-    char   *myname = "dict_delete";
+    const char *myname = "dict_delete";
     DICT_NODE *node;
     DICT   *dict;
     int     result;
@@ -350,7 +350,7 @@ int     dict_delete(const char *dict_name, const char *member)
 int     dict_sequence(const char *dict_name, const int func,
                              const char **member, const char **value)
 {
-    char   *myname = "dict_sequence";
+    const char *myname = "dict_sequence";
     DICT_NODE *node;
     DICT   *dict;
 
@@ -485,7 +485,7 @@ void    dict_walk(DICT_WALK_ACTION action, char *ptr)
 
 const char *dict_changed_name(void)
 {
-    char   *myname = "dict_changed_name";
+    const char *myname = "dict_changed_name";
     struct stat st;
     HTABLE_INFO **ht_info_list;
     HTABLE_INFO **ht;
index 4c338125aef6d2e2ff36396b7eb23b022715406c..a00a328b183ee5e21ef782d8859103c087621aa3 100644 (file)
@@ -152,9 +152,9 @@ static int sanitize(int status)
      */
     switch (status) {
 
-    case DB_NOTFOUND:                          /* get, del */
-    case DB_KEYEXIST:                          /* put */
-       return (1);                             /* non-fatal */
+       case DB_NOTFOUND:               /* get, del */
+       case DB_KEYEXIST:               /* put */
+       return (1);                     /* non-fatal */
 
     case 0:
        return (0);                             /* success */
@@ -269,7 +269,6 @@ static void dict_db_update(DICT *dict, const char *name, const char *value)
        vstring_strcpy(dict->fold_buf, name);
        name = lowercase(vstring_str(dict->fold_buf));
     }
-
     memset(&db_key, 0, sizeof(db_key));
     memset(&db_value, 0, sizeof(db_value));
     db_key.data = (void *) name;
@@ -354,7 +353,6 @@ static int dict_db_delete(DICT *dict, const char *name)
        vstring_strcpy(dict->fold_buf, name);
        name = lowercase(vstring_str(dict->fold_buf));
     }
-
     memset(&db_key, 0, sizeof(db_key));
 
     /*
@@ -408,7 +406,7 @@ static int dict_db_delete(DICT *dict, const char *name)
 static int dict_db_sequence(DICT *dict, int function,
                                    const char **key, const char **value)
 {
-    char   *myname = "dict_db_sequence";
+    const char *myname = "dict_db_sequence";
     DICT_DB *dict_db = (DICT_DB *) dict;
     DB     *db = dict_db->db;
     DBT     db_key;
index 29489946bf7dc10fb0cbb91834eb6af9664ca2a3..d91497e9c151648352d14d21e11ad522abf9aa86 100644 (file)
@@ -300,7 +300,7 @@ static int dict_dbm_delete(DICT *dict, const char *name)
 static int dict_dbm_sequence(DICT *dict, int function,
                                     const char **key, const char **value)
 {
-    char   *myname = "dict_dbm_sequence";
+    const char *myname = "dict_dbm_sequence";
     DICT_DBM *dict_dbm = (DICT_DBM *) dict;
     datum   dbm_key;
     datum   dbm_value;
index da61f07afd77cc01d6f110feb7e442a87843de11..9a59fc7f6882f436f38e06e9269fdb943d787293 100644 (file)
@@ -76,7 +76,7 @@ static char *dict_nis_domain;
 
 static void dict_nis_init(void)
 {
-    char   *myname = "dict_nis_init";
+    const char *myname = "dict_nis_init";
 
     if (yp_get_default_domain(&dict_nis_domain) != 0
        || dict_nis_domain == 0 || *dict_nis_domain == 0
index e52c8725590e7f61eb57a05a56dfe086a0462d21..d56507918afc0c0964c5f9458730455f578548f2 100644 (file)
@@ -257,7 +257,7 @@ static HTABLE *dict_open_hash;
 
 static void dict_open_init(void)
 {
-    char   *myname = "dict_open_init";
+    const char *myname = "dict_open_init";
     DICT_OPEN_INFO *dp;
 
     if (dict_open_hash != 0)
@@ -291,7 +291,7 @@ DICT   *dict_open(const char *dict_spec, int open_flags, int dict_flags)
 DICT   *dict_open3(const char *dict_type, const char *dict_name,
                           int open_flags, int dict_flags)
 {
-    char   *myname = "dict_open";
+    const char *myname = "dict_open";
     DICT_OPEN_INFO *dp;
     DICT   *dict;
 
@@ -314,7 +314,7 @@ DICT   *dict_open3(const char *dict_type, const char *dict_name,
 void    dict_open_register(const char *type,
                                   DICT *(*open) (const char *, int, int))
 {
-    char   *myname = "dict_open_register";
+    const char *myname = "dict_open_register";
     DICT_OPEN_INFO *dp;
 
     if (dict_open_hash == 0)
index 8c6085d648e60510c26549ac17c9f8f2441770c4..31481cdc4cd75fbf2c529766d7b6908ffe2122d1 100644 (file)
@@ -294,7 +294,7 @@ static int dict_sdbm_delete(DICT *dict, const char *name)
 static int dict_sdbm_sequence(DICT *dict, const int function,
                                      const char **key, const char **value)
 {
-    char   *myname = "dict_sdbm_sequence";
+    const char *myname = "dict_sdbm_sequence";
     DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
     datum   dbm_key;
     datum   dbm_value;
index 1e8d2bc103651c0691a30bd11fc43542e7a74da4..1365e84f7aed530b6844010e6ec98e844ba766da 100644 (file)
@@ -154,7 +154,7 @@ static void dict_tcp_disconnect(DICT_TCP *dict_tcp)
 static const char *dict_tcp_lookup(DICT *dict, const char *key)
 {
     DICT_TCP *dict_tcp = (DICT_TCP *) dict;
-    char   *myname = "dict_tcp_lookup";
+    const char *myname = "dict_tcp_lookup";
     int     tries;
     char   *start;
     int     last_ch;
index 929f937c7303d822189a07d2a7134e165ab8601e..007017782da1ebe1af231040278778fd1b5e43d5 100644 (file)
@@ -61,7 +61,7 @@
 
 char   *dir_forest(VSTRING *buf, const char *path, int depth)
 {
-    char   *myname = "dir_forest";
+    const char *myname = "dir_forest";
     static VSTRING *private_buf = 0;
     int     n;
     const char *cp;
index fa8e2daeef3bf11ce9a612cba041ad6717cd2ca6..c7b741dfa2703a4ec3493c633ca95d87980b1a12 100644 (file)
@@ -273,7 +273,7 @@ void    event_drain(int time_limit)
 
 void    event_enable_read(int fd, EVENT_NOTIFY_RDWR callback, char *context)
 {
-    char   *myname = "event_enable_read";
+    const char *myname = "event_enable_read";
     EVENT_FDTABLE *fdp;
 
     if (EVENT_INIT_NEEDED())
@@ -312,7 +312,7 @@ void    event_enable_read(int fd, EVENT_NOTIFY_RDWR callback, char *context)
 
 void    event_enable_write(int fd, EVENT_NOTIFY_RDWR callback, char *context)
 {
-    char   *myname = "event_enable_write";
+    const char *myname = "event_enable_write";
     EVENT_FDTABLE *fdp;
 
     if (EVENT_INIT_NEEDED())
@@ -351,7 +351,7 @@ void    event_enable_write(int fd, EVENT_NOTIFY_RDWR callback, char *context)
 
 void    event_disable_readwrite(int fd)
 {
-    char   *myname = "event_disable_readwrite";
+    const char *myname = "event_disable_readwrite";
     EVENT_FDTABLE *fdp;
 
     if (EVENT_INIT_NEEDED())
@@ -382,7 +382,7 @@ void    event_disable_readwrite(int fd)
 
 time_t  event_request_timer(EVENT_NOTIFY_TIME callback, char *context, int delay)
 {
-    char   *myname = "event_request_timer";
+    const char *myname = "event_request_timer";
     RING   *ring;
     EVENT_TIMER *timer;
 
@@ -446,7 +446,7 @@ time_t  event_request_timer(EVENT_NOTIFY_TIME callback, char *context, int delay
 
 int     event_cancel_timer(EVENT_NOTIFY_TIME callback, char *context)
 {
-    char   *myname = "event_cancel_timer";
+    const char *myname = "event_cancel_timer";
     RING   *ring;
     EVENT_TIMER *timer;
     int     time_left = -1;
@@ -479,7 +479,7 @@ int     event_cancel_timer(EVENT_NOTIFY_TIME callback, char *context)
 
 void    event_loop(int delay)
 {
-    char   *myname = "event_loop";
+    const char *myname = "event_loop";
     static int nested;
     fd_set  rmask;
     fd_set  wmask;
index c0a1ba6e679c1824161fd24dae21166d0075a879..62915c14c6cd51de1c19263b472c257c7201bfa5 100644 (file)
@@ -52,7 +52,7 @@ int     fifo_listen(const char *path, int permissions, int block_mode)
 {
     char    buf[BUF_LEN];
     static int open_mode = 0;
-    char   *myname = "fifo_listen";
+    const char *myname = "fifo_listen";
     struct stat st;
     int     fd;
     int     count;
index 8b56cf02cd460799f2ce5b8a820813b9b0718f45..7cb5acc1162d3a957d83c14a2ee25702e1176d05 100644 (file)
@@ -58,7 +58,7 @@
 int     fifo_trigger(const char *service, const char *buf, ssize_t len, int timeout)
 {
     static VSTRING *why;
-    char   *myname = "fifo_trigger";
+    const char *myname = "fifo_trigger";
     VSTREAM *fp;
     int     fd;
 
index e96daf5d58c8252d32147b437047195a109c9579..50a4aa7bcb4e44048c300f640b2d6855b019cc30 100644 (file)
@@ -67,7 +67,7 @@
 
 void    fsspace(const char *path, struct fsspace * sp)
 {
-    char   *myname = "fsspace";
+    const char *myname = "fsspace";
 
 #ifdef USE_STATFS
 #ifdef USE_STRUCT_FS_DATA                      /* Ultrix */
index f178e01341b4a3c5a5516627770e4a62baca2ad0..34e63ea86ebf0327fc0c8d5d3b082d097816a905 100644 (file)
@@ -80,7 +80,7 @@ void    inet_addr_list_init(INET_ADDR_LIST *list)
 void    inet_addr_list_append(INET_ADDR_LIST *list,
                                      struct sockaddr * addr)
 {
-    char   *myname = "inet_addr_list_append";
+    const char *myname = "inet_addr_list_append";
     MAI_HOSTADDR_STR hostaddr;
     int     new_size;
 
index 249d150a92a8aa532ef7c94b1e6c73743602f449..6d6e93eaacc1999400db2253bfce7f203d49119c 100644 (file)
 
 static int ial_socket(int af)
 {
-    char   *myname = "inet_addr_local[socket]";
+    const char *myname = "inet_addr_local[socket]";
     int     sock;
 
     /*
@@ -159,7 +159,7 @@ static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
                                  INET_ADDR_LIST *mask_list,
                                  int af)
 {
-    char   *myname = "inet_addr_local[getifaddrs]";
+    const char *myname = "inet_addr_local[getifaddrs]";
     struct ifaddrs *ifap, *ifa;
     struct sockaddr *sa, *sam;
 
@@ -241,7 +241,7 @@ static int ial_siocglif(INET_ADDR_LIST *addr_list,
                                INET_ADDR_LIST *mask_list,
                                int af)
 {
-    char   *myname = "inet_addr_local[siocglif]";
+    const char *myname = "inet_addr_local[siocglif]";
     struct lifconf lifc;
     struct lifreq *lifr;
     struct lifreq *lifr_mask;
@@ -319,16 +319,16 @@ static int ial_siocglif(INET_ADDR_LIST *addr_list,
  * that recent versions of these operating systems have getifaddrs.
  */
 #if defined(_SIZEOF_ADDR_IFREQ)
-# define NEXT_INTERFACE(ifr)   ((struct ifreq *) \
+#define NEXT_INTERFACE(ifr)    ((struct ifreq *) \
        ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
-# define IFREQ_SIZE(ifr)       _SIZEOF_ADDR_IFREQ(*ifr)
+#define IFREQ_SIZE(ifr)        _SIZEOF_ADDR_IFREQ(*ifr)
 #elif defined(HAS_SA_LEN)
-# define NEXT_INTERFACE(ifr)   ((struct ifreq *) \
+#define NEXT_INTERFACE(ifr)    ((struct ifreq *) \
        ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
-# define IFREQ_SIZE(ifr)       (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
+#define IFREQ_SIZE(ifr)        (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
 #else
-# define NEXT_INTERFACE(ifr)   (ifr + 1)
-# define IFREQ_SIZE(ifr)       sizeof(ifr[0])
+#define NEXT_INTERFACE(ifr)    (ifr + 1)
+#define IFREQ_SIZE(ifr)        sizeof(ifr[0])
 #endif
 
 /* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
@@ -337,7 +337,7 @@ static int ial_siocgif(INET_ADDR_LIST *addr_list,
                               INET_ADDR_LIST *mask_list,
                               int af)
 {
-    char   *myname = "inet_addr_local[siocgif]";
+    const char *myname = "inet_addr_local[siocgif]";
     struct in_addr addr;
     struct ifconf ifc;
     struct ifreq *ifr;
@@ -449,7 +449,7 @@ static int ial_siocgif(INET_ADDR_LIST *addr_list,
 static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
                                       INET_ADDR_LIST *mask_list)
 {
-    char   *myname = "inet_addr_local[procnet_ifinet6]";
+    const char *myname = "inet_addr_local[procnet_ifinet6]";
     FILE   *fp;
     char    buf[BUFSIZ];
     unsigned plen;
@@ -508,7 +508,7 @@ static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
 int     inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
                                unsigned *addr_family_list)
 {
-    char   *myname = "inet_addr_local";
+    const char *myname = "inet_addr_local";
     int     initial_count = addr_list->used;
     unsigned family;
     int     count;
index 4cd9bcbd48c846498c5d7d945e9a4a52e9c28f9e..06e2c43b7b3a5d898b21caa73705f82da1b5e4a6 100644 (file)
@@ -249,7 +249,7 @@ INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
   */
 int     main(int argc, char **argv)
 {
-    char   *myname = argv[0];
+    const char *myname = argv[0];
     INET_PROTO_INFO *pf;
 
     if (argc < 2)
index 2d27b5f93b10465c95985adfb8015baf55f33299..afb97b2fbd4dddc0dcaf9eafb69364bcb1864846 100644 (file)
@@ -72,7 +72,7 @@ struct inet_trigger {
 static void inet_trigger_event(int event, char *context)
 {
     struct inet_trigger *ip = (struct inet_trigger *) context;
-    static char *myname = "inet_trigger_event";
+    static const char *myname = "inet_trigger_event";
 
     /*
      * Disconnect.
@@ -92,7 +92,7 @@ static void inet_trigger_event(int event, char *context)
 
 int     inet_trigger(const char *service, const char *buf, ssize_t len, int timeout)
 {
-    char   *myname = "inet_trigger";
+    const char *myname = "inet_trigger";
     struct inet_trigger *ip;
     int     fd;
 
index 22398ad612a1c5c4e651341dfc76e6469d94da40..c6c91551d25cf6e0702b8af5383fedb67d26d06e 100644 (file)
@@ -84,7 +84,7 @@
 
 int     mac_parse(const char *value, MAC_PARSE_FN action, char *context)
 {
-    char   *myname = "mac_parse";
+    const char *myname = "mac_parse";
     VSTRING *buf = vstring_alloc(1);   /* result buffer */
     const char *vp;                    /* value pointer */
     const char *pp;                    /* open_paren pointer */
index f8c60eefa3f5beb280300cea4000d2c5837ba277..efeebd939229c0273ce7fdf5af27dcc59042acbd 100644 (file)
@@ -102,10 +102,10 @@ struct MATCH_LIST {
 
 static ARGV *match_list_parse(ARGV *list, char *string)
 {
-    char   *myname = "match_list_parse";
+    const char *myname = "match_list_parse";
     VSTRING *buf = 0;
     VSTREAM *fp;
-    char   *delim = " ,\t\r\n";
+    const char *delim = " ,\t\r\n";
     char   *bp = string;
     char   *pattern;
     char   *map_type_name;
@@ -187,7 +187,7 @@ MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...
 
 int     match_list_match(MATCH_LIST * list,...)
 {
-    char   *myname = "match_list_match";
+    const char *myname = "match_list_match";
     char  **cpp;
     char   *pat;
     int     match;
index e05450185c35c136e3ae131e27a469786d99584a..41c069394d4d4eb8f431ab11454656aa92823b41 100644 (file)
@@ -85,7 +85,7 @@
 
 int     match_string(int unused_flags, const char *string, const char *pattern)
 {
-    char   *myname = "match_string";
+    const char *myname = "match_string";
     int     match;
 
     if (msg_verbose)
@@ -120,7 +120,7 @@ int     match_string(int unused_flags, const char *string, const char *pattern)
 
 int     match_hostname(int flags, const char *name, const char *pattern)
 {
-    char   *myname = "match_hostname";
+    const char *myname = "match_hostname";
     const char *pd;
     const char *entry;
     const char *next;
@@ -187,7 +187,7 @@ int     match_hostname(int flags, const char *name, const char *pattern)
 
 int     match_hostaddr(int unused_flags, const char *addr, const char *pattern)
 {
-    char   *myname = "match_hostaddr";
+    const char *myname = "match_hostaddr";
     char   *saved_patt;
     CIDR_MATCH match_info;
     VSTRING *err;
@@ -219,7 +219,7 @@ int     match_hostaddr(int unused_flags, const char *addr, const char *pattern)
        if (strcasecmp(addr, pattern) == 0)
            return (1);
     } else {
-       size_t addr_len = strlen(addr);
+       size_t  addr_len = strlen(addr);
 
        if (strncasecmp(addr, pattern + 1, addr_len) == 0
            && strcmp(pattern + 1 + addr_len, "]") == 0)
index 7b6a6e7eb631275b75b91da45af68a8fe74b6f03..bbf2e8080b70c8a88407bdd10eafd1fabc5350cf 100644 (file)
@@ -67,7 +67,7 @@
 #define MSG_SYSLOG_RECLEN      2000
 
 struct facility_list {
-    char   *name;
+    const char *name;
     int     facility;
 };
 
@@ -170,8 +170,8 @@ void    msg_syslog_init(const char *name, int logopt, int facility)
     static int first_call = 1;
 
     /*
-     * XXX If this program is set-gid, then TZ must not be trusted.
-     * This scrubbing code is in the wrong place.
+     * XXX If this program is set-gid, then TZ must not be trusted. This
+     * scrubbing code is in the wrong place.
      */
     if (unsafe())
        putenv("TZ=UTC");
index 0d06d4e6d5b8e2858d7d6567ca35447ebc70c598..b6e24e60902ca8bd80147e23815b2a16dcdf407a 100644 (file)
@@ -56,7 +56,7 @@ static VSTREAM *msg_stream;
 
 static void msg_vstream_print(int level, const char *text)
 {
-    static char *level_text[] = {
+    static const char *level_text[] = {
        "info", "warning", "error", "fatal", "panic",
     };
 
index d049b9ec2dd6d7c5f4c77be49689b3c1d1dad86a..bee3735f4edc5552c37d3f3800d005f13c553048 100644 (file)
 /*     const char *names;
 /*     int     flags;
 /*
+/*     int     name_mask_delim_opt(context, table, names, delim, flags)
+/*     const char *context;
+/*     NAME_MASK *table;
+/*     const char *names;
+/*     const char *delim;
+/*     int     flags;
+/*
 /*     const char *str_name_mask_opt(buf, context, table, mask, flags)
 /*     VSTRING *buf;
 /*     const char *context;
@@ -39,7 +46,8 @@
 /*     upon each call.
 /*
 /*     name_mask_opt() and str_name_mask_opt() are extended versions
-/*     with additional fine control.
+/*     with additional fine control. name_mask_delim_opt() supports
+/*     non-default delimiter characters.
 /*
 /*     Arguments:
 /* .IP buf
@@ -57,6 +65,8 @@
 /*     A bit mask.
 /* .IP flags
 /*     Bit-wise OR of zero or more of the following:
+/* .IP delim
+/*     Delimiter characters to use instead of whitespace and commas.
 /* .RS
 /* .IP NAME_MASK_FATAL
 /*     Require that all names listed in \fIname\fR exist in \fItable\fR,
@@ -69,6 +79,9 @@
 /*     and that all bits listed in \fImask\fR exist in \fItable\fR.
 /*     Log a warning, and return 0 (name_mask()) or a null pointer
 /*     (str_name_mask()) if this condition is not met.
+/* .IP NAME_MASK_NUMBER
+/*     Require that all bits listed in \fImask\fR exist in \fItable\fR.
+/*     For unrecognized bits, print the numerical hexadecimal form.
 /* .IP NAME_MASK_ANY_CASE
 /*     Enable case-insensitive matching.
 /*     This feature is not enabled by default when calling name_mask();
 
 #define STR(x) vstring_str(x)
 
-/* name_mask_opt - compute mask corresponding to list of names */
+/* name_mask_delim_opt - compute mask corresponding to list of names */
 
-int     name_mask_opt(const char *context, NAME_MASK *table, const char *names,
-                             int flags)
+int     name_mask_delim_opt(const char *context, NAME_MASK *table,
+                           const char *names, const char *delim, int flags)
 {
-    char   *myname = "name_mask";
+    const char *myname = "name_mask";
     char   *saved_names = mystrdup(names);
     char   *bp = saved_names;
     int     result = 0;
@@ -136,7 +149,7 @@ int     name_mask_opt(const char *context, NAME_MASK *table, const char *names,
      * Break up the names string, and look up each component in the table. If
      * the name is found, merge its mask with the result.
      */
-    while ((name = mystrtok(&bp, ", \t\r\n")) != 0) {
+    while ((name = mystrtok(&bp, delim)) != 0) {
        for (np = table; /* void */ ; np++) {
            if (np->name == 0) {
                if (flags & NAME_MASK_FATAL)
@@ -167,7 +180,7 @@ const char *str_name_mask_opt(VSTRING *buf, const char *context,
                                      NAME_MASK *table,
                                      int mask, int flags)
 {
-    char   *myname = "name_mask";
+    const char *myname = "name_mask";
     NAME_MASK *np;
     int     len;
     static VSTRING *my_buf = 0;
@@ -183,13 +196,15 @@ const char *str_name_mask_opt(VSTRING *buf, const char *context,
 
     for (np = table; mask != 0; np++) {
        if (np->name == 0) {
-           if (flags & NAME_MASK_FATAL)
-               msg_fatal("%s: invalid %s bit in mask: 0x%x",
+           if (flags & NAME_MASK_FATAL) {
+               msg_fatal("%s: unknown %s bit in mask: 0x%x",
                          myname, context, mask);
-           if (flags & NAME_MASK_RETURN) {
-               msg_warn("%s: invalid %s bit in mask: 0x%x",
+           } else if (flags & NAME_MASK_RETURN) {
+               msg_warn("%s: unknown %s bit in mask: 0x%x",
                         myname, context, mask);
                return (0);
+           } else if (flags & NAME_MASK_NUMBER) {
+               vstring_sprintf_append(buf, "0x%x%c", mask, delim);
            }
            break;
        }
index 423bfae6a61326677910f8daa5e5b61ac20c3f25..69631d38e82603741af6b4fd2c991e9ed8c94f5f 100644 (file)
@@ -29,18 +29,23 @@ typedef struct {
 #define NAME_MASK_RETURN       (1<<2)
 #define NAME_MASK_COMMA                (1<<3)
 #define NAME_MASK_PIPE         (1<<4)
+#define NAME_MASK_NUMBER       (1<<5)
 
 #define NAME_MASK_MATCH_REQ    NAME_MASK_FATAL
 
 #define NAME_MASK_NONE         0
 #define NAME_MASK_DEFAULT      (NAME_MASK_FATAL)
+#define NAME_MASK_DEFAULT_DELIM        ", \t\r\n"
 
+#define name_mask_opt(tag, table, str, flags) \
+       name_mask_delim_opt((tag), (table), (str), \
+                           NAME_MASK_DEFAULT_DELIM, (flags))
 #define name_mask(tag, table, str) \
        name_mask_opt((tag), (table), (str), NAME_MASK_DEFAULT)
 #define str_name_mask(tag, table, mask) \
        str_name_mask_opt(((VSTRING *) 0), (tag), (table), (mask), NAME_MASK_DEFAULT)
 
-extern int name_mask_opt(const char *, NAME_MASK *, const char *, int);
+extern int name_mask_delim_opt(const char *, NAME_MASK *, const char *, const char *, int);
 extern const char *str_name_mask_opt(VSTRING *, const char *, NAME_MASK *, int, int);
 
 /* LICENSE
index 54c793321a907cea557b6317d6f787acb9499e9e..55eaabcf59e1d163c8bf9c4a8c785b6ca6bb9ace 100644 (file)
@@ -187,7 +187,7 @@ void    netstring_except(VSTREAM *stream, int exception)
 
 ssize_t netstring_get_length(VSTREAM *stream)
 {
-    char   *myname = "netstring_get_length";
+    const char *myname = "netstring_get_length";
     ssize_t len = 0;
     int     ch;
 
@@ -216,7 +216,7 @@ ssize_t netstring_get_length(VSTREAM *stream)
 
 VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, ssize_t len)
 {
-    char   *myname = "netstring_get_data";
+    const char *myname = "netstring_get_data";
 
     /*
      * Allocate buffer space.
@@ -267,7 +267,7 @@ VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, ssize_t limit)
 
 void    netstring_put(VSTREAM *stream, const char *data, ssize_t len)
 {
-    char   *myname = "netstring_put";
+    const char *myname = "netstring_put";
 
     if (msg_verbose > 1)
        msg_info("%s: write netstring len %ld data %.*s",
@@ -281,7 +281,7 @@ void    netstring_put(VSTREAM *stream, const char *data, ssize_t len)
 
 void    netstring_put_multi(VSTREAM *stream,...)
 {
-    char   *myname = "netstring_put_multi";
+    const char *myname = "netstring_put_multi";
     ssize_t total;
     char   *data;
     ssize_t data_len;
index d15e07d75924ddeab3a0abae6244ff89d2d3e474..69a8cc8ccaa6b45fde6132de6c9ccbd2ee8d7803 100644 (file)
@@ -48,7 +48,7 @@
 
 void    rand_sleep(unsigned delay, unsigned variation)
 {
-    char   *myname = "rand_sleep";
+    const char *myname = "rand_sleep";
     unsigned usec;
 
     /*
index 20016e57e67dfd32c717db3836e4169f199ad48d..a094377ee32296f216fef38d25ae3e0f08552ba4 100644 (file)
@@ -39,7 +39,7 @@
 
 int     sane_link(const char *from, const char *to)
 {
-    char   *myname = "sane_link";
+    const char *myname = "sane_link";
     int     saved_errno;
     struct stat from_st;
     struct stat to_st;
index 416f8976e8fbab24109abc83ae4adacd8d34c441..7a458c3f263ef6023c4cfa52f11fc238dc2f5803 100644 (file)
@@ -39,7 +39,7 @@
 
 int     sane_rename(const char *from, const char *to)
 {
-    char   *myname = "sane_rename";
+    const char *myname = "sane_rename";
     int     saved_errno;
     struct stat st;
 
index 3c8cc2590bc9074308b53d37d09b4b294ffe8922..de124df5536d0ac5d87f1699840dbd431ca4046a 100644 (file)
@@ -117,7 +117,7 @@ char   *scan_dir_path(SCAN_DIR *scan)
 
 void    scan_dir_push(SCAN_DIR *scan, const char *path)
 {
-    char   *myname = "scan_dir_push";
+    const char *myname = "scan_dir_push";
     SCAN_INFO *info;
 
     info = (SCAN_INFO *) mymalloc(sizeof(*info));
@@ -137,7 +137,7 @@ void    scan_dir_push(SCAN_DIR *scan, const char *path)
 
 SCAN_DIR *scan_dir_pop(SCAN_DIR *scan)
 {
-    char   *myname = "scan_dir_pop";
+    const char *myname = "scan_dir_pop";
     SCAN_INFO *info = scan->current;
     SCAN_INFO *parent;
 
@@ -170,7 +170,7 @@ SCAN_DIR *scan_dir_open(const char *path)
 
 char   *scan_dir_next(SCAN_DIR *scan)
 {
-    char   *myname = "scan_dir_next";
+    const char *myname = "scan_dir_next";
     SCAN_INFO *info = scan->current;
     struct dirent *dp;
 
index 5fb30d297ca7708405b35aff5b75f04a307072cf..a452582245441912650186925fa036c446dda0c6 100644 (file)
@@ -119,7 +119,7 @@ struct spawn_args {
 
 static void get_spawn_args(struct spawn_args * args, int init_key, va_list ap)
 {
-    char   *myname = "get_spawn_args";
+    const char *myname = "get_spawn_args";
     int     key;
 
     /*
@@ -200,7 +200,7 @@ static void get_spawn_args(struct spawn_args * args, int init_key, va_list ap)
 
 WAIT_STATUS_T spawn_command(int key,...)
 {
-    char   *myname = "spawn_comand";
+    const char *myname = "spawn_comand";
     va_list ap;
     pid_t   pid;
     WAIT_STATUS_T wait_status;
index be270c93d45e3f13c7554ab56546019961e5db04..1e45cc0c49da0ca025402f73608b04c8aea47b11 100644 (file)
@@ -62,7 +62,7 @@
 int     stream_connect(const char *path, int block_mode, int unused_timeout)
 {
 #ifdef STREAM_CONNECTIONS
-    char   *myname = "stream_connect";
+    const char *myname = "stream_connect";
     int     pair[2];
     int     fifo;
 
index 777f95cc02d50d388871c2a71c54d5bf89513028..a602599d547bb1817c9f024c8685fd498fdc94df 100644 (file)
@@ -54,7 +54,7 @@
 
 int     stream_send_fd(int fd, int sendfd)
 {
-    char   *myname = "stream_send_fd";
+    const char *myname = "stream_send_fd";
 
 #ifdef STREAM_CONNECTIONS
     if (ioctl(fd, I_SENDFD, sendfd) < 0)
index 0935a0d4e6d1e7c0244b27ad363678373cc1bc2e..3d686e9297590d3d03897ef9b18b85c5829f8409 100644 (file)
@@ -69,7 +69,7 @@ struct stream_trigger {
 static void stream_trigger_event(int event, char *context)
 {
     struct stream_trigger *sp = (struct stream_trigger *) context;
-    static char *myname = "stream_trigger_event";
+    static const char *myname = "stream_trigger_event";
 
     /*
      * Disconnect.
@@ -88,7 +88,7 @@ static void stream_trigger_event(int event, char *context)
 
 int     stream_trigger(const char *service, const char *buf, ssize_t len, int timeout)
 {
-    char   *myname = "stream_trigger";
+    const char *myname = "stream_trigger";
     struct stream_trigger *sp;
     int     fd;
 
index a7c137d3787ffd012c154553f4a52f8d732435a9..56fcdabea42cdc83d76676ac95516fd962375a73 100644 (file)
@@ -32,6 +32,8 @@
 #define SUPPORTED
 #include <sys/types.h>
 #include <sys/param.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define USE_PATHS_H
 #define HAS_FLOCK_LOCK
 #define HAS_FCNTL_LOCK
@@ -58,7 +60,8 @@
 # define STATFS_IN_SYS_MOUNT_H
 #endif
 #define HAS_POSIX_REGEXP
-#define HAS_ST_GEN     /* struct stat contains inode generation number */
+#define HAS_ST_GEN                     /* struct stat contains inode
+                                        * generation number */
 #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail"
 #define NATIVE_MAILQ_PATH "/usr/bin/mailq"
 #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases"
 #if defined(RHAPSODY5) || defined(MACOSX)
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define USE_PATHS_H
 #define HAS_FLOCK_LOCK
 #define HAS_FCNTL_LOCK
   */
 #ifdef ULTRIX4
 #define SUPPORTED
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 /* Ultrix by default has only 64 descriptors per process */
 #ifndef FD_SETSIZE
 #define FD_SETSIZE     96
@@ -234,6 +241,8 @@ extern int h_errno;
 #ifdef OSF1
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define USE_PATHS_H
 #define _PATH_DEFPATH "/usr/bin:/usr/ucb"
@@ -272,6 +281,8 @@ extern int opterr;                  /* XXX use <getopt.h> */
 #define SUPPORTED
 #include <sys/types.h>
 #include <memory.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define UNSAFE_CTYPE
 #define fpos_t long
 #define MISSING_SETENV
@@ -319,6 +330,8 @@ extern int opterr;
 #define SUPPORTED
 #define _SVID_GETTOD                   /* Solaris 2.5, XSH4.2 versus SVID */
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define _PATH_MAILDIR  "/var/mail"
 #define _PATH_BSHELL   "/bin/sh"
@@ -380,6 +393,8 @@ extern int opterr;
 #ifdef UW7                             /* UnixWare 7 */
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define _PATH_MAILDIR  "/var/mail"
 #define _PATH_BSHELL   "/bin/sh"
 #define _PATH_DEFPATH  "/usr/bin:/usr/ucb"
@@ -408,6 +423,8 @@ extern int opterr;
 #ifdef UW21                            /* UnixWare 2.1.x */
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define _PATH_MAILDIR   "/var/mail"
 #define _PATH_BSHELL    "/bin/sh"
 #define _PATH_DEFPATH   "/usr/bin:/usr/ucb"
@@ -439,6 +456,8 @@ extern int opterr;
 #ifdef AIX5
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define USE_PATHS_H
 #ifndef _PATH_BSHELL
@@ -495,6 +514,8 @@ extern int opterr;
 #ifdef AIX4
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define _PATH_BSHELL   "/bin/sh"
 #define _PATH_MAILDIR   "/var/spool/mail"      /* paths.h lies */
@@ -523,6 +544,7 @@ extern time_t time(time_t *);
 extern int seteuid(uid_t);
 extern int setegid(gid_t);
 extern int initgroups(const char *, int);
+
 #endif
 #define NATIVE_SENDMAIL_PATH "/usr/lib/sendmail"
 #define NATIVE_MAILQ_PATH "/usr/sbin/mailq"
@@ -536,6 +558,8 @@ extern int initgroups(const char *, int);
 #ifdef AIX3
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define _PATH_BSHELL   "/bin/sh"
 #define _PATH_MAILDIR   "/var/spool/mail"      /* paths.h lies */
@@ -563,6 +587,7 @@ extern time_t time(time_t *);
 extern int seteuid(uid_t);
 extern int setegid(gid_t);
 extern int initgroups(const char *, int);
+
 #define NATIVE_SENDMAIL_PATH "/usr/lib/sendmail"
 
 #define CANT_USE_SEND_RECV_MSG
@@ -574,6 +599,8 @@ extern int initgroups(const char *, int);
 #if defined(IRIX5) || defined(IRIX6)
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define _PATH_MAILDIR  "/var/mail"
 #define _PATH_BSHELL   "/bin/sh"
@@ -616,6 +643,8 @@ extern int initgroups(const char *, int);
 #ifdef LINUX2
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #include <features.h>
 #define USE_PATHS_H
 #define HAS_FLOCK_LOCK
@@ -663,6 +692,8 @@ extern int initgroups(const char *, int);
 #ifdef LINUX1
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define USE_PATHS_H
 #define HAS_FLOCK_LOCK
 #define HAS_FCNTL_LOCK
@@ -697,6 +728,8 @@ extern int initgroups(const char *, int);
 #define SUPPORTED
 #define USE_SIG_RETURN
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define HAS_DBM
 #define HAS_FCNTL_LOCK
 #define INTERNAL_LOCK  MYFLOCK_STYLE_FCNTL
@@ -732,6 +765,8 @@ extern int h_errno;                 /* <netdb.h> imports too much stuff */
 #define SUPPORTED
 #define USE_SIG_RETURN
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define HAS_DBM
 #define HAS_FCNTL_LOCK
 #define INTERNAL_LOCK  MYFLOCK_STYLE_FCNTL
@@ -767,6 +802,8 @@ extern int h_errno;                 /* <netdb.h> imports too much stuff */
 #define SUPPORTED
 #define USE_SIG_RETURN
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define HAS_DBM
 #define HAS_FCNTL_LOCK
 #define INTERNAL_LOCK  MYFLOCK_STYLE_FCNTL
@@ -805,6 +842,8 @@ extern int h_errno;
 #ifdef NEXTSTEP3
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define HAS_DBM
 #define HAS_FLOCK_LOCK
 #define INTERNAL_LOCK  MYFLOCK_STYLE_FLOCK
@@ -840,7 +879,7 @@ extern int h_errno;
 /* It's amazing what is all missing... */
 #define isascii(c)     ((unsigned)(c)<=0177)
 extern int opterr;
-typedef unsigned short  mode_t;
+typedef unsigned short mode_t;
 
 #define MISSING_PID_T
 #define MISSING_STRFTIME_E
@@ -856,6 +895,8 @@ typedef unsigned short  mode_t;
 #ifdef OPENSTEP4
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define HAS_DBM
 #define HAS_FLOCK_LOCK
 #define INTERNAL_LOCK  MYFLOCK_STYLE_FLOCK
@@ -891,7 +932,7 @@ typedef unsigned short  mode_t;
 /* It's amazing what is all missing... */
 #define isascii(c)     ((unsigned)(c)<=0177)
 extern int opterr;
-typedef unsigned short  mode_t;
+typedef unsigned short mode_t;
 
 #define MISSING_PID_T
 #define MISSING_STRFTIME_E
@@ -907,6 +948,8 @@ typedef unsigned short  mode_t;
 #ifdef ReliantUnix543
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define MISSING_SETENV
 #define _PATH_DEFPATH  "/usr/bin:/usr/ucb"
 #define _PATH_BSHELL   "/bin/sh"
@@ -935,6 +978,8 @@ extern int opterr;                  /* XXX use <getopt.h> */
 #ifdef DCOSX1                          /* Siemens Pyramid */
 #define SUPPORTED
 #include <sys/types.h>
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define _PATH_MAILDIR  "/var/mail"
 #define _PATH_BSHELL   "/bin/sh"
 #define _PATH_DEFPATH  "/usr/bin:/usr/ucb"
@@ -967,6 +1012,8 @@ extern int opterr;                 /* XXX use <getopt.h> */
 #include <sys/socket.h>
 extern int h_errno;
 
+#define UINT32_TYPE    unsigned int
+#define UINT16_TYPE    unsigned short
 #define _PATH_MAILDIR  "/usr/spool/mail"
 #define _PATH_BSHELL   "/bin/sh"
 #define _PATH_DEFPATH  "/bin:/usr/bin"
@@ -1231,11 +1278,11 @@ typedef int pid_t;
   */
 #ifndef ALIGN_TYPE
 # if defined(__hpux) && defined(__ia64)
-# define ALIGN_TYPE    __float80
+#  define ALIGN_TYPE   __float80
 # elif defined(__ia64__)
-# define ALIGN_TYPE    long double
+#  define ALIGN_TYPE   long double
 # else
-# define ALIGN_TYPE    double
+#  define ALIGN_TYPE   double
 # endif
 #endif
 
@@ -1326,6 +1373,16 @@ typedef int pid_t;
 #define MUST_READ_AFTER_SENDING_FD
 #endif
 
+ /*
+  * Hope for the best.
+  */
+#ifndef UINT32_TYPE
+#define        UINT32_TYPE uint32_t
+#define UINT16_TYPE uint16_t
+#endif
+#define UINT32_SIZE    4
+#define UINT16_SIZE    2
+
  /*
   * Safety. On some systems, ctype.h misbehaves with non-ASCII or negative
   * characters. More importantly, Postfix uses the ISXXX() macros to ensure
index 51f1cc6f945498b7fa4cf71e51b0c25182c8c732..fdae8fae401ecb052806d88ee643087db4e4fb97 100644 (file)
@@ -76,6 +76,9 @@ ssize_t timed_read(int fd, void *buf, size_t len,
            msg_warn("read() returns EAGAIN on a readable file descriptor!");
            msg_warn("pausing to avoid going into a tight select/read loop!");
            sleep(1);
+           continue;
+       } else if (ret < 0 && errno == EINTR) {
+           continue;
        } else {
            return (ret);
        }
index 73f395feccdc6ec566573009af0e25e7e854682e..45fbb69bf4ccfdf1f4809212cf59fe68793a9d1f 100644 (file)
@@ -80,7 +80,7 @@ static void timed_wait_alarm(int unused_sig)
 int     timed_waitpid(pid_t pid, WAIT_STATUS_T *statusp, int options,
                              int time_limit)
 {
-    char   *myname = "timed_waitpid";
+    const char *myname = "timed_waitpid";
     struct sigaction action;
     struct sigaction old_action;
     int     time_left;
index d6cf9da5977f2de7a8274eab23500b0334a4ddae..d46a8ab7030b18e5f17f8b43aa9477d53a4bf07f 100644 (file)
@@ -82,6 +82,9 @@ ssize_t timed_write(int fd, void *buf, size_t len,
            msg_warn("write() returns EAGAIN on a writable file descriptor!");
            msg_warn("pausing to avoid going into a tight select/write loop!");
            sleep(1);
+           continue;
+       } else if (ret < 0 && errno == EINTR) {
+           continue;
        } else {
            return (ret);
        }
index eda51472345aceace651105fdd77e0a92ba0bc89..7958489c4fe8db04afbacf26a2b0a7f0d2fb5788 100644 (file)
@@ -44,7 +44,7 @@
 
 int     unix_recv_fd(int fd)
 {
-    char   *myname = "unix_recv_fd";
+    const char *myname = "unix_recv_fd";
 
     /*
      * This code does not work with version <2.2 Linux kernels, and it does
@@ -72,7 +72,7 @@ int     unix_recv_fd(int fd)
     }       control_un;
     struct cmsghdr *cmptr;
 
-    memset((char *) &msg, 0, sizeof(msg));             /* Fix 200512 */
+    memset((char *) &msg, 0, sizeof(msg));     /* Fix 200512 */
     msg.msg_control = control_un.control;
     msg.msg_controllen = CMSG_LEN(sizeof(newfd));      /* Fix 200506 */
 #else
index 737780cba103370ef73308321b3a5f6fee73c2cd..a598b4496211d36a5d525a0c69c35a48d361c2e3 100644 (file)
@@ -53,7 +53,7 @@ int     unix_send_fd(int fd, int sendfd)
      * not compile with version <2 Linux libraries.
      */
 #ifdef CANT_USE_SEND_RECV_MSG
-    char   *myname = "unix_send_fd";
+    const char *myname = "unix_send_fd";
 
     msg_warn("%s: your system has no support for file descriptor passing",
             myname);
index 9c4cb8b657e2ac201a6921fd8c9816637eb81f77..ab1897a4513cc99e077f1cb4c2bafbda0b31f36c 100644 (file)
@@ -70,7 +70,7 @@ struct unix_trigger {
 static void unix_trigger_event(int event, char *context)
 {
     struct unix_trigger *up = (struct unix_trigger *) context;
-    static char *myname = "unix_trigger_event";
+    static const char *myname = "unix_trigger_event";
 
     /*
      * Disconnect.
@@ -89,7 +89,7 @@ static void unix_trigger_event(int event, char *context)
 
 int     unix_trigger(const char *service, const char *buf, ssize_t len, int timeout)
 {
-    char   *myname = "unix_trigger";
+    const char *myname = "unix_trigger";
     struct unix_trigger *up;
     int     fd;
 
index 619a9ee896eca1d0389d63d7a55dae222f22bba5..66ebdef5c62d96f860f0aaf381016483a00f0e19 100644 (file)
@@ -77,7 +77,7 @@
 
 int     valid_hostname(const char *name, int gripe)
 {
-    char   *myname = "valid_hostname";
+    const char *myname = "valid_hostname";
     const char *cp;
     int     label_length = 0;
     int     label_count = 0;
@@ -182,7 +182,7 @@ int     valid_hostaddr(const char *addr, int gripe)
 int     valid_ipv4_hostaddr(const char *addr, int gripe)
 {
     const char *cp;
-    char   *myname = "valid_ipv4_hostaddr";
+    const char *myname = "valid_ipv4_hostaddr";
     int     in_byte = 0;
     int     byte_count = 0;
     int     byte_val = 0;
index 86d6481f694fcdaedcbbfd65767e3e70cc3327f3..d71f3589e72a7064c399844ceab5bfcfe172c4d1 100644 (file)
@@ -478,7 +478,7 @@ static void vstream_buf_init(VBUF *bp, int flags)
 static void vstream_buf_alloc(VBUF *bp, ssize_t len)
 {
     ssize_t used = bp->ptr - bp->data;
-    char   *myname = "vstream_buf_alloc";
+    const char *myname = "vstream_buf_alloc";
 
     if (len < bp->len)
        msg_panic("%s: attempt to shrink buffer", myname);
@@ -512,7 +512,7 @@ static void vstream_buf_wipe(VBUF *bp)
 
 static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
 {
-    char   *myname = "vstream_fflush_some";
+    const char *myname = "vstream_fflush_some";
     VBUF   *bp = &stream->buf;
     ssize_t used;
     ssize_t left_over;
@@ -625,7 +625,7 @@ static int vstream_fflush_delayed(VSTREAM *stream)
 static int vstream_buf_get_ready(VBUF *bp)
 {
     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
-    char   *myname = "vstream_buf_get_ready";
+    const char *myname = "vstream_buf_get_ready";
     ssize_t n;
 
     /*
@@ -718,7 +718,7 @@ static int vstream_buf_get_ready(VBUF *bp)
 static int vstream_buf_put_ready(VBUF *bp)
 {
     VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
-    char   *myname = "vstream_buf_put_ready";
+    const char *myname = "vstream_buf_put_ready";
 
     /*
      * Sanity checks. Detect a change of I/O direction or position. If so,
@@ -767,7 +767,7 @@ static int vstream_buf_space(VBUF *bp, ssize_t want)
     ssize_t used;
     ssize_t incr;
     ssize_t shortage;
-    char   *myname = "vstream_buf_space";
+    const char *myname = "vstream_buf_space";
 
     /*
      * Sanity checks. Reserving space implies writing. It is illegal to write
@@ -818,7 +818,7 @@ static int vstream_buf_space(VBUF *bp, ssize_t want)
 
 /* vstream_fpurge - discard unread or unwritten content */
 
-int vstream_fpurge(VSTREAM *stream)
+int     vstream_fpurge(VSTREAM *stream)
 {
     const char *myname = "vstream_fpurge";
     VBUF   *bp = &stream->buf;
@@ -867,7 +867,7 @@ int vstream_fpurge(VSTREAM *stream)
 
 off_t   vstream_fseek(VSTREAM *stream, off_t offset, int whence)
 {
-    char   *myname = "vstream_fseek";
+    const char *myname = "vstream_fseek";
     VBUF   *bp = &stream->buf;
 
     /*
@@ -1128,7 +1128,7 @@ int     vstream_fputs(const char *str, VSTREAM *stream)
 
 void    vstream_control(VSTREAM *stream, int name,...)
 {
-    char   *myname = "vstream_control";
+    const char *myname = "vstream_control";
     va_list ap;
 
     for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) {
index 094621826e4faebf908fd2670e39531174512a38..aace6c82cdcc1fe7c30d453b3fd922d85fc40f01 100644 (file)
@@ -124,7 +124,7 @@ typedef struct VSTREAM_POPEN_ARGS {
 
 static void vstream_parse_args(VSTREAM_POPEN_ARGS *args, va_list ap)
 {
-    char   *myname = "vstream_parse_args";
+    const char *myname = "vstream_parse_args";
     int     key;
 
     /*
@@ -192,7 +192,7 @@ static void vstream_parse_args(VSTREAM_POPEN_ARGS *args, va_list ap)
 
 VSTREAM *vstream_popen(int flags,...)
 {
-    char   *myname = "vstream_popen";
+    const char *myname = "vstream_popen";
     VSTREAM_POPEN_ARGS args;
     va_list ap;
     VSTREAM *stream;
index 9b4f5ebfa5a9c0e5c071eaeed3c48deb5984596a..d69e210e179eacfea1fae0ec5d22106b77af0ef6 100644 (file)
@@ -122,7 +122,7 @@ static WATCHDOG *watchdog_curr;
 
 static void watchdog_event(int unused_sig)
 {
-    char   *myname = "watchdog_event";
+    const char *myname = "watchdog_event";
     WATCHDOG *wp;
 
     /*
@@ -148,7 +148,7 @@ static void watchdog_event(int unused_sig)
 
 WATCHDOG *watchdog_create(unsigned timeout, WATCHDOG_FN action, char *context)
 {
-    char   *myname = "watchdog_create";
+    const char *myname = "watchdog_create";
     struct sigaction sig_action;
     WATCHDOG *wp;
 
@@ -177,7 +177,7 @@ WATCHDOG *watchdog_create(unsigned timeout, WATCHDOG_FN action, char *context)
 
 void    watchdog_destroy(WATCHDOG *wp)
 {
-    char   *myname = "watchdog_destroy";
+    const char *myname = "watchdog_destroy";
 
     watchdog_stop(wp);
     watchdog_curr = wp->saved_watchdog;
@@ -194,7 +194,7 @@ void    watchdog_destroy(WATCHDOG *wp)
 
 void    watchdog_start(WATCHDOG *wp)
 {
-    char   *myname = "watchdog_start";
+    const char *myname = "watchdog_start";
 
     if (wp != watchdog_curr)
        msg_panic("%s: wrong watchdog instance", myname);
@@ -208,7 +208,7 @@ void    watchdog_start(WATCHDOG *wp)
 
 void    watchdog_stop(WATCHDOG *wp)
 {
-    char   *myname = "watchdog_stop";
+    const char *myname = "watchdog_stop";
 
     if (wp != watchdog_curr)
        msg_panic("%s: wrong watchdog instance", myname);
@@ -221,7 +221,7 @@ void    watchdog_stop(WATCHDOG *wp)
 
 void    watchdog_pat(void)
 {
-    char   *myname = "watchdog_pat";
+    const char *myname = "watchdog_pat";
 
     if (watchdog_curr)
        watchdog_curr->trip_run = 0;
index 9544365642b6ec76020cc1084959e328a7232589..7674095da02c533e0f7302d08924ebf3b59e7685 100644 (file)
@@ -65,6 +65,8 @@ ssize_t write_buf(int fd, const char *buf, ssize_t len, int timeout)
        if ((count = write(fd, buf, len)) < 0) {
            if (errno == EAGAIN && timeout > 0)
                continue;
+           if (errno == EINTR)
+               continue;
            return (-1);
        }
        if (count == 0)
index b2160e8d3b8ce7d33804c233c73642450b9acbba..09fc54bb07d7790030d98cc2d77b1ebb0d06c5ae 100644 (file)
@@ -74,7 +74,7 @@
 
 static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_mailbox_file";
+    const char *myname = "deliver_mailbox_file";
     DSN_BUF *why = state.msg_attr.why;
     MBOX   *mp;
     int     mail_copy_status;
@@ -159,7 +159,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
 
 int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
 {
-    char   *myname = "deliver_mailbox";
+    const char *myname = "deliver_mailbox";
     const char *mailbox_res;
     const char *uid_res;
     const char *gid_res;
index 8fcccae684fb9a7d028fa2c5d0ff420f697c461a..a44d09953826816f46272ca79a284eb35ebdcdc2 100644 (file)
@@ -71,7 +71,7 @@
 
 int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_maildir";
+    const char *myname = "deliver_maildir";
     char   *newdir;
     char   *tmpdir;
     char   *curdir;
index 437aa5a442520f48c4d13c8f623f4fe41bf746dd..560a77a495780aa7bec4c44c28a49baf4063eb0c 100644 (file)
@@ -56,7 +56,7 @@
 
 int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
 {
-    char   *myname = "deliver_recipient";
+    const char *myname = "deliver_recipient";
     int     rcpt_stat;
 
     /*
index d789a471e17980ae0827065e2871960ee3b721dd..ad8d64a3c82a93717021bc9d981789ed34b799d0 100644 (file)
@@ -49,7 +49,7 @@
 
 int     deliver_unknown(LOCAL_STATE state)
 {
-    char   *myname = "deliver_unknown";
+    const char *myname = "deliver_unknown";
 
     /*
      * Make verbose logging easier to understand.
index 0323312a6dd93bb119ae30d37b7ad79b2d258e74..51329cf26b743888acfd4335366704e77fb6b5fa 100644 (file)
@@ -344,7 +344,7 @@ int     virtual_mbox_lock_mask;
 
 static int local_deliver(DELIVER_REQUEST *rqst, char *service)
 {
-    char   *myname = "local_deliver";
+    const char *myname = "local_deliver";
     RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len;
     RECIPIENT *rcpt;
     int     rcpt_stat;
index 4dbf2242d2df7afa0ee8a91a7d960e7e2c735bfc..bbaa7d15d40783884ec079b05481377ab3f1eb8b 100644 (file)
@@ -159,7 +159,7 @@ static int xsasl_cyrus_client_get_user(void *context, int unused_id,
                                               const char **result,
                                               unsigned *len)
 {
-    char   *myname = "xsasl_cyrus_get_user";
+    const char *myname = "xsasl_cyrus_get_user";
     XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
 
     if (msg_verbose)
@@ -182,7 +182,7 @@ static int xsasl_cyrus_client_get_user(void *context, int unused_id,
 static int xsasl_cyrus_client_get_passwd(sasl_conn_t *conn, void *context,
                                            int id, sasl_secret_t **psecret)
 {
-    char   *myname = "xsasl_cyrus_get_passwd";
+    const char *myname = "xsasl_cyrus_get_passwd";
     XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
     int     len;
 
@@ -414,7 +414,7 @@ static int xsasl_cyrus_client_first(XSASL_CLIENT *xp,
                                            const char **mechanism,
                                            VSTRING *init_resp)
 {
-    char   *myname = "xsasl_cyrus_client_first";
+    const char *myname = "xsasl_cyrus_client_first";
     XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
     unsigned enc_length;
     unsigned enc_length_out;
@@ -485,7 +485,7 @@ static int xsasl_cyrus_client_first(XSASL_CLIENT *xp,
 static int xsasl_cyrus_client_next(XSASL_CLIENT *xp, const char *server_reply,
                                           VSTRING *client_reply)
 {
-    char   *myname = "xsasl_cyrus_client_next";
+    const char *myname = "xsasl_cyrus_client_next";
     XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
     unsigned enc_length;
     unsigned enc_length_out;
index 182213f32533a1c4a54a6329bd60b8185973f24c..3a2db98f459432184ed654690cfeb79a8e88946b 100644 (file)
@@ -474,7 +474,7 @@ static int xsasl_cyrus_server_auth_response(int sasl_status,
 int     xsasl_cyrus_server_first(XSASL_SERVER *xp, const char *sasl_method,
                                  const char *init_response, VSTRING *reply)
 {
-    char   *myname = "xsasl_cyrus_server_first";
+    const char *myname = "xsasl_cyrus_server_first";
     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
     char   *dec_buffer;
     unsigned dec_length;
@@ -534,7 +534,7 @@ int     xsasl_cyrus_server_first(XSASL_SERVER *xp, const char *sasl_method,
 static int xsasl_cyrus_server_next(XSASL_SERVER *xp, const char *request,
                                           VSTRING *reply)
 {
-    char   *myname = "xsasl_cyrus_server_next";
+    const char *myname = "xsasl_cyrus_server_next";
     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
     unsigned dec_length;
     unsigned request_len;
index 75b02a00ce7dac29d5f88f906129b8fcdf611548..47b6bc98479cc107e0f640e8c3a82cabec6f9df9 100644 (file)
@@ -367,7 +367,7 @@ static void xsasl_dovecot_parse_reply_args(XSASL_DOVECOT_SERVER *server,
 static int xsasl_dovecot_handle_reply(XSASL_DOVECOT_SERVER *server,
                                              VSTRING *reply)
 {
-    char   *myname = "xsasl_dovecot_handle_reply";
+    const char *myname = "xsasl_dovecot_handle_reply";
     char   *line, *cmd;
 
     while (vstring_get_nonl(server->sasl_line,
@@ -429,7 +429,7 @@ static int is_valid_base64(const char *data)
 int     xsasl_dovecot_server_first(XSASL_SERVER *xp, const char *sasl_method,
                                  const char *init_response, VSTRING *reply)
 {
-    char   *myname = "xsasl_dovecot_server_first";
+    const char *myname = "xsasl_dovecot_server_first";
     XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
     int     i;