-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
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)
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
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"
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
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.
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.
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
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.
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.
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 \
* 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
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
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
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
-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=
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.
--- /dev/null
+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.
+
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
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
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
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:
$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
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.
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:
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
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:
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
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.
/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
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:
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
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:
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
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
# 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
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.
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
======================================
"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.
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");
}
#
# 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)
$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
$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
#!/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";
my $syslog_ident = "postfix/policy-spf";
# ----------------------------------------------------------
-# minimal documentation
+# minimal documentation
# ----------------------------------------------------------
#
# 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.
# 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: @_";
}
openlog $syslog_ident, $syslog_options, $syslog_facility;
# ----------------------------------------------------------
-# main
+# main
# ----------------------------------------------------------
#
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{$_});
}
}
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 %_ = @_;
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";
}
return $string;
}
-
<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
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
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>
# ===================================================================
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>=
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>
--- /dev/null
+<!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> -> </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> -> </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="qmqpd.8.html">qmqpd(8)</a>
+</td>
+
+<td> <tt> -> </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> <a href="cleanup.8.html">cleanup(8)</a>
+</td>
+
+<td> <tt> -> </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> -> </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 <unknown-msgid>
+</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>
</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>
<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> = 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. Typically you will
only have RSA certificates issued by a commercial CA. In addition,
<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
</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>
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
<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>
</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>
<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> = yes". </p>
<p> Example: </p>
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> = 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>
<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> = yes" as an <a href="smtpd.8.html">smtpd(8)</a> command
line option. Port 465 (smtps) was once chosen for this feature.
</p>
<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> = yes". This feature implies
+"<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes". When TLS is not enforced,
+"<a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a> = 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>
<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> = 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> = 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> = yes". </p>
<p> Example: </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 < 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> = 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> = 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>
<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>
<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>
</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
<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>
</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ä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> = 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> = no" and "<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = 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> = 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> = yes" and
+"<a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> = 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> = 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> = yes"
+and "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = 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> = 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> = yes" and
+"<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = 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> = 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> = yes"
+and "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = 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 < 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>
<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>
<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> = yes" or "<a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> = 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>
<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> = 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>
<li> <p> When both hostname and next-hop destination lookups produce
a result, the more specific per-site policy (NONE, MUST, etc)
<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> = 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> = 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>
</blockquote>
<p> Example: </p>
-
+
<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<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>
<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> = aNULL",
+to disable anonymous ciphers even with opportunistic TLS, set
+"<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = 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>
<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
</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>
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
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> = 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> = 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".
</p>
</ul>
<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>
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.
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.
<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>:
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
<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>
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:
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>
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>
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>
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.
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:
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>
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>
<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>
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>
<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>
<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>
</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>
<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>
<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>
<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>
<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>
</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>
<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>
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>
<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>
<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>
<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>
<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>
<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>
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
<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 < 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>
<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>
<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>
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>
<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>
<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>
postfix/smtp[pid]: Host offered STARTTLS: [name.of.host]
</pre>
+<p> This feature is available in Postfix 2.2 and later. </p>
+
</DD>
<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
</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>
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>
<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>
<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>
(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>
<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 < 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>
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>
</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>
<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>
<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>
<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>
<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>
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>
not announce or accept SASL authentication over unencrypted
connections. </p>
+<p> This feature is available in Postfix 2.2 and later. </p>
+
</DD>
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>
<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
<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 < 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>
<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>
<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>
<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>
<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>
<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>
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>
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>
<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>
<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>
(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>
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>
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>
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>
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>
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>
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>
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>
<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>
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:
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.
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
<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.
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>
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>
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.
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
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
<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
<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
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:
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.
<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>
<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
<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
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>
<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>
.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
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
.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
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.
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.
.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
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
.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
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
.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
.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
.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
.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
.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.
.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
.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
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
.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
.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.
.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
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.
.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.
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
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.
.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
.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.
.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
.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
.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.
.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.
.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.
.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
.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
.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,
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.
.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.
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.
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,
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
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
.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
.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
.na
.nf
ADDRESS_REWRITING_README Postfix address manipulation
+CONTENT_INSPECTION_README content inspection
.SH "LICENSE"
.na
.nf
.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.
+.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
.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
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.
.SH "VERP SUPPORT CONTROLS"
.na
.nf
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
.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
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;
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;
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;
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;
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;
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;
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;
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;
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/;
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)) {
<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
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
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>
# ===================================================================
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=
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>
--- /dev/null
+<!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> -> </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> -> </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> qmqpd(8)
+</td>
+
+<td> <tt> -> </tt> </td>
+
+<td bgcolor="#f0f0ff" align="center" valign="middle"> cleanup(8)
+</td>
+
+<td> <tt> -> </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> -> </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 <unknown-msgid>
+</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>
../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 \
../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 \
../html/MAILDROP_README.html: MAILDROP_README.html
$(POSTLINK) $? >$@
+../html/MILTER_README.html: MILTER_README.html
+ $(POSTLINK) $? >$@
+
../html/MYSQL_README.html: MYSQL_README.html
$(POSTLINK) $? >$@
../README_FILES/MAILDROP_README: MAILDROP_README.html
$(HT2READ) $? >$@
+../README_FILES/MILTER_README: MILTER_README.html
+ $(HT2READ) $? >$@
+
../README_FILES/MYSQL_README: MYSQL_README.html
$(HT2READ) $? >$@
</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>
<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 = 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. Typically you will
only have RSA certificates issued by a commercial CA. In addition,
<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
</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>
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
<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>
</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>
<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 = yes". </p>
<p> Example: </p>
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 = 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>
<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 = yes" as an smtpd(8) command
line option. Port 465 (smtps) was once chosen for this feature.
</p>
<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 = 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. </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>
<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 = 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 = 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 = yes". </p>
<p> Example: </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 < 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 = 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 = 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>
<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>
<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>
</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
<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>
</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ä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 = 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 = 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. </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 = 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 = yes" and
+"smtp_enforce_tls = 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 = 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 = yes"
+and "smtp_tls_enforce_peername = 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 = 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 = yes" and
+"smtp_tls_enforce_peername = 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 = 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 = yes"
+and "smtp_tls_enforce_peername = 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 < 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>
<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>
<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 = yes" or "smtp_tls_enforce_peername = 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>
<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 = yes" and
+"smtp_tls_enforce_peername = yes" imply "smtp_use_tls = yes". </p>
<li> <p> When both hostname and next-hop destination lookups produce
a result, the more specific per-site policy (NONE, MUST, etc)
<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 = 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 = 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>
</blockquote>
<p> Example: </p>
-
+
<blockquote>
<pre>
/etc/postfix/main.cf:
<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>
<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 = 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. </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>
<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
</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>
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
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 = 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 = no", but it is disabled when both
+"smtp_enforce_tls = yes" and "smtp_tls_enforce_peername = yes".
</p>
</ul>
# 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
<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>
<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
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.
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.
<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
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
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.
<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
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,
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,
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,
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
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
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>
<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
%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 < 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
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
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
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.
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
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
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.
<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>
<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 < 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
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
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
</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
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,
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 < 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.
<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.
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
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
<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
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
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)
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
<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
</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>
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
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);
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",
{
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",
{
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",
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",
static int bounce_append_proto(char *service_name, VSTREAM *client)
{
- char *myname = "bounce_append_proto";
+ const char *myname = "bounce_append_proto";
int flags;
/*
char *, char *, char *, int,
BOUNCE_TEMPLATES *))
{
- char *myname = "bounce_notify_proto";
+ const char *myname = "bounce_notify_proto";
int flags;
int dsn_ret;
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;
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;
void bounce_cleanup_log(void)
{
- char *myname = "bounce_cleanup_log";
+ const char *myname = "bounce_cleanup_log";
/*
* Sanity checks.
void bounce_cleanup_register(char *service, char *queue_id)
{
- char *myname = "bounce_cleanup_register";
+ const char *myname = "bounce_cleanup_register";
/*
* Sanity checks.
void bounce_cleanup_unregister(void)
{
- char *myname = "bounce_cleanup_unregister";
+ const char *myname = "bounce_cleanup_unregister";
/*
* Sanity checks.
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;
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
$(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:
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 \
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
/* .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
/*
* 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
* 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;
}
#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.
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 */
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 */
#ifdef DELAY_ACTION
int defer_delay; /* deferred delivery */
#endif
+ MILTERS *milters; /* mail filters */
} CLEANUP_STATE;
/*
extern VSTRING *cleanup_reject_chars;
extern VSTRING *cleanup_strip_chars;
+ /*
+ * Milters.
+ */
+extern MILTERS *cleanup_milters;
+
/*
* Address canonicalization fine control.
*/
/*
* 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 *);
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)))
#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
* 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
&& (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);
}
/* 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
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
* (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
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);
}
(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);
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;
int junk;
int mapped_type = type;
const char *mapped_buf = buf;
+ int milter_count;
#ifdef DELAY_ACTION
int defer_delay;
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
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) {
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;
}
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;
}
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) {
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;
state->flags |= extra_opts;
return;
}
-
#ifdef DELAY_ACTION
if (type == REC_TYPE_DELAY) {
/* Not part of queue file format. */
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) {
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;
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);
#include <mail_addr.h>
#include <mail_params.h>
+#include <mail_version.h> /* milter_macro_v */
#include <ext_prop.h>
#include <flush_clnt.h>
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,
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,
};
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,
};
*/
int cleanup_ext_prop_mask;
+ /*
+ * Milter support.
+ */
+MILTERS *cleanup_milters;
+
/* cleanup_all - callback for the runtime error handler */
void cleanup_all(void)
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();
}
#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)
{
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);
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);
#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);
}
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)) {
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);
}
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;
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 */
* 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;
* 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>",
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;
/*
--- /dev/null
+/*++
+/* 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
--- /dev/null
+#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
--- /dev/null
+*** 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 ***
/* 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
#include <msg.h>
#include <vstring.h>
#include <vstream.h>
+#include <split_at.h>
/* Global library. */
#include <rec_type.h>
#include <cleanup_user.h>
#include <mail_params.h>
+#include <lex_822.h>
/* Application-specific. */
#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);
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);
+ }
+ }
+}
/* 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));
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;
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;
state->dsn_notify = 0;
state->dsn_orcpt = 0;
state->verp_delims = 0;
+ state->milters = 0;
return (state);
}
myfree(state->dsn_orcpt);
if (state->verp_delims)
myfree(state->verp_delims);
+ if (state->milters)
+ milter_free(state->milters);
myfree((char *) state);
}
static int deliver_message(DELIVER_REQUEST *request)
{
- char *myname = "deliver_message";
+ const char *myname = "deliver_message";
VSTREAM *src;
int result = 0;
int status;
static int deliver_message(DELIVER_REQUEST *request)
{
- char *myname = "deliver_message";
+ const char *myname = "deliver_message";
VSTREAM *src;
int result = 0;
int status;
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;
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;
/*
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;
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;
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
*/
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.
* 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",
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 */
*/
#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.
/* 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",
/* 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
/* 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;
|| 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,
/* 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) {
/* 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;
*/
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);
}
void debug_peer_init(void)
{
- char *myname = "debug_peer_init";
+ const char *myname = "debug_peer_init";
/*
* Sanity check.
void deliver_completed(VSTREAM *stream, long offset)
{
- char *myname = "deliver_completed";
+ const char *myname = "deliver_completed";
if (offset == -1)
return;
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,
{
DSN *hop_status;
int err;
+
/* XXX This DSN structure initialization bypasses integrity checks. */
static DSN dummy_dsn = {"", "", "", "", "", "", ""};
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;
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);
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,
static void dict_ldap_logprint(LDAP_CONST char *data)
{
- char *myname = "dict_ldap_debug";
+ const char *myname = "dict_ldap_debug";
char *buf,
*p;
#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) {
/* 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
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;
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;
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;
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;
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;
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;
int flush_purge(void)
{
- char *myname = "flush_purge";
+ const char *myname = "flush_purge";
int status;
if (msg_verbose)
int flush_refresh(void)
{
- char *myname = "flush_refresh";
+ const char *myname = "flush_refresh";
int status;
if (msg_verbose)
int flush_send(const char *site)
{
- char *myname = "flush_send";
+ const char *myname = "flush_send";
int status;
if (msg_verbose)
int flush_add(const char *site, const char *queue_id)
{
- char *myname = "flush_add";
+ const char *myname = "flush_add";
int status;
if (msg_verbose)
/* 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
"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,
};
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));
#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);
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;
* 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);
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;
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;
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;
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[] = {
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,
};
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,
#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 ""
#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"
#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
#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.
#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
#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
#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 */
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;
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;
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;
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;
* 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"
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;
int mark_corrupt(VSTREAM *src)
{
- char *myname = "mark_corrupt";
+ const char *myname = "mark_corrupt";
uid_t saved_uid;
gid_t saved_gid;
* 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 */
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;
INET_ADDR_LIST local_masks;
char *hosts;
char *host;
- char *sep = " \t,";
+ const char *sep = " \t,";
char *bufp;
int nvirtual;
int nlocal;
{
char *hosts;
char *host;
- char *sep = " \t,";
+ const char *sep = " \t,";
char *bufp;
/*
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;
/*
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.
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.
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;
/*
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;
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);
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) {
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",
#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.
*/
/* 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) {
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;
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 */
/*
* 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>
*/
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;
static uid_t dict_owner(char *table)
{
- char *myname = "dict_owner";
+ const char *myname = "dict_owner";
DICT *dict;
struct stat st;
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;
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;
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;
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;
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);
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;
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;
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;
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;
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;
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;
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;
int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
{
- char *myname = "deliver_recipient";
+ const char *myname = "deliver_recipient";
int rcpt_stat;
/*
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;
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;
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;
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;
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)
void master_avail_listen(MASTER_SERV *serv)
{
- char *myname = "master_avail_listen";
+ const char *myname = "master_avail_listen";
time_t now;
int n;
void master_avail_more(MASTER_SERV *serv, MASTER_PROC *proc)
{
- char *myname = "master_avail_more";
+ const char *myname = "master_avail_more";
int n;
/*
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.
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);
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);
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;
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);
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;
void master_listen_cleanup(MASTER_SERV *serv)
{
- char *myname = "master_listen_cleanup";
+ const char *myname = "master_listen_cleanup";
int n;
/*
int master_notify(int pid, unsigned generation, int status)
{
- char *myname = "master_notify";
+ const char *myname = "master_notify";
MASTER_STATUS stat;
/*
static void master_sigdeath(int sig)
{
- char *myname = "master_sigdeath";
+ const char *myname = "master_sigdeath";
struct sigaction action;
pid_t pid = getpid();
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,
void master_spawn(MASTER_SERV *serv)
{
- char *myname = "master_spawn";
+ const char *myname = "master_spawn";
MASTER_PROC *proc;
MASTER_PID pid;
int n;
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)) {
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;
void master_status_init(MASTER_SERV *serv)
{
- char *myname = "master_status_init";
+ const char *myname = "master_status_init";
/*
* Sanity checks.
void master_status_cleanup(MASTER_SERV *serv)
{
- char *myname = "master_status_cleanup";
+ const char *myname = "master_status_cleanup";
/*
* Sanity checks.
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;
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;
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
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;
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;
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
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;
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;
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;
--- /dev/null
+../../.indent.pro
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+/*++
+/* 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
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+# 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
--- /dev/null
+ /*
+ * 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());
+}
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
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)
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;
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;
void qmgr_active_done(QMGR_MESSAGE *message)
{
- char *myname = "qmgr_active_done";
+ const char *myname = "qmgr_active_done";
struct stat st;
if (msg_verbose)
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;
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;
/*
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,
int status;
RECIPIENT *recipient;
int nrcpt;
- char *whatsup;
if (dsb == 0)
dsb = dsb_create();
QMGR_QUEUE *queue;
QMGR_ENTRY *entry;
DSN dsn;
- char *whatsup;
/*
* Find out if this delivery process is really available. Once elected,
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) {
}
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);
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);
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)
QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
{
- char *myname = "qmgr_message_realloc";
+ const char *myname = "qmgr_message_realloc";
/*
* Sanity checks.
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;
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)
void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
{
- char *myname = "qmgr_queue_throttle";
+ const char *myname = "qmgr_queue_throttle";
/*
* Sanity checks.
void qmgr_queue_done(QMGR_QUEUE *queue)
{
- char *myname = "qmgr_queue_done";
+ const char *myname = "qmgr_queue_done";
QMGR_TRANSPORT *transport = queue->transport;
/*
static void qmgr_scan_start(QMGR_SCAN *scan_info)
{
- char *myname = "qmgr_scan_start";
+ const char *myname = "qmgr_scan_start";
/*
* Sanity check.
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
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
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,
* 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
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));
/*
* 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);
}
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.
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 */
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;
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)
/*
* 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)
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));
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;
* In case the next record is broken.
*/
vstream_fflush(VSTREAM_OUT);
- }
+ } while (rec_type != REC_TYPE_END);
}
/* usage - explain and terminate */
# 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"
+ }
}
}
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))
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)
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;
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;
void qmgr_active_done(QMGR_MESSAGE *message)
{
- char *myname = "qmgr_active_done";
+ const char *myname = "qmgr_active_done";
struct stat st;
if (msg_verbose)
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;
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;
/*
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,
int status;
RECIPIENT *recipient;
int nrcpt;
- char *whatsup;
if (dsb == 0)
dsb = dsb_create();
{
QMGR_ENTRY *entry;
DSN dsn;
- char *whatsup;
/*
* Find out if this delivery process is really available. Once elected,
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;
static void qmgr_job_unlink(QMGR_JOB *job)
{
- char *myname = "qmgr_job_unlink";
+ const char *myname = "qmgr_job_unlink";
QMGR_TRANSPORT *transport = job->transport;
/*
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;
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;
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;
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;
}
}
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) {
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);
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)
QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
{
- char *myname = "qmgr_message_realloc";
+ const char *myname = "qmgr_message_realloc";
/*
* Sanity checks.
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;
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;
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)
void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
{
- char *myname = "qmgr_queue_throttle";
+ const char *myname = "qmgr_queue_throttle";
/*
* Sanity checks.
void qmgr_queue_done(QMGR_QUEUE *queue)
{
- char *myname = "qmgr_queue_done";
+ const char *myname = "qmgr_queue_done";
QMGR_TRANSPORT *transport = queue->transport;
/*
static void qmgr_scan_start(QMGR_SCAN *scan_info)
{
- char *myname = "qmgr_scan_start";
+ const char *myname = "qmgr_scan_start";
/*
* Sanity check.
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
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
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 */
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);
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 */
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;
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ state->addr_family = AF_UNSPEC;
}
/*
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",
state->addr = mystrdup(client_addr.buf);
state->rfc_addr =
concatenate(IPV6_COL, client_addr.buf, (char *) 0);
+ state->addr_family = sa->sa_family;
}
}
{
state->addr = mystrdup(client_addr.buf);
state->rfc_addr = mystrdup(client_addr.buf);
+ state->addr_family = sa->sa_family;
}
/*
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;
}
/*
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;
@$(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
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
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
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
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
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
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
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
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
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
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
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
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
#include <vstring_vstream.h>
#include <stringops.h>
+ /*
+ * TLS levels
+ */
+#include <tls.h>
+
/*
* Application-specific.
*/
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);
}
*/
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);
* 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;
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);
}
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,
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,
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,
};
/* .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. */
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
#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)
/*
* 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");
/*
* Postfix TLS library.
*/
-#ifdef USE_TLS
#include <tls.h>
-#endif
/*
* State information associated with each SMTP delivery request.
#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
*/
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 */
#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 */
* 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)
#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))
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;
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;
void smtp_chat_notify(SMTP_SESSION *session)
{
- char *myname = "smtp_chat_notify";
+ const char *myname = "smtp_chat_notify";
VSTREAM *notice;
char **cpp;
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;
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);
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;
*/
#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);
}
}
char *dest;
char **cpp;
int non_fallback_sites;
+ int retry_plain = 0;
DSN_BUF *why = state->why;
/*
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 {
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,
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,
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,
};
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;
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",
*/
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.
* 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,
* 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"),
static int smtp_start_tls(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
+ tls_client_start_props tls_props;
VSTRING *serverid;
SMTP_RESP fake;
* 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
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;
/*
/* 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:
}
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.
*/
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.
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;
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;
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. */
#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);
#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;
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);
}
}
-/* 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
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);
* 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);
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);
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)
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
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
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
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
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
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
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
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
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
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
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
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
/* .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
#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.
#define REASON_LOST_CONNECTION "lost connection"
#define REASON_ERROR_LIMIT "too many errors"
+ /*
+ * Mail filter initialization status.
+ */
+MILTERS *smtpd_milters;
+
#ifdef USE_TLS
/*
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
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);
static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
- char *err;
+ const char *err;
int discard_mask;
VSTRING *reply_buf;
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);
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);
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 */
* 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)
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);
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;
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;
}
/* 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
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);
}
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);
}
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;
}
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;
&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) {
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);
+ }
+ }
}
/*
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;
int out_error;
char **cpp;
CLEANUP_STAT_DETAIL *detail;
- int smtp_code;
#ifdef USE_TLS
VSTRING *peer_CN;
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));
* 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, "");
}
"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);
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);
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
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");
static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
- char *err;
+ const char *err;
/*
* Sanity checks.
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");
}
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;
}
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)
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);
}
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 */
};
*/
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));
/*
&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);
}
static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
+ const char *err;
int rate;
if (argc != 1) {
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");
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);
}
&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");
int argc;
SMTPD_TOKEN *argv;
SMTPD_CMD *cmdp;
- int count;
- int crate;
+ int tls_rate;
const char *ehlo_words;
+ const char *err;
int status;
/*
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;
&& 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);
&& 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++;
}
* 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);
+ }
}
/*
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;
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))) {
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;
chat_reset(state, 0);
mail_reset(state);
rcpt_reset(state);
+ if (smtpd_milters)
+ milter_disc_event(smtpd_milters);
}
/* smtpd_service - service one client */
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
* 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
+ }
}
/*
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
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,
};
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[] = {
#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[] = {
#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
} 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 */
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 */
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 */
#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
#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.
*/
extern int smtpd_input_transp_mask;
+ /*
+ * More Milter support.
+ */
+extern MILTERS *smtpd_milters;
+
/* LICENSE
/* .ad
/* .fi
/* 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
/* 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;
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 */
void smtpd_chat_notify(SMTPD_STATE *state)
{
- char *myname = "smtpd_chat_notify";
+ const char *myname = "smtpd_chat_notify";
VSTREAM *notice;
char **cpp;
*/
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
/* 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;
/*
/* 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;
/*
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,
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);
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));
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);
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);
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);
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;
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;
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;
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)
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)
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;
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)
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);
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);
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;
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;
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;
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;
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;
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;
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;
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;
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);
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;
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);
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;
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);
const char *reply_class,
const char *def_acl)
{
- char *myname = "check_namadr_access";
+ const char *myname = "check_namadr_access";
int status;
if (msg_verbose)
const char *def_acl)
{
#ifdef USE_TLS
- char *myname = "check_ccert_access";
+ const char *myname = "check_ccert_access";
int found;
if (!state->tls_context)
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;
*
* 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,
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;
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;
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;
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 : "",
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;
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);
char *smtpd_check_queue(SMTPD_STATE *state)
{
- char *myname = "smtpd_check_queue";
+ const char *myname = "smtpd_check_queue";
struct fsspace fsbuf;
int status;
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]);
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+/*++
+/* 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
+/*--*/
+
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();
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;
}
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",
state->addr = mystrdup(client_addr.buf);
state->rfc_addr =
concatenate(IPV6_COL, client_addr.buf, (char *) 0);
+ state->addr_family = sa->sa_family;
}
}
{
state->addr = mystrdup(client_addr.buf);
state->rfc_addr = mystrdup(client_addr.buf);
+ state->addr_family = sa->sa_family;
}
/*
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) {
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;
}
#include <mail_params.h>
#include <mail_proto.h>
#include <mail_error.h>
+#include <ehlo_mask.h>
/* Application-specific. */
{
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;
* 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);
smtpd_sasl_connect(state, VAR_SMTPD_SASL_OPTS, var_smtpd_sasl_opts);
#endif
+ state->milter_argv = 0;
+ state->milter_argc = 0;
+
/*
* Initialize peer information.
*/
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);
* 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 *);
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
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 */
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;
# 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
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
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
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
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
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
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
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
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
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
/* 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 */
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))
/*
* 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) \
* 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 *);
extern void tls_int_seed(void);
extern int tls_ext_seed(int);
- /*
- * tls_temp.c, code that is going away.
- */
#endif /* TLS_INTERNAL */
/* LICENSE
/* Yorktown Heights, NY 10598, USA
/*--*/
+#endif /* USE_TLS */
#endif /* _TLS_H_INCLUDED_ */
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");
/* 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);
/*
* 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);
}
}
{
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)
* 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);
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");
/*
}
/*
- * 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);
/*
* 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
* 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);
}
* 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);
}
* 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
/* 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;
int hostname_matched = 0;
int dNSName_found = 0;
int verify_peername;
+ ARGV *cmatch_argv = 0;
STACK_OF(GENERAL_NAME) * gens;
(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) {
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;
}
}
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("");
* 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",
* 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;
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);
* 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,
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.
* 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 */
* 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);
/*
* 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);
* 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);
/*
* 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);
}
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);
/* 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
/* tls_mgr_policy - request caching policy */
-int tls_mgr_policy(int *policy)
+int tls_mgr_policy(const char *cache_type, int *cachable)
{
int status;
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);
/* 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;
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 */
/* 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;
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,
/* 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;
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 */
#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);
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);
}
#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
/* 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.
*/
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)
myfree((char *) TLScontext);
}
+/* tls_version_split - Split OpenSSL version number into major, minor, ... */
+
static void tls_version_split(long version, TLS_VINFO *info)
{
/*
* 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) {
/* 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)
/* 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
/*
* 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);
/* 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;
/*
* 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.
/*
* Logging.
*/
- if (cp->log_level >= 3)
+ if (cp->verbose)
msg_info("lookup %s session id=%s", cp->cache_label, 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);
/*
* Logging.
*/
- if (cp->log_level >= 3)
+ if (cp->verbose)
msg_info("delete %s session id=%s", cp->cache_label, 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;
/*
* Logging.
*/
- if (log_level >= 3)
+ if (verbose)
msg_info("open %s TLS cache %s", cache_label, dbname);
/*
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;
/*
* Logging.
*/
- if (cp->log_level >= 3)
+ if (cp->verbose)
msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name);
/*
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;
/* 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;
*/
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);
}
/*
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));
/*
/* 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");
/*
}
/*
- * 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);
* 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);
}
* 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);
}
* 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
* 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.
* 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);
}
/*
* 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;
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);
/*
* 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()");
* 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);
/*
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);
/*
* 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
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);
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)
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);
*/
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,
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;
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
/* 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>
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
#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.
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.
* 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 */
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
#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 */
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;
/*
*/
if (request == 0) {
request = vstring_alloc(10);
+ cache_type = vstring_alloc(10);
cache_id = vstring_alloc(10);
buffer = vstring_alloc(10);
}
*/
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;
}
}
*/
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;
}
*/
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;
}
}
* 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);
}
{
char *path;
struct timeval tv;
+ TLSMGR_SCACHE *ent;
/*
* If nothing else works then at least this will get us a few bits of
* 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)
* 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 */
{
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,
};
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;
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)) {
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);
const void *ctable_locate(CTABLE *cache, const char *key)
{
- char *myname = "ctable_locate";
+ const char *myname = "ctable_locate";
CTABLE_ENTRY *entry;
/*
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)
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)
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;
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;
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;
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;
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;
*/
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 */
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;
vstring_strcpy(dict->fold_buf, name);
name = lowercase(vstring_str(dict->fold_buf));
}
-
memset(&db_key, 0, sizeof(db_key));
/*
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;
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;
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
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)
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;
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)
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;
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;
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;
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())
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())
void event_disable_readwrite(int fd)
{
- char *myname = "event_disable_readwrite";
+ const char *myname = "event_disable_readwrite";
EVENT_FDTABLE *fdp;
if (EVENT_INIT_NEEDED())
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;
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;
void event_loop(int delay)
{
- char *myname = "event_loop";
+ const char *myname = "event_loop";
static int nested;
fd_set rmask;
fd_set wmask;
{
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;
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;
void fsspace(const char *path, struct fsspace * sp)
{
- char *myname = "fsspace";
+ const char *myname = "fsspace";
#ifdef USE_STATFS
#ifdef USE_STRUCT_FS_DATA /* Ultrix */
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;
static int ial_socket(int af)
{
- char *myname = "inet_addr_local[socket]";
+ const char *myname = "inet_addr_local[socket]";
int sock;
/*
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;
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;
* 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*) */
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;
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;
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;
*/
int main(int argc, char **argv)
{
- char *myname = argv[0];
+ const char *myname = argv[0];
INET_PROTO_INFO *pf;
if (argc < 2)
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.
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;
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 */
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;
int match_list_match(MATCH_LIST * list,...)
{
- char *myname = "match_list_match";
+ const char *myname = "match_list_match";
char **cpp;
char *pat;
int match;
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)
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;
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;
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)
#define MSG_SYSLOG_RECLEN 2000
struct facility_list {
- char *name;
+ const char *name;
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");
static void msg_vstream_print(int level, const char *text)
{
- static char *level_text[] = {
+ static const char *level_text[] = {
"info", "warning", "error", "fatal", "panic",
};
/* 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;
/* 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
/* 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,
/* 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;
* 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)
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;
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;
}
#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
ssize_t netstring_get_length(VSTREAM *stream)
{
- char *myname = "netstring_get_length";
+ const char *myname = "netstring_get_length";
ssize_t len = 0;
int ch;
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.
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",
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;
void rand_sleep(unsigned delay, unsigned variation)
{
- char *myname = "rand_sleep";
+ const char *myname = "rand_sleep";
unsigned usec;
/*
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;
int sane_rename(const char *from, const char *to)
{
- char *myname = "sane_rename";
+ const char *myname = "sane_rename";
int saved_errno;
struct stat st;
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));
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;
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;
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;
/*
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;
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;
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)
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.
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;
#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
# 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
#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"
#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
#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"
#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"
#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"
#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
#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 */
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"
#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 */
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
#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"
#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
#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
#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
#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
#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
#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
/* 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
#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
/* 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
#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"
#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"
#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"
*/
#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
#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
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);
}
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;
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);
}
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
} 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
* 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);
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.
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;
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;
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;
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);
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;
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;
/*
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,
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
/* 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;
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
{
- char *myname = "vstream_fseek";
+ const char *myname = "vstream_fseek";
VBUF *bp = &stream->buf;
/*
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)) {
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;
/*
VSTREAM *vstream_popen(int flags,...)
{
- char *myname = "vstream_popen";
+ const char *myname = "vstream_popen";
VSTREAM_POPEN_ARGS args;
va_list ap;
VSTREAM *stream;
static void watchdog_event(int unused_sig)
{
- char *myname = "watchdog_event";
+ const char *myname = "watchdog_event";
WATCHDOG *wp;
/*
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;
void watchdog_destroy(WATCHDOG *wp)
{
- char *myname = "watchdog_destroy";
+ const char *myname = "watchdog_destroy";
watchdog_stop(wp);
watchdog_curr = wp->saved_watchdog;
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);
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);
void watchdog_pat(void)
{
- char *myname = "watchdog_pat";
+ const char *myname = "watchdog_pat";
if (watchdog_curr)
watchdog_curr->trip_run = 0;
if ((count = write(fd, buf, len)) < 0) {
if (errno == EAGAIN && timeout > 0)
continue;
+ if (errno == EINTR)
+ continue;
return (-1);
}
if (count == 0)
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;
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;
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;
int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
{
- char *myname = "deliver_recipient";
+ const char *myname = "deliver_recipient";
int rcpt_stat;
/*
int deliver_unknown(LOCAL_STATE state)
{
- char *myname = "deliver_unknown";
+ const char *myname = "deliver_unknown";
/*
* Make verbose logging easier to understand.
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;
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)
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;
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;
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;
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;
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;
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,
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;