-TFILE
-TFORWARD_INFO
-THBC_ACTION_CALL_BACKS
+-THBC_CALL_BACKS
-THBC_CHECKS
-THBC_MAP_INFO
-THBC_OUTPUT_CALL_BACKS
-TSTRING_TABLE
-TSYS_EXITS_DETAIL
-TTLSMGR_SCACHE
+-TTLS_APPL_STATE
+-TTLS_CLIENT_INIT_PROPS
+-TTLS_CLIENT_START_PROPS
-TTLS_PRNG_SEED_INFO
-TTLS_PRNG_SRC
-TTLS_SCACHE
-TTLS_SCACHE_ENTRY
+-TTLS_SERVER_INIT_PROPS
+-TTLS_SERVER_START_PROPS
+-TTLS_SESS_STATE
-TTLS_VINFO
-TTLScontext_t
-TTOK822
-TXSASL_SERVER
-TXSASL_SERVER_IMPL
-TXSASL_SERVER_IMPL_INFO
--Tcipher_probe
+-Tcipher_probe_t
-Toff_t
-Tregex_t
-Tregmatch_t
-Tsfsistat
-Tsize_t
-Tssize_t
--Ttls_client_init_props
--Ttls_client_start_props
--Ttls_info_t
--Ttls_server_props
--Ttls_server_start_props
Cleanup: don't try sending HELO after a 421 EHLO reply.
File: smtp/smtp_proto.c.
+20071221-nonprod
+
+ Using 20071221 as reference point.
+
+ Cleanup: Simplified TLS library cipher and protocol API to
+ just pass string-valued properties to tls_client_init() and
+ tls_client_start(). The client is now agnostic of the
+ mechanics of cipher management internal to the library. The
+ main.cf parameters used internally in the library are now
+ loaded by the library, not the caller. Files:
+ src/smtp/lmtp_params.c, src/smtp/smtp.c, src/smtp/smtp.h,
+ src/smtp/smtp_params.c, src/smtp/smtp_proto.c,
+ src/smtp/smtp_session.c, src/smtpd/smtpd.c, src/tls/tls.h,
+ src/tls/tls_client.c, src/tls/tls_level.c, src/tls/tls_misc.c,
+ src/tls/tls_server.c, src/tls/tls_session.c, src/tls/tls_verify.c
+ and src/tlsmgr/tlsmgr.c
+
+ Cleanup: Client session lookup key "salting" is now handled
+ internally in the tls library. Files: src/tls/tls_client.c
+
+ Cleanup: Cipher state is cached, and only updated when
+ necessary. Files: src/tls/tls_misc.c
+
+ Feature: Extended the syntax of protocol selection to allow
+ exclusions as well as inclusions. Files: src/tls/tls_misc.c
+
+ Cleanup: Updated default verification depth to match reality:
+ default is 9 in OpenSSL and we don't yet override it. When
+ we do (soon), the default will match previous behavior.
+ Files: src/global/mail_params.h
+
+ Bugfix: Reference to obsolete "pfixtls" code won't compile
+ inside #ifdef for OpenSSL <= 0.9.5a. Using an OpenSSL release
+ that old has not been tested for some time, but may now
+ work. Files: src/tls/tls_bio_ops.c.
+
+ Replaced "void *" TLS library application handles by explicit
+ pointer types, while hiding data structure implementation
+ details from the TLS library users. Files: tls/tls_client.c,
+ tls/tls_server.c, smtp/smtp.c, smtpd/smtpd.c.
+
+ The TLS library no longer modifies VSTRINGs passed in by
+ the caller. Where possible, information is passed as "const"
+ from application to library. Files: smtp/smtp_proto.c,
+ tls/tls_client.c.
+
+20071227-nonprod
+
+ Replaced explicit initialization of props structures by
+ emulating function calls with named parameter lists. Files:
+ tls/tls.h, smtp/smtp.c, smtp/smtp_proto.c, smtpd/smtpd.c.
+
20071222
Further polishing of the Milter code and logging. File:
generic "4.3.0 Sevice unavailable", but log the text for
the actual error. File: cleanup/cleanup_milter.c.
+20080102-nonprod
+
+ SMTP client fingerprint security level support and configurable
+ fingerprint digest algorithm. Victor Duchovni. Files:
+ smtp/lmtp_params.c, smtp/smtp.c, smtp/smtp.h,
+ src/smtp/smtp_params.c, src/smtp/smtp_proto.c,
+ src/smtp/smtp_session.c, tls/tls_client.c, tls/tls_level.c,
+ tls/tls_verify.c.
+
+20080103-nonprod
+
+ Missed "invalid TLS configuration" patch for SMTP client.
+ Victor Duchovni. File: smtp/smtp_proto.c.
+
+ SMTP server configurable fingerprint digest algorithm.
+ Victor Duchovni. Files: smtpd/smtpd.c, tls/tls.h,
+ tls/tls_server.c, tls/tls_verify.c.
+
+20080104-nonprod
+
+ Cleanup: finally implemented certificate verification depth
+ limit parameters. Prior to Postfix 2.5 these were ignored.
+ For backwards compatibility, the default verification depth
+ limit is now 9, the OpenSSL default. Victor Duchovni. Files:
+ src/tls/tls_client.c, src/tls/tls_server.c, src/tls/tls_verify.c.
+
+ Robustness: Avoid possibility of NULL pointer issues in
+ application code that checks certificate names, by providing
+ "empty string" values when no data is available. Victor
+ Duchovni. Files: src/tls/tls_verify.c, src/tls/tls_client.c,
+ src/tls/tls_server.c, src/smtpd/smtpd_check.c, src/smtpd/smtpd.c.
+
+ Cleanup: separation of TLS handshake from security level
+ enforcement. The library shakes hands; the application
+ decides if the resulting security is acceptable. Victor
+ Duchovni. Files: smtpd/smtpd.c, smtpd/smtpd_proto.c,
+ tls/tls_server.c, tls/tls_client.c, tls/tls_verify.c.
+
+ Robustness: more robust processing of ASN.1 string attributes
+ in x509v3 certificates, plus additional sanity checks (e.g.
+ embedded null characters). Victor Duchovni. File:
+ src/tls/tls_verify.c.
+
20080104
Workaround: minor change to the Dovecot AUTH request to
prevent dovecot-auth memory wastage. Timo Sirainen. File:
xsasl/xsasl_dovecot_server.c.
-20070807
+20080105-nonprod
+
+ Cleanup: renamed TLS-related symbols for consistency (always
+ include the init, start, stop prefix in the TLS library
+ function and data structure names; consistently distinguish
+ between per-application TLS state and per-session TLS state;
+ consistently use the fpt prefix for fingerprint related
+ variables and structure members; consistent use of monocase
+ typedef-ed names).
+
+20080106-nonprod
+
+ Cleanup: consistent use of <pre> and <blockquote> in examples;
+ instead of emphasizing new Postfix 2.5 behavior in reference
+ documentation, describe the new behavior as "current", with
+ historical behavior as a supplemental note.
+
+20080107
+
+ Feature: new "pass" service type (in addition to "inet",
+ "unix" and "fifo"). The "pass" service type supports
+ front-end daemons that accept all inbound connections and
+ that permit only well-behaved clients to talk to the MTA.
+ This service type had been sitting in the master daemon for
+ years but was disabled by default. Actual applications for
+ this will have to be developed later. Files: util/upass_connect.c,
+ util/upass_trigger.c.
+
+20080108
+
+ Cleanup: where possible, store data structures in read-only
+ memory. Besides the security advantage of no write access,
+ this also gives slightly better memory utilization when
+ many processes execute the same file. Files: pretty much
+ everything that has a static table, except for a few tables
+ in the benchmark tools with flags that are controlled by
+ command-line information.
+
+20080109
- Feature (experimental release only): new "pass" service
- type (in addition to "inet", "unix" and "fifo"). The "pass"
- service type supports front-end daemons that accept all
- inbound connections and that permit only well-behaved clients
- to talk to the MTA. This service type had been sitting in
- the master daemon for years but was disabled by default.
- Files: util/upass_connect.c, util/upass_trigger.c.
+ Cleanup: more read-only data. Files: everything that passes
+ around a HEADER_OPTS pointer.
to avoid the condition under normal conditions. When the condition is caused by
botnets or other malware, the document suggests configuration settings that
help to minimize the impact on legitimate mail. Finally, the document
-introduces Postfix stress-adaptive behavior, and how it can be used to
-automatically switch configuration settings under overload.
+introduces stress-adaptive behavior, introduced with Postfix 2.5, and how it
+can be used to automatically switch configuration settings under overload.
Topics covered in this document:
S\bSy\bym\bmp\bpt\bto\bom\bms\bs o\bof\bf P\bPo\bos\bst\btf\bfi\bix\bx S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br o\bov\bve\ber\brl\blo\boa\bad\bd
Under normal conditions, Postfix responds immediately when a remote SMTP client
-connects. The time needed to deliver mail to Postfix may depend on how busy the
-CPU or disk are, but that should be noticeable only with very large messages.
-Performance degrades more dramatically when the number of remote SMTP clients
-exceeds the number of Postfix SMTP server processes. When a client connects
-while all server processes are busy, the client must wait until a server
-process becomes available.
+connects. The time needed to deliver mail should be noticeable only with very
+large messages. Performance degrades more dramatically when the number of
+remote SMTP clients exceeds the number of Postfix SMTP server processes. When a
+client connects while all server processes are busy, the client must wait until
+a server process becomes available.
Overload may be caused by a legitimate mail (example: a DNS registrar opens a
new zone for registrations), by mistake (mail explosion caused by a forwarding
loop) or by illegitimate mail (worm outbreak, botnet, or other malware
activity). Symptoms of Postfix SMTP mail server overload are:
- * Postfix logs a warning that all server ports are busy:
-
- Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
- (25) has reached its process limit "30": new clients may experience
- noticeable delays
- Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
- condition, increase the process count in master.cf or reduce the
- service time per client
-
* Remote SMTP clients experience a long delay before Postfix sends the "220
hostname.example.com ESMTP Postfix" greeting. If this affects end-user mail
clients, enable the "submission" service entry in master.cf (present since
CONNECT" events. This happens because remote SMTP clients disconnect before
Postfix answers the connection.
-NOTE: The last two symptoms also happen without overload.
+ * Postfix 2.3 and later logs a warning that all server ports are busy:
+
+ Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
+ (25) has reached its process limit "30": new clients may experience
+ noticeable delays
+ Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
+ condition, increase the process count in master.cf or reduce the
+ service time per client
+
+NOTE: The first two symptoms may also happen without overload, for example:
* Broken DNS also causes lengthy delays before "220 hostname.example.com ..."
while the Postfix SMTP server tries to look up the client's hostname.
W\bWA\bAR\bRN\bNI\bIN\bNG\bG
By turning on TLS support in Postfix, you not only get the ability to encrypt
-mail and to authenticate clients or servers. You also turn on thousands and
-thousands of lines of OpenSSL library code. Assuming that OpenSSL is written as
-carefully as Wietse's own code, every 1000 lines introduce one additional bug
-into Postfix.
+mail and to authenticate remote SMTP clients or servers. You also turn on
+thousands and thousands of lines of OpenSSL library code. Assuming that OpenSSL
+is written as carefully as Wietse's own code, every 1000 lines introduce one
+additional bug into Postfix.
W\bWh\bha\bat\bt P\bPo\bos\bst\btf\bfi\bix\bx T\bTL\bLS\bS s\bsu\bup\bpp\bpo\bor\brt\bt d\bdo\boe\bes\bs f\bfo\bor\br y\byo\bou\bu
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
+encrypted, meaning: the key must be accessible without a password. The
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
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.
+CA signed certificate. The remote SMTP client will generally not be able to
+authenticate the self-signed certificate, but unless the client 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
+For servers that are n\bno\bot\bt public Internet MX hosts, Postfix 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 will
-not accidentally run with no certificates.
+failure, a certificate-less Postfix SMTP server will be unable to receive email
+from most TLS enabled clients. To avoid accidental configurations with no
+certificates, Postfix enables certificate-less operation only when the
+administrator explicitly sets "smtpd_tls_cert_file = none". This ensures that
+new Postfix SMTP server configurations 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
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
-with:
+Example: the certificate for "server.example.com" was issued by "intermediate
+CA" which itself has a certificate issued by "root CA". Create the server.pem
+file with:
% c\bca\bat\bt s\bse\ber\brv\bve\ber\br_\b_c\bce\ber\brt\bt.\b.p\bpe\bem\bm i\bin\bnt\bte\ber\brm\bme\bed\bdi\bia\bat\bte\be_\b_C\bCA\bA.\b.p\bpe\bem\bm >\b> s\bse\ber\brv\bve\ber\br.\b.p\bpe\bem\bm
If you want the Postfix SMTP server to accept remote SMTP client certificates
issued by these CAs, append the root certificate to $smtpd_tls_CAfile or
-install it in the $smtpd_tls_CApath directory. When you configure trust in a
-root CA, it is not necessary to explicitly trust intermediary CAs signed by the
-root CA, unless $smtpd_tls_ccert_verifydepth is less than the number of CAs in
-the certificate chain for the clients of interest. With a verify depth of 1 you
-can only verify certificates directly signed by a trusted CA, and all trusted
-intermediary CAs need to be configured explicitly. With a verify depth of 2 you
-can verify clients signed by a root CA or a direct intermediary CA (so long as
-the client is correctly configured to supply its intermediate CA certificate).
+install it in the $smtpd_tls_CApath directory.
RSA key and certificate examples:
$smtpd_tls_CApath directory needs to be accessible inside the optional chroot
jail.
-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.
+When you configure the Postfix SMTP server 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
# Obsolete, but still supported
smtpd_use_tls = yes
-With this, Postfix SMTP server announces STARTTLS support to SMTP clients, but
-does not require that clients use TLS encryption.
+With this, the Postfix SMTP server announces STARTTLS support to remote SMTP
+clients, but does not require that clients use TLS encryption.
Note: when an unprivileged user invokes "sendmail -bs", STARTTLS is never
-offered due to insufficient privileges to access the server private key. This
-is intended behavior.
+offered due to insufficient privileges to access the Postfix SMTP server
+private key. This is intended behavior.
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
TLS is sometimes used in the non-standard "wrapper" mode where a server always
-uses TLS, instead of announcing STARTTLS support and waiting for clients to
-request TLS service. Some clients, namely Outlook [Express] prefer the
-"wrapper" mode. This is true for OE (Win32 < 5.0 and Win32 >=5.0 when run on a
-port<>25 and OE (5.01 Mac on all ports).
+uses TLS, instead of announcing STARTTLS support and waiting for remote SMTP
+clients to request TLS service. Some clients, namely Outlook [Express] prefer
+the "wrapper" mode. This is true for OE (Win32 < 5.0 and Win32 >=5.0 when run
+on a port<>25 and OE (5.01 Mac on all ports).
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 "-
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.
+anonymous ciphers to remote SMTP clients, these are automatically suppressed
+when the Postfix SMTP server is configured to ask for client certificates.
Example:
# Obsolete, but still supported
smtpd_enforce_tls = 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
-also suffice for longer chains (root CA issues special CA which then issues the
-actual certificate...)
+The client certificate verification depth is specified with the main.cf
+smtpd_tls_ccert_verifydepth parameter. The default verification depth is 9 (the
+OpenSSL default), for compatibility with Postfix versions before 2.5 where
+smtpd_tls_ccert_verifydepth was ignored. When you configure trust in a root CA,
+it is not necessary to explicitly trust intermediary CAs signed by the root CA,
+unless $smtpd_tls_ccert_verifydepth is less than the number of CAs in the
+certificate chain for the clients of interest. With a verify depth of 1 you can
+only verify certificates directly signed by a trusted CA, and all trusted
+intermediary CAs need to be configured explicitly. With a verify depth of 2 you
+can verify clients signed by a root CA or a direct intermediary CA (so long as
+the client is correctly configured to supply its intermediate CA certificate).
Example:
/etc/postfix/main.cf:
- smtpd_tls_ccert_verifydepth = 5
+ smtpd_tls_ccert_verifydepth = 2
S\bSu\bup\bpp\bpo\bor\brt\bti\bin\bng\bg A\bAU\bUT\bTH\bH o\bov\bve\ber\br T\bTL\bLS\bS o\bon\bnl\bly\by
server access control:
permit_tls_clientcerts
- Allow the remote SMTP client SMTP request if the client certificate
- passes verification, and if its fingerprint is listed in the list of
- client certificates (see relay_clientcerts discussion below).
+ Allow the remote SMTP client request if the client certificate
+ fingerprint is listed in the client certificate table (see
+ relay_clientcerts discussion below).
permit_tls_all_clientcerts
- Allow the remote client SMTP request if the client certificate passes
- verification.
+ Allow the remote SMTP client request if the client certificate passes
+ trust chain verification. Useful with private-label CAs that only issue
+ certificates to trusted clients (and not otherwise).
check_ccert_access type:table
- If the client certificate passes verification, use its fingerprint as a
- key for the specified access(5) table.
+ Use the remote SMTP client certificate fingerprint as the lookup key
+ for the specified access(5) table.
+
+The digest algorithm used to construct the client certificate fingerprints is
+specified with the main.cf smtpd_tls_fingerprint_digest parameter. The default
+is "md5", for compatibility with Postfix versions < 2.5.
The permit_tls_all_clientcerts feature must be used with caution, because it
can result in too many access permissions. Use this feature only if a special
reject_unauth_destination
...
-The Postfix list manipulation routines give special treatment to whitespace and
-some other characters, making the use of certificate names impractical. Instead
-we use the certificate fingerprints as they are difficult to fake but easy to
-use for lookup. Postfix lookup tables are in the form of (key, value) pairs.
-Since we only need the key, the value can be chosen freely, e.g. the name of
-the user or host.
-
-Example:
+Example: Postfix lookup tables are in the form of (key, value) pairs. Since we
+only need the key, the value can be chosen freely, e.g. the name of the user or
+host:
/etc/postfix/main.cf:
relay_clientcerts = hash:/etc/postfix/relay_clientcerts
smtpd_tls_key_file = /etc/postfix/key.pem
smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5
- # Postfix 2.3 and later
smtpd_tls_security_level = encrypt
- # Obsolete, but still supported
- smtpd_enforce_tls = yes
-
-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 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 default parameters chosen by OpenSSL
-are already different from those distributed with other TLS packages.
+ smtpd_tls_mandatory_protocols = TLSv1
+ # Also available with Postfix >= 2.5:
+ smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+
+If you want to take advantage of ciphers with ephemeral Diffie-Hellman (EDH)
+key exchange (this offers "forward-secrecy"), DH parameters are needed. Instead
+of using the built-in DH parameters for both 1024-bit (non-export ciphers) and
+512-bit (export ciphers), 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. Postfix defaults to compiled-in
+parameters that are shared by all Postfix users who don't generate their own
+settings.
To generate your own set of DH parameters, use:
- % o\bop\bpe\ben\bns\bss\bsl\bl g\bge\ben\bnd\bdh\bh -\b-o\bou\but\bt /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/d\bdh\bh_\b_1\b10\b02\b24\b4.\b.p\bpe\bem\bm -\b-2\b2 -\b-r\bra\ban\bnd\bd /\b/v\bva\bar\br/\b/r\bru\bun\bn/\b/e\beg\bgd\bd-\b-p\bpo\boo\bol\bl
- 1\b10\b02\b24\b4
- % o\bop\bpe\ben\bns\bss\bsl\bl g\bge\ben\bnd\bdh\bh -\b-o\bou\but\bt /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/d\bdh\bh_\b_5\b51\b12\b2.\b.p\bpe\bem\bm -\b-2\b2 -\b-r\bra\ban\bnd\bd /\b/v\bva\bar\br/\b/r\bru\bun\bn/\b/e\beg\bgd\bd-\b-p\bpo\boo\bol\bl 5\b51\b12\b2
+ % o\bop\bpe\ben\bns\bss\bsl\bl g\bge\ben\bnd\bdh\bh -\b-o\bou\but\bt /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/d\bdh\bh_\b_5\b51\b12\b2.\b.p\bpe\bem\bm -\b-2\b2 5\b51\b12\b2
+ % o\bop\bpe\ben\bns\bss\bsl\bl g\bge\ben\bnd\bdh\bh -\b-o\bou\but\bt /\b/e\bet\btc\bc/\b/p\bpo\bos\bst\btf\bfi\bix\bx/\b/d\bdh\bh_\b_1\b10\b02\b24\b4.\b.p\bpe\bem\bm -\b-2\b2 1\b10\b02\b24\b4
Examples:
* Disabling TLS in the SMTP/LMTP client
* Enabling TLS in the SMTP/LMTP client
* Mandating TLS encryption
+ * Certificate fingerprint verification
* Mandating server certificate verification
* Secure server certificate verification
* Per-destination TLS policy
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
+The smtp(8) and lmtp(8) delivery agents are implemented by a single dual-
+purpose program. Specifically, 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 Postfix 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:
+Do not configure Postfix SMTP 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 =
If you want the Postfix SMTP client to accept remote SMTP server certificates
issued by these CAs, append the root certificate to $smtp_tls_CAfile or install
-it in the $smtp_tls_CApath directory. When you configure trust in a root CA, it
-is not necessary to explicitly trust intermediary CAs signed by the root CA,
-unless $smtp_tls_scert_verifydepth is less than the number of CAs in the
-certificate chain for the servers of interest. With a verify depth of 1 you can
-only verify certificates directly signed by a trusted CA, and all trusted
-intermediary CAs need to be configured explicitly. With a verify depth of 2 you
-can verify servers signed by a root CA or a direct intermediary CA (so long as
-the server is correctly configured to supply its intermediate CA certificate).
+it in the $smtp_tls_CApath directory.
RSA key and certificate examples:
Opportunistic TLS.
e\ben\bnc\bcr\bry\byp\bpt\bt
Mandatory TLS encryption.
+f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt
+ Certificate fingerprint verification.
v\bve\ber\bri\bif\bfy\by
Mandatory server certificate verification.
s\bse\bec\bcu\bur\bre\be
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.
+supported by the remote SMTP 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 even 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
/etc/postfix/tls_per_site:
[example.net]:587 MUST_NOPEERMATCH
+C\bCe\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt v\bve\ber\bri\bif\bfi\bic\bca\bat\bti\bio\bon\bn
+
+Certificate fingerprint verification is available with Postfix 2.5 and later.
+At this security level ("smtp_tls_security_level = fingerprint"), no trusted
+certificate authorities are used or required. The certificate trust chain,
+expiration date, ... are not checked. Instead, the
+smtp_tls_fingerprint_cert_match parameter or the "match" attribute in the
+policy table lists the valid "fingerprints" of the remote SMTP server
+certificate.
+
+If certificate fingerprints are exchanged securely, this is the strongest, and
+least scalable security level. The administrator needs to securely collect the
+fingerprints of the X.509 certificates of each peer server, store them into a
+local file, and update this local file whenever the peer server's public
+certificate changes. This may be feasible for an SMTP "VPN" connecting a small
+number of branch offices over the Internet, or for secure connections to a
+central mail hub. It works poorly if the remote SMTP server is managed by a
+third party, and its public certificate changes periodically without prior
+coordination with the verifying site.
+
+The digest algorithm used to calculate the fingerprint is selected by the
+s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt parameter. In the policy table multiple
+fingerprints can be combined with a "|" delimiter in a single match attribute,
+or multiple match attributes can be employed. The ":" character is not used as
+a delimiter as it occurs between each pair of fingerprint (hexadecimal) digits.
+
+Example: fingerprint TLS security with an internal mailhub. Two matching
+fingerprints are listed. The relayhost may be multiple physical hosts behind a
+load-balancer, each with its own private/public key and self-signed
+certificate. Alternatively, a single relayhost may be in the process of
+switching from one set of private/public keys to another, and both keys are
+trusted just prior to the transition.
+
+ relayhost = [mailhub.example.com]
+ smtp_tls_security_level = fingerprint
+ smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_cert_match =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+
+Example: Certificate fingerprint verification with selected destinations. As in
+the example above, we show two matching fingerprints:
+
+ /etc/postfix/main.cf:
+ smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ smtp_tls_fingerprint_digest = md5
+
+ /etc/postfix/tls_policy:
+ example.com fingerprint
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+
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 if the server certificate is valid (not expired or revoked, and signed
-by a trusted certificate authority) and if the server certificate name matches
-a known pattern. 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 name matching
-strategy. Fine-tuning the matching strategy is generally only appropriate for
-secure-channel destinations.
+sessions if the remote SMTP server certificate is valid (not expired or
+revoked, and signed by a trusted certificate authority) and where the server
+certificate name matches a known pattern. 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 name matching 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
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.
+extension are used to verify the remote SMTP 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 trust chain and subject name verification is not viable
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.
+In this example, the Postfix SMTP 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}/
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.
+extension are used to verify the remote SMTP 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
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 Postfix SMTP client will encrypt all traffic and verify the destination
+name immune from forged DNS responses. MX lookups are still used to find the
+hostnames of the SMTP servers for example.com, but these hostnames 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
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. Mail is delivered only if remote SMTP server
+ Mandatory encryption. Mail is delivered only if the remote SMTP server
offers STARTTLS and the TLS handshake succeeds. 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.
+ smtp_tls_mandatory_ciphers parameter, and the optional "protocols"
+ attribute overrides the main.cf smtp_tls_mandatory_protocols parameter.
+f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt
+ Certificate fingerprint verification. Available with Postfix 2.5 and later.
+ At this security level, there are no trusted certificate authorities. The
+ certificate trust chain, expiration date, ... are not checked. Instead, the
+ optional m\bma\bat\btc\bch\bh attribute, or else the main.cf
+ s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_c\bce\ber\brt\bt_\b_m\bma\bat\btc\bch\bh parameter, lists the valid fingerprints of
+ the server certificate. The digest algorithm used to calculate fingerprints
+ is selected by the s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_f\bfi\bin\bng\bge\ber\brp\bpr\bri\bin\bnt\bt_\b_d\bdi\big\bge\bes\bst\bt parameter. Multiple
+ fingerprints can be combined with a "|" delimiter in a single match
+ attribute, or multiple match attributes can be employed. The ":" character
+ is not used as a delimiter as it occurs between each pair of fingerprint
+ (hexadecimal) digits.
v\bve\ber\bri\bif\bfy\by
Mandatory server certificate verification. Mail is delivered only if the
- TLS handshake succeeds, if the server certificate can be validated (not
- expired or revoked, and signed by a trusted certificate authority), and if
- the server certificate name matches the optional "match" attribute (or the
- main.cf smtp_tls_verify_cert_match parameter value when no optional "match"
- attribute is specified).
+ TLS handshake succeeds, if the remote SMTP server certificate can be
+ validated (not expired or revoked, and signed by a trusted certificate
+ authority), and if the server certificate name matches the optional "match"
+ attribute (or the main.cf smtp_tls_verify_cert_match parameter value when
+ no optional "match" attribute is specified).
s\bse\bec\bcu\bur\bre\be
- Secure-channel TLS. Mail is delivered only if the TLS handshake succeeds,
- if the server certificate can be validated (not expired or revoked, and
- signed by a trusted certificate authority), and if the server certificate
- name matches the optional "match" attribute (or the main.cf
- smtp_tls_secure_cert_match parameter value when no optional "match"
- attribute is specified).
+ Secure certificate verification. Mail is delivered only if the TLS
+ handshake succeeds, if the remote SMTP server certificate can be validated
+ (not expired or revoked, and signed by a trusted certificate authority),
+ and if the server certificate name matches the optional "match" attribute
+ (or the main.cf smtp_tls_secure_cert_match parameter value when no optional
+ "match" attribute is specified).
Notes:
* The "match" attribute is especially useful to verify TLS certificates for
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ # Postfix 2.5 and later
+ smtp_tls_fingerprint_digest = md5
/etc/postfix/tls_policy:
example.edu none
example.mil may
example.net secure
.example.net secure match=.example.net:example.net
[mail.example.org]:587 secure match=nexthop
+ # Postfix 2.5 and later
+ [thumb.example.org] fingerprint
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
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
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 d\bde\bep\bpt\bth\bh
-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 (where the root CA issues a special CA certificate which then
-issues the actual certificate).
+The server certificate verification depth is specified with the main.cf
+smtp_tls_scert_verifydepth parameter. The default verification depth is 9 (the
+OpenSSL default), for compatibility with Postfix versions before 2.5 where
+smtp_tls_scert_verifydepth was ignored. When you configure trust in a root CA,
+it is not necessary to explicitly trust intermediary CAs signed by the root CA,
+unless $smtp_tls_scert_verifydepth is less than the number of CAs in the
+certificate chain for the servers of interest. With a verify depth of 1 you can
+only verify certificates directly signed by a trusted CA, and all trusted
+intermediary CAs need to be configured explicitly. With a verify depth of 2 you
+can verify servers signed by a root CA or a direct intermediary CA (so long as
+the server is correctly configured to supply its intermediate CA certificate).
Example:
/etc/postfix/main.cf:
- smtp_tls_scert_verifydepth = 5
+ smtp_tls_scert_verifydepth = 2
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 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 anonymous ciphers even
-at the "encrypt" security level, set "smtp_tls_mandatory_exclude_ciphers =
-aNULL"; and 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.
+remote SMTP server certificates are verified. If you want to disable anonymous
+ciphers even at the "encrypt" security level, set
+"smtp_tls_mandatory_exclude_ciphers = aNULL"; and 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:
smtp_tls_mandatory_ciphers = medium
smtp_tls_mandatory_exclude_ciphers = RC4, MD5
smtp_tls_exclude_ciphers = aNULL
+ smtp_tls_mandatory_protocols = SSLv3, TLSv1
+ # Also available with Postfix >= 2.5:
+ smtp_tls_mandatory_protocols = !SSLv2
C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be S\bSM\bMT\bTP\bPS\bS s\bsu\bup\bpp\bpo\bor\brt\bt
smtp_tls_CAfile = /etc/postfix/cacert.pem
smtp_tls_session_cache_database =
btree:/var/lib/postfix/smtp_tls_session_cache
- smtp_use_tls = yes
+ smtp_tls_security_level = may
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
* Victor Duchovni was instrumental with the re-implementation of the
smtp_tls_per_site code in terms of enforcement levels, which simplified the
implementation greatly.
+ * Victor Duchovni implemented the fingerprint security level, added more
+ sanity checks, and separated TLS connection management from security policy
+ enforcement. The latter change simplified the code that verifies
+ certificate signatures, certificate names, and certificate fingerprints.
The mail_release_date configuration parameter (format: yyyymmdd)
specifies the release date of a stable release or snapshot release.
+Incompatibility with Postfix snapshot 20080109
+==============================================
+
+TLS logging output has changed to make it more useful. Existing
+logfile parser regular expressions may need adjustment.
+
+- More log entries include the "hostnamename[ipaddress]" of the
+ remote SMTP peer.
+
+- Certificate trust chain error reports show only the first
+ error certificate (closest to the trust chain root), and the
+ reporting is more human-readable for the most likely errors.
+
+- After the completion of the TLS handshake, the session is logged
+ with TLS loglevel >= 1 as either "Untrusted", "Trusted" or
+ "Verified" (SMTP client only).
+ - "Untrusted" means that the certificate trust chain is invalid,
+ or that the root CA is not trusted.
+ - "Trusted" means that the certificate trust chain is valid, and
+ that the root CA is trusted.
+ - "Verified" means that the certificate meets the SMTP client's
+ matching criteria for the destination:
+ - In the case of a destination name match, "Verified" also
+ implies "Trusted".
+ - In the case of a fingerprint match, CA trust is not applicable.
+
+- The logging of protocol states with TLS loglevel >= 2 no longer
+ reports bogus error conditions when OpenSSL asks Postfix to refill
+ (or flush) network I/O buffers. This loglevel is for debugging
+ only; use 0 or 1 in production configurations.
+
+Major changes with Postfix snapshot 20080109
+============================================
+
+The Postfix SMTP client has a new "fingerprint" security level.
+This avoids dependencies on CAs, and relies entirely on bi-lateral
+exchange of public keys (really self-signed or private CA signed
+X.509 public key certificates). Scalability is clearly limited. For
+details, see the fingerprint discussion in TLS_README.
+
+The Postfix SMTP server can now use SHA1 instead of MD5 to compute
+remote SMTP client certificate fingerprints. For backwards
+compatibility, the default algorithm is MD5. For details, see the
+"smtpd_tls_fingerprint_digest" parameter in the postconf(5) manual.
+
+The maximum certificate trust chain depth (verifydepth) is finally
+implemented in the Postfix TLS library. Previously, the parameter
+had no effect. The default depth was changed to 9 (the OpenSSL
+default) for backwards compatibility.
+
+If you have explicity limited the verification depth in main.cf,
+check that the configured limit meets your needs. See the
+"lmtp_tls_scert_verifydepth", "smtp_tls_scert_verifydepth" and
+"smtpd_tls_ccert_verifydepth" parameters in the postconf(5) manual.
+
+The selection of SSL/TLS protocols for mandatory TLS can now use
+exclusion rather than inclusion. Either form is acceptable; see the
+"lmtp_tls_mandatory_protocols", "smtp_tls_mandatory_protocols" and
+"smtpd_tls_mandatory_protocols" parameters in the postconf(5) manual.
+
Major changes with Postfix snapshot 20080107
============================================
New "pass" service type in master.cf. Written years ago, this
-allows a front-end daemon to accept all connections from the network,
-and forward only those from well-behaved clients to Postfix. Since
-this uses file descriptor passing, it imposes no overhead once a
-connection is handed over to Postfix. This is available in the
-experimental release only. See master(5) for a few details.
+allows a future front-end daemon to accept all connections from the
+network, and forward only those from well-behaved clients to Postfix.
+Since this uses file descriptor passing, it imposes no overhead
+once a connection is handed over to Postfix. See master(5) for a
+few details.
Incompatibility with Postfix snapshot 20071224
==============================================
This list does not really follow priority.
+* Code cleanup: split smtp_session.c into generic SMTP, legacy TLS,
+ and current TLS. The amount of TLS code now dominates the file.
+ Do this after all other code revisions stabilize, to avoid
+ complicating code reviews.
+
+* Code cleanup: TLS_LEV_NOTFOUND no longer belongs in the TLS
+ library. It is an SMTP-client only feature. To fix, change the
+ policy lookup API and use a different method to indicate if a
+ policy was found. At the same time, fix policy lookup to initialize
+ session->tls_level.
+
+* Code cleanup: see if multiple consecutive switches can be aggregated
+ (set_cipher_grade() and session_tls_init()).
+
* Implement support of CRL checking. OpenSSL 0.9.7 finally supports CRLs,
so Postfix/TLS should support loading CRLs.
Wish list:
+ Consolidate duplicated code *_server_accept_{pass,inet}().
+
Consolidate duplicated code in {inet,unix,upass}_trigger.c.
In the SMTP client, handle 421 replies in smtp_loop() by
overload, and how to avoid the condition under normal conditions.
When the condition is caused by botnets or other malware, the
document suggests configuration settings that help to minimize the
-impact on legitimate mail. Finally, the document introduces Postfix
-stress-adaptive behavior, and how it can be used to automatically
-switch configuration settings under overload. </p>
+impact on legitimate mail. Finally, the document introduces
+stress-adaptive behavior, introduced with Postfix 2.5, and how it
+can be used to automatically switch configuration settings under
+overload. </p>
<p> Topics covered in this document: </p>
<h2><a name="overload"> Symptoms of Postfix SMTP server overload </a></h2>
<p> Under normal conditions, Postfix responds immediately when a
-remote SMTP client connects. The time needed to deliver mail to
-Postfix may depend on how busy the CPU or disk are, but that should
+remote SMTP client connects. The time needed to deliver mail should
be noticeable only with very large messages. Performance degrades
more dramatically when the number of remote SMTP clients exceeds
the number of Postfix SMTP server processes. When a client connects
<ul>
-<li> <p> Postfix logs a warning that all server ports are busy: </p>
-
-<pre>
-Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
- (25) has reached its process limit "30": new clients may experience
- noticeable delays
-Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
- condition, increase the process count in <a href="master.5.html">master.cf</a> or reduce the
- service time per client
-</pre>
-
<li> <p> Remote SMTP clients experience a long delay before Postfix
sends the "220 hostname.example.com ESMTP Postfix" greeting. If
this affects end-user mail clients, enable the "submission" service
connection after CONNECT" events. This happens because remote SMTP
clients disconnect before Postfix answers the connection. </p>
+<li> <p> Postfix 2.3 and later logs a warning that all server ports
+are busy: </p>
+
+<pre>
+Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
+ (25) has reached its process limit "30": new clients may experience
+ noticeable delays
+Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
+ condition, increase the process count in <a href="master.5.html">master.cf</a> or reduce the
+ service time per client
+</pre>
+
</ul>
-<p> NOTE: The last two symptoms also happen without overload. </p>
+<p> NOTE: The first two symptoms may also happen without overload,
+for example: </p>
<ul>
<h2> WARNING </h2>
<p> By turning on TLS support in Postfix, you not only get the
-ability to encrypt mail and to authenticate clients or servers.
+ability to encrypt mail and to authenticate remote SMTP clients or servers.
You also turn on thousands and thousands of lines of OpenSSL library
code. Assuming that OpenSSL is written as carefully as Wietse's
own code, every 1000 lines introduce one additional bug into
<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
+without a password. The 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
<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
+self-signed or private-CA signed certificate. The remote SMTP client
+will generally not be
+able to authenticate the self-signed certificate, but unless the
+client 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
+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
+back to plain text after a TLS handshake failure, a certificate-less
+Postfix SMTP server will
be unable to receive email from most TLS enabled clients. To avoid
-accidental configurations with no certificates, Postfix 2.3 enables
+accidental configurations with no certificates, Postfix 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 will not accidentally run with no certificates. </p>
+SMTP server configurations 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,
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
+<p> Example: the certificate for "server.example.com" was issued by
"intermediate CA" which itself has a certificate issued by "root
CA". Create the server.pem file with: </p>
<p> If you want the Postfix SMTP server to accept remote SMTP client
certificates issued by these CAs, append the root certificate to
-$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> directory. When
-you configure trust in a root CA, it is not necessary to explicitly trust
-intermediary CAs signed by the root CA, unless $<a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a>
-is less than the number of CAs in the certificate chain for the clients
-of interest. With a verify depth of 1 you can only verify certificates
-directly signed by a trusted CA, and all trusted intermediary CAs need to
-be configured explicitly. With a verify depth of 2 you can verify clients
-signed by a root CA or a direct intermediary CA (so long as the client
-is correctly configured to supply its intermediate CA certificate). </p>
+$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> directory. </p>
<p> RSA key and certificate examples: </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 <a
+<p> When you configure the Postfix SMTP server 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>
</pre>
</blockquote>
-<p> With this, Postfix SMTP server announces STARTTLS support to
-SMTP clients, but does not require that clients use TLS encryption.
+<p> With this, the Postfix SMTP server announces STARTTLS support to
+remote SMTP clients, but does not require that clients use TLS encryption.
</p>
<p> Note: when an unprivileged user invokes "sendmail -bs", STARTTLS
-is never offered due to insufficient privileges to access the server
+is never offered due to insufficient privileges to access the Postfix
+SMTP server
private key. This is intended behavior. </p>
<p> <a name="server_enforce">You can ENFORCE the use of TLS</a>,
<p> TLS is sometimes used in the non-standard "wrapper" mode where
a server always uses TLS, instead of announcing STARTTLS support
-and waiting for clients to request TLS service. Some clients, namely
+and waiting for remote SMTP clients to request TLS service. Some
+clients, namely
Outlook [Express] prefer the "wrapper" mode. This is true for OE
(Win32 < 5.0 and Win32 >=5.0 when run on a port<>25
and OE (5.01 Mac on all ports). </p>
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>
+offers anonymous ciphers to remote SMTP clients, these are automatically
+suppressed
+when the Postfix SMTP server is configured to ask for client
+certificates. </p>
<p> Example: </p>
</pre>
</blockquote>
-<p> 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 also suffice for longer chains (root
-CA issues special CA which then issues the actual certificate...)
-</p>
+<p> The client certificate verification depth is specified with the
+<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> parameter. The default verification
+depth is 9 (the OpenSSL default), for compatibility with Postfix
+versions before 2.5 where <a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> was ignored.
+When you configure trust in a
+root CA, it is not necessary to explicitly trust intermediary CAs signed
+by the root CA, unless $<a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> is less than the
+number of CAs in the certificate chain for the clients of interest. With
+a verify depth of 1 you can only verify certificates directly signed
+by a trusted CA, and all trusted intermediary CAs need to be configured
+explicitly. With a verify depth of 2 you can verify clients signed by a
+root CA or a direct intermediary CA (so long as the client is correctly
+configured to supply its intermediate CA certificate). </p>
<p> Example: </p>
<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
- <a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> = 5
+ <a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> = 2
</pre>
</blockquote>
<dl>
-<dt> <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> </dt> <dd> <p> Allow the remote SMTP
-client SMTP request if the client certificate passes verification,
-and if its fingerprint is listed in the list of client certificates
-(see <a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a> discussion below). </p> </dd>
+<dt> <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> </dt> <dd> <p> Allow the remote SMTP client
+request if the client certificate fingerprint is listed in the
+client certificate table (see <a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a> discussion below). </p>
+</dd>
-<dt> <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </dt> <dd> <p> Allow the remote
-client SMTP request if the client certificate passes verification.
-</p> </dd>
+<dt> <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </dt> <dd> <p> Allow the remote SMTP
+client request if the client certificate passes trust chain verification.
+Useful with private-label CAs that only issue certificates to trusted
+clients (and not otherwise). </p> </dd>
-<dt> <a href="postconf.5.html#check_ccert_access">check_ccert_access</a> <a href="DATABASE_README.html">type:table</a></dt> <dd>
-<p> If the client certificate passes verification, use its fingerprint
-as a key for the specified <a href="access.5.html">access(5)</a> table. </p> </dd>
+<dt> <a href="postconf.5.html#check_ccert_access">check_ccert_access</a> <a href="DATABASE_README.html">type:table</a></dt> <dd> <p> Use the remote SMTP
+client
+certificate fingerprint as the lookup key for the specified <a href="access.5.html">access(5)</a>
+table. </p> </dd>
</dl>
</blockquote>
+<p> The digest algorithm used to construct the client certificate
+fingerprints is specified with the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>
+parameter. The default is "md5", for compatibility with Postfix
+versions < 2.5. </p>
+
<p> The <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> feature must be used with caution,
because it can result in too many access permissions. Use this
feature only if a special CA issues the client certificates, and
</pre>
</blockquote>
-<p> The Postfix list manipulation routines give special treatment
-to whitespace and some other characters, making the use of certificate
-names impractical. Instead we use the certificate fingerprints as
-they are difficult to fake but easy to use for lookup. Postfix
-lookup tables are in the form of (key, value) pairs. Since we only
-need the key, the value can be chosen freely, e.g. the name of
-the user or host.</p>
+<p> Example: Postfix lookup tables are in the form of (key, value)
+pairs. Since we only need the key, the value can be chosen freely, e.g.
+the name of the user or host:</p>
-<p> Example: </p>
-
<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<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_mandatory_ciphers">smtpd_tls_mandatory_ciphers</a> = high
<a href="postconf.5.html#smtpd_tls_mandatory_exclude_ciphers">smtpd_tls_mandatory_exclude_ciphers</a> = aNULL, MD5
- # Postfix 2.3 and later
<a href="postconf.5.html#smtpd_tls_security_level">smtpd_tls_security_level</a> = encrypt
- # Obsolete, but still supported
- <a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> = yes
+ <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = TLSv1
+ # Also available with Postfix ≥ 2.5:
+ <a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
</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 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 default parameters chosen by OpenSSL are already
-different from those distributed with other TLS packages. </p>
+<p> If you want to take advantage of ciphers with ephemeral Diffie-Hellman
+(EDH) key exchange (this offers "forward-secrecy"), DH parameters are
+needed. Instead of using the built-in DH parameters for both 1024-bit
+(non-export ciphers) and 512-bit (export ciphers), 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. Postfix defaults to compiled-in parameters
+that are shared by all Postfix users who don't generate their own
+settings. </p>
<p> To generate your own set of DH parameters, use: </p>
<blockquote>
<pre>
-% <b>openssl gendh -out /etc/postfix/dh_1024.pem -2 -rand /var/run/egd-pool 1024</b>
-% <b>openssl gendh -out /etc/postfix/dh_512.pem -2 -rand /var/run/egd-pool 512</b>
+% <b>openssl gendh -out /etc/postfix/dh_512.pem -2 512</b>
+% <b>openssl gendh -out /etc/postfix/dh_1024.pem -2 1024</b>
</pre>
</blockquote>
<li><a href="#client_tls_encrypt"> Mandating TLS encryption </a>
+<li><a href="#client_tls_fprint"> Certificate fingerprint verification </a>
+
<li><a href="#client_tls_verify"> Mandating server certificate verification </a>
<li><a href="#client_tls_secure"> Secure server certificate verification </a>
<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 <a href="smtp.8.html">smtp(8)</a> and <a href="lmtp.8.html">lmtp(8)</a> delivery agents are implemented by a
+single dual-purpose program. Specifically, 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
+<p> The Postfix 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
<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
+<p> Do not configure Postfix SMTP 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>
<p> If you want the Postfix SMTP client to accept remote SMTP server
certificates issued by these CAs, append the root certificate to
-$<a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a> directory. When
-you configure trust in a root CA, it is not necessary to explicitly trust
-intermediary CAs signed by the root CA, unless $<a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a>
-is less than the number of CAs in the certificate chain for the servers
-of interest. With a verify depth of 1 you can only verify certificates
-directly signed by a trusted CA, and all trusted intermediary CAs need to
-be configured explicitly. With a verify depth of 2 you can verify servers
-signed by a root CA or a direct intermediary CA (so long as the server
-is correctly configured to supply its intermediate CA certificate). </p>
+$<a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtp_tls_CApath">smtp_tls_CApath</a> directory. </p>
<p> RSA key and certificate examples: </p>
<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>fingerprint</b></dt>
+<dd><a href="#client_tls_fprint">Certificate fingerprint verification.</a>
<dt><b>verify</b></dt>
<dd><a href="#client_tls_verify">Mandatory server certificate verification.</a>
<dt><b>secure</b></dt>
<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
+the STARTTLS ESMTP feature is supported by the remote SMTP 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
+encryption is always used, mail delivery continues even 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>
</pre>
</blockquote>
+<h3><a name="client_tls_fprint"> Certificate fingerprint verification </a>
+</h3>
+
+<p> Certificate fingerprint verification is available with Postfix 2.5 and
+later. At this security level ("<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = fingerprint"),
+no trusted certificate authorities are used or required. The certificate
+trust chain, expiration date, ... are not checked. Instead, the
+<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> parameter or the "match" attribute
+in the <a href="#client_tls_policy">policy</a> table lists the valid
+"fingerprints" of the remote SMTP server certificate. </p>
+
+<p> If certificate fingerprints are exchanged securely, this is the
+strongest, and least scalable security level. The administrator needs to
+securely collect the fingerprints of the X.509 certificates of each peer
+server, store them into a local file, and update this local file
+whenever the peer server's public certificate
+changes. This may be feasible for an SMTP "VPN" connecting a small
+number of branch offices over the Internet, or for secure connections
+to a central mail hub. It works poorly if the remote SMTP server is
+managed by a
+third party, and its public certificate changes periodically without
+prior coordination with the verifying site. </p>
+
+<p> The digest algorithm used to calculate the fingerprint is
+selected by the <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b> parameter. In the <a
+href="#client_tls_policy">policy</a> table multiple fingerprints can be
+combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits. </p>
+
+<p> Example: fingerprint TLS security with an internal mailhub.
+Two matching fingerprints are listed. The <a href="postconf.5.html#relayhost">relayhost</a> may be multiple
+physical hosts behind a load-balancer, each with its own private/public
+key and self-signed certificate. Alternatively, a single <a href="postconf.5.html#relayhost">relayhost</a> may
+be in the process of switching from one set of private/public keys to
+another, and both keys are trusted just prior to the transition. </p>
+
+<blockquote>
+<pre>
+ <a href="postconf.5.html#relayhost">relayhost</a> = [mailhub.example.com]
+ <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = fingerprint
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+ <a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</pre>
+</blockquote>
+
+<p> Example: Certificate fingerprint verification with selected destinations.
+As in the example above, we show two matching fingerprints: </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
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+</pre>
+</blockquote>
+<blockquote>
+<pre>
+/etc/postfix/tls_policy:
+ example.com fingerprint
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</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 if the server certificate is valid (not
+TLS encrypted sessions if the remote SMTP server certificate is
+valid (not
expired or revoked, and signed by a trusted certificate authority)
-and if the server certificate name matches a known pattern. Mandatory
+and where the server certificate name matches a known pattern.
+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
<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
+certificate extension are used to verify the remote SMTP 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> Example: </p>
-<p> In this example, the client encrypts all traffic to the
+<p> In this example, the Postfix SMTP 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>
<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
+extension are used to verify the remote SMTP 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> 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
+<p> The Postfix SMTP 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
+the hostnames of the SMTP servers for <i>example.com</i>, but these
+hostnames 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
<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. Mail is
-delivered only if remote SMTP server offers STARTTLS and the TLS
-handshake succeeds. 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. </dd>
-
-<dt><b>verify</b></dt> <dd>Mandatory server certificate verification.
-Mail is delivered only if the TLS handshake succeeds, if the server
-certificate can be validated (not expired or revoked, and signed
-by a trusted certificate authority), and if the server certificate
-name matches the optional "match" attribute (or 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 value when no optional "match"
-attribute is specified). </dd>
-
-<dt><b>secure</b></dt> <dd>Secure-channel TLS. Mail is delivered
-only if the TLS handshake succeeds, if the server certificate can
-be validated (not expired or revoked, and signed by a trusted
-certificate authority), and if the server certificate name matches
-the optional "match" attribute (or 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 value when no optional "match" attribute is specified).
-</dd>
+<dt><b>none</b></dt> <dd><a href="#client_tls_none">No TLS</a>. No
+additional attributes are supported at this level. </dd>
+
+<dt><b>may</b></dt> <dd><a href="#client_tls_may">Opportunistic TLS</a>.
+No additional attributes are supported at this level. </dd>
+
+<dt><b>encrypt</b></dt> <dd><a href="#client_tls_encrypt">Mandatory
+encryption</a>. Mail is delivered only if the remote SMTP
+server offers STARTTLS and the TLS handshake succeeds. 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" attribute
+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. </dd>
+
+<dt><b>fingerprint</b></dt> <dd><a href="#client_tls_fprint">Certificate
+fingerprint verification.</a> Available with Postfix 2.5 and
+later. At this security level, there are no trusted certificate
+authorities. The certificate trust chain, expiration date, ... are
+not checked. Instead, the optional <b>match</b> attribute, or else
+the <a href="postconf.5.html">main.cf</a> <b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a></b> parameter,
+lists the valid fingerprints of the server certificate. The
+digest algorithm used to calculate fingerprints is selected by the
+<b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b> parameter. Multiple fingerprints can
+be combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits. </dd>
+
+<dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
+server certificate verification</a>. Mail is delivered only if the
+TLS handshake
+succeeds, if the remote SMTP server certificate can be validated (not
+expired or revoked, and signed by a trusted certificate authority), and
+if the server certificate name matches the optional "match" attribute (or
+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 value when no optional
+"match" attribute is specified). </dd>
+
+<dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
+verification.</a> Mail is delivered only if the TLS handshake succeeds,
+if the remote SMTP server certificate can be validated (not expired
+or revoked, and signed by a trusted certificate authority), and if the
+server certificate name matches the optional "match" attribute (or 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 value when no optional
+"match" attribute is specified). </dd>
</dl>
<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
+ # Postfix 2.5 and later
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
/etc/postfix/tls_policy:
example.edu none
example.mil may
example.net secure
.example.net secure match=.example.net:example.net
[mail.example.org]:587 secure match=nexthop
+ # Postfix 2.5 and later
+ [thumb.example.org] fingerprint
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
</pre>
</blockquote>
<h3><a name="client_vrfy_server">Server certificate verification depth</a> </h3>
-<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 (where the root CA issues
-a special CA certificate which then issues the actual certificate). </p>
-
-<p> Example: </p>
+<p> The server certificate verification depth is specified with the
+<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> parameter. The default verification
+depth is 9 (the OpenSSL default), for compatibility with Postfix
+versions before 2.5 where <a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> was ignored.
+When you configure trust
+in a root CA, it is not necessary to explicitly trust intermediary CAs
+signed by the root CA, unless $<a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> is less than the
+number of CAs in the certificate chain for the servers of interest. With
+a verify depth of 1 you can only verify certificates directly signed
+by a trusted CA, and all trusted intermediary CAs need to be configured
+explicitly. With a verify depth of 2 you can verify servers signed by a
+root CA or a direct intermediary CA (so long as the server is correctly
+configured to supply its intermediate CA certificate). </p>
+<p> Example: </p>
+
<blockquote>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
- <a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> = 5
+ <a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> = 2
</pre>
</blockquote>
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
+disabled when remote SMTP server certificates are verified. If you
+want to
disable anonymous ciphers even at the "encrypt" security level, set
"<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> = aNULL"; and to
disable anonymous ciphers even with opportunistic TLS, set
<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
+ <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = SSLv3, TLSv1
+ # Also available with Postfix ≥ 2.5:
+ <a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2
</pre>
</blockquote>
<a href="postconf.5.html#smtp_tls_CAfile">smtp_tls_CAfile</a> = /etc/postfix/cacert.pem
<a href="postconf.5.html#smtp_tls_session_cache_database">smtp_tls_session_cache_database</a> =
btree:/var/lib/postfix/smtp_tls_session_cache
- <a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> = yes
+ <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
<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
of the <a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> code in terms of enforcement levels, which
simplified the implementation greatly.
+<li> Victor Duchovni implemented the fingerprint security level,
+added more sanity checks, and separated TLS connection management
+from security policy enforcement. The latter change simplified the
+code that verifies certificate signatures, certificate names, and
+certificate fingerprints.
+
</ul>
</body>
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 protocols that the Postfix SMTP client
- will use with mandatory TLS encryption.
+ List of SSL/TLS protocols that the Postfix SMTP
+ client will use with mandatory TLS encryption.
- <b><a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> (5)</b>
+ <b><a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> (9)</b>
The verification depth for remote SMTP server cer-
tificates.
Postfix SMTP client uses for TLS encrypted SMTP
sessions with a verified server certificate.
+ Available in Postfix version 2.5 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> (empty)</b>
+ List of acceptable remote SMTP server certificate
+ fingerprints for the "fingerprint" TLS security
+ level (<b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a></b> = fingerprint).
+
+ <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (md5)</b>
+ The message digest algorithm used to construct
+ remote SMTP server certificate fingerprints.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
- The following configuration parameters exist for compati-
+ 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
+ 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
+ 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>
- With mandatory TLS encryption, require that the
+ With mandatory TLS encryption, 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
+ TLS usage policy by next-hop destination and by
remote SMTP server hostname.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
<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 maximal number of parallel deliveries to the
- same destination via the smtp message delivery
+ The maximal number of parallel deliveries to the
+ same destination via the smtp message delivery
transport.
<b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
- The maximal number of recipients per delivery via
+ The maximal number of recipients per delivery via
the smtp message delivery transport.
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
- The SMTP client time limit for completing a TCP
+ The SMTP client time limit for completing a TCP
connection, or zero (use the operating system
built-in time limit).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- The SMTP client time limit for sending the HELO or
- EHLO command, and for receiving the initial server
+ The SMTP client time limit for sending the HELO or
+ EHLO command, and for receiving the initial server
response.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
- The LMTP client time limit for sending the LHLO
+ The LMTP client time limit for sending the LHLO
command, and for receiving the initial server
response.
command, and for receiving the server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The SMTP client time limit for sending the MAIL
- FROM command, and for receiving the server
+ The SMTP client time limit for sending the MAIL
+ FROM command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The SMTP client time limit for sending the SMTP
- RCPT TO command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ RCPT TO command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The SMTP client time limit for sending the SMTP
- DATA command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ DATA command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
message content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
".", and for receiving the server response.
<b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
- The SMTP client time limit for sending the QUIT
+ The SMTP client time limit for sending the QUIT
command, and for receiving the server response.
Available in Postfix version 2.1 and later:
lookups, or zero (no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery
- request before giving up or delivering to a fall-
+ The maximal number of SMTP sessions per delivery
+ request before giving up or delivering to a fall-
back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
- The SMTP client time limit for sending the RSET
+ The SMTP client time limit for sending the RSET
command, and for receiving the server response.
Available in Postfix version 2.2 and earlier:
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the
+ Permanently enable SMTP connection caching for the
specified destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a
+ Temporarily enable SMTP connection caching while a
destination has a high volume of mail in the active
queue.
<b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
When SMTP connection caching is enabled, the amount
- of time that an unused SMTP client socket is kept
+ of time that an unused SMTP client socket is kept
open before it is closed.
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or
+ Time limit for connection cache connect, send or
receive operations.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a
- remote client or server matches a pattern in the
+ The increment in verbose logging level when a
+ remote client or server matches a pattern in the
<a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname
- or network address patterns that cause the verbose
- logging level to increase by the amount specified
+ Optional list of remote client or server hostname
+ or network address patterns that cause the verbose
+ logging level to increase by the amount specified
in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- The recipient of postmaster notifications about
- mail delivery problems that are caused by policy,
+ The recipient of postmaster notifications about
+ mail delivery problems that are caused by policy,
resource, software or protocol errors.
<b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
- What categories of Postfix-generated mail are sub-
- ject to before-queue content inspection by
+ What categories of Postfix-generated mail are sub-
+ ject to before-queue content inspection by
<a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
- The list of error classes that are reported to the
+ The list of error classes that are reported to the
postmaster.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- Where the Postfix SMTP client should deliver mail
+ Where the Postfix SMTP client should deliver mail
when it detects a "mail loops back to myself" error
condition.
<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#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
- Disable DNS lookups in the Postfix SMTP and LMTP
+ Disable DNS lookups in the Postfix SMTP and LMTP
clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
tem receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (ipv4)</b>
- The Internet protocols Postfix will attempt to use
+ The Internet protocols Postfix will attempt to use
when making or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
over an internal communication channel.
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
- The default TCP port that the Postfix LMTP client
+ The default TCP port that the Postfix LMTP client
connects to.
<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 an incoming connection
+ The maximum amount of time that an idle Postfix
+ daemon process waits for an incoming connection
before terminating voluntarily.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of incoming connections that a
- Postfix daemon process will service before termi-
+ The maximal number of incoming connections that a
+ Postfix daemon process will service before termi-
nating voluntarily.
<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#proxy_interfaces">proxy_interfaces</a> (empty)</b>
The network interface addresses that this mail sys-
- tem receives mail on by way of a proxy or network
+ tem receives mail on by way of a proxy or network
address translation unit.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv4 connection.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv6 connection.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
- The hostname to send in the SMTP EHLO or HELO com-
+ The hostname to send in the SMTP EHLO or HELO com-
mand.
<b><a href="postconf.5.html#lmtp_lhloname">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The hostname to send in the LMTP LHLO command.
<b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
- What mechanisms when the Postfix SMTP client uses
+ What mechanisms when the Postfix SMTP client uses
to look up a host's IP address.
<b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
- Randomize the order of equal-preference MX host
+ Randomize the order of equal-preference MX host
addresses.
<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".
Available with Postfix 2.2 and earlier:
<b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
<b>SEE ALSO</b>
<a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
<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>
SuSE Rhein/Main AG
65760 Eschborn, Germany
- Connection caching in cooperation with:
- Victor Duchovni
- Morgan Stanley
-
TLS support originally by:
Lutz Jaenicke
BTU Cottbus
Universitaetsplatz 3-4
D-03044 Cottbus, Germany
+ Revised TLS and SMTP connection cache support by:
+ Victor Duchovni
+ Morgan Stanley
+
SMTP(8)
</pre> </body> </html>
tor passing) per connection request, and is
accessible to local clients only.
- This feature is not part of the stable Post-
- fix release.
-
- The service name is a pathname relative to
- the Postfix queue directory (pathname con-
- trolled with the <b><a href="postconf.5.html#queue_directory">queue_directory</a></b> configura-
+ The service name is a pathname relative to
+ the Postfix queue directory (pathname con-
+ trolled with the <b><a href="postconf.5.html#queue_directory">queue_directory</a></b> configura-
tion parameter in <a href="postconf.5.html">main.cf</a>).
+ This feature is available as of Postfix ver-
+ sion 2.5.
+
<b>Private (default: y)</b>
Whether or not access is restricted to the mail
system. Internet (type <b>inet</b>) services can't be
parameter. </p>
-</DD>
-
-<DT><b><a name="<i>transport</i>_destination_concurrency_failed_cohort_limit"><i>transport</i>_destination_concurrency_failed_cohort_limit</a>
-(default: $<a href="postconf.5.html#default_destination_concurrency_failed_cohort_limit">default_destination_concurrency_failed_cohort_limit</a>)</b></DT><DD>
-
-<p> A transport-specific override for the
-<a href="postconf.5.html#default_destination_concurrency_failed_cohort_limit">default_destination_concurrency_failed_cohort_limit</a> parameter value,
-where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
-transport. </p>
-
-<p> This feature is available in Postfix 2.5 and later. </p>
-
-
</DD>
<DT><b><a name="access_map_reject_code">access_map_reject_code</a>
</DD>
<DT><b><a name="empty_address_relayhost_maps_lookup_key">empty_address_relayhost_maps_lookup_key</a>
-(default: <>)</b></DT><DD>
+(default: <>)</b></DT><DD>
<p> The <a href="postconf.5.html#sender_dependent_relayhost_maps">sender_dependent_relayhost_maps</a> search string that will be
used instead of the null sender address. </p>
client, for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/<a href="master.5.html">master.cf</a>:
- mylmtp ... lmtp -o <a href="postconf.5.html#lmtp_lhloname">lmtp_lhlo_name</a>=foo.bar.com
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+ mylmtp ... lmtp -o <a href="postconf.5.html#lmtp_lhloname">lmtp_lhlo_name</a>=foo.bar.com
</pre>
+</blockquote>
<p>
This feature is available in Postfix 2.3 and later.
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="lmtp_tls_fingerprint_cert_match">lmtp_tls_fingerprint_cert_match</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="lmtp_tls_fingerprint_digest">lmtp_tls_fingerprint_digest</a>
+(default: md5)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_tls_key_file">lmtp_tls_key_file</a>
</DD>
<DT><b><a name="lmtp_tls_scert_verifydepth">lmtp_tls_scert_verifydepth</a>
-(default: 5)</b></DT><DD>
+(default: 9)</b></DT><DD>
<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</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_security_level">lmtp_tls_security_level</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</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>
<dt><b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> </b></dt>
<dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
-client TLS certificate is successfully verified, and the client
-certificate fingerprint is listed in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>. </dd>
+client TLS certificate fingerprint is listed in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>.
+The fingerprint digest algorithm is configurable via the
+<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter (hard-coded as md5 prior to
+Postfix version 2.5). </dd>
<dt><b><a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </b></dt>
message headers, and always append my own domain to incomplete
header addresses. </p>
+<blockquote>
<pre>
- <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all
</pre>
+</blockquote>
<p> The purist (and default) setting: rewrite headers only in mail
from Postfix sendmail and in SMTP mail from this machine. </p>
+<blockquote>
<pre>
- <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="postconf.5.html#permit_inet_interfaces">permit_inet_interfaces</a>
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="postconf.5.html#permit_inet_interfaces">permit_inet_interfaces</a>
</pre>
+</blockquote>
<p> The intermediate setting: rewrite header addresses and append
$<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> information only with mail from Postfix
rewriting when mail from a remote client is forwarded by a neighboring
system. </p>
+<blockquote>
<pre>
- <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a>,
- <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a> <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a>
- <a href="postconf.5.html#check_address_map">check_address_map</a> hash:/etc/postfix/pop-before-smtp
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a>,
+ <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a> <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a>
+ <a href="postconf.5.html#check_address_map">check_address_map</a> hash:/etc/postfix/pop-before-smtp
</pre>
+</blockquote>
</DD>
first match. Thus,
</p>
+<blockquote>
<pre>
- <a href="postconf.5.html#masquerade_domains">masquerade_domains</a> = foo.example.com example.com
+<a href="postconf.5.html#masquerade_domains">masquerade_domains</a> = foo.example.com example.com
</pre>
+</blockquote>
<p>
strips "user@any.thing.foo.example.com" to "user@foo.example.com",
or its subdomains. Thus,
</p>
+<blockquote>
<pre>
- <a href="postconf.5.html#masquerade_domains">masquerade_domains</a> = !foo.example.com example.com
+<a href="postconf.5.html#masquerade_domains">masquerade_domains</a> = !foo.example.com example.com
</pre>
+</blockquote>
<p>
does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
<DT><b><a name="relay_clientcerts">relay_clientcerts</a>
(default: empty)</b></DT><DD>
-<p> The list of remote SMTP client certificates for which the
-Postfix SMTP server will allow access with the <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a>
-feature. This feature does not use certificate names, because
-Postfix list manipulation routines treat whitespace and some other
-characters as special. Instead we use certificate fingerprints as
-they are difficult to fake but easy to use for lookup. </p>
+<p> List of tables with remote SMTP client-certificate fingerprints
+for which the Postfix SMTP server will allow access with the
+<a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> feature.
+The fingerprint digest algorithm is configurable via the
+<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter (hard-coded as md5 prior to
+Postfix version 2.5). </p>
<p> Postfix lookup tables are in the form of (key, value) pairs.
Since we only need the key, the value can be chosen freely, e.g.
addresses from remote SMTP clients, so that those addresses cannot
be confused with local addresses. </p>
+<blockquote>
<pre>
- <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> = domain.invalid
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> = domain.invalid
</pre>
+</blockquote>
<p> The default, purist, setting: don't rewrite headers from remote
clients at all. </p>
+<blockquote>
<pre>
- <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> =
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> =
</pre>
+</blockquote>
</DD>
for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/<a href="master.5.html">master.cf</a>:
- smtp ... smtp -o <a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a>=11.22.33.44
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+ smtp ... smtp -o <a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a>=11.22.33.44
</pre>
+</blockquote>
<p> Note 1: when <a href="postconf.5.html#inet_interfaces">inet_interfaces</a> specifies no more than one IPv4
address, and that address is a non-loopback address, it is
for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/<a href="master.5.html">master.cf</a>:
- smtp ... smtp -o <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a>=1:2:3:4:5:6:7:8
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+ smtp ... smtp -o <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a>=1:2:3:4:5:6:7:8
</pre>
+</blockquote>
<p> Note 1: when <a href="postconf.5.html#inet_interfaces">inet_interfaces</a> specifies no more than one IPv6
address, and that address is a non-loopback address, it is
client, for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/<a href="master.5.html">master.cf</a>:
- mysmtp ... smtp -o <a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a>=foo.bar.com
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+ mysmtp ... smtp -o <a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a>=foo.bar.com
</pre>
+</blockquote>
<p>
This feature is available in Postfix 2.0 and later.
a broken SMTP server, configure a special SMTP client in <a href="master.5.html">master.cf</a>:
</p>
+<blockquote>
<pre>
- /etc/postfix/<a href="master.5.html">master.cf</a>:
- broken-smtp . . . smtp -o <a href="postconf.5.html#smtp_quote_rfc821_envelope">smtp_quote_rfc821_envelope</a>=no
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+ broken-smtp . . . smtp -o <a href="postconf.5.html#smtp_quote_rfc821_envelope">smtp_quote_rfc821_envelope</a>=no
</pre>
+</blockquote>
<p>
and route mail for the destination in question to the "broken-smtp"
<p> The SASL authentication security options that the Postfix SMTP
client uses for TLS encrypted SMTP sessions with a verified server
-certificate. This feature is still under construction. It will not be
-included in the Postfix 2.3 release. </p>
-
-<p> This feature should be available in Postfix 2.4 and later. </p>
+certificate. This feature is under construction as of Postfix version
+2.3. </p>
</DD>
<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> =
+<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> Examples (some of these will cause problems): </p>
+<blockquote>
<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> = AES256-SHA, DES-CBC3-MD5
<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a> = kEDH+aRSA
</pre>
+</blockquote>
<p> The first setting, disables anonymous ciphers. The next setting
disables ciphers that use the MD5 digest algorithm or the (single) DES
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a>
+(default: empty)</b></DT><DD>
+
+<p> List of acceptable remote SMTP server certificate fingerprints
+for the "fingerprint" TLS security level (<b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a></b> =
+fingerprint). At this security level, certificate authorities are
+not used, and certificate expiration times are ignored. Instead,
+server certificates are verified directly via their "fingerprint". The
+fingerprint is a message digest of the server certificate. The digest
+algorithm is selected via the <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b>
+parameter. </p>
+
+<p> When an <b><a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a></b> table entry specifies the
+"fingerprint" security level, any "match" attributes in that entry specify
+the list of valid fingerprints for the corresponding destination. Multiple
+fingerprints can be combined with a "|" delimiter in a single match
+attribute, or multiple match attributes can be employed. </p>
+
+<p> Example: Certificate fingerprint verification with internal mailhub.
+Two matching fingerprints are listed. The <a href="postconf.5.html#relayhost">relayhost</a> may be multiple
+physical hosts behind a load-balancer, each with its own private/public
+key and self-signed certificate. Alternatively, a single <a href="postconf.5.html#relayhost">relayhost</a> may
+be in the process of switching from one set of private/public keys to
+another, and both keys are trusted just prior to the transition. </p>
+
+<blockquote>
+<pre>
+<a href="postconf.5.html#relayhost">relayhost</a> = [mailhub.example.com]
+<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = fingerprint
+<a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</pre>
+</blockquote>
+
+<p> Example: Certificate fingerprint verification with selected destinations.
+As in the example above, we show two matching fingerprints: </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
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
+</pre>
+<pre>
+/etc/postfix/tls_policy:
+ example.com fingerprint
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a>
+(default: md5)</b></DT><DD>
+
+<p> The message digest algorithm used to construct remote SMTP server
+certificate fingerprints. At the "fingerprint" TLS security level
+(<b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a></b> = fingerprint), the server certificate is
+verified by directly matching its <i>fingerprint</i>. The fingerprint
+is the message digest of the server certificate using the selected
+algorithm. With a digest algorithm resistant to "second pre-image"
+attacks, it is not feasible to create a new public key and a matching
+certificate that has the same fingerprint. </p>
+
+<p> The default algorithm is <b>md5</b>; this is consistent with
+the backwards compatible setting of the digest used to verify client
+certificates in the SMTP server. </p>
+
+<p> The best practice algorithm is now <b>sha1</b>. Recent advances in hash
+function cryptanalysis have led to md5 being deprecated in favor of sha1.
+However, as long as there are no known "second pre-image" attacks
+against md5, its use in this context can still be considered safe.
+</p>
+
+<p> While additional digest algorithms are often available with OpenSSL's
+libcrypto, only those used by libssl in SSL cipher suites are available to
+Postfix. For now this means just md5 or sha1. </p>
+
+<p> To find the fingerprint of a specific certificate file, with a
+specific digest algorithm, run:
+</p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -<i>digest</i> -in <i>certfile</i>.pem
+</pre>
+</blockquote>
+
+<p> The text to the right of "=" sign is the desired fingerprint.
+For example: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
+SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_tls_key_file">smtp_tls_key_file</a>
<DT><b><a name="smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a>
(default: SSLv3, TLSv1)</b></DT><DD>
-<p> List of TLS protocols that the Postfix SMTP client will use
-with mandatory TLS encryption. In <a href="postconf.5.html">main.cf</a> the values
-are separated by whitespace, commas or colons. In the policy table
+<p> List of SSL/TLS protocols that the Postfix SMTP client will use with
+mandatory TLS encryption. In <a href="postconf.5.html">main.cf</a> the values are separated by
+whitespace, commas or colons. In the policy table "protocols" attribute
(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>
+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> With Postfix ≥ 2.5 the parameter syntax is expanded to support
+protocol exclusions. One can now explicitly exclude SSLv2 by setting
+"<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2". To exclude both SSLv2 and
+SSLv3 set "<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is still
+supported; use the form you find more intuitive. </p>
+
+<p> Since SSL version 2 has known protocol weaknesses and is now
+deprecated, the default setting excludes "SSLv2". 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> Example: </p>
+
+<pre>
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = TLSv1
+# Alternative form with Postfix ≥ 2.5:
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
+</pre>
+
<p> This feature is available in Postfix 2.3 and later. </p>
In the policy table, multiple protocols must be separated by colons,
as attribute values may not contain whitespace or commas. </dd>
+<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
+verification. Available with Postfix 2.5 and later. At this security
+level, there are no trusted certificate authorities. The certificate
+trust chain, expiration date, ... are not checked. Instead,
+the optional <b>match</b> attribute, or else the <a href="postconf.5.html">main.cf</a>
+<b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a></b> parameter, lists the
+valid "fingerprints" of the server certificate. The digest
+algorithm used to calculate the fingerprint is selected by the
+<b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b> parameter. Multiple fingerprints can
+be combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits. </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
</p>
<pre>
-<a href="postconf.5.html">main.cf</a>:
+/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
+ # Postfix 2.5 and later
+ <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> = md5
</pre>
+
<pre>
-tls_policy:
+/etc/postfix/tls_policy:
example.edu none
example.mil may
example.gov encrypt protocols=TLSv1
example.net secure
.example.net secure match=.example.net:example.net
[mail.example.org]:587 secure match=nexthop
+ # Postfix 2.5 and later
+ [thumb.example.org] fingerprint
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
</pre>
<p> <b>Note:</b> The <b>hostname</b> strategy if listed in a non-default
</DD>
<DT><b><a name="smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a>
-(default: 5)</b></DT><DD>
+(default: 9)</b></DT><DD>
+
+<p> The verification depth for remote SMTP server certificates. A depth
+of 1 is sufficient if the issuing CA is listed in a local CA file. </p>
-<p> 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...). </p>
+<p> The default verification depth is 9 (the OpenSSL default) for
+compatibility with earlier Postfix behavior. Prior to Postfix 2.5,
+the default value was 5, but the limit was not actually enforced. If
+you have set this to a lower non-default value, certificates with longer
+trust chains may now fail to verify. Certificate chains with 1 or 2
+CAs are common, deeper chains are more rare and any number between 5
+and 9 should suffice in practice. You can choose a lower number if,
+for example, you trust certificates directly signed by an issuing CA
+but not any CAs it delegates to. </p>
<p> This feature is available in Postfix 2.2 and later. </p>
Sample <a href="postconf.5.html">main.cf</a> setting:
</p>
+<blockquote>
<pre>
<a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> = nexthop
</pre>
+</blockquote>
<p>
Sample policy table override:
</p>
+<blockquote>
<pre>
example.net secure match=example.com:.example.com
.example.net secure match=example.com:.example.com
</pre>
+</blockquote>
<p> This feature is available in Postfix 2.3 and later. </p>
mandatory encrypted sessions. This security level is not an appropriate
default for systems delivering mail to the Internet. </dd>
+<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
+verification. Available with Postfix 2.5 and later. At this security
+level, there are no trusted certificate authorities. The certificate
+trust chain, expiration date, ... are not checked. Instead,
+the <b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a></b> parameter lists
+the valid "fingerprints" of the server certificate. The digest
+algorithm used to calculate the fingerprint is selected by the
+<b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a></b> parameter. </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
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
+# No TLS. Formerly: <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.
+<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
+# Opportunistic TLS.
+<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
+# Mandatory (high-grade) TLS encryption.
+<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
+# Mandatory TLS verification of hostname or nexthop domain.
+<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>
+
+<pre>
+# Secure channel TLS with exact nexthop name match.
+<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>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
+# Certificate fingerprint verification (Postfix ≥ 2.5).
+# The CA-less "fingerprint" security level only scales to a limited
+# number of destinations. As a global default rather than a per-site
+# setting, this is practical when mail for all recipients is sent
+# to a central mail hub.
+<a href="postconf.5.html#relayhost">relayhost</a> = [mailhub.example.com]
+<a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = fingerprint
+<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
+<a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> = high
+<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
</pre>
<p> This feature is available in Postfix 2.3 and later. </p>
<dt><b><a name="check_ccert_access">check_ccert_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>
<dd> Use the client certificate fingerprint as lookup key for the
-specified <a href="access.5.html">access(5)</a> database; with Postfix version 2.2, also require
-that the SMTP client certificate is verified successfully. This
-feature is available with Postfix version 2.2 and later.</dd>
+specified <a href="access.5.html">access(5)</a> database; with Postfix version 2.2, also require that
+the SMTP client certificate is verified successfully.
+The fingerprint digest algorithm is configurable via the
+<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter (hard-coded as md5 prior to
+Postfix version 2.5). This feature is available with Postfix version
+2.2 and later. </dd>
<dt><b><a name="check_client_access">check_client_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>
<dt><b><a name="permit_tls_clientcerts">permit_tls_clientcerts</a></b></dt>
-<dd>Permit the request when the remote SMTP client certificate is
-verified successfully, and the certificate fingerprint is listed
-in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>. This feature is available with Postfix version 2.2.</dd>
+<dd>Permit the request when the remote SMTP client certificate
+fingerprint is listed in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>.
+The fingerprint digest algorithm is configurable via the
+<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter (hard-coded as md5 prior to
+Postfix version 2.5). This feature is available with Postfix version
+2.2. </dd>
+
<dt><b><a name="reject_rbl_client">reject_rbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
<dd>Reject the request when the reversed client network address is
<dd>Pause for the specified number of seconds and proceed with
the next restriction in the list, if any. This may stop zombie
mail when used as:
-
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> =
refuse to receive mail:
</p>
+<blockquote>
<pre>
- reject, defer, <a href="postconf.5.html#defer_if_permit">defer_if_permit</a>, <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
+reject, defer, <a href="postconf.5.html#defer_if_permit">defer_if_permit</a>, <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
</pre>
+</blockquote>
<p>
Specify a list of restrictions, separated by commas and/or whitespace.
access restriction can be used to permit relay access, like this:
</p>
+<blockquote>
<pre>
- <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
- <a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a>, <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a>, ...
+<a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
+ <a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a>, <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a>, ...
</pre>
+</blockquote>
<p> To reject all SMTP connections from unauthenticated clients,
specify "<a href="postconf.5.html#smtpd_delay_reject">smtpd_delay_reject</a> = yes" (which is the default) and use:
</p>
+<blockquote>
<pre>
- <a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> = <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a>, reject
+<a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> = <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a>, reject
</pre>
+</blockquote>
<p>
See the <a href="SASL_README.html">SASL_README</a> file for SASL configuration and operation details.
<p> Example: </p>
-<blockquote>
<pre>
- <a href="postconf.5.html#smtpd_tls_always_issue_session_ids">smtpd_tls_always_issue_session_ids</a> = no
+<a href="postconf.5.html#smtpd_tls_always_issue_session_ids">smtpd_tls_always_issue_session_ids</a> = no
</pre>
-</blockquote>
<p> This feature is available in Postfix 2.3 and later. </p>
</DD>
<DT><b><a name="smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a>
-(default: 5)</b></DT><DD>
+(default: 9)</b></DT><DD>
<p> 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...).
-</p>
+file. </p>
+
+<p> The default verification depth is 9 (the OpenSSL default) for
+compatibility with earlier Postfix behavior. Prior to Postfix 2.5,
+the default value was 5, but the limit was not actually enforced. If
+you have set this to a lower non-default value, certificates with longer
+trust chains may now fail to verify. Certificate chains with 1 or 2
+CAs are common, deeper chains are more rare and any number between 5
+and 9 should suffice in practice. You can choose a lower number if,
+for example, you trust certificates directly signed by an issuing CA
+but not any CAs it delegates to. </p>
<p> This feature is available in Postfix 2.2 and later. </p>
(default: empty)</b></DT><DD>
<p> File with the Postfix SMTP server DSA certificate in PEM format.
-This file may also contain the Postfix SMTP server private key. <p>
+This file may also contain the Postfix SMTP server private DSA key. </p>
<p> See the discussion under <a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> for more details.
</p>
with other TLS packages, it is more secure to generate your own
set of parameters with something like the following command: </p>
+<blockquote>
<pre>
-openssl gendh -out /etc/postfix/dh_1024.pem -2 -rand /var/run/egd-pool 1024
+openssl gendh -out /etc/postfix/dh_1024.pem -2 1024
</pre>
+</blockquote>
<p> Your actual source for entropy may differ. Some systems have
/dev/random; on other system you may consider using the "Entropy
<p> Examples (some of these will cause problems): </p>
+<blockquote>
<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> = AES256-SHA, DES-CBC3-MD5
<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> = kEDH+aRSA
</pre>
+</blockquote>
<p> The first setting disables anonymous ciphers. The next setting
disables ciphers that use the MD5 digest algorithm or the (single) DES
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>
+(default: md5)</b></DT><DD>
+
+<p> The message digest algorithm used to construct client-certificate
+fingerprints for <b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and
+<b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a></b>. The default algorithm is <b>md5</b>,
+for backwards compatibility with Postfix releases prior to 2.5.
+</p>
+
+<p> The best practice algorithm is now <b>sha1</b>. Recent advances in hash
+function cryptanalysis have led to md5 being deprecated in favor of sha1.
+However, as long as there are no known "second pre-image" attacks
+against md5, its use in this context can still be considered safe.
+</p>
+
+<p> While additional digest algorithms are often available with OpenSSL's
+libcrypto, only those used by libssl in SSL cipher suites are available to
+Postfix. For now this means just md5 or sha1. </p>
+
+<p> To find the fingerprint of a specific certificate file, with a
+specific digest algorithm, run: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -<i>digest</i> -in <i>certfile</i>.pem
+</pre>
+</blockquote>
+
+<p> The text to the right of "=" sign is the desired fingerprint.
+For example: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
+SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+</pre>
+</blockquote>
+
+<p> Example: client-certificate access table, with sha1 fingerprints: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> = sha1
+ <a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> =
+ <a href="postconf.5.html#check_ccert_access">check_ccert_access</a> hash:/etc/postfix/access,
+ reject
+</pre>
+<pre>
+/etc/postfix/access:
+ # Action folded to next line...
+ AF:88:7C:AD:51:95:6F:36:96:F6:01:FB:2E:48:CD:AB:49:25:A2:3B
+ OK
+ 85:16:78:FD:73:6E:CE:70:E0:31:5F:0D:3C:C8:6D:C4:2C:24:59:E1
+ <a href="postconf.5.html#permit_auth_destination">permit_auth_destination</a>
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
</DD>
<DT><b><a name="smtpd_tls_key_file">smtpd_tls_key_file</a>
(default: $<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>)</b></DT><DD>
<p> File with the Postfix SMTP server RSA private key in PEM format.
-This file may be combined with the Postfix SMTP server certificate
-file specified
-with $<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>. </p>
+This file may be combined with the Postfix SMTP server RSA certificate
+file specified with $<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>. </p>
<p> The private key must be accessible without a pass-phrase, i.e. it
must not be encrypted, but file permissions should grant read/write
<DT><b><a name="smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a>
(default: SSLv3, TLSv1)</b></DT><DD>
-<p> The TLS protocols accepted by the Postfix SMTP server with
-mandatory TLS encryption. With opportunistic TLS encryption, all
-protocols are always accepted. If the list is empty, the server
-supports all available TLS protocol versions. A non-empty value
-is a list of protocol names separated by whitespace, commas or
-colons. The supported protocol names are "SSLv2", "SSLv3" and
-"TLSv1", and are not case sensitive. </p>
+<p> The SSL/TLS protocols accepted by the Postfix SMTP server with
+mandatory TLS encryption. If the list is empty, the server supports all
+available SSL/TLS protocol versions. A non-empty value is a list
+of protocol
+names separated by whitespace, commas or colons. The supported protocol
+names are "SSLv2", "SSLv3" and "TLSv1", and are not case sensitive. </p>
+
+<p> With Postfix ≥ 2.5 the parameter syntax is expanded to support
+protocol exclusions. One can now explicitly exclude SSLv2 by setting
+"<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2". To exclude both SSLv2 and
+SSLv3 set "<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is still
+supported, use the form you find more intuitive. </p>
+
+<p> Since SSL version 2 has known protocol weaknesses and is now
+deprecated, the default setting excludes "SSLv2". This means that
+by default, SSL version 2 will not be used at the "encrypt" security
+level. </p>
<p> Example: </p>
<pre>
-<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = SSLv3, TLSv1
+<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = TLSv1
+# Alternative form with Postfix ≥ 2.5:
+<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> = !SSLv2, !SSLv3
</pre>
<p> This feature is available in Postfix 2.3 and later. </p>
<DT><b><a name="smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>
(default: no)</b></DT><DD>
-<p> With mandatory TLS encryption, require a remote SMTP client
+<p> With mandatory TLS encryption, require a trusted remote SMTP client
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>
</dl>
-<p> Note 1: the "verify" and "secure" levels are not supported.
+<p> Note 1: the "fingerprint", "verify" and "secure" levels are not
+supported here.
The Postfix SMTP server logs a warning and uses "encrypt" instead.
To verify SMTP client certificates, see <a href="TLS_README.html">TLS_README</a> for a discussion
of the <a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a>, <a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>, and <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a>
the message delivery transport. </p>
+</DD>
+
+<DT><b><a name="transport_destination_concurrency_failed_cohort_limit">transport_destination_concurrency_failed_cohort_limit</a>
+(default: $<a href="postconf.5.html#default_destination_concurrency_failed_cohort_limit">default_destination_concurrency_failed_cohort_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the
+<a href="postconf.5.html#default_destination_concurrency_failed_cohort_limit">default_destination_concurrency_failed_cohort_limit</a> parameter value,
+where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
</DD>
<DT><b><a name="transport_destination_concurrency_limit">transport_destination_concurrency_limit</a>
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 protocols that the Postfix SMTP client
- will use with mandatory TLS encryption.
+ List of SSL/TLS protocols that the Postfix SMTP
+ client will use with mandatory TLS encryption.
- <b><a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> (5)</b>
+ <b><a href="postconf.5.html#smtp_tls_scert_verifydepth">smtp_tls_scert_verifydepth</a> (9)</b>
The verification depth for remote SMTP server cer-
tificates.
Postfix SMTP client uses for TLS encrypted SMTP
sessions with a verified server certificate.
+ Available in Postfix version 2.5 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_fingerprint_cert_match">smtp_tls_fingerprint_cert_match</a> (empty)</b>
+ List of acceptable remote SMTP server certificate
+ fingerprints for the "fingerprint" TLS security
+ level (<b><a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a></b> = fingerprint).
+
+ <b><a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (md5)</b>
+ The message digest algorithm used to construct
+ remote SMTP server certificate fingerprints.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
- The following configuration parameters exist for compati-
+ 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
+ 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
+ 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>
- With mandatory TLS encryption, require that the
+ With mandatory TLS encryption, 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
+ TLS usage policy by next-hop destination and by
remote SMTP server hostname.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
<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 maximal number of parallel deliveries to the
- same destination via the smtp message delivery
+ The maximal number of parallel deliveries to the
+ same destination via the smtp message delivery
transport.
<b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
- The maximal number of recipients per delivery via
+ The maximal number of recipients per delivery via
the smtp message delivery transport.
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
- The SMTP client time limit for completing a TCP
+ The SMTP client time limit for completing a TCP
connection, or zero (use the operating system
built-in time limit).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- The SMTP client time limit for sending the HELO or
- EHLO command, and for receiving the initial server
+ The SMTP client time limit for sending the HELO or
+ EHLO command, and for receiving the initial server
response.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
- The LMTP client time limit for sending the LHLO
+ The LMTP client time limit for sending the LHLO
command, and for receiving the initial server
response.
command, and for receiving the server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The SMTP client time limit for sending the MAIL
- FROM command, and for receiving the server
+ The SMTP client time limit for sending the MAIL
+ FROM command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The SMTP client time limit for sending the SMTP
- RCPT TO command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ RCPT TO command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The SMTP client time limit for sending the SMTP
- DATA command, and for receiving the server
+ The SMTP client time limit for sending the SMTP
+ DATA command, and for receiving the server
response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
message content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
- The SMTP client time limit for sending the SMTP
+ The SMTP client time limit for sending the SMTP
".", and for receiving the server response.
<b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
- The SMTP client time limit for sending the QUIT
+ The SMTP client time limit for sending the QUIT
command, and for receiving the server response.
Available in Postfix version 2.1 and later:
lookups, or zero (no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery
- request before giving up or delivering to a fall-
+ The maximal number of SMTP sessions per delivery
+ request before giving up or delivering to a fall-
back <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
- The SMTP client time limit for sending the RSET
+ The SMTP client time limit for sending the RSET
command, and for receiving the server response.
Available in Postfix version 2.2 and earlier:
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the
+ Permanently enable SMTP connection caching for the
specified destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a
+ Temporarily enable SMTP connection caching while a
destination has a high volume of mail in the active
queue.
<b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
When SMTP connection caching is enabled, the amount
- of time that an unused SMTP client socket is kept
+ of time that an unused SMTP client socket is kept
open before it is closed.
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or
+ Time limit for connection cache connect, send or
receive operations.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- The increment in verbose logging level when a
- remote client or server matches a pattern in the
+ The increment in verbose logging level when a
+ remote client or server matches a pattern in the
<a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- Optional list of remote client or server hostname
- or network address patterns that cause the verbose
- logging level to increase by the amount specified
+ Optional list of remote client or server hostname
+ or network address patterns that cause the verbose
+ logging level to increase by the amount specified
in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- The recipient of postmaster notifications about
- mail delivery problems that are caused by policy,
+ The recipient of postmaster notifications about
+ mail delivery problems that are caused by policy,
resource, software or protocol errors.
<b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
- What categories of Postfix-generated mail are sub-
- ject to before-queue content inspection by
+ What categories of Postfix-generated mail are sub-
+ ject to before-queue content inspection by
<a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
- The list of error classes that are reported to the
+ The list of error classes that are reported to the
postmaster.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- Where the Postfix SMTP client should deliver mail
+ Where the Postfix SMTP client should deliver mail
when it detects a "mail loops back to myself" error
condition.
<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#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
- Disable DNS lookups in the Postfix SMTP and LMTP
+ Disable DNS lookups in the Postfix SMTP and LMTP
clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
tem receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (ipv4)</b>
- The Internet protocols Postfix will attempt to use
+ The Internet protocols Postfix will attempt to use
when making or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
over an internal communication channel.
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
- The default TCP port that the Postfix LMTP client
+ The default TCP port that the Postfix LMTP client
connects to.
<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 an incoming connection
+ The maximum amount of time that an idle Postfix
+ daemon process waits for an incoming connection
before terminating voluntarily.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of incoming connections that a
- Postfix daemon process will service before termi-
+ The maximal number of incoming connections that a
+ Postfix daemon process will service before termi-
nating voluntarily.
<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#proxy_interfaces">proxy_interfaces</a> (empty)</b>
The network interface addresses that this mail sys-
- tem receives mail on by way of a proxy or network
+ tem receives mail on by way of a proxy or network
address translation unit.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv4 connection.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- An optional numerical network address that the
- Postfix SMTP client should bind to when making an
+ An optional numerical network address that the
+ Postfix SMTP client should bind to when making an
IPv6 connection.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
- The hostname to send in the SMTP EHLO or HELO com-
+ The hostname to send in the SMTP EHLO or HELO com-
mand.
<b><a href="postconf.5.html#lmtp_lhloname">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The hostname to send in the LMTP LHLO command.
<b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
- What mechanisms when the Postfix SMTP client uses
+ What mechanisms when the Postfix SMTP client uses
to look up a host's IP address.
<b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
- Randomize the order of equal-preference MX host
+ Randomize the order of equal-preference MX host
addresses.
<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".
Available with Postfix 2.2 and earlier:
<b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay hosts for SMTP destinations
+ Optional list of relay hosts for SMTP destinations
that can't be found or that are unreachable.
<b>SEE ALSO</b>
<a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
<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>
SuSE Rhein/Main AG
65760 Eschborn, Germany
- Connection caching in cooperation with:
- Victor Duchovni
- Morgan Stanley
-
TLS support originally by:
Lutz Jaenicke
BTU Cottbus
Universitaetsplatz 3-4
D-03044 Cottbus, Germany
+ Revised TLS and SMTP connection cache support by:
+ Victor Duchovni
+ Morgan Stanley
+
SMTP(8)
</pre> </body> </html>
server, do not announce or accept SASL authentica-
tion over unencrypted connections.
- <b><a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> (5)</b>
+ <b><a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> (9)</b>
The verification depth for remote SMTP client cer-
tificates.
tory TLS security levels.
<b><a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> (SSLv3, TLSv1)</b>
- The TLS protocols accepted by the Postfix SMTP
+ The SSL/TLS protocols accepted by the Postfix SMTP
server with mandatory TLS encryption.
<b><a href="postconf.5.html#smtpd_tls_received_header">smtpd_tls_received_header</a> (no)</b>
CommonName.
<b><a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a> (no)</b>
- With mandatory TLS encryption, require a remote
- SMTP client certificate in order to allow TLS con-
- nections to proceed.
+ With mandatory TLS encryption, require a trusted
+ remote SMTP client certificate in order to allow
+ TLS connections to proceed.
<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
The OpenSSL cipherlist for "NULL" grade ciphers
that provide authentication without encryption.
+ Available in Postfix version 2.5 and later:
+
+ <b><a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> (md5)</b>
+ The message digest algorithm used to construct
+ client-certificate fingerprints for
+ <b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and <b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a></b>.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
The following configuration parameters exist for compati-
bility with Postfix versions before 2.3. Support for these
Universitaetsplatz 3-4
D-03044 Cottbus, Germany
+ Revised TLS support by:
+ Victor Duchovni
+ Morgan Stanley
+
SMTPD(8)
</pre> </body> </html>
open connection (file descriptor passing) per connection
request, and is accessible to local clients only.
-This feature is not part of the stable Postfix release.
-
The service name is a pathname relative to the Postfix
queue directory (pathname controlled with the \fBqueue_directory\fR
configuration parameter in main.cf).
+
+This feature is available as of Postfix version 2.5.
.RE
.IP "\fBPrivate (default: y)\fR"
Whether or not access is restricted to the mail system.
The recipient of undeliverable mail that cannot be returned to
the sender. This feature is enabled with the notify_classes
parameter.
-<DT>\fB\fItransport\fR_destination_concurrency_failed_cohort_limit
-(default: $default_destination_concurrency_failed_cohort_limit)\fR</DT><DD>
-.PP
-A transport-specific override for the
-default_destination_concurrency_failed_cohort_limit parameter value,
-where \fItransport\fR is the master.cf name of the message delivery
-transport.
-.PP
-This feature is available in Postfix 2.5 and later.
.SH access_map_reject_code (default: 554)
The numerical Postfix SMTP server response code when a client
is rejected by an \fBaccess\fR(5) map restriction.
Specify a list of network/netmask patterns, separated by commas
and/or whitespace. The mask specifies the number of bits in the
network part of a host address. You can also specify hostnames or
-\e&.domain names (the initial dot causes the domain to match any name
+\&.domain names (the initial dot causes the domain to match any name
below it), "/file/name" or "type:table" patterns. A "/file/name"
pattern is replaced by its contents; a "type:table" lookup table
is matched when a table entry matches a lookup string (the lookup
This information can be specified in the main.cf file for all LMTP
clients, or it can be specified in the master.cf file for a specific
client, for example:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- /etc/postfix/master.cf:
- mylmtp ... lmtp -o lmtp_lhlo_name=foo.bar.com
+/etc/postfix/master.cf:
+ mylmtp ... lmtp -o lmtp_lhlo_name=foo.bar.com
.fi
.ad
.ft R
+.in -4
.PP
This feature is available in Postfix 2.3 and later.
.SH lmtp_lhlo_timeout (default: 300s)
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_fingerprint_cert_match (default: empty)
+The LMTP-specific version of the smtp_tls_fingerprint_cert_match
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.5 and later.
+.SH lmtp_tls_fingerprint_digest (default: md5)
+The LMTP-specific version of the smtp_tls_fingerprint_digest
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix 2.5 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.
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
-.SH lmtp_tls_scert_verifydepth (default: 5)
+.SH lmtp_tls_scert_verifydepth (default: 9)
The LMTP-specific version of the smtp_tls_scert_verifydepth
configuration parameter. See there for details.
.PP
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_security_level (default: empty)
+The LMTP-specific version of the smtp_tls_security_level 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.
protocol.
.IP "\fBpermit_tls_clientcerts \fR"
Append the domain name in $myorigin or $mydomain when the
-client TLS certificate is successfully verified, and the client
-certificate fingerprint is listed in $relay_clientcerts.
+client TLS certificate fingerprint is listed in $relay_clientcerts.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5).
.IP "\fBpermit_tls_all_clientcerts \fR"
Append the domain name in $myorigin or $mydomain when the
client TLS certificate is successfully verified, regardless of
The Postfix < 2.2 backwards compatible setting: always rewrite
message headers, and always append my own domain to incomplete
header addresses.
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- local_header_rewrite_clients = static:all
+local_header_rewrite_clients = static:all
.fi
.ad
.ft R
+.in -4
.PP
The purist (and default) setting: rewrite headers only in mail
from Postfix sendmail and in SMTP mail from this machine.
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- local_header_rewrite_clients = permit_inet_interfaces
+local_header_rewrite_clients = permit_inet_interfaces
.fi
.ad
.ft R
+.in -4
.PP
The intermediate setting: rewrite header addresses and append
$myorigin or $mydomain information only with mail from Postfix
Note: this setting will not prevent remote mail header address
rewriting when mail from a remote client is forwarded by a neighboring
system.
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- local_header_rewrite_clients = permit_mynetworks,
- permit_sasl_authenticated permit_tls_clientcerts
- check_address_map hash:/etc/postfix/pop-before-smtp
+local_header_rewrite_clients = permit_mynetworks,
+ permit_sasl_authenticated permit_tls_clientcerts
+ check_address_map hash:/etc/postfix/pop-before-smtp
.fi
.ad
.ft R
+.in -4
.SH local_recipient_maps (default: proxy:unix:passwd.byname $alias_maps)
Lookup tables with all names or addresses of local recipients:
a recipient address is local when its domain matches $mydestination,
.PP
The list is processed left to right, and processing stops at the
first match. Thus,
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- masquerade_domains = foo.example.com example.com
+masquerade_domains = foo.example.com example.com
.fi
.ad
.ft R
+.in -4
.PP
strips "user@any.thing.foo.example.com" to "user@foo.example.com",
but strips "user@any.thing.else.example.com" to "user@example.com".
.PP
A domain name prefixed with ! means do not masquerade this domain
or its subdomains. Thus,
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- masquerade_domains = !foo.example.com example.com
+masquerade_domains = !foo.example.com example.com
.fi
.ad
.ft R
+.in -4
.PP
does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
but strips "user@any.thing.else.example.com" to "user@example.com".
.PP
Do not change this unless you have a complete understanding of RFC 821.
.SH relay_clientcerts (default: empty)
-The list of remote SMTP client certificates for which the
-Postfix SMTP server will allow access with the permit_tls_clientcerts
-feature. This feature does not use certificate names, because
-Postfix list manipulation routines treat whitespace and some other
-characters as special. Instead we use certificate fingerprints as
-they are difficult to fake but easy to use for lookup.
+List of tables with remote SMTP client-certificate fingerprints
+for which the Postfix SMTP server will allow access with the
+permit_tls_clientcerts feature.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5).
.PP
Postfix lookup tables are in the form of (key, value) pairs.
Since we only need the key, the value can be chosen freely, e.g.
The safe setting: append "domain.invalid" to incomplete header
addresses from remote SMTP clients, so that those addresses cannot
be confused with local addresses.
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- remote_header_rewrite_domain = domain.invalid
+remote_header_rewrite_domain = domain.invalid
.fi
.ad
.ft R
+.in -4
.PP
The default, purist, setting: don't rewrite headers from remote
clients at all.
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- remote_header_rewrite_domain =
+remote_header_rewrite_domain =
.fi
.ad
.ft R
+.in -4
.SH require_home_directory (default: no)
Whether or not a \fBlocal\fR(8) recipient's home directory must exist
before mail delivery is attempted. By default this test is disabled.
This can be specified in the main.cf file for all SMTP clients, or
it can be specified in the master.cf file for a specific client,
for example:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- /etc/postfix/master.cf:
- smtp ... smtp -o smtp_bind_address=11.22.33.44
+/etc/postfix/master.cf:
+ smtp ... smtp -o smtp_bind_address=11.22.33.44
.fi
.ad
.ft R
+.in -4
.PP
Note 1: when inet_interfaces specifies no more than one IPv4
address, and that address is a non-loopback address, it is
This can be specified in the main.cf file for all SMTP clients, or
it can be specified in the master.cf file for a specific client,
for example:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- /etc/postfix/master.cf:
- smtp ... smtp -o smtp_bind_address6=1:2:3:4:5:6:7:8
+/etc/postfix/master.cf:
+ smtp ... smtp -o smtp_bind_address6=1:2:3:4:5:6:7:8
.fi
.ad
.ft R
+.in -4
.PP
Note 1: when inet_interfaces specifies no more than one IPv6
address, and that address is a non-loopback address, it is
This information can be specified in the main.cf file for all SMTP
clients, or it can be specified in the master.cf file for a specific
client, for example:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- /etc/postfix/master.cf:
- mysmtp ... smtp -o smtp_helo_name=foo.bar.com
+/etc/postfix/master.cf:
+ mysmtp ... smtp -o smtp_helo_name=foo.bar.com
.fi
.ad
.ft R
+.in -4
.PP
This feature is available in Postfix 2.0 and later.
.SH smtp_helo_timeout (default: 300s)
.PP
The default is to comply with RFC 821. If you have to send mail to
a broken SMTP server, configure a special SMTP client in master.cf:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- /etc/postfix/master.cf:
- broken-smtp . . . smtp -o smtp_quote_rfc821_envelope=no
+/etc/postfix/master.cf:
+ broken-smtp . . . smtp -o smtp_quote_rfc821_envelope=no
.fi
.ad
.ft R
+.in -4
.PP
and route mail for the destination in question to the "broken-smtp"
message delivery with a \fBtransport\fR(5) table.
.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. This feature is still under construction. It will not be
-included in the Postfix 2.3 release.
-.PP
-This feature should be available in Postfix 2.4 and later.
+certificate. This feature is under construction as of Postfix version
+2.3.
.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
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
+.sp
.in +4
.nf
.na
.ft C
- smtp_tls_cert_file =
- smtp_tls_dcert_file =
- smtp_tls_key_file =
- smtp_tls_dkey_file =
+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.
case only ciphers matching \fBall\fR the properties are excluded.
.PP
Examples (some of these will cause problems):
-.PP
+.sp
+.in +4
.nf
.na
.ft C
.fi
.ad
.ft R
+.in -4
.PP
The first setting, disables anonymous ciphers. The next setting
disables ciphers that use the MD5 digest algorithm or the (single) DES
key exchange with RSA authentication.
.PP
This feature is available in Postfix 2.3 and later.
+.SH smtp_tls_fingerprint_cert_match (default: empty)
+List of acceptable remote SMTP server certificate fingerprints
+for the "fingerprint" TLS security level (\fBsmtp_tls_security_level\fR =
+fingerprint). At this security level, certificate authorities are
+not used, and certificate expiration times are ignored. Instead,
+server certificates are verified directly via their "fingerprint". The
+fingerprint is a message digest of the server certificate. The digest
+algorithm is selected via the \fBsmtp_tls_fingerprint_digest\fR
+parameter.
+.PP
+When an \fBsmtp_tls_policy_maps\fR table entry specifies the
+"fingerprint" security level, any "match" attributes in that entry specify
+the list of valid fingerprints for the corresponding destination. Multiple
+fingerprints can be combined with a "|" delimiter in a single match
+attribute, or multiple match attributes can be employed.
+.PP
+Example: Certificate fingerprint verification with internal mailhub.
+Two matching fingerprints are listed. The relayhost may be multiple
+physical hosts behind a load-balancer, each with its own private/public
+key and self-signed certificate. Alternatively, a single relayhost may
+be in the process of switching from one set of private/public keys to
+another, and both keys are trusted just prior to the transition.
+.sp
+.in +4
+.nf
+.na
+.ft C
+relayhost = [mailhub.example.com]
+smtp_tls_security_level = fingerprint
+smtp_tls_fingerprint_digest = md5
+smtp_tls_fingerprint_cert_match =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+.fi
+.ad
+.ft R
+.in -4
+.PP
+Example: Certificate fingerprint verification with selected destinations.
+As in the example above, we show two matching fingerprints:
+.sp
+.in +4
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+ smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ smtp_tls_fingerprint_digest = md5
+.fi
+.ad
+.ft R
+.nf
+.na
+.ft C
+/etc/postfix/tls_policy:
+ example.com fingerprint
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix 2.5 and later.
+.SH smtp_tls_fingerprint_digest (default: md5)
+The message digest algorithm used to construct remote SMTP server
+certificate fingerprints. At the "fingerprint" TLS security level
+(\fBsmtp_tls_security_level\fR = fingerprint), the server certificate is
+verified by directly matching its \fIfingerprint\fR. The fingerprint
+is the message digest of the server certificate using the selected
+algorithm. With a digest algorithm resistant to "second pre-image"
+attacks, it is not feasible to create a new public key and a matching
+certificate that has the same fingerprint.
+.PP
+The default algorithm is \fBmd5\fR; this is consistent with
+the backwards compatible setting of the digest used to verify client
+certificates in the SMTP server.
+.PP
+The best practice algorithm is now \fBsha1\fR. Recent advances in hash
+function cryptanalysis have led to md5 being deprecated in favor of sha1.
+However, as long as there are no known "second pre-image" attacks
+against md5, its use in this context can still be considered safe.
+.PP
+While additional digest algorithms are often available with OpenSSL's
+libcrypto, only those used by libssl in SSL cipher suites are available to
+Postfix. For now this means just md5 or sha1.
+.PP
+To find the fingerprint of a specific certificate file, with a
+specific digest algorithm, run:
+.sp
+.in +4
+.nf
+.na
+.ft C
+$ openssl x509 -noout -fingerprint -\fIdigest\fR -in \fIcertfile\fR.pem
+.fi
+.ad
+.ft R
+.in -4
+.PP
+The text to the right of "=" sign is the desired fingerprint.
+For example:
+.sp
+.in +4
+.nf
+.na
+.ft C
+$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
+SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix 2.5 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 Postfix SMTP client RSA certificate
.PP
This feature is available in Postfix 2.3 and later.
.SH smtp_tls_mandatory_protocols (default: SSLv3, TLSv1)
-List of TLS protocols that the Postfix SMTP client will use
-with mandatory TLS encryption. In main.cf the values
-are separated by whitespace, commas or colons. In the policy table
+List of SSL/TLS protocols that the Postfix SMTP client will use with
+mandatory TLS encryption. In main.cf the values are separated by
+whitespace, commas or colons. In the policy table "protocols" attribute
(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".
+empty value means allow all protocols. The valid protocol names, (see
+\\fBfBSSL_get_version\fR(3)\fR), are "SSLv2", "SSLv3" and "TLSv1".
+.PP
+With Postfix >= 2.5 the parameter syntax is expanded to support
+protocol exclusions. One can now explicitly exclude SSLv2 by setting
+"smtp_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
+SSLv3 set "smtp_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is still
+supported; use the form you find more intuitive.
.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.
+Since SSL version 2 has known protocol weaknesses and is now
+deprecated, the default setting excludes "SSLv2". 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
+Example:
+.PP
+.nf
+.na
+.ft C
+smtp_tls_mandatory_protocols = TLSv1
+# Alternative form with Postfix >= 2.5:
+smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+.fi
+.ad
+.ft R
+.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,
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 "\fBfingerprint\fR"
+Certificate fingerprint
+verification. Available with Postfix 2.5 and later. At this security
+level, there are no trusted certificate authorities. The certificate
+trust chain, expiration date, ... are not checked. Instead,
+the optional \fBmatch\fR attribute, or else the main.cf
+\fBsmtp_tls_fingerprint_cert_match\fR parameter, lists the
+valid "fingerprints" of the server certificate. The digest
+algorithm used to calculate the fingerprint is selected by the
+\fBsmtp_tls_fingerprint_digest\fR parameter. Multiple fingerprints can
+be combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits.
.IP "\fBverify\fR"
Mandatory TLS verification. At this security
level, DNS MX lookups are trusted to be secure enough, and the name
.nf
.na
.ft C
-main.cf:
+/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ # Postfix 2.5 and later
+ smtp_tls_fingerprint_digest = md5
.fi
.ad
.ft R
+.PP
.nf
.na
.ft C
-tls_policy:
+/etc/postfix/tls_policy:
example.edu none
example.mil may
example.gov encrypt protocols=TLSv1
example.net secure
.example.net secure match=.example.net:example.net
[mail.example.org]:587 secure match=nexthop
+ # Postfix 2.5 and later
+ [thumb.example.org] fingerprint
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
.fi
.ad
.ft R
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...).
+.SH smtp_tls_scert_verifydepth (default: 9)
+The verification depth for remote SMTP server certificates. A depth
+of 1 is sufficient if the issuing CA is listed in a local CA file.
+.PP
+The default verification depth is 9 (the OpenSSL default) for
+compatibility with earlier Postfix behavior. Prior to Postfix 2.5,
+the default value was 5, but the limit was not actually enforced. If
+you have set this to a lower non-default value, certificates with longer
+trust chains may now fail to verify. Certificate chains with 1 or 2
+CAs are common, deeper chains are more rare and any number between 5
+and 9 should suffice in practice. You can choose a lower number if,
+for example, you trust certificates directly signed by an issuing CA
+but not any CAs it delegates to.
.PP
This feature is available in Postfix 2.2 and later.
.SH smtp_tls_secure_cert_match (default: nexthop, dot-nexthop)
(man-in-the-middle) attacks on DNS.
.PP
Sample main.cf setting:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
.fi
.ad
.ft R
+.in -4
.PP
Sample policy table override:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
example.net secure match=example.com:.example.com
-\e&.example.net secure match=example.com:.example.com
+\&.example.net secure match=example.com:.example.com
.fi
.ad
.ft R
+.in -4
.PP
This feature is available in Postfix 2.3 and later.
.SH smtp_tls_security_level (default: empty)
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 "\fBfingerprint\fR"
+Certificate fingerprint
+verification. Available with Postfix 2.5 and later. At this security
+level, there are no trusted certificate authorities. The certificate
+trust chain, expiration date, ... are not checked. Instead,
+the \fBsmtp_tls_fingerprint_cert_match\fR parameter lists
+the valid "fingerprints" of the server certificate. The digest
+algorithm used to calculate the fingerprint is selected by the
+\fBsmtp_tls_fingerprint_digest\fR parameter.
.IP "\fBverify\fR"
Mandatory TLS verification. At this security
level, DNS MX lookups are trusted to be secure enough, and the name
.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
+# No TLS. Formerly: smtp_use_tls=no and smtp_enforce_tls=no.
+smtp_tls_security_level = none
.fi
.ad
.ft R
.PP
-Opportunistic TLS:
.nf
.na
.ft C
-main.cf:
- smtp_tls_security_level = may
+# Opportunistic TLS.
+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
+# Mandatory (high-grade) TLS encryption.
+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
+# Mandatory TLS verification of hostname or nexthop domain.
+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
+# Secure channel TLS with exact nexthop name match.
+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
+.nf
+.na
+.ft C
+# Certificate fingerprint verification (Postfix >= 2.5).
+# The CA-less "fingerprint" security level only scales to a limited
+# number of destinations. As a global default rather than a per-site
+# setting, this is practical when mail for all recipients is sent
+# to a central mail hub.
+relayhost = [mailhub.example.com]
+smtp_tls_security_level = fingerprint
+smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+smtp_tls_mandatory_ciphers = high
+smtp_tls_fingerprint_cert_match =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
.fi
.ad
.ft R
.na
.ft C
example.com verify match=hostname:nexthop
-\e&.example.com verify match=example.com:.example.com:hostname
+\&.example.com verify match=example.com:.example.com:hostname
.fi
.ad
.ft R
Specify a list of network/netmask patterns, separated by commas
and/or whitespace. The mask specifies the number of bits in the
network part of a host address. You can also specify hostnames or
-\e&.domain names (the initial dot causes the domain to match any name
+\&.domain names (the initial dot causes the domain to match any name
below it), "/file/name" or "type:table" patterns. A "/file/name"
pattern is replaced by its contents; a "type:table" lookup table
is matched when a table entry matches a lookup string (the lookup
Specify a list of network/netmask patterns, separated by commas
and/or whitespace. The mask specifies the number of bits in the
network part of a host address. You can also specify hostnames or
-\e&.domain names (the initial dot causes the domain to match any name
+\&.domain names (the initial dot causes the domain to match any name
below it), "/file/name" or "type:table" patterns. A "/file/name"
pattern is replaced by its contents; a "type:table" lookup table
is matched when a table entry matches a lookup string (the lookup
Specify a list of network/netmask patterns, separated by commas
and/or whitespace. The mask specifies the number of bits in the
network part of a host address. You can also specify hostnames or
-\e&.domain names (the initial dot causes the domain to match any name
+\&.domain names (the initial dot causes the domain to match any name
below it), "/file/name" or "type:table" patterns. A "/file/name"
pattern is replaced by its contents; a "type:table" lookup table
is matched when a table entry matches a lookup string (the lookup
client network address information.
.IP "\fBcheck_ccert_access \fItype:table\fR\fR"
Use the client certificate fingerprint as lookup key for the
-specified \fBaccess\fR(5) database; with Postfix version 2.2, also require
-that the SMTP client certificate is verified successfully. This
-feature is available with Postfix version 2.2 and later.
+specified \fBaccess\fR(5) database; with Postfix version 2.2, also require that
+the SMTP client certificate is verified successfully.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5). This feature is available with Postfix version
+2.2 and later.
.IP "\fBcheck_client_access \fItype:table\fR\fR"
Search the specified access database for the client hostname,
parent domains, client IP address, or networks obtained by stripping
CA, otherwise all clients with a recognized certificate would be
allowed to relay. This feature is available with Postfix version 2.2.
.IP "\fBpermit_tls_clientcerts\fR"
-Permit the request when the remote SMTP client certificate is
-verified successfully, and the certificate fingerprint is listed
-in $relay_clientcerts. This feature is available with Postfix version 2.2.
+Permit the request when the remote SMTP client certificate
+fingerprint is listed in $relay_clientcerts.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5). This feature is available with Postfix version
+2.2.
.IP "\fBreject_rbl_client \fIrbl_domain=d.d.d.d\fR\fR"
Reject the request when the reversed client network address is
listed with the A record "\fId.d.d.d\fR" under \fIrbl_domain\fR
Pause for the specified number of seconds and proceed with
the next restriction in the list, if any. This may stop zombie
mail when used as:
-.PP
.nf
.na
.ft C
IMPORTANT: If you change this parameter setting, you must specify
at least one of the following restrictions. Otherwise Postfix will
refuse to receive mail:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- reject, defer, defer_if_permit, reject_unauth_destination
+reject, defer, defer_if_permit, reject_unauth_destination
.fi
.ad
.ft R
+.in -4
.PP
Specify a list of restrictions, separated by commas and/or whitespace.
Continue long lines by starting the next line with whitespace.
.PP
If a remote SMTP client is authenticated, the permit_sasl_authenticated
access restriction can be used to permit relay access, like this:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- smtpd_recipient_restrictions =
- permit_mynetworks, permit_sasl_authenticated, ...
+smtpd_recipient_restrictions =
+ permit_mynetworks, permit_sasl_authenticated, ...
.fi
.ad
.ft R
+.in -4
.PP
To reject all SMTP connections from unauthenticated clients,
specify "smtpd_delay_reject = yes" (which is the default) and use:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
- smtpd_client_restrictions = permit_sasl_authenticated, reject
+smtpd_client_restrictions = permit_sasl_authenticated, reject
.fi
.ad
.ft R
+.in -4
.PP
See the SASL_README file for SASL configuration and operation details.
.SH smtpd_sasl_authenticated_header (default: no)
with other MTAs.
.PP
Example:
-.na
-.nf
-.in +4
+.PP
.nf
.na
.ft C
- smtpd_tls_always_issue_session_ids = no
+smtpd_tls_always_issue_session_ids = no
.fi
.ad
.ft R
-.in -4
-.fi
-.ad
.PP
This feature is available in Postfix 2.3 and later.
.SH smtpd_tls_ask_ccert (default: no)
connections.
.PP
This feature is available in Postfix 2.2 and later.
-.SH smtpd_tls_ccert_verifydepth (default: 5)
+.SH smtpd_tls_ccert_verifydepth (default: 9)
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...).
+file.
+.PP
+The default verification depth is 9 (the OpenSSL default) for
+compatibility with earlier Postfix behavior. Prior to Postfix 2.5,
+the default value was 5, but the limit was not actually enforced. If
+you have set this to a lower non-default value, certificates with longer
+trust chains may now fail to verify. Certificate chains with 1 or 2
+CAs are common, deeper chains are more rare and any number between 5
+and 9 should suffice in practice. You can choose a lower number if,
+for example, you trust certificates directly signed by an issuing CA
+but not any CAs it delegates to.
.PP
This feature is available in Postfix 2.2 and later.
.SH smtpd_tls_cert_file (default: empty)
Postfix 2.3 and later; use smtpd_tls_mandatory_ciphers instead.
.SH smtpd_tls_dcert_file (default: empty)
File with the Postfix SMTP server DSA certificate in PEM format.
-This file may also contain the Postfix SMTP server private key.
+This file may also contain the Postfix SMTP server private DSA key.
.PP
See the discussion under smtpd_tls_cert_file for more details.
.PP
Instead of using the exact same parameter sets as distributed
with other TLS packages, it is more secure to generate your own
set of parameters with something like the following command:
-.PP
+.sp
+.in +4
.nf
.na
.ft C
-openssl gendh -out /etc/postfix/dh_1024.pem -2 -rand /var/run/egd-pool 1024
+openssl gendh -out /etc/postfix/dh_1024.pem -2 1024
.fi
.ad
.ft R
+.in -4
.PP
Your actual source for entropy may differ. Some systems have
/dev/random; on other system you may consider using the "Entropy
only ciphers matching \fBall\fR the properties are excluded.
.PP
Examples (some of these will cause problems):
-.PP
+.sp
+.in +4
.nf
.na
.ft C
.fi
.ad
.ft R
+.in -4
.PP
The first setting disables anonymous ciphers. The next setting
disables ciphers that use the MD5 digest algorithm or the (single) DES
key exchange with RSA authentication.
.PP
This feature is available in Postfix 2.3 and later.
+.SH smtpd_tls_fingerprint_digest (default: md5)
+The message digest algorithm used to construct client-certificate
+fingerprints for \fBcheck_ccert_access\fR and
+\fBpermit_tls_clientcerts\fR. The default algorithm is \fBmd5\fR,
+for backwards compatibility with Postfix releases prior to 2.5.
+.PP
+The best practice algorithm is now \fBsha1\fR. Recent advances in hash
+function cryptanalysis have led to md5 being deprecated in favor of sha1.
+However, as long as there are no known "second pre-image" attacks
+against md5, its use in this context can still be considered safe.
+.PP
+While additional digest algorithms are often available with OpenSSL's
+libcrypto, only those used by libssl in SSL cipher suites are available to
+Postfix. For now this means just md5 or sha1.
+.PP
+To find the fingerprint of a specific certificate file, with a
+specific digest algorithm, run:
+.sp
+.in +4
+.nf
+.na
+.ft C
+$ openssl x509 -noout -fingerprint -\fIdigest\fR -in \fIcertfile\fR.pem
+.fi
+.ad
+.ft R
+.in -4
+.PP
+The text to the right of "=" sign is the desired fingerprint.
+For example:
+.sp
+.in +4
+.nf
+.na
+.ft C
+$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
+SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+.fi
+.ad
+.ft R
+.in -4
+.PP
+Example: client-certificate access table, with sha1 fingerprints:
+.sp
+.in +4
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+ smtpd_tls_fingerprint_digest = sha1
+ smtpd_client_restrictions =
+ check_ccert_access hash:/etc/postfix/access,
+ reject
+.fi
+.ad
+.ft R
+.nf
+.na
+.ft C
+/etc/postfix/access:
+ # Action folded to next line...
+ AF:88:7C:AD:51:95:6F:36:96:F6:01:FB:2E:48:CD:AB:49:25:A2:3B
+ OK
+ 85:16:78:FD:73:6E:CE:70:E0:31:5F:0D:3C:C8:6D:C4:2C:24:59:E1
+ permit_auth_destination
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix 2.5 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 Postfix SMTP server certificate
-file specified
-with $smtpd_tls_cert_file.
+This file may be combined with the Postfix SMTP server RSA certificate
+file specified with $smtpd_tls_cert_file.
.PP
The private key must be accessible without a pass-phrase, i.e. it
must not be encrypted, but file permissions should grant read/write
.PP
This feature is available in Postfix 2.3 and later.
.SH smtpd_tls_mandatory_protocols (default: SSLv3, TLSv1)
-The TLS protocols accepted by the Postfix SMTP server with
-mandatory TLS encryption. With opportunistic TLS encryption, all
-protocols are always accepted. If the list is empty, the server
-supports all available TLS protocol versions. A non-empty value
-is a list of protocol names separated by whitespace, commas or
-colons. The supported protocol names are "SSLv2", "SSLv3" and
-"TLSv1", and are not case sensitive.
+The SSL/TLS protocols accepted by the Postfix SMTP server with
+mandatory TLS encryption. If the list is empty, the server supports all
+available SSL/TLS protocol versions. A non-empty value is a list
+of protocol
+names separated by whitespace, commas or colons. The supported protocol
+names are "SSLv2", "SSLv3" and "TLSv1", and are not case sensitive.
+.PP
+With Postfix >= 2.5 the parameter syntax is expanded to support
+protocol exclusions. One can now explicitly exclude SSLv2 by setting
+"smtpd_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
+SSLv3 set "smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is still
+supported, use the form you find more intuitive.
+.PP
+Since SSL version 2 has known protocol weaknesses and is now
+deprecated, the default setting excludes "SSLv2". This means that
+by default, SSL version 2 will not be used at the "encrypt" security
+level.
.PP
Example:
.PP
.nf
.na
.ft C
-smtpd_tls_mandatory_protocols = SSLv3, TLSv1
+smtpd_tls_mandatory_protocols = TLSv1
+# Alternative form with Postfix >= 2.5:
+smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
.fi
.ad
.ft R
.PP
This feature is available in Postfix 2.2 and later.
.SH smtpd_tls_req_ccert (default: no)
-With mandatory TLS encryption, require a remote SMTP client
+With mandatory TLS encryption, require a trusted remote SMTP client
certificate in order to allow TLS connections to proceed. This
option implies "smtpd_tls_ask_ccert = yes".
.PP
of a publicly-referenced SMTP server. Instead, this option should
be used only on dedicated servers.
.PP
-Note 1: the "verify" and "secure" levels are not supported.
+Note 1: the "fingerprint", "verify" and "secure" levels are not
+supported here.
The Postfix SMTP server logs a warning and uses "encrypt" instead.
To verify SMTP client certificates, see TLS_README for a discussion
of the smtpd_tls_ask_ccert, smtpd_tls_req_ccert, and permit_tls_clientcerts
A transport-specific override for the default_delivery_slot_loan
parameter value, where \fItransport\fR is the master.cf name of
the message delivery transport.
+.SH transport_destination_concurrency_failed_cohort_limit (default: $default_destination_concurrency_failed_cohort_limit)
+A transport-specific override for the
+default_destination_concurrency_failed_cohort_limit parameter value,
+where \fItransport\fR is the master.cf name of the message delivery
+transport.
+.PP
+This feature is available in Postfix 2.5 and later.
.SH transport_destination_concurrency_limit (default: $default_destination_concurrency_limit)
A transport-specific override for the
default_destination_concurrency_limit parameter value, where
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 protocols that the Postfix SMTP client will use
-with mandatory TLS encryption.
-.IP "\fBsmtp_tls_scert_verifydepth (5)\fR"
+List of SSL/TLS protocols that the Postfix SMTP client will use with
+mandatory TLS encryption.
+.IP "\fBsmtp_tls_scert_verifydepth (9)\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
The SASL authentication security options that the Postfix SMTP
client uses for TLS encrypted SMTP sessions with a verified server
certificate.
+.PP
+Available in Postfix version 2.5 and later:
+.IP "\fBsmtp_tls_fingerprint_cert_match (empty)\fR"
+List of acceptable remote SMTP server certificate fingerprints
+for the "fingerprint" TLS security level (\fBsmtp_tls_security_level\fR =
+fingerprint).
+.IP "\fBsmtp_tls_fingerprint_digest (md5)\fR"
+The message digest algorithm used to construct remote SMTP server
+certificate fingerprints.
.SH "OBSOLETE STARTTLS CONTROLS"
.na
.nf
SuSE Rhein/Main AG
65760 Eschborn, Germany
-Connection caching in cooperation with:
-Victor Duchovni
-Morgan Stanley
-
TLS support originally by:
Lutz Jaenicke
BTU Cottbus
Allgemeine Elektrotechnik
Universitaetsplatz 3-4
D-03044 Cottbus, Germany
+
+Revised TLS and SMTP connection cache support by:
+Victor Duchovni
+Morgan Stanley
When TLS encryption is optional in the Postfix SMTP server, do
not announce or accept SASL authentication over unencrypted
connections.
-.IP "\fBsmtpd_tls_ccert_verifydepth (5)\fR"
+.IP "\fBsmtpd_tls_ccert_verifydepth (9)\fR"
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.
Additional list of ciphers or cipher types to exclude from the
SMTP server cipher list at mandatory TLS security levels.
.IP "\fBsmtpd_tls_mandatory_protocols (SSLv3, TLSv1)\fR"
-The TLS protocols accepted by the Postfix SMTP server with
+The SSL/TLS protocols accepted by the Postfix SMTP server with
mandatory TLS encryption.
.IP "\fBsmtpd_tls_received_header (no)\fR"
Request that the Postfix SMTP server produces Received: message
as well as the client CommonName and client certificate issuer
CommonName.
.IP "\fBsmtpd_tls_req_ccert (no)\fR"
-With mandatory TLS encryption, require a remote SMTP client
+With mandatory TLS encryption, require a trusted remote SMTP client
certificate in order to allow TLS connections to proceed.
.IP "\fBsmtpd_tls_session_cache_database (empty)\fR"
Name of the file containing the optional Postfix SMTP server
.IP "\fBtls_null_cipherlist (eNULL:!aNULL)\fR"
The OpenSSL cipherlist for "NULL" grade ciphers that provide
authentication without encryption.
+.PP
+Available in Postfix version 2.5 and later:
+.IP "\fBsmtpd_tls_fingerprint_digest (md5)\fR"
+The message digest algorithm used to construct client-certificate
+fingerprints for \fBcheck_ccert_access\fR and
+\fBpermit_tls_clientcerts\fR.
.SH "OBSOLETE STARTTLS CONTROLS"
.na
.nf
Allgemeine Elektrotechnik
Universitaetsplatz 3-4
D-03044 Cottbus, Germany
+
+Revised TLS support by:
+Victor Duchovni
+Morgan Stanley
$block .= $_;
} while(($_ = <>) && /\S/);
- $block =~ s/\n\./\n\\\&./g;
+ # How the %!#$^@ do I get a backslash substituted into a string?
+ # Even \134 comes out as \e. What brain damage is this?
+ #$block =~ s/\n\./\n\\\&./g;
+ $block =~ s/\n\./\n\134\&./g;
if ($block =~ /<H2>/) {
$block =~ s/<H2><a[^>]+>([^<]+)<\/a><\/H2>/\n.SH \1\n/g;
$block =~ tr/a-z/A-Z/;
$block =~ s/\s*<\/dt>/"/g;
$block =~ s/<tt>\s*//g;
$block =~ s/\s*<\/tt>//g;
- $block =~ s/<blockquote>/\n.na\n.nf\n.in +4\n/g;
- $block =~ s/<\/blockquote>/\n.in -4\n.fi\n.ad\n/g;
+ $block =~ s/<blockquote>/\n.sp\n.in +4\n/g;
+ $block =~ s/<\/blockquote>/\n.in -4\n/g;
$block =~ s/\n<br>/\n.br\n/g;
$block =~ s/<br>\s*/\n.br\n/g;
+ $block =~ s/≤/<=/g;
$block =~ s/</</g;
+ $block =~ s/≥/>=/g;
$block =~ s/>/>/g;
$block =~ s/&/\&/g;
$block =~ s/\s+\n/\n/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_fingerprint_cert_match\b;<a href="postconf.5.html#lmtp_tls_fingerprint_cert_match">$&</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_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_fingerprint_digest\b;<a href="postconf.5.html#lmtp_tls_fingerprint_digest">$&</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;\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_fingerprint_digest\b;<a href="postconf.5.html#smtp_tls_fingerprint_digest">$&</a>;g;
s;\bsmtp_tls_mandatory_ciphers\b;<a href="postconf.5.html#smtp_tls_mandatory_ciphers">$&</a>;g;
s;\bsmtp_tls_cipherlist\b;<a href="postconf.5.html#smtp_tls_cipherlist">$&</a>;g;
s;\bsmtp_tls_exclude_ciphers\b;<a href="postconf.5.html#smtp_tls_exclude_ciphers">$&</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_fingerprint_cert_match\b;<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">$&</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;\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_exclude_ciphers\b;<a href="postconf.5.html#smtpd_tls_exclude_ciphers">$&</a>;g;
+ s;\bsmtpd_tls_fingerprint_digest\b;<a href="postconf.5.html#smtpd_tls_fingerprint_digest">$&</a>;g;
s;\bsmtpd_tls_mandatory_ciphers\b;<a href="postconf.5.html#smtpd_tls_mandatory_ciphers">$&</a>;g;
s;\bsmtpd_tls_mandatory_exclude_ciphers\b;<a href="postconf.5.html#smtpd_tls_mandatory_exclude_ciphers">$&</a>;g;
s;\bsmtpd_tls_dcert_file\b;<a href="postconf.5.html#smtpd_tls_dcert_file">$&</a>;g;
while(<POSTCONF>) {
- next if /^#/;
+ next if /^#/ && $text eq "";
next unless ($name || /\S/);
if (/^%(PARAM|CLASS)/) {
../man/man5/postconf.5: postconf.man.prolog postconf.proto postconf.man.epilog \
../mantools/xpostconf ../mantools/postconf2html ../mantools/postconf2man
(cat postconf.man.prolog; ../mantools/xpostconf postconf.proto | \
- ../mantools/postconf2html | ../mantools/postconf2man; \
- cat postconf.man.epilog ) > $@
+ ../mantools/postconf2html | ../mantools/postconf2man | \
+ sed 's/\\e&/\\\&/'; cat postconf.man.epilog ) > $@
../html/postconf.5.html: postconf.html.prolog postconf.proto \
postconf.html.epilog ../mantools/xpostconf ../mantools/postconf2html \
overload, and how to avoid the condition under normal conditions.
When the condition is caused by botnets or other malware, the
document suggests configuration settings that help to minimize the
-impact on legitimate mail. Finally, the document introduces Postfix
-stress-adaptive behavior, and how it can be used to automatically
-switch configuration settings under overload. </p>
+impact on legitimate mail. Finally, the document introduces
+stress-adaptive behavior, introduced with Postfix 2.5, and how it
+can be used to automatically switch configuration settings under
+overload. </p>
<p> Topics covered in this document: </p>
<h2><a name="overload"> Symptoms of Postfix SMTP server overload </a></h2>
<p> Under normal conditions, Postfix responds immediately when a
-remote SMTP client connects. The time needed to deliver mail to
-Postfix may depend on how busy the CPU or disk are, but that should
+remote SMTP client connects. The time needed to deliver mail should
be noticeable only with very large messages. Performance degrades
more dramatically when the number of remote SMTP clients exceeds
the number of Postfix SMTP server processes. When a client connects
<ul>
-<li> <p> Postfix logs a warning that all server ports are busy: </p>
-
-<pre>
-Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
- (25) has reached its process limit "30": new clients may experience
- noticeable delays
-Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
- condition, increase the process count in master.cf or reduce the
- service time per client
-</pre>
-
<li> <p> Remote SMTP clients experience a long delay before Postfix
sends the "220 hostname.example.com ESMTP Postfix" greeting. If
this affects end-user mail clients, enable the "submission" service
connection after CONNECT" events. This happens because remote SMTP
clients disconnect before Postfix answers the connection. </p>
+<li> <p> Postfix 2.3 and later logs a warning that all server ports
+are busy: </p>
+
+<pre>
+Oct 3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
+ (25) has reached its process limit "30": new clients may experience
+ noticeable delays
+Oct 3 20:39:27 spike postfix/master[28905]: warning: to avoid this
+ condition, increase the process count in master.cf or reduce the
+ service time per client
+</pre>
+
</ul>
-<p> NOTE: The last two symptoms also happen without overload. </p>
+<p> NOTE: The first two symptoms may also happen without overload,
+for example: </p>
<ul>
<h2> WARNING </h2>
<p> By turning on TLS support in Postfix, you not only get the
-ability to encrypt mail and to authenticate clients or servers.
+ability to encrypt mail and to authenticate remote SMTP clients or servers.
You also turn on thousands and thousands of lines of OpenSSL library
code. Assuming that OpenSSL is written as carefully as Wietse's
own code, every 1000 lines introduce one additional bug into
<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
+without a password. The 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
<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
+self-signed or private-CA signed certificate. The remote SMTP client
+will generally not be
+able to authenticate the self-signed certificate, but unless the
+client 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
+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
+back to plain text after a TLS handshake failure, a certificate-less
+Postfix SMTP server will
be unable to receive email from most TLS enabled clients. To avoid
-accidental configurations with no certificates, Postfix 2.3 enables
+accidental configurations with no certificates, Postfix enables
certificate-less operation only when the administrator explicitly sets
"smtpd_tls_cert_file = none". This ensures that new Postfix
-configurations will not accidentally run with no certificates. </p>
+SMTP server configurations 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,
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
+<p> Example: the certificate for "server.example.com" was issued by
"intermediate CA" which itself has a certificate issued by "root
CA". Create the server.pem file with: </p>
<p> If you want the Postfix SMTP server to accept remote SMTP client
certificates issued by these CAs, append the root certificate to
-$smtpd_tls_CAfile or install it in the $smtpd_tls_CApath directory. When
-you configure trust in a root CA, it is not necessary to explicitly trust
-intermediary CAs signed by the root CA, unless $smtpd_tls_ccert_verifydepth
-is less than the number of CAs in the certificate chain for the clients
-of interest. With a verify depth of 1 you can only verify certificates
-directly signed by a trusted CA, and all trusted intermediary CAs need to
-be configured explicitly. With a verify depth of 2 you can verify clients
-signed by a root CA or a direct intermediary CA (so long as the client
-is correctly configured to supply its intermediate CA certificate). </p>
+$smtpd_tls_CAfile or install it in the $smtpd_tls_CApath directory. </p>
<p> RSA key and certificate examples: </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 <a
+<p> When you configure the Postfix SMTP server 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
</pre>
</blockquote>
-<p> With this, Postfix SMTP server announces STARTTLS support to
-SMTP clients, but does not require that clients use TLS encryption.
+<p> With this, the Postfix SMTP server announces STARTTLS support to
+remote SMTP clients, but does not require that clients use TLS encryption.
</p>
<p> Note: when an unprivileged user invokes "sendmail -bs", STARTTLS
-is never offered due to insufficient privileges to access the server
+is never offered due to insufficient privileges to access the Postfix
+SMTP server
private key. This is intended behavior. </p>
<p> <a name="server_enforce">You can ENFORCE the use of TLS</a>,
<p> TLS is sometimes used in the non-standard "wrapper" mode where
a server always uses TLS, instead of announcing STARTTLS support
-and waiting for clients to request TLS service. Some clients, namely
+and waiting for remote SMTP clients to request TLS service. Some
+clients, namely
Outlook [Express] prefer the "wrapper" mode. This is true for OE
(Win32 < 5.0 and Win32 >=5.0 when run on a port<>25
and OE (5.01 Mac on all ports). </p>
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>
+offers anonymous ciphers to remote SMTP clients, these are automatically
+suppressed
+when the Postfix SMTP server is configured to ask for client
+certificates. </p>
<p> Example: </p>
</pre>
</blockquote>
-<p> 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 also suffice for longer chains (root
-CA issues special CA which then issues the actual certificate...)
-</p>
+<p> The client certificate verification depth is specified with the
+main.cf smtpd_tls_ccert_verifydepth parameter. The default verification
+depth is 9 (the OpenSSL default), for compatibility with Postfix
+versions before 2.5 where smtpd_tls_ccert_verifydepth was ignored.
+When you configure trust in a
+root CA, it is not necessary to explicitly trust intermediary CAs signed
+by the root CA, unless $smtpd_tls_ccert_verifydepth is less than the
+number of CAs in the certificate chain for the clients of interest. With
+a verify depth of 1 you can only verify certificates directly signed
+by a trusted CA, and all trusted intermediary CAs need to be configured
+explicitly. With a verify depth of 2 you can verify clients signed by a
+root CA or a direct intermediary CA (so long as the client is correctly
+configured to supply its intermediate CA certificate). </p>
<p> Example: </p>
<blockquote>
<pre>
/etc/postfix/main.cf:
- smtpd_tls_ccert_verifydepth = 5
+ smtpd_tls_ccert_verifydepth = 2
</pre>
</blockquote>
<dl>
-<dt> permit_tls_clientcerts </dt> <dd> <p> Allow the remote SMTP
-client SMTP request if the client certificate passes verification,
-and if its fingerprint is listed in the list of client certificates
-(see relay_clientcerts discussion below). </p> </dd>
+<dt> permit_tls_clientcerts </dt> <dd> <p> Allow the remote SMTP client
+request if the client certificate fingerprint is listed in the
+client certificate table (see relay_clientcerts discussion below). </p>
+</dd>
-<dt> permit_tls_all_clientcerts </dt> <dd> <p> Allow the remote
-client SMTP request if the client certificate passes verification.
-</p> </dd>
+<dt> permit_tls_all_clientcerts </dt> <dd> <p> Allow the remote SMTP
+client request if the client certificate passes trust chain verification.
+Useful with private-label CAs that only issue certificates to trusted
+clients (and not otherwise). </p> </dd>
-<dt> check_ccert_access type:table</dt> <dd>
-<p> If the client certificate passes verification, use its fingerprint
-as a key for the specified access(5) table. </p> </dd>
+<dt> check_ccert_access type:table</dt> <dd> <p> Use the remote SMTP
+client
+certificate fingerprint as the lookup key for the specified access(5)
+table. </p> </dd>
</dl>
</blockquote>
+<p> The digest algorithm used to construct the client certificate
+fingerprints is specified with the main.cf smtpd_tls_fingerprint_digest
+parameter. The default is "md5", for compatibility with Postfix
+versions < 2.5. </p>
+
<p> The permit_tls_all_clientcerts feature must be used with caution,
because it can result in too many access permissions. Use this
feature only if a special CA issues the client certificates, and
</pre>
</blockquote>
-<p> The Postfix list manipulation routines give special treatment
-to whitespace and some other characters, making the use of certificate
-names impractical. Instead we use the certificate fingerprints as
-they are difficult to fake but easy to use for lookup. Postfix
-lookup tables are in the form of (key, value) pairs. Since we only
-need the key, the value can be chosen freely, e.g. the name of
-the user or host.</p>
+<p> Example: Postfix lookup tables are in the form of (key, value)
+pairs. Since we only need the key, the value can be chosen freely, e.g.
+the name of the user or host:</p>
-<p> Example: </p>
-
<blockquote>
<pre>
/etc/postfix/main.cf:
smtpd_tls_key_file = /etc/postfix/key.pem
smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5
- # Postfix 2.3 and later
smtpd_tls_security_level = encrypt
- # Obsolete, but still supported
- smtpd_enforce_tls = yes
+ smtpd_tls_mandatory_protocols = TLSv1
+ # Also available with Postfix ≥ 2.5:
+ smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
</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 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 default parameters chosen by OpenSSL are already
-different from those distributed with other TLS packages. </p>
+<p> If you want to take advantage of ciphers with ephemeral Diffie-Hellman
+(EDH) key exchange (this offers "forward-secrecy"), DH parameters are
+needed. Instead of using the built-in DH parameters for both 1024-bit
+(non-export ciphers) and 512-bit (export ciphers), 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. Postfix defaults to compiled-in parameters
+that are shared by all Postfix users who don't generate their own
+settings. </p>
<p> To generate your own set of DH parameters, use: </p>
<blockquote>
<pre>
-% <b>openssl gendh -out /etc/postfix/dh_1024.pem -2 -rand /var/run/egd-pool 1024</b>
-% <b>openssl gendh -out /etc/postfix/dh_512.pem -2 -rand /var/run/egd-pool 512</b>
+% <b>openssl gendh -out /etc/postfix/dh_512.pem -2 512</b>
+% <b>openssl gendh -out /etc/postfix/dh_1024.pem -2 1024</b>
</pre>
</blockquote>
<li><a href="#client_tls_encrypt"> Mandating TLS encryption </a>
+<li><a href="#client_tls_fprint"> Certificate fingerprint verification </a>
+
<li><a href="#client_tls_verify"> Mandating server certificate verification </a>
<li><a href="#client_tls_secure"> Secure server certificate verification </a>
<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 smtp(8) and lmtp(8) delivery agents are implemented by a
+single dual-purpose program. Specifically, 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
+<p> The Postfix 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
<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
+<p> Do not configure Postfix SMTP 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>
<p> If you want the Postfix SMTP client to accept remote SMTP server
certificates issued by these CAs, append the root certificate to
-$smtp_tls_CAfile or install it in the $smtp_tls_CApath directory. When
-you configure trust in a root CA, it is not necessary to explicitly trust
-intermediary CAs signed by the root CA, unless $smtp_tls_scert_verifydepth
-is less than the number of CAs in the certificate chain for the servers
-of interest. With a verify depth of 1 you can only verify certificates
-directly signed by a trusted CA, and all trusted intermediary CAs need to
-be configured explicitly. With a verify depth of 2 you can verify servers
-signed by a root CA or a direct intermediary CA (so long as the server
-is correctly configured to supply its intermediate CA certificate). </p>
+$smtp_tls_CAfile or install it in the $smtp_tls_CApath directory. </p>
<p> RSA key and certificate examples: </p>
<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>fingerprint</b></dt>
+<dd><a href="#client_tls_fprint">Certificate fingerprint verification.</a>
<dt><b>verify</b></dt>
<dd><a href="#client_tls_verify">Mandatory server certificate verification.</a>
<dt><b>secure</b></dt>
<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
+the STARTTLS ESMTP feature is supported by the remote SMTP 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
+encryption is always used, mail delivery continues even if the server
certificate is untrusted or bears the wrong name. </p>
<p> At this security level and higher, the smtp_tls_mandatory_protocols
</pre>
</blockquote>
+<h3><a name="client_tls_fprint"> Certificate fingerprint verification </a>
+</h3>
+
+<p> Certificate fingerprint verification is available with Postfix 2.5 and
+later. At this security level ("smtp_tls_security_level = fingerprint"),
+no trusted certificate authorities are used or required. The certificate
+trust chain, expiration date, ... are not checked. Instead, the
+smtp_tls_fingerprint_cert_match parameter or the "match" attribute
+in the <a href="#client_tls_policy">policy</a> table lists the valid
+"fingerprints" of the remote SMTP server certificate. </p>
+
+<p> If certificate fingerprints are exchanged securely, this is the
+strongest, and least scalable security level. The administrator needs to
+securely collect the fingerprints of the X.509 certificates of each peer
+server, store them into a local file, and update this local file
+whenever the peer server's public certificate
+changes. This may be feasible for an SMTP "VPN" connecting a small
+number of branch offices over the Internet, or for secure connections
+to a central mail hub. It works poorly if the remote SMTP server is
+managed by a
+third party, and its public certificate changes periodically without
+prior coordination with the verifying site. </p>
+
+<p> The digest algorithm used to calculate the fingerprint is
+selected by the <b>smtp_tls_fingerprint_digest</b> parameter. In the <a
+href="#client_tls_policy">policy</a> table multiple fingerprints can be
+combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits. </p>
+
+<p> Example: fingerprint TLS security with an internal mailhub.
+Two matching fingerprints are listed. The relayhost may be multiple
+physical hosts behind a load-balancer, each with its own private/public
+key and self-signed certificate. Alternatively, a single relayhost may
+be in the process of switching from one set of private/public keys to
+another, and both keys are trusted just prior to the transition. </p>
+
+<blockquote>
+<pre>
+ relayhost = [mailhub.example.com]
+ smtp_tls_security_level = fingerprint
+ smtp_tls_fingerprint_digest = md5
+ smtp_tls_fingerprint_cert_match =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</pre>
+</blockquote>
+
+<p> Example: Certificate fingerprint verification with selected destinations.
+As in the example above, we show two matching fingerprints: </p>
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+ smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ smtp_tls_fingerprint_digest = md5
+</pre>
+</blockquote>
+<blockquote>
+<pre>
+/etc/postfix/tls_policy:
+ example.com fingerprint
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</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 if the server certificate is valid (not
+TLS encrypted sessions if the remote SMTP server certificate is
+valid (not
expired or revoked, and signed by a trusted certificate authority)
-and if the server certificate name matches a known pattern. Mandatory
+and where the server certificate name matches a known pattern.
+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
<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
+certificate extension are used to verify the remote SMTP 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> Example: </p>
-<p> In this example, the client encrypts all traffic to the
+<p> In this example, the Postfix SMTP 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>
<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
+extension are used to verify the remote SMTP 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> Secure-channel TLS without transport(5) table overrides: </p>
-<p> The client will encrypt all traffic and verify the destination name
+<p> The Postfix SMTP 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
+the hostnames of the SMTP servers for <i>example.com</i>, but these
+hostnames 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
<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. Mail is
-delivered only if remote SMTP server offers STARTTLS and the TLS
-handshake succeeds. 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. </dd>
-
-<dt><b>verify</b></dt> <dd>Mandatory server certificate verification.
-Mail is delivered only if the TLS handshake succeeds, if the server
-certificate can be validated (not expired or revoked, and signed
-by a trusted certificate authority), and if the server certificate
-name matches the optional "match" attribute (or the main.cf
-smtp_tls_verify_cert_match parameter value when no optional "match"
-attribute is specified). </dd>
-
-<dt><b>secure</b></dt> <dd>Secure-channel TLS. Mail is delivered
-only if the TLS handshake succeeds, if the server certificate can
-be validated (not expired or revoked, and signed by a trusted
-certificate authority), and if the server certificate name matches
-the optional "match" attribute (or the main.cf smtp_tls_secure_cert_match
-parameter value when no optional "match" attribute is specified).
-</dd>
+<dt><b>none</b></dt> <dd><a href="#client_tls_none">No TLS</a>. No
+additional attributes are supported at this level. </dd>
+
+<dt><b>may</b></dt> <dd><a href="#client_tls_may">Opportunistic TLS</a>.
+No additional attributes are supported at this level. </dd>
+
+<dt><b>encrypt</b></dt> <dd><a href="#client_tls_encrypt">Mandatory
+encryption</a>. Mail is delivered only if the remote SMTP
+server offers STARTTLS and the TLS handshake succeeds. At this
+level and higher the optional "ciphers" attribute overrides the
+main.cf smtp_tls_mandatory_ciphers parameter, and the optional
+"protocols" attribute
+overrides the main.cf smtp_tls_mandatory_protocols parameter. </dd>
+
+<dt><b>fingerprint</b></dt> <dd><a href="#client_tls_fprint">Certificate
+fingerprint verification.</a> Available with Postfix 2.5 and
+later. At this security level, there are no trusted certificate
+authorities. The certificate trust chain, expiration date, ... are
+not checked. Instead, the optional <b>match</b> attribute, or else
+the main.cf <b>smtp_tls_fingerprint_cert_match</b> parameter,
+lists the valid fingerprints of the server certificate. The
+digest algorithm used to calculate fingerprints is selected by the
+<b>smtp_tls_fingerprint_digest</b> parameter. Multiple fingerprints can
+be combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits. </dd>
+
+<dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
+server certificate verification</a>. Mail is delivered only if the
+TLS handshake
+succeeds, if the remote SMTP server certificate can be validated (not
+expired or revoked, and signed by a trusted certificate authority), and
+if the server certificate name matches the optional "match" attribute (or
+the main.cf smtp_tls_verify_cert_match parameter value when no optional
+"match" attribute is specified). </dd>
+
+<dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
+verification.</a> Mail is delivered only if the TLS handshake succeeds,
+if the remote SMTP server certificate can be validated (not expired
+or revoked, and signed by a trusted certificate authority), and if the
+server certificate name matches the optional "match" attribute (or the
+main.cf smtp_tls_secure_cert_match parameter value when no optional
+"match" attribute is specified). </dd>
</dl>
<pre>
/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ # Postfix 2.5 and later
+ smtp_tls_fingerprint_digest = md5
/etc/postfix/tls_policy:
example.edu none
example.mil may
example.net secure
.example.net secure match=.example.net:example.net
[mail.example.org]:587 secure match=nexthop
+ # Postfix 2.5 and later
+ [thumb.example.org] fingerprint
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
</pre>
</blockquote>
<h3><a name="client_vrfy_server">Server certificate verification depth</a> </h3>
-<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 (where the root CA issues
-a special CA certificate which then issues the actual certificate). </p>
-
-<p> Example: </p>
+<p> The server certificate verification depth is specified with the
+main.cf smtp_tls_scert_verifydepth parameter. The default verification
+depth is 9 (the OpenSSL default), for compatibility with Postfix
+versions before 2.5 where smtp_tls_scert_verifydepth was ignored.
+When you configure trust
+in a root CA, it is not necessary to explicitly trust intermediary CAs
+signed by the root CA, unless $smtp_tls_scert_verifydepth is less than the
+number of CAs in the certificate chain for the servers of interest. With
+a verify depth of 1 you can only verify certificates directly signed
+by a trusted CA, and all trusted intermediary CAs need to be configured
+explicitly. With a verify depth of 2 you can verify servers signed by a
+root CA or a direct intermediary CA (so long as the server is correctly
+configured to supply its intermediate CA certificate). </p>
+<p> Example: </p>
+
<blockquote>
<pre>
/etc/postfix/main.cf:
- smtp_tls_scert_verifydepth = 5
+ smtp_tls_scert_verifydepth = 2
</pre>
</blockquote>
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
+disabled when remote SMTP server certificates are verified. If you
+want to
disable anonymous ciphers even at the "encrypt" security level, set
"smtp_tls_mandatory_exclude_ciphers = aNULL"; and to
disable anonymous ciphers even with opportunistic TLS, set
smtp_tls_mandatory_ciphers = medium
smtp_tls_mandatory_exclude_ciphers = RC4, MD5
smtp_tls_exclude_ciphers = aNULL
+ smtp_tls_mandatory_protocols = SSLv3, TLSv1
+ # Also available with Postfix ≥ 2.5:
+ smtp_tls_mandatory_protocols = !SSLv2
</pre>
</blockquote>
smtp_tls_CAfile = /etc/postfix/cacert.pem
smtp_tls_session_cache_database =
btree:/var/lib/postfix/smtp_tls_session_cache
- smtp_use_tls = yes
+ smtp_tls_security_level = may
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
of the smtp_tls_per_site code in terms of enforcement levels, which
simplified the implementation greatly.
+<li> Victor Duchovni implemented the fingerprint security level,
+added more sanity checks, and separated TLS connection management
+from security policy enforcement. The latter change simplified the
+code that verifies certificate signatures, certificate names, and
+certificate fingerprints.
+
</ul>
</body>
# open connection (file descriptor passing) per connection
# request, and is accessible to local clients only.
#
-# This feature is not part of the stable Postfix release.
-#
# The service name is a pathname relative to the Postfix
# queue directory (pathname controlled with the \fBqueue_directory\fR
# configuration parameter in main.cf).
+#
+# This feature is available as of Postfix version 2.5.
# .RE
# .IP "\fBPrivate (default: y)\fR"
# Whether or not access is restricted to the mail system.
# * Text between <!-- and --> is stripped out. The <!-- and -->
# must appear on separate lines.
#
+# * Text after a blank line must start with an HTML element.
+#
# Also:
#
# * All <dt> and <dd>text must be closed with </dt> and </dd>.
#
+# * Use <blockquote><pre>..</pre></blockquote> for examples
+# between narrative text, instead of indenting examples by hand.
+#
+# * Use <pre>..</pre> for the "Examples:" section at the end
+# of a parameter description.
+#
# The postlink tool automatically inserts hyperlinks for the following,
# so you must not hyperlink that information yourself:
#
first match. Thus,
</p>
+<blockquote>
<pre>
- masquerade_domains = foo.example.com example.com
+masquerade_domains = foo.example.com example.com
</pre>
+</blockquote>
<p>
strips "user@any.thing.foo.example.com" to "user@foo.example.com",
or its subdomains. Thus,
</p>
+<blockquote>
<pre>
- masquerade_domains = !foo.example.com example.com
+masquerade_domains = !foo.example.com example.com
</pre>
+</blockquote>
<p>
does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/master.cf:
- smtp ... smtp -o smtp_bind_address=11.22.33.44
+/etc/postfix/master.cf:
+ smtp ... smtp -o smtp_bind_address=11.22.33.44
</pre>
+</blockquote>
<p> Note 1: when inet_interfaces specifies no more than one IPv4
address, and that address is a non-loopback address, it is
for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/master.cf:
- smtp ... smtp -o smtp_bind_address6=1:2:3:4:5:6:7:8
+/etc/postfix/master.cf:
+ smtp ... smtp -o smtp_bind_address6=1:2:3:4:5:6:7:8
</pre>
+</blockquote>
<p> Note 1: when inet_interfaces specifies no more than one IPv6
address, and that address is a non-loopback address, it is
client, for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/master.cf:
- mysmtp ... smtp -o smtp_helo_name=foo.bar.com
+/etc/postfix/master.cf:
+ mysmtp ... smtp -o smtp_helo_name=foo.bar.com
</pre>
+</blockquote>
<p>
This feature is available in Postfix 2.0 and later.
a broken SMTP server, configure a special SMTP client in master.cf:
</p>
+<blockquote>
<pre>
- /etc/postfix/master.cf:
- broken-smtp . . . smtp -o smtp_quote_rfc821_envelope=no
+/etc/postfix/master.cf:
+ broken-smtp . . . smtp -o smtp_quote_rfc821_envelope=no
</pre>
+</blockquote>
<p>
and route mail for the destination in question to the "broken-smtp"
<dt><b><a name="check_ccert_access">check_ccert_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>
<dd> Use the client certificate fingerprint as lookup key for the
-specified access(5) database; with Postfix version 2.2, also require
-that the SMTP client certificate is verified successfully. This
-feature is available with Postfix version 2.2 and later.</dd>
+specified access(5) database; with Postfix version 2.2, also require that
+the SMTP client certificate is verified successfully.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5). This feature is available with Postfix version
+2.2 and later. </dd>
<dt><b><a name="check_client_access">check_client_access</a> <i><a href="DATABASE_README.html">type:table</a></i></b></dt>
<dt><b><a name="permit_tls_clientcerts">permit_tls_clientcerts</a></b></dt>
-<dd>Permit the request when the remote SMTP client certificate is
-verified successfully, and the certificate fingerprint is listed
-in $relay_clientcerts. This feature is available with Postfix version 2.2.</dd>
+<dd>Permit the request when the remote SMTP client certificate
+fingerprint is listed in $relay_clientcerts.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5). This feature is available with Postfix version
+2.2. </dd>
+
<dt><b><a name="reject_rbl_client">reject_rbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
<dd>Reject the request when the reversed client network address is
<dd>Pause for the specified number of seconds and proceed with
the next restriction in the list, if any. This may stop zombie
mail when used as:
-
<pre>
/etc/postfix/main.cf:
smtpd_client_restrictions =
refuse to receive mail:
</p>
+<blockquote>
<pre>
- reject, defer, defer_if_permit, reject_unauth_destination
+reject, defer, defer_if_permit, reject_unauth_destination
</pre>
+</blockquote>
<p>
Specify a list of restrictions, separated by commas and/or whitespace.
access restriction can be used to permit relay access, like this:
</p>
+<blockquote>
<pre>
- smtpd_recipient_restrictions =
- permit_mynetworks, permit_sasl_authenticated, ...
+smtpd_recipient_restrictions =
+ permit_mynetworks, permit_sasl_authenticated, ...
</pre>
+</blockquote>
<p> To reject all SMTP connections from unauthenticated clients,
specify "smtpd_delay_reject = yes" (which is the default) and use:
</p>
+<blockquote>
<pre>
- smtpd_client_restrictions = permit_sasl_authenticated, reject
+smtpd_client_restrictions = permit_sasl_authenticated, reject
</pre>
+</blockquote>
<p>
See the SASL_README file for SASL configuration and operation details.
addresses from remote SMTP clients, so that those addresses cannot
be confused with local addresses. </p>
+<blockquote>
<pre>
- remote_header_rewrite_domain = domain.invalid
+remote_header_rewrite_domain = domain.invalid
</pre>
+</blockquote>
<p> The default, purist, setting: don't rewrite headers from remote
clients at all. </p>
+<blockquote>
<pre>
- remote_header_rewrite_domain =
+remote_header_rewrite_domain =
</pre>
+</blockquote>
%PARAM local_header_rewrite_clients permit_inet_interfaces
<dt><b>permit_tls_clientcerts </b></dt>
<dd> Append the domain name in $myorigin or $mydomain when the
-client TLS certificate is successfully verified, and the client
-certificate fingerprint is listed in $relay_clientcerts. </dd>
+client TLS certificate fingerprint is listed in $relay_clientcerts.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5). </dd>
<dt><b>permit_tls_all_clientcerts </b></dt>
message headers, and always append my own domain to incomplete
header addresses. </p>
+<blockquote>
<pre>
- local_header_rewrite_clients = static:all
+local_header_rewrite_clients = static:all
</pre>
+</blockquote>
<p> The purist (and default) setting: rewrite headers only in mail
from Postfix sendmail and in SMTP mail from this machine. </p>
+<blockquote>
<pre>
- local_header_rewrite_clients = permit_inet_interfaces
+local_header_rewrite_clients = permit_inet_interfaces
</pre>
+</blockquote>
<p> The intermediate setting: rewrite header addresses and append
$myorigin or $mydomain information only with mail from Postfix
rewriting when mail from a remote client is forwarded by a neighboring
system. </p>
+<blockquote>
<pre>
- local_header_rewrite_clients = permit_mynetworks,
- permit_sasl_authenticated permit_tls_clientcerts
- check_address_map hash:/etc/postfix/pop-before-smtp
+local_header_rewrite_clients = permit_mynetworks,
+ permit_sasl_authenticated permit_tls_clientcerts
+ check_address_map hash:/etc/postfix/pop-before-smtp
</pre>
+</blockquote>
%PARAM smtpd_tls_cert_file
%PARAM smtpd_tls_key_file $smtpd_tls_cert_file
<p> File with the Postfix SMTP server RSA private key in PEM format.
-This file may be combined with the Postfix SMTP server certificate
-file specified
-with $smtpd_tls_cert_file. </p>
+This file may be combined with the Postfix SMTP server RSA certificate
+file specified with $smtpd_tls_cert_file. </p>
<p> The private key must be accessible without a pass-phrase, i.e. it
must not be encrypted, but file permissions should grant read/write
%PARAM smtpd_tls_dcert_file
<p> File with the Postfix SMTP server DSA certificate in PEM format.
-This file may also contain the Postfix SMTP server private key. <p>
+This file may also contain the Postfix SMTP server private DSA key. </p>
<p> See the discussion under smtpd_tls_cert_file for more details.
</p>
%PARAM smtpd_tls_req_ccert no
-<p> With mandatory TLS encryption, require a remote SMTP client
+<p> With mandatory TLS encryption, require a trusted remote SMTP client
certificate in order to allow TLS connections to proceed. This
option implies "smtpd_tls_ask_ccert = yes". </p>
<p> This feature is available in Postfix 2.2 and later. </p>
-%PARAM smtpd_tls_ccert_verifydepth 5
+%PARAM smtpd_tls_ccert_verifydepth 9
<p> 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...).
-</p>
+file. </p>
+
+<p> The default verification depth is 9 (the OpenSSL default) for
+compatibility with earlier Postfix behavior. Prior to Postfix 2.5,
+the default value was 5, but the limit was not actually enforced. If
+you have set this to a lower non-default value, certificates with longer
+trust chains may now fail to verify. Certificate chains with 1 or 2
+CAs are common, deeper chains are more rare and any number between 5
+and 9 should suffice in practice. You can choose a lower number if,
+for example, you trust certificates directly signed by an issuing CA
+but not any CAs it delegates to. </p>
<p> This feature is available in Postfix 2.2 and later. </p>
%PARAM relay_clientcerts
-<p> The list of remote SMTP client certificates for which the
-Postfix SMTP server will allow access with the permit_tls_clientcerts
-feature. This feature does not use certificate names, because
-Postfix list manipulation routines treat whitespace and some other
-characters as special. Instead we use certificate fingerprints as
-they are difficult to fake but easy to use for lookup. </p>
+<p> List of tables with remote SMTP client-certificate fingerprints
+for which the Postfix SMTP server will allow access with the
+permit_tls_clientcerts feature.
+The fingerprint digest algorithm is configurable via the
+smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
+Postfix version 2.5). </p>
<p> Postfix lookup tables are in the form of (key, value) pairs.
Since we only need the key, the value can be chosen freely, e.g.
with other TLS packages, it is more secure to generate your own
set of parameters with something like the following command: </p>
+<blockquote>
<pre>
-openssl gendh -out /etc/postfix/dh_1024.pem -2 -rand /var/run/egd-pool 1024
+openssl gendh -out /etc/postfix/dh_1024.pem -2 1024
</pre>
+</blockquote>
<p> Your actual source for entropy may differ. Some systems have
/dev/random; on other system you may consider using the "Entropy
<blockquote>
<pre>
- smtp_tls_cert_file =
- smtp_tls_dcert_file =
- smtp_tls_key_file =
- smtp_tls_dkey_file =
+smtp_tls_cert_file =
+smtp_tls_dcert_file =
+smtp_tls_key_file =
+smtp_tls_dkey_file =
</pre>
</blockquote>
<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
+%PARAM smtp_tls_scert_verifydepth 9
+
+<p> The verification depth for remote SMTP server certificates. A depth
+of 1 is sufficient if the issuing CA is listed in a local CA file. </p>
-<p> 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...). </p>
+<p> The default verification depth is 9 (the OpenSSL default) for
+compatibility with earlier Postfix behavior. Prior to Postfix 2.5,
+the default value was 5, but the limit was not actually enforced. If
+you have set this to a lower non-default value, certificates with longer
+trust chains may now fail to verify. Certificate chains with 1 or 2
+CAs are common, deeper chains are more rare and any number between 5
+and 9 should suffice in practice. You can choose a lower number if,
+for example, you trust certificates directly signed by an issuing CA
+but not any CAs it delegates to. </p>
<p> This feature is available in Postfix 2.2 and later. </p>
This feature is available in Postfix 2.3 and later.
</p>
-%PARAM empty_address_relayhost_maps_lookup_key <>
+%PARAM empty_address_relayhost_maps_lookup_key <>
<p> The sender_dependent_relayhost_maps search string that will be
used instead of the null sender address. </p>
client, for example:
</p>
+<blockquote>
<pre>
- /etc/postfix/master.cf:
- mylmtp ... lmtp -o lmtp_lhlo_name=foo.bar.com
+/etc/postfix/master.cf:
+ mylmtp ... lmtp -o lmtp_lhlo_name=foo.bar.com
</pre>
+</blockquote>
<p>
This feature is available in Postfix 2.3 and later.
<p> This feature is available in Postfix 2.3 and later. </p>
-%PARAM lmtp_tls_scert_verifydepth 5
+%PARAM lmtp_tls_scert_verifydepth 9
<p> The LMTP-specific version of the smtp_tls_scert_verifydepth
configuration parameter. See there for details. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
+%PARAM lmtp_tls_security_level
+
+<p> The LMTP-specific version of the smtp_tls_security_level configuration
+parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
%PARAM lmtp_tls_enforce_peername yes
<p> The LMTP-specific version of the smtp_tls_enforce_peername
<p> The SASL authentication security options that the Postfix SMTP
client uses for TLS encrypted SMTP sessions with a verified server
-certificate. This feature is still under construction. It will not be
-included in the Postfix 2.3 release. </p>
-
-<p> This feature should be available in Postfix 2.4 and later. </p>
+certificate. This feature is under construction as of Postfix version
+2.3. </p>
%PARAM lmtp_sasl_tls_verified_security_options $lmtp_sasl_tls_security_options
In the policy table, multiple protocols must be separated by colons,
as attribute values may not contain whitespace or commas. </dd>
+<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
+verification. Available with Postfix 2.5 and later. At this security
+level, there are no trusted certificate authorities. The certificate
+trust chain, expiration date, ... are not checked. Instead,
+the optional <b>match</b> attribute, or else the main.cf
+<b>smtp_tls_fingerprint_cert_match</b> parameter, lists the
+valid "fingerprints" of the server certificate. The digest
+algorithm used to calculate the fingerprint is selected by the
+<b>smtp_tls_fingerprint_digest</b> parameter. Multiple fingerprints can
+be combined with a "|" delimiter in a single match attribute, or multiple
+match attributes can be employed. The ":" character is not used as a
+delimiter as it occurs between each pair of fingerprint (hexadecimal)
+digits. </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
</p>
<pre>
-main.cf:
+/etc/postfix/main.cf:
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ # Postfix 2.5 and later
+ smtp_tls_fingerprint_digest = md5
</pre>
+
<pre>
-tls_policy:
+/etc/postfix/tls_policy:
example.edu none
example.mil may
example.gov encrypt protocols=TLSv1
example.net secure
.example.net secure match=.example.net:example.net
[mail.example.org]:587 secure match=nexthop
+ # Postfix 2.5 and later
+ [thumb.example.org] fingerprint
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
</pre>
<p> <b>Note:</b> The <b>hostname</b> strategy if listed in a non-default
%PARAM smtp_tls_mandatory_protocols SSLv3, TLSv1
-<p> List of TLS protocols that the Postfix SMTP client will use
-with mandatory TLS encryption. In main.cf the values
-are separated by whitespace, commas or colons. In the policy table
+<p> List of SSL/TLS protocols that the Postfix SMTP client will use with
+mandatory TLS encryption. In main.cf the values are separated by
+whitespace, commas or colons. In the policy table "protocols" attribute
(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>
+empty value means allow all protocols. The valid protocol names, (see
+<b>SSL_get_version(3)</b>), are "SSLv2", "SSLv3" and "TLSv1". </p>
+
+<p> With Postfix ≥ 2.5 the parameter syntax is expanded to support
+protocol exclusions. One can now explicitly exclude SSLv2 by setting
+"smtp_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
+SSLv3 set "smtp_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is still
+supported; use the form you find more intuitive. </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> Since SSL version 2 has known protocol weaknesses and is now
+deprecated, the default setting excludes "SSLv2". 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> Example: </p>
+
+<pre>
+smtp_tls_mandatory_protocols = TLSv1
+# Alternative form with Postfix ≥ 2.5:
+smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+</pre>
+
<p> This feature is available in Postfix 2.3 and later. </p>
%PARAM smtp_tls_verify_cert_match hostname
Sample main.cf setting:
</p>
+<blockquote>
<pre>
smtp_tls_secure_cert_match = nexthop
</pre>
+</blockquote>
<p>
Sample policy table override:
</p>
+<blockquote>
<pre>
example.net secure match=example.com:.example.com
.example.net secure match=example.com:.example.com
</pre>
+</blockquote>
<p> This feature is available in Postfix 2.3 and later. </p>
%PARAM smtpd_tls_mandatory_protocols SSLv3, TLSv1
-<p> The TLS protocols accepted by the Postfix SMTP server with
-mandatory TLS encryption. With opportunistic TLS encryption, all
-protocols are always accepted. If the list is empty, the server
-supports all available TLS protocol versions. A non-empty value
-is a list of protocol names separated by whitespace, commas or
-colons. The supported protocol names are "SSLv2", "SSLv3" and
-"TLSv1", and are not case sensitive. </p>
+<p> The SSL/TLS protocols accepted by the Postfix SMTP server with
+mandatory TLS encryption. If the list is empty, the server supports all
+available SSL/TLS protocol versions. A non-empty value is a list
+of protocol
+names separated by whitespace, commas or colons. The supported protocol
+names are "SSLv2", "SSLv3" and "TLSv1", and are not case sensitive. </p>
+
+<p> With Postfix ≥ 2.5 the parameter syntax is expanded to support
+protocol exclusions. One can now explicitly exclude SSLv2 by setting
+"smtpd_tls_mandatory_protocols = !SSLv2". To exclude both SSLv2 and
+SSLv3 set "smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3". Listing
+the protocols to include, rather than protocols to exclude, is still
+supported, use the form you find more intuitive. </p>
+
+<p> Since SSL version 2 has known protocol weaknesses and is now
+deprecated, the default setting excludes "SSLv2". This means that
+by default, SSL version 2 will not be used at the "encrypt" security
+level. </p>
<p> Example: </p>
<pre>
-smtpd_tls_mandatory_protocols = SSLv3, TLSv1
+smtpd_tls_mandatory_protocols = TLSv1
+# Alternative form with Postfix ≥ 2.5:
+smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
</pre>
<p> This feature is available in Postfix 2.3 and later. </p>
mandatory encrypted sessions. This security level is not an appropriate
default for systems delivering mail to the Internet. </dd>
+<dt><b>fingerprint</b></dt> <dd>Certificate fingerprint
+verification. Available with Postfix 2.5 and later. At this security
+level, there are no trusted certificate authorities. The certificate
+trust chain, expiration date, ... are not checked. Instead,
+the <b>smtp_tls_fingerprint_cert_match</b> parameter lists
+the valid "fingerprints" of the server certificate. The digest
+algorithm used to calculate the fingerprint is selected by the
+<b>smtp_tls_fingerprint_digest</b> parameter. </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
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
+# No TLS. Formerly: smtp_use_tls=no and smtp_enforce_tls=no.
+smtp_tls_security_level = none
+</pre>
+
+<pre>
+# Opportunistic TLS.
+smtp_tls_security_level = may
</pre>
-<p>Opportunistic TLS:</p>
<pre>
-main.cf:
- smtp_tls_security_level = may
+# Mandatory (high-grade) TLS encryption.
+smtp_tls_security_level = encrypt
+smtp_tls_mandatory_ciphers = high
</pre>
-<p>Mandatory (high-grade) TLS encryption:</p>
<pre>
-main.cf:
- smtp_tls_security_level = encrypt
- smtp_tls_mandatory_ciphers = high
+# Mandatory TLS verification of hostname or nexthop domain.
+smtp_tls_security_level = verify
+smtp_tls_mandatory_ciphers = high
+smtp_tls_verify_cert_match = hostname, nexthop, dot-nexthop
</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
+# Secure channel TLS with exact nexthop name match.
+smtp_tls_security_level = secure
+smtp_tls_mandatory_protocols = TLSv1
+smtp_tls_mandatory_ciphers = high
+smtp_tls_secure_cert_match = 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
+# Certificate fingerprint verification (Postfix ≥ 2.5).
+# The CA-less "fingerprint" security level only scales to a limited
+# number of destinations. As a global default rather than a per-site
+# setting, this is practical when mail for all recipients is sent
+# to a central mail hub.
+relayhost = [mailhub.example.com]
+smtp_tls_security_level = fingerprint
+smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+smtp_tls_mandatory_ciphers = high
+smtp_tls_fingerprint_cert_match =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
</pre>
<p> This feature is available in Postfix 2.3 and later. </p>
<p> Examples (some of these will cause problems): </p>
+<blockquote>
<pre>
smtpd_tls_exclude_ciphers = aNULL
smtpd_tls_exclude_ciphers = MD5, DES
smtpd_tls_exclude_ciphers = AES256-SHA, DES-CBC3-MD5
smtpd_tls_exclude_ciphers = kEDH+aRSA
</pre>
+</blockquote>
<p> The first setting disables anonymous ciphers. The next setting
disables ciphers that use the MD5 digest algorithm or the (single) DES
<p> Examples (some of these will cause problems): </p>
+<blockquote>
<pre>
smtp_tls_exclude_ciphers = aNULL
smtp_tls_exclude_ciphers = MD5, DES
smtp_tls_exclude_ciphers = AES256-SHA, DES-CBC3-MD5
smtp_tls_exclude_ciphers = kEDH+aRSA
</pre>
+</blockquote>
<p> The first setting, disables anonymous ciphers. The next setting
disables ciphers that use the MD5 digest algorithm or the (single) DES
</dl>
-<p> Note 1: the "verify" and "secure" levels are not supported.
+<p> Note 1: the "fingerprint", "verify" and "secure" levels are not
+supported here.
The Postfix SMTP server logs a warning and uses "encrypt" instead.
To verify SMTP client certificates, see TLS_README for a discussion
of the smtpd_tls_ask_ccert, smtpd_tls_req_ccert, and permit_tls_clientcerts
<p> Example: </p>
-<blockquote>
<pre>
- smtpd_tls_always_issue_session_ids = no
+smtpd_tls_always_issue_session_ids = no
</pre>
-</blockquote>
<p> This feature is available in Postfix 2.3 and later. </p>
<p> This feature is available in Postfix 2.4 and later. </p>
+%PARAM smtp_tls_fingerprint_digest md5
+
+<p> The message digest algorithm used to construct remote SMTP server
+certificate fingerprints. At the "fingerprint" TLS security level
+(<b>smtp_tls_security_level</b> = fingerprint), the server certificate is
+verified by directly matching its <i>fingerprint</i>. The fingerprint
+is the message digest of the server certificate using the selected
+algorithm. With a digest algorithm resistant to "second pre-image"
+attacks, it is not feasible to create a new public key and a matching
+certificate that has the same fingerprint. </p>
+
+<p> The default algorithm is <b>md5</b>; this is consistent with
+the backwards compatible setting of the digest used to verify client
+certificates in the SMTP server. </p>
+
+<p> The best practice algorithm is now <b>sha1</b>. Recent advances in hash
+function cryptanalysis have led to md5 being deprecated in favor of sha1.
+However, as long as there are no known "second pre-image" attacks
+against md5, its use in this context can still be considered safe.
+</p>
+
+<p> While additional digest algorithms are often available with OpenSSL's
+libcrypto, only those used by libssl in SSL cipher suites are available to
+Postfix. For now this means just md5 or sha1. </p>
+
+<p> To find the fingerprint of a specific certificate file, with a
+specific digest algorithm, run:
+</p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -<i>digest</i> -in <i>certfile</i>.pem
+</pre>
+</blockquote>
+
+<p> The text to the right of "=" sign is the desired fingerprint.
+For example: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
+SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM smtp_tls_fingerprint_cert_match
+
+<p> List of acceptable remote SMTP server certificate fingerprints
+for the "fingerprint" TLS security level (<b>smtp_tls_security_level</b> =
+fingerprint). At this security level, certificate authorities are
+not used, and certificate expiration times are ignored. Instead,
+server certificates are verified directly via their "fingerprint". The
+fingerprint is a message digest of the server certificate. The digest
+algorithm is selected via the <b>smtp_tls_fingerprint_digest</b>
+parameter. </p>
+
+<p> When an <b>smtp_tls_policy_maps</b> table entry specifies the
+"fingerprint" security level, any "match" attributes in that entry specify
+the list of valid fingerprints for the corresponding destination. Multiple
+fingerprints can be combined with a "|" delimiter in a single match
+attribute, or multiple match attributes can be employed. </p>
+
+<p> Example: Certificate fingerprint verification with internal mailhub.
+Two matching fingerprints are listed. The relayhost may be multiple
+physical hosts behind a load-balancer, each with its own private/public
+key and self-signed certificate. Alternatively, a single relayhost may
+be in the process of switching from one set of private/public keys to
+another, and both keys are trusted just prior to the transition. </p>
+
+<blockquote>
+<pre>
+relayhost = [mailhub.example.com]
+smtp_tls_security_level = fingerprint
+smtp_tls_fingerprint_digest = md5
+smtp_tls_fingerprint_cert_match =
+ 3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</pre>
+</blockquote>
+
+<p> Example: Certificate fingerprint verification with selected destinations.
+As in the example above, we show two matching fingerprints: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+ smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
+ smtp_tls_fingerprint_digest = md5
+</pre>
+<pre>
+/etc/postfix/tls_policy:
+ example.com fingerprint
+ match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
+ match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM lmtp_tls_fingerprint_cert_match
+
+<p> The LMTP-specific version of the smtp_tls_fingerprint_cert_match
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM lmtp_tls_fingerprint_digest md5
+
+<p> The LMTP-specific version of the smtp_tls_fingerprint_digest
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM smtpd_tls_fingerprint_digest md5
+
+<p> The message digest algorithm used to construct client-certificate
+fingerprints for <b>check_ccert_access</b> and
+<b>permit_tls_clientcerts</b>. The default algorithm is <b>md5</b>,
+for backwards compatibility with Postfix releases prior to 2.5.
+</p>
+
+<p> The best practice algorithm is now <b>sha1</b>. Recent advances in hash
+function cryptanalysis have led to md5 being deprecated in favor of sha1.
+However, as long as there are no known "second pre-image" attacks
+against md5, its use in this context can still be considered safe.
+</p>
+
+<p> While additional digest algorithms are often available with OpenSSL's
+libcrypto, only those used by libssl in SSL cipher suites are available to
+Postfix. For now this means just md5 or sha1. </p>
+
+<p> To find the fingerprint of a specific certificate file, with a
+specific digest algorithm, run: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -<i>digest</i> -in <i>certfile</i>.pem
+</pre>
+</blockquote>
+
+<p> The text to the right of "=" sign is the desired fingerprint.
+For example: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -noout -fingerprint -sha1 -in cert.pem
+SHA1 Fingerprint=D4:6A:AB:19:24:79:F8:32:BB:A6:CB:66:82:C0:8E:9B:EE:29:A8:1A
+</pre>
+</blockquote>
+
+<p> Example: client-certificate access table, with sha1 fingerprints: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+ smtpd_tls_fingerprint_digest = sha1
+ smtpd_client_restrictions =
+ check_ccert_access hash:/etc/postfix/access,
+ reject
+</pre>
+<pre>
+/etc/postfix/access:
+ # Action folded to next line...
+ AF:88:7C:AD:51:95:6F:36:96:F6:01:FB:2E:48:CD:AB:49:25:A2:3B
+ OK
+ 85:16:78:FD:73:6E:CE:70:E0:31:5F:0D:3C:C8:6D:C4:2C:24:59:E1
+ permit_auth_destination
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
%PARAM lmtp_pix_workaround_maps
<p> The LMTP-specific version of the smtp_pix_workaround_maps
<p> This feature is available in Postfix 2.5 and later. </p>
-%PARAM <i>transport</i>_destination_concurrency_failed_cohort_limit $default_destination_concurrency_failed_cohort_limit
+%PARAM transport_destination_concurrency_failed_cohort_limit $default_destination_concurrency_failed_cohort_limit
<p> A transport-specific override for the
default_destination_concurrency_failed_cohort_limit parameter value,
{
static VSTRING *request;
static VSTRING *ident;
- static ANVIL_REQ_TABLE request_table[] = {
+ static const ANVIL_REQ_TABLE request_table[] = {
ANVIL_REQ_CONN, anvil_remote_connect,
ANVIL_REQ_MAIL, anvil_remote_mail,
ANVIL_REQ_RCPT, anvil_remote_rcpt,
ANVIL_REQ_LOOKUP, anvil_remote_lookup,
0, 0,
};
- ANVIL_REQ_TABLE *rp;
+ const ANVIL_REQ_TABLE *rp;
/*
* Sanity check. This service takes no command-line arguments.
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_ANVIL_TIME_UNIT, DEF_ANVIL_TIME_UNIT, &var_anvil_time_unit, 1, 0,
VAR_ANVIL_STAT_TIME, DEF_ANVIL_STAT_TIME, &var_anvil_stat_time, 1, 0,
0,
int main(int argc, char **argv)
{
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0,
0,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 0, 8640000,
VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
0,
};
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_BOUNCE_RCPT, DEF_BOUNCE_RCPT, &var_bounce_rcpt, 1, 0,
VAR_2BOUNCE_RCPT, DEF_2BOUNCE_RCPT, &var_2bounce_rcpt, 1, 0,
#define STRING_AND_LEN(x) (x), (sizeof(x) - 1)
-static BOUNCE_TIME_DIVISOR time_divisors[] = {
+static const BOUNCE_TIME_DIVISOR time_divisors[] = {
STRING_AND_LEN("seconds"), 1,
STRING_AND_LEN("minutes"), 60,
STRING_AND_LEN("hours"), 60 * 60,
int *value; /* parameter value */
} BOUNCE_TIME_PARAMETER;
-static BOUNCE_TIME_PARAMETER time_parameter[] = {
+static const BOUNCE_TIME_PARAMETER time_parameter[] = {
STRING_AND_LEN(VAR_DELAY_WARN_TIME), &var_delay_warn_time,
STRING_AND_LEN(VAR_MAX_QUEUE_TIME), &var_max_queue_time,
0, 0,
char *context)
{
BOUNCE_TEMPLATE *tp = (BOUNCE_TEMPLATE *) context;
- BOUNCE_TIME_PARAMETER *bp;
- BOUNCE_TIME_DIVISOR *bd;
+ const BOUNCE_TIME_PARAMETER *bp;
+ const BOUNCE_TIME_DIVISOR *bd;
static VSTRING *buf;
int result;
{
const char *myname = "cleanup_bounce";
VSTRING *buf = vstring_alloc(100);
- CLEANUP_STAT_DETAIL *detail;
+ const CLEANUP_STAT_DETAIL *detail;
DSN_SPLIT dp;
const char *dsn_status;
const char *dsn_text;
void cleanup_pre_jail(char *unused_name, char **unused_argv)
{
- static NAME_MASK send_canon_class_table[] = {
+ static const NAME_MASK send_canon_class_table[] = {
CANON_CLASS_ENV_FROM, CLEANUP_CANON_FLAG_ENV_FROM,
CANON_CLASS_HDR_FROM, CLEANUP_CANON_FLAG_HDR_FROM,
0,
};
- static NAME_MASK rcpt_canon_class_table[] = {
+ static const NAME_MASK rcpt_canon_class_table[] = {
CANON_CLASS_ENV_RCPT, CLEANUP_CANON_FLAG_ENV_RCPT,
CANON_CLASS_HDR_RCPT, CLEANUP_CANON_FLAG_HDR_RCPT,
0,
};
- static NAME_MASK canon_class_table[] = {
+ static const NAME_MASK canon_class_table[] = {
CANON_CLASS_ENV_FROM, CLEANUP_CANON_FLAG_ENV_FROM,
CANON_CLASS_ENV_RCPT, CLEANUP_CANON_FLAG_ENV_RCPT,
CANON_CLASS_HDR_FROM, CLEANUP_CANON_FLAG_HDR_FROM,
0,
};
- static NAME_MASK masq_class_table[] = {
+ static const NAME_MASK masq_class_table[] = {
MASQ_CLASS_ENV_FROM, CLEANUP_MASQ_FLAG_ENV_FROM,
MASQ_CLASS_ENV_RCPT, CLEANUP_MASQ_FLAG_ENV_RCPT,
MASQ_CLASS_HDR_FROM, CLEANUP_MASQ_FLAG_HDR_FROM,
/* cleanup_rewrite_sender - sender address rewriting */
-static void cleanup_rewrite_sender(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts,
+static void cleanup_rewrite_sender(CLEANUP_STATE *state,
+ const HEADER_OPTS *hdr_opts,
VSTRING *header_buf)
{
TOK822 *tree;
/* cleanup_rewrite_recip - recipient address rewriting */
-static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts,
+static void cleanup_rewrite_recip(CLEANUP_STATE *state,
+ const HEADER_OPTS *hdr_opts,
VSTRING *header_buf)
{
TOK822 *tree;
* queue record processing, and prevents bounces from being sent.
*/
if (STREQUAL(value, "REJECT", command_len)) {
- CLEANUP_STAT_DETAIL *detail;
+ const CLEANUP_STAT_DETAIL *detail;
if (state->reason)
myfree(state->reason);
/* cleanup_header_callback - process one complete header line */
static void cleanup_header_callback(void *context, int header_class,
- HEADER_OPTS *hdr_opts, VSTRING *header_buf,
+ const HEADER_OPTS *hdr_opts,
+ VSTRING *header_buf,
off_t unused_offset)
{
CLEANUP_STATE *state = (CLEANUP_STATE *) context;
const char *buf, ssize_t len)
{
const char *myname = "cleanup_message_headerbody";
- MIME_STATE_DETAIL *detail;
+ const MIME_STATE_DETAIL *detail;
const char *cp;
char *dst;
static const char *cleanup_milter_error(CLEANUP_STATE *state, int err)
{
const char *myname = "cleanup_milter_error";
- CLEANUP_STAT_DETAIL *dp;
+ const CLEANUP_STAT_DETAIL *dp;
/*
* For consistency with error reporting within the milter infrastructure,
off_t curr_offset; /* offset after found record */
off_t ptr_offset; /* pointer to found record */
VSTRING *ptr_buf = 0;
- int rec_type;
+ int rec_type = REC_TYPE_ERROR;
int last_type;
ssize_t len;
int hdr_count = 0;
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_FFLUSH_REFRESH, DEF_FFLUSH_REFRESH, &var_fflush_refresh, 1, 0,
VAR_FFLUSH_PURGE, DEF_FFLUSH_PURGE, &var_fflush_purge, 1, 0,
0,
/* const char *cleanup_strerror(code)
/* int code;
/*
-/* CLEANUP_STAT_DETAIL *cleanup_stat_detail(code)
+/* const CLEANUP_STAT_DETAIL *cleanup_stat_detail(code)
/* int code;
/* DESCRIPTION
/* cleanup_strerror() maps a status code returned by the \fIcleanup\fR
* multiple errors, to it is important to list the most severe errors first,
* because cleanup_strerror() can report only one error.
*/
-static CLEANUP_STAT_DETAIL cleanup_stat_map[] = {
+static const 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_detail - map status code to table entry with assorted data */
-CLEANUP_STAT_DETAIL *cleanup_stat_detail(unsigned status)
+const CLEANUP_STAT_DETAIL *cleanup_stat_detail(unsigned status)
{
unsigned i;
} CLEANUP_STAT_DETAIL;
extern const char *cleanup_strerror(unsigned);
-extern CLEANUP_STAT_DETAIL *cleanup_stat_detail(unsigned);
+extern const CLEANUP_STAT_DETAIL *cleanup_stat_detail(unsigned);
extern const char *cleanup_strflags(unsigned);
/* LICENSE
* even know about, because map types may be added dynamically on some
* platforms.
*/
-static NAME_CODE data_redirect_map_types[] = {
+static const NAME_CODE data_redirect_map_types[] = {
DICT_TYPE_HASH, 1,
DICT_TYPE_BTREE, 1,
DICT_TYPE_DBM, 1,
{
char *cp;
DELIVERED_HDR_INFO *info;
- HEADER_OPTS *hdr;
+ const HEADER_OPTS *hdr;
/*
* Sanity check.
/* Application-specific. */
-static NAME_MASK dsn_notify_table[] = {
+static const NAME_MASK dsn_notify_table[] = {
"NEVER", DSN_NOTIFY_NEVER,
"SUCCESS", DSN_NOTIFY_SUCCESS,
"FAILURE", DSN_NOTIFY_FAILURE,
0, 0,
};
-static NAME_CODE dsn_ret_table[] = {
+static const NAME_CODE dsn_ret_table[] = {
"FULL", DSN_RET_FULL,
"HDRS", DSN_RET_HDRS,
0, 0,
/*
* The lookup table.
*/
-static NAME_MASK ehlo_mask_table[] = {
+static const NAME_MASK ehlo_mask_table[] = {
"8BITMIME", EHLO_MASK_8BITMIME,
"AUTH", EHLO_MASK_AUTH,
"ETRN", EHLO_MASK_ETRN,
int ext_prop_mask(const char *param_name, const char *pattern)
{
- static NAME_MASK table[] = {
+ static const NAME_MASK table[] = {
"canonical", EXT_PROP_CANONICAL,
"virtual", EXT_PROP_VIRTUAL,
"alias", EXT_PROP_ALIAS,
/* void *context;
/* HBC_CHECKS *hbc;
/* int header_class;
-/* HEADER_OPTS *hdr_opts;
+/* const HEADER_OPTS *hdr_opts;
/* VSTRING *header;
/*
/* char *hbc_body_checks(context, hbc, body_line, body_line_len)
/* hbc_header_checks - process one complete header line */
char *hbc_header_checks(void *context, HBC_CHECKS *hbc, int header_class,
- HEADER_OPTS *hdr_opts,
+ const HEADER_OPTS *hdr_opts,
VSTRING *header, off_t offset)
{
const char *myname = "hbc_header_checks";
/* head_out - MIME_STATE header call-back */
-static void head_out(void *context, int header_class, HEADER_OPTS *header_info,
+static void head_out(void *context, int header_class,
+ const HEADER_OPTS *header_info,
VSTRING *buf, off_t offset)
{
HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
HBC_CALL_BACKS *);
extern HBC_CHECKS *hbc_body_checks_create(const char *, const char *,
HBC_CALL_BACKS *);
-extern char *hbc_header_checks(void *, HBC_CHECKS *, int, HEADER_OPTS *,
+extern char *hbc_header_checks(void *, HBC_CHECKS *, int, const HEADER_OPTS *,
VSTRING *, off_t);
extern char *hbc_body_checks(void *, HBC_CHECKS *, const char *, ssize_t, off_t);
/* SYNOPSIS
/* #include <header_opts.h>
/*
-/* HEADER_OPTS *header_opts_find(string)
+/* const HEADER_OPTS *header_opts_find(string)
/* const char *string;
/* DESCRIPTION
/* header_opts_find() takes a message header line and looks up control
* Header names are given in the preferred capitalization. The lookups are
* case-insensitive.
*/
-static HEADER_OPTS header_opts[] = {
+static const HEADER_OPTS header_opts[] = {
"Apparently-To", HDR_APPARENTLY_TO, HDR_OPT_RECIP,
"Bcc", HDR_BCC, HDR_OPT_DROP | HDR_OPT_XRECIP,
"Cc", HDR_CC, HDR_OPT_XRECIP,
static void header_opts_init(void)
{
- HEADER_OPTS *hp;
+ const HEADER_OPTS *hp;
const char *cp;
/*
/* header_opts_find - look up header options */
-HEADER_OPTS *header_opts_find(const char *string)
+const HEADER_OPTS *header_opts_find(const char *string)
{
const char *cp;
trimblanks(vstring_str(header_key), cp - string)
- vstring_str(header_key));
VSTRING_TERMINATE(header_key);
- return ((HEADER_OPTS *) htable_find(header_hash, vstring_str(header_key)));
+ return ((const HEADER_OPTS *) htable_find(header_hash, vstring_str(header_key)));
}
#define HDR_OPT_XRECIP (HDR_OPT_RECIP | HDR_OPT_EXTRACT)
-extern HEADER_OPTS *header_opts_find(const char *);
+extern const HEADER_OPTS *header_opts_find(const char *);
/* LICENSE
/* .ad
int input_transp_mask(const char *param_name, const char *pattern)
{
- static NAME_MASK table[] = {
+ static const NAME_MASK table[] = {
"no_unknown_recipient_checks", INPUT_TRANSP_UNKNOWN_RCPT,
"no_address_mappings", INPUT_TRANSP_ADDRESS_MAPPING,
"no_header_body_checks", INPUT_TRANSP_HEADER_BODY,
int int_filt_flags(int class)
{
- static NAME_MASK table[] = {
+ static const NAME_MASK table[] = {
"notify", INT_FILT_NOTIFY,
"bounce", INT_FILT_BOUNCE,
0,
int max; /* upper bound or zero */
} CONFIG_TIME_TABLE;
-extern void get_mail_conf_str_table(CONFIG_STR_TABLE *);
-extern void get_mail_conf_int_table(CONFIG_INT_TABLE *);
-extern void get_mail_conf_long_table(CONFIG_LONG_TABLE *);
-extern void get_mail_conf_bool_table(CONFIG_BOOL_TABLE *);
-extern void get_mail_conf_time_table(CONFIG_TIME_TABLE *);
-extern void get_mail_conf_raw_table(CONFIG_RAW_TABLE *);
+extern void get_mail_conf_str_table(const CONFIG_STR_TABLE *);
+extern void get_mail_conf_int_table(const CONFIG_INT_TABLE *);
+extern void get_mail_conf_long_table(const CONFIG_LONG_TABLE *);
+extern void get_mail_conf_bool_table(const CONFIG_BOOL_TABLE *);
+extern void get_mail_conf_time_table(const CONFIG_TIME_TABLE *);
+extern void get_mail_conf_raw_table(const CONFIG_RAW_TABLE *);
/*
* Tables to initialize parameters from the global configuration file or
int *target; /* pointer to global variable */
} CONFIG_BOOL_FN_TABLE;
-extern void get_mail_conf_str_fn_table(CONFIG_STR_FN_TABLE *);
-extern void get_mail_conf_int_fn_table(CONFIG_INT_FN_TABLE *);
-extern void get_mail_conf_long_fn_table(CONFIG_LONG_FN_TABLE *);
-extern void get_mail_conf_bool_fn_table(CONFIG_BOOL_FN_TABLE *);
-extern void get_mail_conf_raw_fn_table(CONFIG_RAW_FN_TABLE *);
+extern void get_mail_conf_str_fn_table(const CONFIG_STR_FN_TABLE *);
+extern void get_mail_conf_int_fn_table(const CONFIG_INT_FN_TABLE *);
+extern void get_mail_conf_long_fn_table(const CONFIG_LONG_FN_TABLE *);
+extern void get_mail_conf_bool_fn_table(const CONFIG_BOOL_FN_TABLE *);
+extern void get_mail_conf_raw_fn_table(const CONFIG_RAW_FN_TABLE *);
/* LICENSE
/* .ad
/* int value;
/*
/* void get_mail_conf_bool_table(table)
-/* CONFIG_BOOL_TABLE *table;
+/* const CONFIG_BOOL_TABLE *table;
/*
/* void get_mail_conf_bool_fn_table(table)
-/* CONFIG_BOOL_TABLE *table;
+/* const CONFIG_BOOL_TABLE *table;
/* DESCRIPTION
/* This module implements configuration parameter support for
/* boolean values. The internal representation is zero (false)
/* get_mail_conf_bool_table - look up table of booleans */
-void get_mail_conf_bool_table(CONFIG_BOOL_TABLE *table)
+void get_mail_conf_bool_table(const CONFIG_BOOL_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_bool(table->name, table->defval);
/* get_mail_conf_bool_fn_table - look up booleans, defaults are functions */
-void get_mail_conf_bool_fn_table(CONFIG_BOOL_FN_TABLE *table)
+void get_mail_conf_bool_fn_table(const CONFIG_BOOL_FN_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_bool_fn(table->name, table->defval);
/* int value;
/*
/* void get_mail_conf_int_table(table)
-/* CONFIG_INT_TABLE *table;
+/* const CONFIG_INT_TABLE *table;
/*
/* void get_mail_conf_int_fn_table(table)
-/* CONFIG_INT_TABLE *table;
+/* const CONFIG_INT_TABLE *table;
/* AUXILIARY FUNCTIONS
/* int get_mail_conf_int2(name1, name2, defval, min, max);
/* const char *name1;
/* get_mail_conf_int_table - look up table of integers */
-void get_mail_conf_int_table(CONFIG_INT_TABLE *table)
+void get_mail_conf_int_table(const CONFIG_INT_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_int(table->name, table->defval,
/* get_mail_conf_int_fn_table - look up integers, defaults are functions */
-void get_mail_conf_int_fn_table(CONFIG_INT_FN_TABLE *table)
+void get_mail_conf_int_fn_table(const CONFIG_INT_FN_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_int_fn(table->name, table->defval,
/* long value;
/*
/* void get_mail_conf_long_table(table)
-/* CONFIG_LONG_TABLE *table;
+/* const CONFIG_LONG_TABLE *table;
/*
/* void get_mail_conf_long_fn_table(table)
-/* CONFIG_LONG_TABLE *table;
+/* const CONFIG_LONG_TABLE *table;
/* AUXILIARY FUNCTIONS
/* int get_mail_conf_long2(name1, name2, defval, min, max);
/* const char *name1;
/* get_mail_conf_long_table - look up table of integers */
-void get_mail_conf_long_table(CONFIG_LONG_TABLE *table)
+void get_mail_conf_long_table(const CONFIG_LONG_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_long(table->name, table->defval,
/* get_mail_conf_long_fn_table - look up integers, defaults are functions */
-void get_mail_conf_long_fn_table(CONFIG_LONG_FN_TABLE *table)
+void get_mail_conf_long_fn_table(const CONFIG_LONG_FN_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_long_fn(table->name, table->defval,
/* int max;
/*
/* void get_mail_conf_raw_table(table)
-/* CONFIG_RAW_TABLE *table;
+/* const CONFIG_RAW_TABLE *table;
/*
/* void get_mail_conf_raw_fn_table(table)
-/* CONFIG_RAW_TABLE *table;
+/* const CONFIG_RAW_TABLE *table;
/* DESCRIPTION
/* This module implements support for string-valued global
/* configuration parameters that are loaded without $name expansion.
/* get_mail_conf_raw_table - look up table of strings */
-void get_mail_conf_raw_table(CONFIG_RAW_TABLE *table)
+void get_mail_conf_raw_table(const CONFIG_RAW_TABLE *table)
{
while (table->name) {
if (table->target[0])
/* get_mail_conf_raw_fn_table - look up strings, defaults are functions */
-void get_mail_conf_raw_fn_table(CONFIG_RAW_FN_TABLE *table)
+void get_mail_conf_raw_fn_table(const CONFIG_RAW_FN_TABLE *table)
{
while (table->name) {
if (table->target[0])
/* const char *value;
/*
/* void get_mail_conf_str_table(table)
-/* CONFIG_STR_TABLE *table;
+/* const CONFIG_STR_TABLE *table;
/*
/* void get_mail_conf_str_fn_table(table)
-/* CONFIG_STR_TABLE *table;
+/* const CONFIG_STR_TABLE *table;
/* AUXILIARY FUNCTIONS
/* char *get_mail_conf_str2(name, suffix, defval, min, max)
/* const char *name;
/* get_mail_conf_str_table - look up table of strings */
-void get_mail_conf_str_table(CONFIG_STR_TABLE *table)
+void get_mail_conf_str_table(const CONFIG_STR_TABLE *table)
{
while (table->name) {
if (table->target[0])
/* get_mail_conf_str_fn_table - look up strings, defaults are functions */
-void get_mail_conf_str_fn_table(CONFIG_STR_FN_TABLE *table)
+void get_mail_conf_str_fn_table(const CONFIG_STR_FN_TABLE *table)
{
while (table->name) {
if (table->target[0])
/* int value;
/*
/* void get_mail_conf_time_table(table)
-/* CONFIG_TIME_TABLE *table;
+/* const CONFIG_TIME_TABLE *table;
/* AUXILIARY FUNCTIONS
/* int get_mail_conf_time2(name1, name2, defval, def_unit, min, max);
/* const char *name1;
/* get_mail_conf_time_table - look up table of integers */
-void get_mail_conf_time_table(CONFIG_TIME_TABLE *table)
+void get_mail_conf_time_table(const CONFIG_TIME_TABLE *table)
{
while (table->name) {
table->target[0] = get_mail_conf_time(table->name, table->defval,
static int hours;
static int days;
static int weeks;
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
"seconds", "10s", &seconds, 0, 0,
"minutes", "10m", &minutes, 0, 0,
"hours", "10h", &hours, 0, 0,
struct DICT *(*open) (const char *, int, int);
} DICT_OPEN_INFO;
-static DICT_OPEN_INFO dict_open_info[] = {
+static const DICT_OPEN_INFO dict_open_info[] = {
DICT_TYPE_PROXY, dict_proxy_open,
#ifdef HAS_LDAP
DICT_TYPE_LDAP, dict_ldap_open,
void mail_dict_init(void)
{
- DICT_OPEN_INFO *dp;
+ const DICT_OPEN_INFO *dp;
for (dp = dict_open_info; dp->type; dp++)
dict_open_register(dp->type, dp->open);
* also contains a function that is being called explicitly. REF/DEF and all
* that.
*/
-NAME_MASK mail_error_masks[] = {
+const NAME_MASK mail_error_masks[] = {
"bounce", MAIL_ERROR_BOUNCE,
"2bounce", MAIL_ERROR_2BOUNCE,
"delay", MAIL_ERROR_DELAY,
#define MAIL_ERROR_2BOUNCE (1<<5)
#define MAIL_ERROR_DELAY (1<<6)
-extern NAME_MASK mail_error_masks[];
+extern const NAME_MASK mail_error_masks[];
/* LICENSE
/* .ad
void mail_params_init()
{
- static CONFIG_STR_TABLE first_str_defaults[] = {
+ static const CONFIG_STR_TABLE first_str_defaults[] = {
VAR_SYSLOG_FACILITY, DEF_SYSLOG_FACILITY, &var_syslog_facility, 1, 0,
VAR_INET_PROTOCOLS, DEF_INET_PROTOCOLS, &var_inet_protocols, 1, 0,
0,
};
- static CONFIG_STR_FN_TABLE function_str_defaults[] = {
+ static const CONFIG_STR_FN_TABLE function_str_defaults[] = {
VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
0,
};
- static CONFIG_STR_TABLE other_str_defaults[] = {
+ static const CONFIG_STR_TABLE other_str_defaults[] = {
VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name, 1, 0,
VAR_SYSLOG_NAME, DEF_SYSLOG_NAME, &var_syslog_name, 1, 0,
VAR_MAIL_OWNER, DEF_MAIL_OWNER, &var_mail_owner, 1, 0,
VAR_INT_FILT_CLASSES, DEF_INT_FILT_CLASSES, &var_int_filt_classes, 0, 0,
0,
};
- static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
+ static const CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
VAR_MYNETWORKS, mynetworks, &var_mynetworks, 0, 0,
0,
};
- static CONFIG_INT_TABLE other_int_defaults[] = {
+ static const CONFIG_INT_TABLE other_int_defaults[] = {
VAR_MAX_USE, DEF_MAX_USE, &var_use_limit, 1, 0,
VAR_DONT_REMOVE, DEF_DONT_REMOVE, &var_dont_remove, 0, 0,
VAR_LINE_LIMIT, DEF_LINE_LIMIT, &var_line_limit, 512, 0,
VAR_DELAY_MAX_RES, DEF_DELAY_MAX_RES, &var_delay_max_res, MIN_DELAY_MAX_RES, MAX_DELAY_MAX_RES,
0,
};
- static CONFIG_TIME_TABLE time_defaults[] = {
+ static const CONFIG_TIME_TABLE time_defaults[] = {
VAR_EVENT_DRAIN, DEF_EVENT_DRAIN, &var_event_drain, 1, 0,
VAR_MAX_IDLE, DEF_MAX_IDLE, &var_idle_limit, 1, 0,
VAR_IPC_TIMEOUT, DEF_IPC_TIMEOUT, &var_ipc_timeout, 1, 0,
VAR_IN_FLOW_DELAY, DEF_IN_FLOW_DELAY, &var_in_flow_delay, 0, 10,
0,
};
- static CONFIG_BOOL_TABLE bool_defaults[] = {
+ static const CONFIG_BOOL_TABLE bool_defaults[] = {
VAR_DISABLE_DNS, DEF_DISABLE_DNS, &var_disable_dns,
VAR_SOFT_BOUNCE, DEF_SOFT_BOUNCE, &var_soft_bounce,
VAR_OWNREQ_SPECIAL, DEF_OWNREQ_SPECIAL, &var_ownreq_special,
extern bool var_smtpd_tls_req_ccert;
#define VAR_SMTPD_TLS_CCERT_VD "smtpd_tls_ccert_verifydepth"
-#define DEF_SMTPD_TLS_CCERT_VD 5
+#define DEF_SMTPD_TLS_CCERT_VD 9
extern int var_smtpd_tls_ccert_vd;
#define VAR_SMTPD_TLS_CERT_FILE "smtpd_tls_cert_file"
#define DEF_SMTPD_TLS_MAND_EXCL ""
extern char *var_smtpd_tls_mand_excl;
+#define VAR_SMTPD_TLS_FPT_DGST "smtpd_tls_fingerprint_digest"
+#define DEF_SMTPD_TLS_FPT_DGST "md5"
+extern char *var_smtpd_tls_fpt_dgst;
+
#define VAR_SMTPD_TLS_512_FILE "smtpd_tls_dh512_param_file"
#define DEF_SMTPD_TLS_512_FILE ""
extern char *var_smtpd_tls_dh512_param_file;
extern char *var_smtp_tls_level;
#define VAR_SMTP_TLS_SCERT_VD "smtp_tls_scert_verifydepth"
-#define DEF_SMTP_TLS_SCERT_VD 5
+#define DEF_SMTP_TLS_SCERT_VD 9
#define VAR_LMTP_TLS_SCERT_VD "lmtp_tls_scert_verifydepth"
-#define DEF_LMTP_TLS_SCERT_VD 5
+#define DEF_LMTP_TLS_SCERT_VD 9
extern int var_smtp_tls_scert_vd;
#define VAR_SMTP_TLS_CERT_FILE "smtp_tls_cert_file"
#define DEF_LMTP_TLS_MAND_EXCL ""
extern char *var_smtp_tls_mand_excl;
+#define VAR_SMTP_TLS_FPT_DGST "smtp_tls_fingerprint_digest"
+#define DEF_SMTP_TLS_FPT_DGST "md5"
+#define VAR_LMTP_TLS_FPT_DGST "lmtp_tls_fingerprint_digest"
+#define DEF_LMTP_TLS_FPT_DGST "md5"
+extern char *var_smtp_tls_fpt_dgst;
+
#define VAR_SMTP_TLS_LOGLEVEL "smtp_tls_loglevel"
#define DEF_SMTP_TLS_LOGLEVEL 0
#define VAR_LMTP_TLS_LOGLEVEL "lmtp_tls_loglevel"
extern char *var_smtp_tls_sec_cmatch;
+#define VAR_SMTP_TLS_FPT_CMATCH "smtp_tls_fingerprint_cert_match"
+#define DEF_SMTP_TLS_FPT_CMATCH ""
+#define VAR_LMTP_TLS_FPT_CMATCH "lmtp_tls_fingerprint_cert_match"
+#define DEF_LMTP_TLS_FPT_CMATCH ""
+extern char *var_smtp_tls_fpt_cmatch;
+
/*
* SASL authentication support, SMTP server side.
*/
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20080107"
+#define MAIL_RELEASE_DATE "20080109"
#define MAIL_VERSION_NUMBER "2.5"
#ifdef SNAPSHOT
* though some systems do not use dotlock files by default (4.4BSD), such
* locks can be necessary when accessing mailbox files over NFS.
*/
-static NAME_MASK mbox_mask[] = {
+static const NAME_MASK mbox_mask[] = {
#ifdef HAS_FLOCK_LOCK
"flock", MBOX_FLOCK_LOCK,
#endif
ARGV *mbox_lock_names(void)
{
- NAME_MASK *np;
+ const NAME_MASK *np;
ARGV *argv;
argv = argv_alloc(2);
/* err_print, context)
/* int flags;
/* void (*head_out)(void *ptr, int header_class,
-/* HEADER_OPTS *header_info,
+/* const HEADER_OPTS *header_info,
/* VSTRING *buf, off_t offset);
/* void (*head_end)(void *ptr);
/* void (*body_out)(void *ptr, int rec_type,
/* .in -4
/* } MIME_STATE_DETAIL;
/*
-/* MIME_STATE_DETAIL *mime_state_detail(error_code)
+/* const MIME_STATE_DETAIL *mime_state_detail(error_code)
/* int error_code;
/* DESCRIPTION
/* This module implements a one-pass MIME processor with optional
#define MIME_ENC_BINARY 9 /* domain only */
#endif
-static MIME_ENCODING mime_encoding_map[] = { /* RFC 2045 */
+static const MIME_ENCODING mime_encoding_map[] = { /* RFC 2045 */
"7bit", MIME_ENC_7BIT, MIME_ENC_7BIT, /* domain */
"8bit", MIME_ENC_8BIT, MIME_ENC_8BIT, /* domain */
"binary", MIME_ENC_BINARY, MIME_ENC_BINARY, /* domain */
/* mime_state_content_type - process content-type header */
static void mime_state_content_type(MIME_STATE *state,
- HEADER_OPTS *header_info)
+ const HEADER_OPTS *header_info)
{
const char *cp;
ssize_t tok_count;
/* mime_state_content_encoding - process content-transfer-encoding header */
static void mime_state_content_encoding(MIME_STATE *state,
- HEADER_OPTS *header_info)
+ const HEADER_OPTS *header_info)
{
const char *cp;
- MIME_ENCODING *cmp;
+ const MIME_ENCODING *cmp;
#define PARSE_CONTENT_ENCODING_HEADER(state, ptr) \
header_token(state->token, 1, state->token_buffer, ptr, (char *) 0, 0)
static const char *mime_state_enc_name(int encoding)
{
- MIME_ENCODING *cmp;
+ const MIME_ENCODING *cmp;
for (cmp = mime_encoding_map; cmp->name != 0; cmp++)
if (encoding == cmp->encoding)
int input_is_text = (rec_type == REC_TYPE_NORM
|| rec_type == REC_TYPE_CONT);
MIME_STACK *sp;
- HEADER_OPTS *header_info;
+ const HEADER_OPTS *header_info;
const unsigned char *cp;
#define SAVE_PREV_REC_TYPE_AND_RETURN_ERR_FLAGS(state, rec_type) do { \
* must precede less serious errors, because the error-to-text conversion
* can report only one error.
*/
-static MIME_STATE_DETAIL mime_err_detail[] = {
+static const MIME_STATE_DETAIL mime_err_detail[] = {
MIME_ERR_NESTING, "5.6.0", "MIME nesting exceeds safety limit",
MIME_ERR_TRUNC_HEADER, "5.6.0", "message header length exceeds safety limit",
MIME_ERR_8BIT_IN_HEADER, "5.6.0", "improper use of 8-bit data in message header",
const char *mime_state_error(int error_code)
{
- MIME_STATE_DETAIL *mp;
+ const MIME_STATE_DETAIL *mp;
if (error_code == 0)
msg_panic("mime_state_error: there is no error");
/* mime_state_detail - error code to table entry with assorted data */
-MIME_STATE_DETAIL *mime_state_detail(int error_code)
+const MIME_STATE_DETAIL *mime_state_detail(int error_code)
{
- MIME_STATE_DETAIL *mp;
+ const MIME_STATE_DETAIL *mp;
if (error_code == 0)
msg_panic("mime_state_detail: there is no error");
#define REC_LEN 1024
-static void head_out(void *context, int class, HEADER_OPTS *unused_info,
+static void head_out(void *context, int class, const HEADER_OPTS *unused_info,
VSTRING *buf, off_t offset)
{
VSTREAM *stream = (VSTREAM *) context;
* External interface. All MIME_STATE structure members are private.
*/
typedef struct MIME_STATE MIME_STATE;
-typedef void (*MIME_STATE_HEAD_OUT) (void *, int, HEADER_OPTS *, VSTRING *, off_t);
+typedef void (*MIME_STATE_HEAD_OUT) (void *, int, const HEADER_OPTS *, VSTRING *, off_t);
typedef void (*MIME_STATE_BODY_OUT) (void *, int, const char *, ssize_t, off_t);
typedef void (*MIME_STATE_ANY_END) (void *);
typedef void (*MIME_STATE_ERR_PRINT) (void *, int, const char *, ssize_t);
#define MIME_ERR_8BIT_IN_7BIT_BODY (1<<3)
#define MIME_ERR_ENCODING_DOMAIN (1<<4)
-extern MIME_STATE_DETAIL *mime_state_detail(int);
+extern const MIME_STATE_DETAIL *mime_state_detail(int);
extern const char *mime_state_error(int);
/*
/*
* Information about available database types. Here, we list only those map
- * types that exist as files. Network-based maps are not of interest.
+ * types that support "create" operations.
+ *
+ * We use a different table (in dict_open.c) when querying maps.
*/
typedef struct {
char *type;
MKMAP *(*before_open) (const char *);
} MKMAP_OPEN_INFO;
-MKMAP_OPEN_INFO mkmap_types[] = {
+static const MKMAP_OPEN_INFO mkmap_types[] = {
DICT_TYPE_PROXY, mkmap_proxy_open,
#ifdef HAS_CDB
DICT_TYPE_CDB, mkmap_cdb_open,
int open_flags, int dict_flags)
{
MKMAP *mkmap;
- MKMAP_OPEN_INFO *mp;
+ const MKMAP_OPEN_INFO *mp;
/*
* Find out what map type to use.
#define MASK_STYLE_SUBNET (1 << 1)
#define MASK_STYLE_HOST (1 << 2)
-static NAME_MASK mask_styles[] = {
+static const NAME_MASK mask_styles[] = {
MYNETWORKS_STYLE_CLASS, MASK_STYLE_CLASS,
MYNETWORKS_STYLE_SUBNET, MASK_STYLE_SUBNET,
MYNETWORKS_STYLE_HOST, MASK_STYLE_HOST,
char **cpp;
ARGV *argv;
DSN_SPLIT dp;
- SYS_EXITS_DETAIL *sp;
+ const SYS_EXITS_DETAIL *sp;
/*
* Process the variadic argument list. This also does sanity checks on
/* const char *sys_exits_strerror(code)
/* int code;
/*
-/* SYS_EXITS_DETAIL *sys_exits_detail(code)
+/* const SYS_EXITS_DETAIL *sys_exits_detail(code)
/* int code;
/*
/* int sys_exits_softerror(code)
/* Application-specific. */
-static SYS_EXITS_DETAIL sys_exits_table[] = {
+static const SYS_EXITS_DETAIL sys_exits_table[] = {
EX_USAGE, "5.3.0", "command line usage error",
EX_DATAERR, "5.6.0", "data format error",
EX_NOINPUT, "5.3.0", "cannot open input",
/* sys_exits_detail - map exit status info table entry */
-SYS_EXITS_DETAIL *sys_exits_detail(int code)
+const SYS_EXITS_DETAIL *sys_exits_detail(int code)
{
if (!SYS_EXITS_CODE(code)) {
return (sys_exits_fake(code));
} SYS_EXITS_DETAIL;
extern const char *sys_exits_strerror(int);
-extern SYS_EXITS_DETAIL *sys_exits_detail(int);
+extern const SYS_EXITS_DETAIL *sys_exits_detail(int);
extern int sys_exits_softerror(int);
#define SYS_EXITS_CODE(n) ((n) >= EX__BASE && (n) <= EX__MAX)
static void local_mask_init(void)
{
- static NAME_MASK file_mask[] = {
+ static const NAME_MASK file_mask[] = {
"alias", EXPAND_TYPE_ALIAS,
"forward", EXPAND_TYPE_FWD,
"include", EXPAND_TYPE_INCL,
0,
};
- static NAME_MASK command_mask[] = {
+ static const NAME_MASK command_mask[] = {
"alias", EXPAND_TYPE_ALIAS,
"forward", EXPAND_TYPE_FWD,
"include", EXPAND_TYPE_INCL,
0,
};
- static NAME_MASK deliver_mask[] = {
+ static const NAME_MASK deliver_mask[] = {
"command", DELIVER_HDR_CMD,
"file", DELIVER_HDR_FILE,
"forward", DELIVER_HDR_FWD,
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
0,
};
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
VAR_MAILBOX_LIMIT, DEF_MAILBOX_LIMIT, &var_mailbox_limit, 0, 0,
0,
};
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
VAR_HOME_MAILBOX, DEF_HOME_MAILBOX, &var_home_mailbox, 0, 0,
VAR_ALLOW_COMMANDS, DEF_ALLOW_COMMANDS, &var_allow_commands, 0, 0,
VAR_MAILBOX_CMD_MAPS, DEF_MAILBOX_CMD_MAPS, &var_mailbox_cmd_maps, 0, 0,
0,
};
- static CONFIG_BOOL_TABLE bool_table[] = {
+ static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_BIFF, DEF_BIFF, &var_biff,
VAR_EXP_OWN_ALIAS, DEF_EXP_OWN_ALIAS, &var_exp_own_alias,
VAR_STAT_HOME_DIR, DEF_STAT_HOME_DIR, &var_stat_home_dir,
};
/* Suppress $name expansion upon loading. */
- static CONFIG_RAW_TABLE raw_table[] = {
+ static const CONFIG_RAW_TABLE raw_table[] = {
VAR_EXEC_DIRECTORY, DEF_EXEC_DIRECTORY, &var_exec_directory, 0, 0,
VAR_FORWARD_PATH, DEF_FORWARD_PATH, &var_forward_path, 0, 0,
VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0,
#define MASTER_SERV_TYPE_UNIX 1 /* AF_UNIX domain socket */
#define MASTER_SERV_TYPE_INET 2 /* AF_INET domain socket */
#define MASTER_SERV_TYPE_FIFO 3 /* fifo (named pipe) */
-#ifdef SNAPSHOT /* see also master_proto.h */
#define MASTER_SERV_TYPE_PASS 4 /* AF_UNIX domain socket */
-#endif
/*
* Default process management policy values. This is only the bare minimum.
/* inet_listen(3), internet-domain listener
/* unix_listen(3), unix-domain listener
/* fifo_listen(3), named-pipe listener
+/* upass_listen(3), file descriptor passing listener
/* set_eugid(3), set effective user/group attributes
/* LICENSE
/* .ad
#define MASTER_XPORT_NAME_UNIX "unix" /* local IPC */
#define MASTER_XPORT_NAME_FIFO "fifo" /* local IPC */
#define MASTER_XPORT_NAME_INET "inet" /* non-local IPC */
-#ifdef SNAPSHOT /* see also master.h */
#define MASTER_XPORT_NAME_PASS "pass" /* local IPC */
-#endif
/*
* Format of a status message sent by a child process to the process
void master_vars_init(void)
{
char *path;
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_INET_PROTOCOLS, DEF_INET_PROTOCOLS, &var_inet_protocols, 1, 0,
0,
};
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0,
0,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_THROTTLE_TIME, DEF_THROTTLE_TIME, &var_throttle_time, 1, 0,
0,
};
/* inet_trigger(3), internet-domain client
/* unix_trigger(3), unix-domain client
/* fifo_trigger(3), fifo client
+/* upass_trigger(3), file descriptor passing client
/* LICENSE
/* .ad
/* .fi
/* milter8_header - milter8_message call-back for message header */
static void milter8_header(void *ptr, int unused_header_class,
- HEADER_OPTS *header_info,
+ const HEADER_OPTS *header_info,
VSTRING *buf, off_t unused_offset)
{
const char *myname = "milter8_header";
MILTER8 *milter = (MILTER8 *) m;
MIME_STATE *mime_state;
int rec_type;
- MIME_STATE_DETAIL *detail;
+ const MIME_STATE_DETAIL *detail;
int mime_errs = 0;
MILTER_MSG_CONTEXT msg_ctx;
VSTRING *buf;
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0,
VAR_CONC_POS_FDBACK, DEF_CONC_POS_FDBACK, &var_conc_pos_feedback, 1, 0,
VAR_CONC_NEG_FDBACK, DEF_CONC_NEG_FDBACK, &var_conc_neg_feedback, 1, 0,
0,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0,
VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0,
VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0,
VAR_DEST_RATE_DELAY, DEF_DEST_RATE_DELAY, &var_dest_rate_delay, 0, 0,
0,
};
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0,
VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0,
VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0,
VAR_CONC_COHORT_LIM, DEF_CONC_COHORT_LIM, &var_conc_cohort_limit, 0, 0,
0,
};
- static CONFIG_BOOL_TABLE bool_table[] = {
+ static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off,
VAR_CONC_FDBACK_DEBUG, DEF_CONC_FDBACK_DEBUG, &var_conc_feedback_debug,
0,
/*
* Lookup tables for main.cf feedback method names.
*/
-NAME_CODE qmgr_feedback_map[] = {
+const NAME_CODE qmgr_feedback_map[] = {
CONC_FDBACK_NAME_WIN, QMGR_FEEDBACK_IDX_WIN,
#ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
CONC_FDBACK_NAME_SQRT_WIN, QMGR_FEEDBACK_IDX_SQRT_WIN,
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
0,
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
0,
};
# or lower/upper bounds still result in "postconf -d" duplicates,
# which are a sign of an error somewhere...
-/^(static| )*CONFIG_INT_TABLE .*\{/,/\};/ {
+/^(static| )*(const +)?CONFIG_INT_TABLE .*\{/,/\};/ {
if ($1 ~ /VAR/) {
print "int " substr($3,2,length($3)-2) ";" > "int_vars.h"
if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
}
}
}
-/^(static| )*CONFIG_STR_TABLE .*\{/,/\};/ {
+/^(static| )*(const +)?CONFIG_STR_TABLE .*\{/,/\};/ {
if ($1 ~ /^VAR/) {
print "char *" substr($3,2,length($3)-2) ";" > "str_vars.h"
if (++stab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
}
}
}
-/^(static| )*CONFIG_RAW_TABLE .*\{/,/\};/ {
+/^(static| )*(const +)?CONFIG_RAW_TABLE .*\{/,/\};/ {
if ($1 ~ /^VAR/) {
print "char *" substr($3,2,length($3)-2) ";" > "raw_vars.h"
if (++rtab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
}
}
}
-/^(static| )*CONFIG_BOOL_TABLE .*\{/,/\};/ {
+/^(static| )*(const +)?CONFIG_BOOL_TABLE .*\{/,/\};/ {
if ($1 ~ /^VAR/) {
print "int " substr($3,2,length($3)-2) ";" > "bool_vars.h"
if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
}
}
}
-/^(static| )*CONFIG_TIME_TABLE .*\{/,/\};/ {
+/^(static| )*(const +)?CONFIG_TIME_TABLE .*\{/,/\};/ {
if ($1 ~ /^VAR/) {
print "int " substr($3,2,length($3)-2) ";" > "time_vars.h"
if (++ttab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
/*
* Lookup tables generated by scanning actual C source files.
*/
-static CONFIG_TIME_TABLE time_table[] = {
+static const CONFIG_TIME_TABLE time_table[] = {
#include "time_table.h"
0,
};
-static CONFIG_BOOL_TABLE bool_table[] = {
+static const CONFIG_BOOL_TABLE bool_table[] = {
#include "bool_table.h"
0,
};
-static CONFIG_INT_TABLE int_table[] = {
+static const CONFIG_INT_TABLE int_table[] = {
#include "int_table.h"
0,
};
-static CONFIG_STR_TABLE str_table[] = {
+static const CONFIG_STR_TABLE str_table[] = {
#include "str_table.h"
#include "auto_table.h" /* XXX */
#include "install_table.h"
0,
};
-static CONFIG_RAW_TABLE raw_table[] = {
+static const CONFIG_RAW_TABLE raw_table[] = {
#include "raw_table.h"
0,
};
static const char *check_mydomainname(void);
static const char *check_mynetworks(void);
-static CONFIG_STR_FN_TABLE str_fn_table[] = {
+static const CONFIG_STR_FN_TABLE str_fn_table[] = {
VAR_MYHOSTNAME, check_myhostname, &var_myhostname, 1, 0,
VAR_MYDOMAIN, check_mydomainname, &var_mydomain, 1, 0,
0,
};
-static CONFIG_STR_FN_TABLE str_fn_table_2[] = {
+static const CONFIG_STR_FN_TABLE str_fn_table_2[] = {
VAR_MYNETWORKS, check_mynetworks, &var_mynetworks, 1, 0,
0,
};
static void hash_parameters(void)
{
- CONFIG_TIME_TABLE *ctt;
- CONFIG_BOOL_TABLE *cbt;
- CONFIG_INT_TABLE *cit;
- CONFIG_STR_TABLE *cst;
- CONFIG_STR_FN_TABLE *csft;
- CONFIG_RAW_TABLE *rst;
+ const CONFIG_TIME_TABLE *ctt;
+ const CONFIG_BOOL_TABLE *cbt;
+ const CONFIG_INT_TABLE *cit;
+ const CONFIG_STR_TABLE *cst;
+ const CONFIG_STR_FN_TABLE *csft;
+ const CONFIG_RAW_TABLE *rst;
param_table = htable_create(100);
*/
char *var_submit_acl;
-static CONFIG_STR_TABLE str_table[] = {
+static const CONFIG_STR_TABLE str_table[] = {
VAR_SUBMIT_ACL, DEF_SUBMIT_ACL, &var_submit_acl, 0, 0,
0,
};
int fd;
int ch;
ARGV *import_env;
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_SENDMAIL_PATH, DEF_SENDMAIL_PATH, &var_sendmail_path, 1, 0,
VAR_MAILQ_PATH, DEF_MAILQ_PATH, &var_mailq_path, 1, 0,
VAR_NEWALIAS_PATH, DEF_NEWALIAS_PATH, &var_newalias_path, 1, 0,
char *var_flush_acl;
char *var_showq_acl;
-static CONFIG_STR_TABLE str_table[] = {
+static const CONFIG_STR_TABLE str_table[] = {
VAR_FLUSH_ACL, DEF_FLUSH_ACL, &var_flush_acl, 0, 0,
VAR_SHOWQ_ACL, DEF_SHOWQ_ACL, &var_showq_acl, 0, 0,
0,
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
VAR_VIRT_ALIAS_DOMS, DEF_VIRT_ALIAS_DOMS, &var_virt_alias_doms, 0, 0,
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0,
VAR_CONC_POS_FDBACK, DEF_CONC_POS_FDBACK, &var_conc_pos_feedback, 1, 0,
VAR_CONC_NEG_FDBACK, DEF_CONC_NEG_FDBACK, &var_conc_neg_feedback, 1, 0,
0,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0,
VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0,
VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0,
VAR_DEST_RATE_DELAY, DEF_DEST_RATE_DELAY, &var_dest_rate_delay, 0, 0,
0,
};
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0,
VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0,
VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0,
VAR_CONC_COHORT_LIM, DEF_CONC_COHORT_LIM, &var_conc_cohort_limit, 0, 0,
0,
};
- static CONFIG_BOOL_TABLE bool_table[] = {
+ static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off,
VAR_CONC_FDBACK_DEBUG, DEF_CONC_FDBACK_DEBUG, &var_conc_feedback_debug,
0,
/*
* Lookup tables for main.cf feedback method names.
*/
-NAME_CODE qmgr_feedback_map[] = {
+const NAME_CODE qmgr_feedback_map[] = {
CONC_FDBACK_NAME_WIN, QMGR_FEEDBACK_IDX_WIN,
#ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
CONC_FDBACK_NAME_SQRT_WIN, QMGR_FEEDBACK_IDX_SQRT_WIN,
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_QMTPD_TMOUT, DEF_QMTPD_TMOUT, &var_qmqpd_timeout, 1, 0,
VAR_QMTPD_ERR_SLEEP, DEF_QMTPD_ERR_SLEEP, &var_qmqpd_err_sleep, 0, 0,
0,
};
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
VAR_QMQPD_CLIENTS, DEF_QMQPD_CLIENTS, &var_qmqpd_clients, 0, 0,
VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
0,
};
- static CONFIG_BOOL_TABLE bool_table[] = {
+ static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_QMQPD_CLIENT_PORT_LOG, DEF_QMQPD_CLIENT_PORT_LOG, &var_qmqpd_client_port_log,
0,
};
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_SCACHE_TTL_LIM, DEF_SCACHE_TTL_LIM, &var_scache_ttl_lim, 1, 0,
VAR_SCACHE_STAT_TIME, DEF_SCACHE_STAT_TIME, &var_scache_stat_time, 1, 0,
0,
*/
char *var_submit_acl;
-static CONFIG_STR_TABLE str_table[] = {
+static const CONFIG_STR_TABLE str_table[] = {
VAR_SUBMIT_ACL, DEF_SUBMIT_ACL, &var_submit_acl, 0, 0,
0,
};
/* output_header - output one message header */
static void output_header(void *context, int header_class,
- HEADER_OPTS *header_info,
+ const HEADER_OPTS *header_info,
VSTRING *buf, off_t offset)
{
SM_STATE *state = (SM_STATE *) context;
int main(int argc, char **argv)
{
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
0,
};
- static CONFIG_STR_TABLE lmtp_str_table[] = {
+ static const CONFIG_STR_TABLE lmtp_str_table[] = {
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
VAR_LMTP_TLS_MAND_CIPH, DEF_LMTP_TLS_MAND_CIPH, &var_smtp_tls_mand_ciph, 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_MAND_PROTO, DEF_LMTP_TLS_MAND_PROTO, &var_smtp_tls_mand_proto, 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,
+ VAR_LMTP_TLS_FPT_CMATCH, DEF_LMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
+ VAR_LMTP_TLS_FPT_DGST, DEF_LMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 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_BODY_CHKS, DEF_LMTP_BODY_CHKS, &var_smtp_body_chks, 0, 0,
0,
};
- static CONFIG_TIME_TABLE lmtp_time_table[] = {
+ static const CONFIG_TIME_TABLE lmtp_time_table[] = {
VAR_LMTP_CONN_TMOUT, DEF_LMTP_CONN_TMOUT, &var_smtp_conn_tmout, 0, 0,
VAR_LMTP_HELO_TMOUT, DEF_LMTP_HELO_TMOUT, &var_smtp_helo_tmout, 1, 0,
VAR_LMTP_XFWD_TMOUT, DEF_LMTP_XFWD_TMOUT, &var_smtp_xfwd_tmout, 1, 0,
VAR_SCACHE_PROTO_TMOUT, DEF_SCACHE_PROTO_TMOUT, &var_scache_proto_tmout, 1, 0,
0,
};
- static CONFIG_INT_TABLE lmtp_int_table[] = {
+ static const CONFIG_INT_TABLE lmtp_int_table[] = {
VAR_LMTP_LINE_LIMIT, DEF_LMTP_LINE_LIMIT, &var_smtp_line_limit, 0, 0,
VAR_LMTP_MXADDR_LIMIT, DEF_LMTP_MXADDR_LIMIT, &var_smtp_mxaddr_limit, 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,
};
- static CONFIG_BOOL_TABLE lmtp_bool_table[] = {
+ static const CONFIG_BOOL_TABLE lmtp_bool_table[] = {
VAR_LMTP_SKIP_5XX, DEF_LMTP_SKIP_5XX, &var_smtp_skip_5xx_greeting,
VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
VAR_LMTP_SASL_ENABLE, DEF_LMTP_SASL_ENABLE, &var_smtp_sasl_enable,
/* 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 protocols that the Postfix SMTP client will use
-/* with mandatory TLS encryption.
-/* .IP "\fBsmtp_tls_scert_verifydepth (5)\fR"
+/* List of SSL/TLS protocols that the Postfix SMTP client will use with
+/* mandatory TLS encryption.
+/* .IP "\fBsmtp_tls_scert_verifydepth (9)\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
/* The SASL authentication security options that the Postfix SMTP
/* client uses for TLS encrypted SMTP sessions with a verified server
/* certificate.
+/* .PP
+/* Available in Postfix version 2.5 and later:
+/* .IP "\fBsmtp_tls_fingerprint_cert_match (empty)\fR"
+/* List of acceptable remote SMTP server certificate fingerprints
+/* for the "fingerprint" TLS security level (\fBsmtp_tls_security_level\fR =
+/* fingerprint).
+/* .IP "\fBsmtp_tls_fingerprint_digest (md5)\fR"
+/* The message digest algorithm used to construct remote SMTP server
+/* certificate fingerprints.
/* OBSOLETE STARTTLS CONTROLS
/* .ad
/* .fi
/* SuSE Rhein/Main AG
/* 65760 Eschborn, Germany
/*
-/* Connection caching in cooperation with:
-/* Victor Duchovni
-/* Morgan Stanley
-/*
/* TLS support originally by:
/* Lutz Jaenicke
/* BTU Cottbus
/* Allgemeine Elektrotechnik
/* Universitaetsplatz 3-4
/* D-03044 Cottbus, Germany
+/*
+/* Revised TLS and SMTP connection cache support by:
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
char *var_smtp_tls_sec_cmatch;
int var_smtp_tls_scert_vd;
char *var_smtp_tls_vfy_cmatch;
-int var_tls_daemon_rand_bytes;
+char *var_smtp_tls_fpt_cmatch;
+char *var_smtp_tls_fpt_dgst;
#endif
#ifdef USE_TLS
/*
- * OpenSSL client state.
+ * OpenSSL client state (opaque handle)
*/
-SSL_CTX *smtp_tls_ctx;
+TLS_APPL_STATE *smtp_tls_ctx;
#endif
static void post_init(char *unused_name, char **unused_argv)
{
- static NAME_MASK lookup_masks[] = {
+ static const NAME_MASK lookup_masks[] = {
SMTP_HOST_LOOKUP_DNS, SMTP_HOST_FLAG_DNS,
SMTP_HOST_LOOKUP_NATIVE, SMTP_HOST_FLAG_NATIVE,
0,
VAR_SMTP_SASL_ENABLE);
#endif
- if (*var_smtp_tls_level)
- use_tls = tls_level_lookup(var_smtp_tls_level) > TLS_LEV_NONE;
- else
- use_tls = var_smtp_enforce_tls || var_smtp_use_tls;
+ if (*var_smtp_tls_level != 0)
+ switch (tls_level_lookup(var_smtp_tls_level)) {
+ case TLS_LEV_SECURE:
+ case TLS_LEV_VERIFY:
+ case TLS_LEV_FPRINT:
+ case TLS_LEV_ENCRYPT:
+ var_smtp_use_tls = var_smtp_enforce_tls = 1;
+ break;
+ case TLS_LEV_MAY:
+ var_smtp_use_tls = 1;
+ var_smtp_enforce_tls = 0;
+ break;
+ case TLS_LEV_NONE:
+ var_smtp_use_tls = var_smtp_enforce_tls = 0;
+ break;
+ default:
+ /* tls_level_lookup() logs no warning. */
+ /* session_tls_init() assumes that var_smtp_tls_level is sane. */
+ msg_fatal("Invalid TLS level \"%s\"", var_smtp_tls_level);
+ }
+ use_tls = (var_smtp_use_tls || var_smtp_enforce_tls);
/*
* Initialize the TLS data before entering the chroot jail
*/
if (use_tls || var_smtp_tls_per_site[0] || var_smtp_tls_policy[0]) {
#ifdef USE_TLS
- tls_client_init_props props;
+ 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.
+ *
+ * Large parameter lists are error-prone, so we emulate a language
+ * feature that C does not have natively: named parameter lists.
*/
- 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_ctx =
+ TLS_CLIENT_INIT(&props,
+ log_level = var_smtp_tls_loglevel,
+ verifydepth = var_smtp_tls_scert_vd,
+ cache_type = strcmp(var_procname, "smtp") == 0 ?
+ TLS_MGR_SCACHE_SMTP : TLS_MGR_SCACHE_LMTP,
+ cert_file = var_smtp_tls_cert_file,
+ key_file = var_smtp_tls_key_file,
+ dcert_file = var_smtp_tls_dcert_file,
+ dkey_file = var_smtp_tls_dkey_file,
+ CAfile = var_smtp_tls_CAfile,
+ CApath = var_smtp_tls_CApath,
+ fpt_dgst = var_smtp_tls_fpt_dgst);
smtp_tls_list_init();
#else
msg_warn("TLS has been selected, but TLS support is not compiled in");
#ifdef USE_TLS
-extern SSL_CTX *smtp_tls_ctx; /* client-side TLS engine */
+extern TLS_APPL_STATE *smtp_tls_ctx; /* client-side TLS engine */
#endif
* TLS related state, don't forget to initialize in session_tls_init()!
*/
#ifdef USE_TLS
- TLScontext_t *tls_context; /* TLS session state */
+ TLS_SESS_STATE *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 */
+ char *tls_protocols; /* Acceptable SSL protocols */
+ char *tls_grade; /* Cipher grade: "export", ... */
+ VSTRING *tls_exclusions; /* Excluded SSL ciphers */
+ ARGV *tls_matchargv; /* Cert match patterns */
#endif
SMTP_STATE *state; /* back link */
- static CONFIG_STR_TABLE smtp_str_table[] = {
+ static const CONFIG_STR_TABLE smtp_str_table[] = {
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_SMTP_FALLBACK, DEF_SMTP_FALLBACK, &var_fallback_relay, 0, 0,
VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0,
VAR_SMTP_TLS_MAND_CIPH, DEF_SMTP_TLS_MAND_CIPH, &var_smtp_tls_mand_ciph, 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_MAND_PROTO, DEF_SMTP_TLS_MAND_PROTO, &var_smtp_tls_mand_proto, 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,
+ VAR_SMTP_TLS_FPT_CMATCH, DEF_SMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
+ VAR_SMTP_TLS_FPT_DGST, DEF_SMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 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_BODY_CHKS, DEF_SMTP_BODY_CHKS, &var_smtp_body_chks, 0, 0,
0,
};
- static CONFIG_TIME_TABLE smtp_time_table[] = {
+ static const CONFIG_TIME_TABLE smtp_time_table[] = {
VAR_SMTP_CONN_TMOUT, DEF_SMTP_CONN_TMOUT, &var_smtp_conn_tmout, 0, 0,
VAR_SMTP_HELO_TMOUT, DEF_SMTP_HELO_TMOUT, &var_smtp_helo_tmout, 1, 0,
VAR_SMTP_XFWD_TMOUT, DEF_SMTP_XFWD_TMOUT, &var_smtp_xfwd_tmout, 1, 0,
VAR_SCACHE_PROTO_TMOUT, DEF_SCACHE_PROTO_TMOUT, &var_scache_proto_tmout, 1, 0,
0,
};
- static CONFIG_INT_TABLE smtp_int_table[] = {
+ static const CONFIG_INT_TABLE smtp_int_table[] = {
VAR_SMTP_LINE_LIMIT, DEF_SMTP_LINE_LIMIT, &var_smtp_line_limit, 0, 0,
VAR_SMTP_MXADDR_LIMIT, DEF_SMTP_MXADDR_LIMIT, &var_smtp_mxaddr_limit, 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,
};
- static CONFIG_BOOL_TABLE smtp_bool_table[] = {
+ static const CONFIG_BOOL_TABLE smtp_bool_table[] = {
VAR_SMTP_SKIP_5XX, DEF_SMTP_SKIP_5XX, &var_smtp_skip_5xx_greeting,
VAR_IGN_MX_LOOKUP_ERR, DEF_IGN_MX_LOOKUP_ERR, &var_ign_mx_lookup_err,
VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
char *words;
char *word;
int n;
- static NAME_CODE xforward_features[] = {
+ static const NAME_CODE xforward_features[] = {
XFORWARD_NAME, SMTP_FEATURE_XFORWARD_NAME,
XFORWARD_ADDR, SMTP_FEATURE_XFORWARD_ADDR,
XFORWARD_PORT, SMTP_FEATURE_XFORWARD_PORT,
SOCKOPT_SIZE optlen;
const char *ehlo_words;
int discard_mask;
- static NAME_MASK pix_bug_table[] = {
+ static const NAME_MASK pix_bug_table[] = {
PIX_BUG_DISABLE_ESMTP, SMTP_FEATURE_PIX_NO_ESMTP,
PIX_BUG_DELAY_DOTCRLF, SMTP_FEATURE_PIX_DELAY_DOTCRLF,
0,
translit(resp->str, "\n", " ")));
}
+ /*
+ * If the policy table specifies a bogus TLS security level, fail
+ * now.
+ */
+#ifdef USE_TLS
+ if (session->tls_level == TLS_LEV_INVALID)
+ /* Warning is already logged. */
+ return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+ SMTP_RESP_FAKE(&fake, "4.7.0"),
+ "client TLS configuration problem"));
+#endif
+
/*
* XXX Some PIX firewall versions require flush before ".<CR><LF>" so
* it does not span a packet boundary. This hurts performance so it
static int smtp_start_tls(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
- tls_client_start_props tls_props;
+ TLS_CLIENT_START_PROPS tls_props;
VSTRING *serverid;
SMTP_RESP fake;
DONT_CACHE_THIS_SESSION;
/*
- * The actual TLS handshake may succeed, but tls_client_start() may fail
- * anyway, for example because server certificate verification is
- * required but failed, or because enforce_peername is required but the
- * names listed in the server certificate don't match the peer hostname.
+ * As of Postfix 2.5, tls_client_start() tries hard to always complete
+ * the TLS handshake. It records the verification and match status in the
+ * resulting TLScontext. It is now up to the application to abort the TLS
+ * connection if it chooses.
*
* XXX When tls_client_start() fails then we don't know what state the SMTP
* connection is in, so we give up on this connection even if we are not
* required to use TLS.
*
- * XXX Other tests for specific combinations of required/failed behavior
- * follow below AFTER the tls_client_start() call. These tests should be
- * done inside tls_client_start() or its call-backs, to keep the SMTP
- * client code clean (as it is in the SMTP server).
- *
* The following assumes sites that use TLS in a perverse configuration:
* multiple hosts per hostname, or even multiple hosts per IP address.
* All this without a shared TLS session cache, and they still want to
* 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:%s:%u:%s", state->service, session->addr,
- ntohs(session->port), session->helo ? session->helo : "");
-
- /*
- * 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.
+ * 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.
*
- * - 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 need to choose between replace and append.
+ * XXX: The TLS library may salt the serverid with further details of the
+ * protocol and cipher requirements.
*
- * 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. But the name is not available when the session is revived
- * until the handshake is complete, which is too late.
- *
- * XXX: When a cached session is reloaded, its 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 from the TLS 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.
+ * Large parameter lists are error-prone, so we emulate a language feature
+ * that C does not have natively: named parameter lists.
*/
- 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_MAND_PROTO,
- session->tls_protocols));
- if (session->tls_level >= TLS_LEV_ENCRYPT)
- 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);
+ serverid = vstring_alloc(10);
+ vstring_sprintf(serverid, "%s:%s:%u:%s", state->service, session->addr,
+ ntohs(session->port), session->helo ? session->helo : "");
+ session->tls_context =
+ TLS_CLIENT_START(&tls_props,
+ ctx = smtp_tls_ctx,
+ stream = session->stream,
+ log_level = var_smtp_tls_loglevel,
+ timeout = var_smtp_starttls_tmout,
+ tls_level = session->tls_level,
+ nexthop = session->tls_nexthop,
+ host = session->host,
+ namaddr = session->namaddrport,
+ serverid = vstring_str(serverid),
+ protocols = session->tls_protocols,
+ cipher_grade = session->tls_grade,
+ cipher_exclusions
+ = vstring_str(session->tls_exclusions),
+ matchargv = session->tls_matchargv,
+ fpt_dgst = var_smtp_tls_fpt_dgst);
vstring_free(serverid);
+
if (session->tls_context == 0) {
/*
"Cannot start TLS: handshake failure"));
}
+ /*
+ * If we are verifying the server certificate and are not happy with the
+ * result, abort the delivery here. We have a usable TLS session with the
+ * server, so no need to disable I/O, ... we can even be polite and send
+ * "QUIT".
+ *
+ * See src/tls/tls_level.c. Levels above encrypt require matching. Levels >=
+ * verify require CA trust.
+ */
+ if (session->tls_level >= TLS_LEV_VERIFY)
+ if (!TLS_CERT_IS_TRUSTED(session->tls_context))
+ return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+ SMTP_RESP_FAKE(&fake, "4.7.5"),
+ "Server certificate not trusted"));
+ if (session->tls_level > TLS_LEV_ENCRYPT)
+ if (!TLS_CERT_IS_MATCHED(session->tls_context))
+ return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+ SMTP_RESP_FAKE(&fake, "4.7.5"),
+ "Server certificate not verified"));
+
/*
* At this point we have to re-negotiate the "EHLO" to reget the
* feature-list.
/* smtp_header_out - output one message header */
static void smtp_header_out(void *context, int unused_header_class,
- HEADER_OPTS *unused_info, VSTRING *buf,
- off_t offset)
+ const HEADER_OPTS *unused_info,
+ VSTRING *buf, off_t offset)
{
char *start = vstring_str(buf);
char *line;
/* smtp_header_rewrite - rewrite message header before output */
static void smtp_header_rewrite(void *context, int header_class,
- HEADER_OPTS *header_info, VSTRING *buf,
- off_t offset)
+ const HEADER_OPTS *header_info,
+ VSTRING *buf, off_t offset)
{
SMTP_STATE *state = (SMTP_STATE *) context;
int did_rewrite = 0;
static void smtp_mime_fail(SMTP_STATE *state, int mime_errs)
{
- MIME_STATE_DETAIL *detail;
+ const MIME_STATE_DETAIL *detail;
SMTP_RESP fake;
detail = mime_state_detail(mime_errs);
smtp_sasl_start(session, VAR_SMTP_SASL_OPTS,
var_smtp_sasl_opts);
#ifdef SNAPSHOT /* XXX: Not yet */
- else if (session->tls_context->peer_verified)
+ else if (TLS_CERT_IS_MATCHED(session->tls_context))
smtp_sasl_start(session, VAR_SMTP_SASL_TLSV_OPTS,
var_smtp_sasl_tlsv_opts);
#endif
/* tls_policy_lookup_one - look up destination TLS policy */
-static int tls_policy_lookup_one(SMTP_SESSION *session,
- int *site_level, int *cipher_level,
+static int tls_policy_lookup_one(SMTP_SESSION *session, int *site_level,
const char *site_name,
const char *site_class)
{
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))
+#define WHERE \
+ vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \
+ 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);
+ msg_warn("%s: invalid empty policy", WHERE);
+ *site_level = TLS_LEV_INVALID;
FREE_RETURN(1); /* No further lookups */
}
*site_level = tls_level_lookup(tok);
- if (*site_level == TLS_LEV_NOTFOUND) {
- msg_warn("%s: unknown security level '%s' ignored",
- str_context(cbuf, site_class, site_name), tok);
+ if (*site_level == TLS_LEV_INVALID) {
+ /* tls_level_lookup() logs no warning. */
+ msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
FREE_RETURN(1); /* No further lookups */
}
+
+ /*
+ * Warn about ignored attributes when TLS is disabled.
+ */
+ if (*site_level < TLS_LEV_MAY) {
+ while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0)
+ msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
+ WHERE, tok);
+ FREE_RETURN(1);
+ }
+
+ /*
+ * Errors in attributes may have security consequences, don't ignore
+ * errors that can degrade security.
+ */
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;
+ *site_level = TLS_LEV_INVALID;
+ msg_warn("%s: malformed attribute/value pair \"%s\": %s",
+ WHERE, tok, err);
+ break;
}
+ /* Only one instance per policy. */
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;
+ msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"",
+ WHERE, name, policy_name(*site_level));
+ *site_level = TLS_LEV_INVALID;
+ break;
+ }
+ if (*val == 0) {
+ msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
+ *site_level = TLS_LEV_INVALID;
+ break;
}
- *cipher_level = tls_cipher_level(val);
- if (*cipher_level == TLS_CIPHER_NONE) {
- msg_warn("%s: invalid %s value '%s' ignored",
- str_context(cbuf, site_class, site_name),
- name, val);
+ if (session->tls_grade) {
+ msg_warn("%s: attribute \"%s\" is specified multiple times",
+ WHERE, name);
+ *site_level = TLS_LEV_INVALID;
+ break;
}
+ /* set_cipher_grade() assumes this is NULL with level < encrypt */
+ session->tls_grade = mystrdup(val);
continue;
}
+ /* Only one instance per policy. */
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;
+ msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"",
+ WHERE, name, policy_name(*site_level));
+ *site_level = TLS_LEV_INVALID;
+ break;
}
- if (*val == 0) {
- msg_warn("%s: empty %s value ignored",
- str_context(cbuf, site_class, site_name), name);
- continue;
+ if (session->tls_protocols) {
+ msg_warn("%s: attribute \"%s\" is specified multiple times",
+ WHERE, name);
+ *site_level = TLS_LEV_INVALID;
+ break;
}
- vstring_sprintf(cbuf, "protocol in TLS policy table, %s '%s':",
- site_class, site_name);
- session->tls_protocols = tls_protocol_mask(vstring_str(cbuf), val);
+ session->tls_protocols = mystrdup(val);
continue;
}
+ /* Multiple instance(s) per policy. */
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;
+ char *delim = *site_level == TLS_LEV_FPRINT ? "|" : ":";
+
+ if (*site_level <= TLS_LEV_ENCRYPT) {
+ msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"",
+ WHERE, name, policy_name(*site_level));
+ *site_level = TLS_LEV_INVALID;
+ break;
}
if (*val == 0) {
- msg_warn("%s: empty %s value ignored",
- str_context(cbuf, site_class, site_name), name);
- continue;
+ msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
+ *site_level = TLS_LEV_INVALID;
+ break;
}
- session->tls_certmatch = mystrdup(val);
+ if (session->tls_matchargv == 0)
+ session->tls_matchargv = argv_split(val, delim);
+ else
+ argv_split_append(session->tls_matchargv, val, delim);
continue;
+ } else {
+ msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
+ *site_level = TLS_LEV_INVALID;
+ break;
}
- 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 *cipher_level,
+static void tls_policy_lookup(SMTP_SESSION *session, int *site_level,
const char *site_name,
const char *site_class)
{
* sub-domains of the recipient domain.
*/
if (!valid_hostname(site_name, DONT_GRIPE)) {
- tls_policy_lookup_one(session, site_level, cipher_level,
- site_name, site_class);
+ tls_policy_lookup_one(session, site_level, site_name, site_class);
return;
}
+
+ /*
+ * XXX For clarity consider using ``do { .. } while'', instead of using
+ * ``while { .. }'' with loop control at the bottom.
+ */
while (1) {
/* Try the given domain */
- if (tls_policy_lookup_one(session, site_level, cipher_level,
- site_name, site_class))
+ if (tls_policy_lookup_one(session, site_level, site_name, site_class))
return;
/* Re-try with parent domain */
if ((site_name = strchr(site_name + 1, '.')) == 0)
}
}
-/* set_cipherlist - Choose cipherlist per security level and cipher suite */
+/* set_cipher_grade - Set cipher grade and exclusions */
-static void set_cipherlist(SMTP_SESSION *session, int cipher_level, int lmtp)
+static void set_cipher_grade(SMTP_SESSION *session)
{
- const char *cipherlist = 0;
- const char *exclude = var_smtp_tls_excl_ciph;
- const char *also_exclude = "";
const char *mand_exclude = "";
+ const char *also_exclude = "";
/*
* Use main.cf cipher level if no per-destination value specified. With
* at least authenticate!
*/
switch (session->tls_level) {
+ case TLS_LEV_INVALID:
case TLS_LEV_NONE:
- session->tls_cipherlist = 0;
return;
case TLS_LEV_MAY:
- cipher_level = TLS_CIPHER_EXPORT; /* Interoperate! */
+ /* tls_policy_lookup_one() leaves this NULL with level < encrypt. */
+ session->tls_grade = mystrdup("export");/* XXX: For now */
break;
case TLS_LEV_ENCRYPT:
- also_exclude = "eNULL";
- if (cipher_level == TLS_CIPHER_NONE)
- cipher_level = tls_cipher_level(var_smtp_tls_mand_ciph);
+ if (session->tls_grade == 0)
+ session->tls_grade = mystrdup(var_smtp_tls_mand_ciph);
mand_exclude = var_smtp_tls_mand_excl;
+ also_exclude = "eNULL";
break;
+ case TLS_LEV_FPRINT:
case TLS_LEV_VERIFY:
case TLS_LEV_SECURE:
- also_exclude = "aNULL";
- if (cipher_level == TLS_CIPHER_NONE)
- cipher_level = tls_cipher_level(var_smtp_tls_mand_ciph);
+ if (session->tls_grade == 0)
+ session->tls_grade = mystrdup(var_smtp_tls_mand_ciph);
mand_exclude = var_smtp_tls_mand_excl;
+ also_exclude = "aNULL";
break;
}
- cipherlist = tls_cipher_list(cipher_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_MAND_CIPH : VAR_SMTP_TLS_MAND_CIPH,
- var_smtp_tls_mand_ciph);
- cipherlist = tls_cipher_list(TLS_CIPHER_MEDIUM, exclude, mand_exclude,
- also_exclude, TLS_END_EXCLUDE);
- if (cipherlist == 0)
- msg_panic("NULL medium cipherlist");
+#define ADD_EXCLUDE(vstr, str) \
+ do { \
+ if (*(str)) \
+ vstring_sprintf_append((vstr), "%s%s", \
+ VSTRING_LEN(vstr) ? " " : "", (str)); \
+ } while (0)
+
+ /*
+ * Soon, the "exclude" policy table attribute will be able to override
+ * the main.cf mandatory exclusion list, and the latter may become
+ * obsolete.
+ */
+ if (session->tls_exclusions == 0) {
+ session->tls_exclusions = vstring_alloc(10);
+ ADD_EXCLUDE(session->tls_exclusions, var_smtp_tls_excl_ciph);
+ ADD_EXCLUDE(session->tls_exclusions, mand_exclude);
}
- session->tls_cipherlist = mystrdup(cipherlist);
+ ADD_EXCLUDE(session->tls_exclusions, also_exclude);
}
/* session_tls_init - session TLS parameters */
static void session_tls_init(SMTP_SESSION *session, const char *dest,
const char *host, int flags)
{
+ const char *myname = "session_tls_init";
int global_level;
int site_level;
- int lmtp = flags & SMTP_MISC_FLAG_USE_LMTP;
- int cipher_level = TLS_CIPHER_NONE;
/*
* Initialize all TLS related session properties.
session->tls_level = TLS_LEV_NONE;
session->tls_retry_plain = 0;
session->tls_protocols = 0;
- session->tls_cipherlist = 0;
- session->tls_certmatch = 0;
+ session->tls_grade = 0;
+ session->tls_exclusions = 0;
+ session->tls_matchargv = 0;
/*
* Compute the global TLS policy. This is the default policy level when
* per-site policy.
*/
if (*var_smtp_tls_level) {
+ /* Require that var_smtp_tls_level is sanitized upon startup. */
global_level = tls_level_lookup(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);
- }
+ if (global_level == TLS_LEV_INVALID)
+ msg_panic("%s: invalid TLS security level: \"%s\"",
+ myname, var_smtp_tls_level);
} else if (var_smtp_enforce_tls) {
global_level = var_smtp_tls_enforce_peername ?
TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
site_level = TLS_LEV_NOTFOUND;
if (tls_policy) {
- tls_policy_lookup(session, &site_level, &cipher_level,
- dest, "next-hop destination");
+ tls_policy_lookup(session, &site_level, dest, "next-hop destination");
} else if (tls_per_site) {
tls_site_lookup(&site_level, dest, "next-hop destination");
if (strcasecmp(dest, host) != 0)
session->tls_level = site_level;
/*
- * Use main.cf protocols setting if not set in per-destination table
+ * Use main.cf protocols setting if not set in per-destination table.
*/
- if (session->tls_level >= TLS_LEV_ENCRYPT
- && session->tls_protocols == 0
- && *var_smtp_tls_mand_proto)
+ if (session->tls_level > TLS_LEV_NONE && session->tls_protocols == 0)
session->tls_protocols =
- tls_protocol_mask(VAR_SMTP_TLS_MAND_PROTO, var_smtp_tls_mand_proto);
+ mystrdup((session->tls_level == TLS_LEV_MAY) ?
+ "" : var_smtp_tls_mand_proto);
/*
- * 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.
+ * Compute cipher grade (if set in per-destination table, else
+ * set_cipher() uses main.cf settings) and security level dependent
+ * cipher exclusion list.
*/
- set_cipherlist(session, cipher_level, lmtp);
+ set_cipher_grade(session);
/*
- * Use main.cf cert_match setting if not set in per-destination table
+ * Use main.cf cert_match setting if not set in per-destination table.
*/
- if (session->tls_level >= TLS_LEV_ENCRYPT
- && session->tls_certmatch == 0) {
+ if (session->tls_matchargv == 0) {
switch (session->tls_level) {
+ case TLS_LEV_INVALID:
+ case TLS_LEV_NONE:
+ case TLS_LEV_MAY:
case TLS_LEV_ENCRYPT:
break;
+ case TLS_LEV_FPRINT:
+ session->tls_matchargv =
+ argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |");
+ break;
case TLS_LEV_VERIFY:
- session->tls_certmatch = mystrdup(var_smtp_tls_vfy_cmatch);
+ session->tls_matchargv =
+ argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :");
break;
case TLS_LEV_SECURE:
- session->tls_certmatch = mystrdup(var_smtp_tls_sec_cmatch);
+ session->tls_matchargv =
+ argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :");
break;
default:
- msg_panic("unexpected mandatory TLS security level: %d",
+ msg_panic("unexpected TLS security level: %d",
session->tls_level);
}
}
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);
+ if (session->tls_protocols)
+ myfree(session->tls_protocols);
+ if (session->tls_grade)
+ myfree(session->tls_grade);
+ if (session->tls_exclusions)
+ vstring_free(session->tls_exclusions);
+ if (session->tls_matchargv)
+ argv_free(session->tls_matchargv);
#endif
if (session->stream)
vstream_fclose(session->stream);
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/attr.h
smtpd_milter.o: ../../include/mail_params.h
smtpd_milter.o: ../../include/mail_stream.h
smtpd_milter.o: ../../include/milter.h
smtpd_proxy.o: smtpd_proxy.c
smtpd_proxy.o: smtpd_proxy.h
smtpd_sasl_glue.o: ../../include/argv.h
+smtpd_sasl_glue.o: ../../include/attr.h
smtpd_sasl_glue.o: ../../include/mail_params.h
smtpd_sasl_glue.o: ../../include/mail_stream.h
smtpd_sasl_glue.o: ../../include/milter.h
/* When TLS encryption is optional in the Postfix SMTP server, do
/* not announce or accept SASL authentication over unencrypted
/* connections.
-/* .IP "\fBsmtpd_tls_ccert_verifydepth (5)\fR"
+/* .IP "\fBsmtpd_tls_ccert_verifydepth (9)\fR"
/* 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.
/* Additional list of ciphers or cipher types to exclude from the
/* SMTP server cipher list at mandatory TLS security levels.
/* .IP "\fBsmtpd_tls_mandatory_protocols (SSLv3, TLSv1)\fR"
-/* The TLS protocols accepted by the Postfix SMTP server with
+/* The SSL/TLS protocols accepted by the Postfix SMTP server with
/* mandatory TLS encryption.
/* .IP "\fBsmtpd_tls_received_header (no)\fR"
/* Request that the Postfix SMTP server produces Received: message
/* as well as the client CommonName and client certificate issuer
/* CommonName.
/* .IP "\fBsmtpd_tls_req_ccert (no)\fR"
-/* With mandatory TLS encryption, require a remote SMTP client
+/* With mandatory TLS encryption, require a trusted remote SMTP client
/* certificate in order to allow TLS connections to proceed.
/* .IP "\fBsmtpd_tls_session_cache_database (empty)\fR"
/* Name of the file containing the optional Postfix SMTP server
/* .IP "\fBtls_null_cipherlist (eNULL:!aNULL)\fR"
/* The OpenSSL cipherlist for "NULL" grade ciphers that provide
/* authentication without encryption.
+/* .PP
+/* Available in Postfix version 2.5 and later:
+/* .IP "\fBsmtpd_tls_fingerprint_digest (md5)\fR"
+/* The message digest algorithm used to construct client-certificate
+/* fingerprints for \fBcheck_ccert_access\fR and
+/* \fBpermit_tls_clientcerts\fR.
/* OBSOLETE STARTTLS CONTROLS
/* .ad
/* .fi
/* Allgemeine Elektrotechnik
/* Universitaetsplatz 3-4
/* D-03044 Cottbus, Germany
+/*
+/* Revised TLS support by:
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
bool var_smtpd_tls_req_ccert;
int var_smtpd_tls_scache_timeout;
bool var_smtpd_tls_set_sessid;
-int var_tls_daemon_rand_bytes;
+char *var_smtpd_tls_fpt_dgst;
#endif
/*
* TLS initialization status.
*/
-static SSL_CTX *smtpd_tls_ctx;
+static TLS_APPL_STATE *smtpd_tls_ctx;
+static int wantcert;
#endif
+static int enforce_tls;
+
#ifdef USE_SASL_AUTH
/*
VSTREAM *out_stream;
int out_error;
char **cpp;
- CLEANUP_STAT_DETAIL *detail;
+ const CLEANUP_STAT_DETAIL *detail;
const char *rfc3848_sess;
const char *rfc3848_auth;
state->tls_context->cipher_name,
state->tls_context->cipher_usebits,
state->tls_context->cipher_algbits);
- if (state->tls_context->peer_CN) {
+ if (TLS_CERT_IS_PRESENT(state->tls_context)) {
peer_CN = VSTRING_STRDUP(state->tls_context->peer_CN);
comment_sanitize(peer_CN);
issuer_CN = VSTRING_STRDUP(state->tls_context->issuer_CN ?
state->tls_context->issuer_CN : "");
comment_sanitize(issuer_CN);
- if (state->tls_context->peer_verified)
- out_fprintf(out_stream, REC_TYPE_NORM,
- "\t(Client CN \"%s\", Issuer \"%s\" (verified OK))",
- STR(peer_CN), STR(issuer_CN));
- else
- out_fprintf(out_stream, REC_TYPE_NORM,
- "\t(Client CN \"%s\", Issuer \"%s\" (not verified))",
- STR(peer_CN), STR(issuer_CN));
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ "\t(Client CN \"%s\", Issuer \"%s\" (%s))",
+ STR(peer_CN), STR(issuer_CN),
+ TLS_CERT_IS_TRUSTED(state->tls_context) ?
+ "verified OK" : "not verified");
vstring_free(issuer_CN);
vstring_free(peer_CN);
} else if (var_smtpd_tls_ask_ccert)
char *attr_name;
int update_namaddr = 0;
int name_status;
- static NAME_CODE peer_codes[] = {
+ static const NAME_CODE peer_codes[] = {
XCLIENT_UNAVAILABLE, SMTPD_PEER_CODE_PERM,
XCLIENT_TEMPORARY, SMTPD_PEER_CODE_TEMP,
0, SMTPD_PEER_CODE_OK,
};
- static NAME_CODE proto_names[] = {
+ static const NAME_CODE proto_names[] = {
MAIL_PROTO_SMTP, 1,
MAIL_PROTO_ESMTP, 2,
0, -1,
const char *bare_value;
char *attr_name;
int updated = 0;
- static NAME_CODE xforward_flags[] = {
+ static const NAME_CODE xforward_flags[] = {
XFORWARD_NAME, SMTPD_STATE_XFORWARD_NAME,
XFORWARD_ADDR, SMTPD_STATE_XFORWARD_ADDR,
XFORWARD_PORT, SMTPD_STATE_XFORWARD_PORT,
MAIL_ATTR_RWR_LOCAL, /* Postfix internal form */
MAIL_ATTR_RWR_REMOTE, /* Postfix internal form */
};
- static NAME_CODE xforward_to_context[] = {
+ static const NAME_CODE xforward_to_context[] = {
XFORWARD_DOM_LOCAL, 0, /* XFORWARD representation */
XFORWARD_DOM_REMOTE, 1, /* XFORWARD representation */
0, -1,
static void smtpd_start_tls(SMTPD_STATE *state)
{
int rate;
- tls_server_start_props props;
+ TLS_SERVER_START_PROPS props;
+ static char *cipher_grade;
+ static VSTRING *cipher_exclusions;
+ int cert_present;
/*
* Wrapper mode uses a dedicated port and always requires TLS.
* command. For this reason, Postfix does not require client certificate
* verification unless TLS is required.
*
- * XXX We append the service name to the session cache ID, so that there
- * won't be collisions between multiple master.cf entries that use
- * different roots of trust. This does not eliminate collisions between
- * multiple inetd.conf entries that use different roots of trust. For a
- * universal solution we would have to append the local IP address + port
- * number information.
+ * The cipher grade and exclusions don't change between sessions. Compute
+ * just once and cache.
*/
- memset((char *) &props, 0, sizeof(props));
- props.ctx = smtpd_tls_ctx;
- props.stream = state->client;
- props.log_level = var_smtpd_tls_loglevel;
- props.timeout = var_smtpd_starttls_tmout;
- props.requirecert = (var_smtpd_tls_req_ccert && state->tls_enforce_tls);
- props.serverid = state->service;
- props.peername = state->name;
- props.peeraddr = state->addr;
- state->tls_context = tls_server_start(&props);
+#define ADD_EXCLUDE(vstr, str) \
+ do { \
+ if (*(str)) \
+ vstring_sprintf_append((vstr), "%s%s", \
+ VSTRING_LEN(vstr) ? " " : "", (str)); \
+ } while (0)
+
+ if (cipher_grade == 0) {
+ cipher_grade =
+ enforce_tls ? var_smtpd_tls_mand_ciph : "export";
+ cipher_exclusions = vstring_alloc(10);
+ ADD_EXCLUDE(cipher_exclusions, var_smtpd_tls_excl_ciph);
+ if (enforce_tls)
+ ADD_EXCLUDE(cipher_exclusions, var_smtpd_tls_mand_excl);
+ if (wantcert)
+ ADD_EXCLUDE(cipher_exclusions, "aNULL");
+ }
/*
- * XXX The client event count/rate control must be consistent in its use
- * of client address information in connect and disconnect events. For
- * now we exclude xclient authorized hosts from event count/rate control.
+ * Perform the TLS handshake now. Check the client certificate
+ * requirements later, if necessary.
+ */
+ state->tls_context =
+ TLS_SERVER_START(&props,
+ ctx = smtpd_tls_ctx,
+ stream = state->client,
+ log_level = var_smtpd_tls_loglevel,
+ timeout = var_smtpd_starttls_tmout,
+ requirecert = (var_smtpd_tls_req_ccert
+ && state->tls_enforce_tls),
+ serverid = state->service,
+ namaddr = state->namaddr,
+ cipher_grade = cipher_grade,
+ cipher_exclusions = STR(cipher_exclusions),
+ fpt_dgst = var_smtpd_tls_fpt_dgst);
+
+ /*
+ * For new (i.e. not re-used) TLS sessions, increment the client's new
+ * TLS session rate counter. We enforce the limit here only for human
+ * factors reasons (reduce the WTF factor), even though it is too late to
+ * save the CPU that was already burnt on PKI ops. The real safety
+ * mechanism applies with future STARTTLS commands (or wrappermode
+ * connections), prior to the SSL handshake.
+ *
+ * XXX The client event count/rate control must be consistent in its use of
+ * client address information in connect and disconnect events. For now
+ * we exclude xclient authorized hosts from event count/rate control.
*/
if (var_smtpd_cntls_limit > 0
&& state->tls_context
if (state->tls_context == 0)
vstream_longjmp(state->client, SMTP_ERR_EOF);
+ /*
+ * If we are requiring verified client certs, enforce the constraint
+ * here. We have a usable TLS session with the client, so no need to
+ * disable I/O, ... we can even be polite and send "421 ...".
+ */
+ if (props.requirecert && TLS_CERT_IS_TRUSTED(state->tls_context) == 0) {
+
+ /*
+ * Fetch and reject the next command (should be EHLO), then
+ * disconnect (side-effect of returning "421 ...".
+ */
+ cert_present = TLS_CERT_IS_PRESENT(state->tls_context);
+ msg_info("NOQUEUE: abort: TLS from %s: %s",
+ state->namaddr, cert_present ?
+ "Client certificate not trusted" :
+ "No client certificate presented");
+ smtpd_chat_query(state);
+ smtpd_chat_reply(state, "421 4.7.1 %s Error: %s",
+ var_myhostname, cert_present ?
+ "Client certificate not trusted" :
+ "No client certificate presented");
+ state->error_mask |= MAIL_ERROR_POLICY;
+ return;
+ }
+
/*
* When TLS is turned on, we may offer AUTH methods that would not be
* offered within a plain-text session.
}
/*
- * XXX The client event count/rate control must be consistent in its use
- * of client address information in connect and disconnect events. For
- * now we exclude xclient authorized hosts from event count/rate control.
+ * Enforce TLS handshake rate limit when this client negotiated too many
+ * new TLS sessions in the recent past.
+ *
+ * XXX The client event count/rate control must be consistent in its use of
+ * client address information in connect and disconnect events. For now
+ * we exclude xclient authorized hosts from event count/rate control.
*/
if (var_smtpd_cntls_limit > 0
&& SMTPD_STAND_ALONE(state) == 0
* the STARTTLS command. This code does not return when the handshake
* fails.
*
- * XXX We start TLS before we apply access control, concurrency or
- * connection rate limits, so that we can inform the client why
- * service is denied. This means we spend a lot of CPU just to tell
- * the client that we don't provide service. TLS wrapper mode is
+ * Enforce TLS handshake rate limit when this client negotiated too many
+ * new TLS sessions in the recent past.
+ *
+ * XXX This means we don't complete a TLS handshake just to tell the
+ * client that we don't provide service. TLS wrapper mode is
* obsolete, so we don't have to provide perfect support.
*/
#ifdef USE_TLS
static void pre_jail_init(char *unused_name, char **unused_argv)
{
- int enforce_tls;
int use_tls;
/*
* security levels. We implement only a subset for now. If we implement
* more levels, wrappermode should override only weaker TLS security
* levels.
+ *
+ * Note: tls_level_lookup() logs no warning.
*/
if (!var_smtpd_tls_wrappermode && *var_smtpd_tls_level) {
switch (tls_level_lookup(var_smtpd_tls_level)) {
default:
- msg_warn("%s: ignoring unknown TLS level \"%s\"",
- VAR_SMTPD_TLS_LEVEL, var_smtpd_tls_level);
+ msg_fatal("Invalid TLS level \"%s\"", var_smtpd_tls_level);
+ /* NOTREACHED */
break;
case TLS_LEV_SECURE:
case TLS_LEV_VERIFY:
+ case TLS_LEV_FPRINT:
msg_warn("%s: unsupported TLS level \"%s\", using \"encrypt\"",
VAR_SMTPD_TLS_LEVEL, var_smtpd_tls_level);
/* FALLTHROUGH */
if (getuid() == 0 || getuid() == var_owner_uid) {
if (use_tls) {
#ifdef USE_TLS
- tls_server_props props;
+ TLS_SERVER_INIT_PROPS props;
+ const char *cert_file;
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.set_sessid = var_smtpd_tls_set_sessid;
- 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 = enforce_tls && *var_smtpd_tls_mand_proto ?
- tls_protocol_mask(VAR_SMTPD_TLS_MAND_PROTO,
- var_smtpd_tls_mand_proto) : 0;
- props.ask_ccert = var_smtpd_tls_ask_ccert;
/*
* Can't use anonymous ciphers if we want client 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;
+ wantcert = (var_smtpd_tls_ask_ccert
+ || (enforce_tls && var_smtpd_tls_req_ccert));
+ if (strcasecmp(var_smtpd_tls_cert_file, "none") == 0) {
+ oknocert = 1;
+ cert_file = "";
+ } else {
+ oknocert = 0;
+ cert_file = var_smtpd_tls_cert_file;
+ }
+ havecert =
+ (*cert_file || *var_smtpd_tls_dcert_file);
+ /* Some TLS configuration errors are not show stoppers. */
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(enforce_tls ?
- tls_cipher_level(var_smtpd_tls_mand_ciph) :
- TLS_CIPHER_EXPORT,
- var_smtpd_tls_excl_ciph,
- havecert ? "" : "aRSA aDSS",
- wantcert ? "aNULL" : "",
- enforce_tls ? var_smtpd_tls_mand_excl :
- TLS_END_EXCLUDE,
- TLS_END_EXCLUDE);
-
- if (props.cipherlist == 0) {
- msg_warn("unknown '%s' value '%s' ignored, using 'export'",
- VAR_SMTPD_TLS_MAND_CIPH, var_smtpd_tls_mand_ciph);
- props.cipherlist =
- tls_cipher_list(TLS_CIPHER_EXPORT,
- var_smtpd_tls_excl_ciph,
- havecert ? "" : "aRSA aDSS",
- wantcert ? "aNULL" : "",
- enforce_tls ? var_smtpd_tls_mand_excl :
- TLS_END_EXCLUDE,
- TLS_END_EXCLUDE);
- if (props.cipherlist == 0)
- msg_panic("NULL export cipherlist");
- }
- 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");
+ /* After a show-stopper error, reply with 454 to STARTTLS. */
+ if (havecert || (oknocert && !wantcert))
+
+ /*
+ * Large parameter lists are error-prone, so we emulate a
+ * language feature that C does not have natively: named
+ * parameter lists.
+ */
+ smtpd_tls_ctx =
+ TLS_SERVER_INIT(&props,
+ log_level = var_smtpd_tls_loglevel,
+ verifydepth = var_smtpd_tls_ccert_vd,
+ cache_type = TLS_MGR_SCACHE_SMTPD,
+ scache_timeout
+ = var_smtpd_tls_scache_timeout,
+ set_sessid = var_smtpd_tls_set_sessid,
+ cert_file = cert_file,
+ key_file = var_smtpd_tls_key_file,
+ dcert_file = var_smtpd_tls_dcert_file,
+ dkey_file = var_smtpd_tls_dkey_file,
+ CAfile = var_smtpd_tls_CAfile,
+ CApath = var_smtpd_tls_CApath,
+ dh1024_param_file
+ = var_smtpd_tls_dh1024_param_file,
+ dh512_param_file
+ = var_smtpd_tls_dh512_param_file,
+ protocols = enforce_tls ?
+ var_smtpd_tls_mand_proto : "",
+ ask_ccert = var_smtpd_tls_ask_ccert,
+ fpt_dgst = var_smtpd_tls_fpt_dgst);
else
msg_warn("No server certs available. TLS won't be enabled");
#else
int main(int argc, char **argv)
{
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0,
VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0,
VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 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,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0,
VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
VAR_SMTPD_PROXY_TMOUT, DEF_SMTPD_PROXY_TMOUT, &var_smtpd_proxy_tmout, 1, 0,
VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, &var_milt_msg_time, 1, 0,
0,
};
- static CONFIG_BOOL_TABLE bool_table[] = {
+ static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
VAR_STRICT_RFC821_ENV, DEF_STRICT_RFC821_ENV, &var_strict_rfc821_env,
VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log,
0,
};
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_CLIENT_CHECKS, DEF_CLIENT_CHECKS, &var_client_checks, 0, 0,
VAR_SMTPD_TLS_MAND_CIPH, DEF_SMTPD_TLS_MAND_CIPH, &var_smtpd_tls_mand_ciph, 1, 0,
VAR_SMTPD_TLS_EXCL_CIPH, DEF_SMTPD_TLS_EXCL_CIPH, &var_smtpd_tls_excl_ciph, 0, 0,
VAR_SMTPD_TLS_MAND_EXCL, DEF_SMTPD_TLS_MAND_EXCL, &var_smtpd_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_SMTPD_TLS_MAND_PROTO, DEF_SMTPD_TLS_MAND_PROTO, &var_smtpd_tls_mand_proto, 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_FPT_DGST, DEF_SMTPD_TLS_FPT_DGST, &var_smtpd_tls_fpt_dgst, 1, 0,
#endif
VAR_SMTPD_TLS_LEVEL, DEF_SMTPD_TLS_LEVEL, &var_smtpd_tls_level, 0, 0,
VAR_SMTPD_SASL_TYPE, DEF_SMTPD_SASL_TYPE, &var_smtpd_sasl_type, 1, 0,
VAR_STRESS, DEF_STRESS, &var_stress, 0, 0,
0,
};
- static CONFIG_RAW_TABLE raw_table[] = {
+ static const CONFIG_RAW_TABLE raw_table[] = {
VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter, 1, 0,
VAR_DEF_RBL_REPLY, DEF_DEF_RBL_REPLY, &var_def_rbl_reply, 1, 0,
0,
int tls_use_tls; /* can use TLS */
int tls_enforce_tls; /* must use TLS */
int tls_auth_only; /* use SASL over TLS only */
- TLScontext_t *tls_context; /* TLS session state */
+ TLS_SESS_STATE *tls_context; /* TLS session state */
#endif
/*
if (!state->tls_context)
return SMTPD_CHECK_DUNNO;
- if (state->tls_context->peer_verified && permit_all_certs) {
+ if (TLS_CERT_IS_TRUSTED(state->tls_context) && permit_all_certs) {
if (msg_verbose)
msg_info("Relaying allowed for all verified client certificates");
return (SMTPD_CHECK_OK);
* When directly checking the fingerprint, it is OK if the issuing CA is
* not trusted.
*/
- if (state->tls_context->peer_fingerprint) {
+ if (TLS_CERT_IS_PRESENT(state->tls_context)) {
found = maps_find(relay_ccerts, state->tls_context->peer_fingerprint,
DICT_FLAG_NONE);
if (found) {
const char *myname = "check_ccert_access";
int found;
- if (!state->tls_context)
- return SMTPD_CHECK_DUNNO;
-
/*
* When directly checking the fingerprint, it is OK if the issuing CA is
* not trusted.
*/
- if (state->tls_context->peer_fingerprint) {
+ if (TLS_CERT_IS_PRESENT(state->tls_context)) {
if (msg_verbose)
msg_info("%s: %s", myname, state->tls_context->peer_fingerprint);
#ifdef USE_TLS
#define ENCODE_CN(coded_CN, coded_CN_buf, CN) do { \
- if (state->tls_context == 0 \
- || state->tls_context->peer_verified == 0 || (CN) == 0) { \
+ if (!TLS_CERT_IS_TRUSTED(state->tls_context) || *(CN) == 0) { \
coded_CN_buf = 0; \
coded_CN = ""; \
} else { \
state->sasl_sender : "",
#endif
#ifdef USE_TLS
-#define IF_VERIFIED(x) \
- ((state->tls_context && \
- state->tls_context->peer_verified && ((x) != 0)) ? (x) : "")
#define IF_ENCRYPTED(x, y) ((state->tls_context && ((x) != 0)) ? (x) : (y))
- ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT,
- IF_VERIFIED(subject),
- ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSUER,
- IF_VERIFIED(issuer),
+ ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT, subject,
+ ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSUER, issuer,
/*
* When directly checking the fingerprint, it is OK if the issuing CA is
#undef DEF_LOCAL_RCPT_MAPS
#define DEF_LOCAL_RCPT_MAPS ""
-static STRING_TABLE string_table[] = {
+static const STRING_TABLE string_table[] = {
VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains,
VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin,
VAR_MYDEST, DEF_MYDEST, &var_mydest,
int var_plaintext_code;
bool var_smtpd_peername_lookup;
-static INT_TABLE int_table[] = {
+static const INT_TABLE int_table[] = {
"msg_verbose", 0, &msg_verbose,
VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code,
VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code,
ARGV **target;
} REST_TABLE;
-static REST_TABLE rest_table[] = {
+static const REST_TABLE rest_table[] = {
"client_restrictions", &client_restrctions,
"helo_restrictions", &helo_restrctions,
"sender_restrictions", &mail_restrctions,
*/
#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)
+#define IF_TRUSTED(x) (TLS_CERT_IS_TRUSTED(state->tls_context) ? (x) : 0)
if (strcmp(name, S8_MAC_TLS_VERSION) == 0)
return (IF_ENCRYPTED(state->tls_context->protocol));
return (STR(state->expand_buf));
}
if (strcmp(name, S8_MAC_CERT_SUBJECT) == 0)
- return (IF_VERIFIED(state->tls_context->peer_CN));
+ return (IF_TRUSTED(state->tls_context->peer_CN));
if (strcmp(name, S8_MAC_CERT_ISSUER) == 0)
- return (IF_VERIFIED(state->tls_context->issuer_CN));
+ return (IF_TRUSTED(state->tls_context->issuer_CN));
#endif
/*
VSTRING *buf;
int bad;
char *word;
- static NAME_CODE xforward_features[] = {
+ static const NAME_CODE xforward_features[] = {
XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
XFORWARD_PORT, SMTPD_PROXY_XFORWARD_PORT,
XFORWARD_DOMAIN, SMTPD_PROXY_XFORWARD_DOMAIN,
0, 0,
};
- CLEANUP_STAT_DETAIL *detail;
+ const CLEANUP_STAT_DETAIL *detail;
int (*connect_fn) (const char *, int, int);
const char *endpoint;
int last_char;
int err = 0;
static VSTRING *buffer = 0;
- CLEANUP_STAT_DETAIL *detail;
+ const CLEANUP_STAT_DETAIL *detail;
/*
* Errors first. Be prepared for delayed errors from the DATA phase.
int main(int argc, char **argv)
{
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
0,
};
@$(EXPORT) make -f Makefile.in Makefile 1>&2
# do not edit below this line - it is generated by 'make depend'
+tls_bio_ops.o: ../../include/argv.h
tls_bio_ops.o: ../../include/iostuff.h
tls_bio_ops.o: ../../include/msg.h
tls_bio_ops.o: ../../include/name_code.h
tls_bio_ops.o: ../../include/vstring.h
tls_bio_ops.o: tls.h
tls_bio_ops.o: tls_bio_ops.c
+tls_certkey.o: ../../include/argv.h
tls_certkey.o: ../../include/msg.h
tls_certkey.o: ../../include/name_code.h
tls_certkey.o: ../../include/name_mask.h
tls_client.o: tls.h
tls_client.o: tls_client.c
tls_client.o: tls_mgr.h
+tls_dh.o: ../../include/argv.h
tls_dh.o: ../../include/msg.h
tls_dh.o: ../../include/name_code.h
tls_dh.o: ../../include/name_mask.h
tls_dh.o: ../../include/vstring.h
tls_dh.o: tls.h
tls_dh.o: tls_dh.c
+tls_level.o: ../../include/argv.h
tls_level.o: ../../include/name_code.h
tls_level.o: ../../include/name_mask.h
tls_level.o: ../../include/sys_defs.h
tls_mgr.o: tls_mgr.c
tls_mgr.o: tls_mgr.h
tls_misc.o: ../../include/argv.h
+tls_misc.o: ../../include/mail_conf.h
+tls_misc.o: ../../include/mail_params.h
tls_misc.o: ../../include/msg.h
tls_misc.o: ../../include/mymalloc.h
tls_misc.o: ../../include/name_code.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/argv.h
tls_rsa.o: ../../include/name_code.h
tls_rsa.o: ../../include/name_mask.h
tls_rsa.o: ../../include/sys_defs.h
tls_scache.o: ../../include/vstring.h
tls_scache.o: tls_scache.c
tls_scache.o: tls_scache.h
+tls_seed.o: ../../include/argv.h
tls_seed.o: ../../include/msg.h
tls_seed.o: ../../include/name_code.h
tls_seed.o: ../../include/name_mask.h
tls_server.o: tls.h
tls_server.o: tls_mgr.h
tls_server.o: tls_server.c
+tls_session.o: ../../include/argv.h
tls_session.o: ../../include/msg.h
tls_session.o: ../../include/mymalloc.h
tls_session.o: ../../include/name_code.h
tls_session.o: ../../include/vstring.h
tls_session.o: tls.h
tls_session.o: tls_session.c
+tls_stream.o: ../../include/argv.h
tls_stream.o: ../../include/iostuff.h
tls_stream.o: ../../include/msg.h
tls_stream.o: ../../include/name_code.h
tls_stream.o: ../../include/vstring.h
tls_stream.o: tls.h
tls_stream.o: tls_stream.c
+tls_verify.o: ../../include/argv.h
tls_verify.o: ../../include/msg.h
tls_verify.o: ../../include/mymalloc.h
tls_verify.o: ../../include/name_code.h
tls_verify.o: ../../include/name_mask.h
+tls_verify.o: ../../include/stringops.h
tls_verify.o: ../../include/sys_defs.h
tls_verify.o: ../../include/vbuf.h
tls_verify.o: ../../include/vstream.h
* Utility library.
*/
#include <name_code.h>
+#include <argv.h>
/*
* TLS enforcement levels. Non-sentinel values may also be used to indicate
* the actual security level of a session.
+ *
+ * XXX TLS_LEV_NOTFOUND no longer belongs in this list. The SMTP client will
+ * have to use something else to report that policy table lookup failed.
*/
-#define TLS_LEV_NOTFOUND -1 /* sentinel */
+#define TLS_LEV_INVALID -2 /* sentinel */
+#define TLS_LEV_NOTFOUND -1 /* XXX not in policy table */
#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 */
+#define TLS_LEV_FPRINT 3 /* "peer" CA-less verification */
+#define TLS_LEV_VERIFY 4 /* certificate verified */
+#define TLS_LEV_SECURE 5 /* "secure" verification */
-extern NAME_CODE tls_level_table[];
+extern const NAME_CODE tls_level_table[];
#define tls_level_lookup(s) name_code(tls_level_table, NAME_CODE_FLAG_NONE, (s))
#define str_tls_level(l) str_name_code(tls_level_table, (l))
/*
* TLS session context, also used by the VSTREAM call-back routines for SMTP
* input/output, and by OpenSSL call-back routines for key verification.
+ *
+ * Only some members are (read-only) accessible by the public.
*/
#define CCERT_BUFSIZ 256
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 */
+ /* Public, read-only. */
char *peer_CN; /* Peer Common Name */
char *issuer_CN; /* Issuer Common Name */
char *peer_fingerprint; /* ASCII fingerprint */
- char *peername;
- int enforce_verify_errors;
- int enforce_CN;
- int hostname_matched;
- int peer_verified;
+ int peer_status; /* Certificate and match status */
const char *protocol;
const char *cipher_name;
int cipher_usebits;
int cipher_algbits;
+ /* Private. */
+ 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 *namaddr; /* nam[addr] for logging */
+ int tls_level; /* Application security level */
int log_level; /* TLS library logging level */
int session_reused; /* this session was reused */
-} TLScontext_t;
+ int am_server; /* Are we an SSL server or client? */
+} TLS_SESS_STATE;
+
+ /*
+ * Peer status bits. TLS_CERT_FLAG_MATCHED implies TLS_CERT_FLAG_TRUSTED
+ * only in the case of a hostname match.
+ */
+#define TLS_CERT_FLAG_PRESENT (1<<0)
+#define TLS_CERT_FLAG_ALTNAME (1<<1)
+#define TLS_CERT_FLAG_TRUSTED (1<<2)
+#define TLS_CERT_FLAG_MATCHED (1<<3)
+#define TLS_CERT_FLAG_LOGGED (1<<4) /* Logged trust chain error */
+
+#define TLS_CERT_IS_PRESENT(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_PRESENT))
+#define TLS_CERT_IS_ALTNAME(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_ALTNAME))
+#define TLS_CERT_IS_TRUSTED(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_TRUSTED))
+#define TLS_CERT_IS_MATCHED(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_MATCHED))
+
+ /*
+ * Opaque client context handle.
+ */
+typedef struct TLS_APPL_STATE TLS_APPL_STATE;
+
+#ifdef TLS_INTERNAL
+
+ /*
+ * Client and Server application contexts
+ */
+struct TLS_APPL_STATE {
+ SSL_CTX *ssl_ctx;
+ char *cache_type;
+ char *cipher_exclusions; /* Last cipher selection state */
+ char *cipher_list; /* Last cipher selection state */
+ int cipher_grade; /* Last cipher selection state */
+ VSTRING *why;
+};
+
+ /*
+ * tls_misc.c One time finalization of application context.
+ */
+extern void tls_free_app_context(TLS_APPL_STATE *);
+
+ /*
+ * tls_misc.c
+ */
+
+extern void tls_param_init(void);
/*
- * Client protocol selection bitmask
+ * Protocol selection.
*/
+#define TLS_PROTOCOL_INVALID (~0) /* All protocol bits masked */
#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 \
+#define TLS_KNOWN_PROTOCOLS \
( TLS_PROTOCOL_SSLv2 | TLS_PROTOCOL_SSLv3 | TLS_PROTOCOL_TLSv1 )
+extern int tls_protocol_mask(const char *);
+
/*
- * tls_misc.c
+ * Cipher grade selection.
*/
#define TLS_CIPHER_NONE 0
#define TLS_CIPHER_NULL 1
#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)
+extern const NAME_CODE tls_cipher_grade_table[];
-#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_grade(str) \
+ name_code(tls_cipher_grade_table, NAME_CODE_FLAG_NONE, (str))
+#define str_tls_cipher_grade(gr) \
+ str_name_code(tls_cipher_grade_table, (gr))
-#define tls_cipher_level(str) \
- name_code(tls_cipher_level_table, NAME_CODE_FLAG_NONE, (str))
+ /*
+ * Cipher lists with exclusions.
+ */
+extern const char *tls_set_ciphers(TLS_APPL_STATE *, const char *,
+ const char *, const char *);
-#define TLS_END_EXCLUDE ((char *)0)
-extern const char *tls_cipher_list(int,...);
-extern const char *tls_set_cipher_list(SSL_CTX *, const char *);
+#endif
/*
* tls_client.c
const char *dkey_file;
const char *CAfile;
const char *CApath;
-} tls_client_init_props;
+ const char *fpt_dgst; /* Fingerprint digest algorithm */
+} TLS_CLIENT_INIT_PROPS;
typedef struct {
- SSL_CTX *ctx;
+ TLS_APPL_STATE *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))
+ const char *nexthop; /* destination domain */
+ const char *host; /* MX hostname */
+ const char *namaddr; /* nam[addr] for logging */
+ const char *serverid; /* Session cache key */
+ const char *protocols; /* Enabled protocols */
+ const char *cipher_grade; /* Minimum cipher grade */
+ const char *cipher_exclusions; /* Ciphers to exclude */
+ const ARGV *matchargv; /* Cert match patterns */
+ const char *fpt_dgst; /* Fingerprint digest algorithm */
+} TLS_CLIENT_START_PROPS;
+
+extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
+extern TLS_SESS_STATE *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))
+
+#define TLS_CLIENT_INIT(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
+ a10) \
+ tls_client_init((((props)->a1), ((props)->a2), ((props)->a3), \
+ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
+ ((props)->a8), ((props)->a9), ((props)->a10), (props)))
+
+#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
+ a10, a11, a12, a13, a14) \
+ tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
+ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
+ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
+ ((props)->a12), ((props)->a13), ((props)->a14), (props)))
/*
* tls_server.c
const char *dkey_file;
const char *CAfile;
const char *CApath;
- const char *cipherlist;
- int protocols; /* protocols, 0 => all */
+ const char *protocols;
const char *dh1024_param_file;
const char *dh512_param_file;
int ask_ccert;
-} tls_server_props;
+ const char *fpt_dgst; /* Fingerprint digest algorithm */
+} TLS_SERVER_INIT_PROPS;
typedef struct {
- SSL_CTX *ctx; /* SSL application context */
+ TLS_APPL_STATE *ctx; /* TLS application context */
VSTREAM *stream; /* Client stream */
int log_level; /* TLS log level */
int timeout; /* TLS handshake timeout */
int requirecert; /* Insist on client cert? */
- char *serverid; /* Server instance (salt cache key) */
- char *peername; /* Client name */
- char *peeraddr; /* Client address */
-} tls_server_start_props;
-
-extern SSL_CTX *tls_server_init(const tls_server_props *);
-extern TLScontext_t *tls_server_start(const tls_server_start_props *props);
-
-#define tls_server_stop(ctx , stream, timeout, failure, TLScontext) \
- tls_session_stop((ctx), (stream), (timeout), (failure), (TLScontext))
+ const char *serverid; /* Server instance (salt cache key) */
+ const char *namaddr; /* Client nam[addr] for logging */
+ const char *cipher_grade;
+ const char *cipher_exclusions;
+ const char *fpt_dgst; /* Fingerprint digest algorithm */
+} TLS_SERVER_START_PROPS;
+
+extern TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *);
+extern TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props);
+
+#define tls_server_stop(ctx, stream, timeout, failure, TLScontext) \
+ tls_session_stop(ctx, (stream), (timeout), (failure), (TLScontext))
+
+#define TLS_SERVER_INIT(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
+ a10, a11, a12, a13, a14, a15, a16) \
+ tls_server_init((((props)->a1), ((props)->a2), ((props)->a3), \
+ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
+ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
+ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
+ ((props)->a16), (props)))
+
+#define TLS_SERVER_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+ tls_server_start((((props)->a1), ((props)->a2), ((props)->a3), \
+ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
+ ((props)->a8), ((props)->a9), ((props)->a10), (props)))
/*
* tls_session.c
*/
-extern void tls_session_stop(SSL_CTX *, VSTREAM *, int, int,
- TLScontext_t *);
+extern void tls_session_stop(TLS_APPL_STATE *, VSTREAM *, int, int, TLS_SESS_STATE *);
#ifdef TLS_INTERNAL
/*
* tls_stream.c.
*/
-extern void tls_stream_start(VSTREAM *, TLScontext_t *);
+extern void tls_stream_start(VSTREAM *, TLS_SESS_STATE *);
extern void tls_stream_stop(VSTREAM *);
/*
* Because of its ugly multi-personality user interface we invoke it via
* not-so-ugly single-personality wrappers.
*/
-extern int tls_bio(int, int, TLScontext_t *,
+extern int tls_bio(int, int, TLS_SESS_STATE *,
int (*) (SSL *), /* handshake */
int (*) (SSL *, void *, int), /* read */
int (*) (SSL *, const void *, int), /* write */
/*
* tls_verify.c
*/
-extern char *tls_peer_CN(X509 *);
-extern char *tls_issuer_CN(X509 *);
+extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
+extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
+extern const char *tls_dns_name(const GENERAL_NAME *, const TLS_SESS_STATE *);
+extern char *tls_fingerprint(X509 *, const char *);
extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
/*
* 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 TLS_APPL_STATE *tls_alloc_app_context(SSL_CTX *);
+extern TLS_SESS_STATE *tls_alloc_sess_context(int, const char *);
+extern void tls_free_context(TLS_SESS_STATE *);
extern void tls_check_version(void);
extern long tls_bug_bits(void);
extern void tls_print_errors(void);
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
#endif /* USE_TLS */
/* int tls_bio_connect(fd, timeout, context)
/* int fd;
/* int timeout;
-/* TLScontext_t *context;
+/* TLS_SESS_STATE *context;
/*
/* int tls_bio_accept(fd, timeout, context)
/* int fd;
/* int timeout;
-/* TLScontext_t *context;
+/* TLS_SESS_STATE *context;
/*
/* int tls_bio_shutdown(fd, timeout, context)
/* int fd;
/* int timeout;
-/* TLScontext_t *context;
+/* TLS_SESS_STATE *context;
/*
/* int tls_bio_read(fd, buf, len, timeout, context)
/* int fd;
/* void *buf;
/* int len;
/* int timeout;
-/* TLScontext_t *context;
+/* TLS_SESS_STATE *context;
/*
/* int tls_bio_write(fd, buf, len, timeout, context)
/* int fd;
/* void *buf;
/* int len;
/* int timeout;
-/* TLScontext_t *context;
+/* TLS_SESS_STATE *context;
/* DESCRIPTION
/* This layer synchronizes the TLS network buffers with the network
/* while performing TLS handshake or input/output operations.
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
/* tls_bio - perform SSL input/output operation with extreme prejudice */
-int tls_bio(int fd, int timeout, TLScontext_t *TLScontext,
+int tls_bio(int fd, int timeout, TLS_SESS_STATE *TLScontext,
int (*hsfunc) (SSL *),
int (*rfunc) (SSL *, void *, int),
int (*wfunc) (SSL *, const void *, int),
*/
if (err == SSL_ERROR_SSL) {
if (ERR_peek_error() == 0x0407006AL) {
- pfixtls_print_errors();
+ tls_print_errors();
msg_info("OpenSSL <= 0.9.5a workaround called: certificate errors ignored");
err = SSL_get_error(TLScontext->con, status);
}
/*++
/* NAME
-/* tls
+/* tls_client
/* SUMMARY
/* client-side TLS engine
/* SYNOPSIS
/* #include <tls.h>
/*
-/* SSL_CTX *tls_client_init(init_props)
-/* const tls_client_init_props *init_props;
+/* TLS_APPL_STATE *tls_client_init(init_props)
+/* const TLS_CLIENT_INIT_PROPS *init_props;
/*
-/* TLScontext_t *tls_client_start(start_props)
-/* const tls_client_start_props *start_props;
+/* TLS_SESS_STATE *tls_client_start(start_props)
+/* const TLS_CLIENT_START_PROPS *start_props;
/*
-/* void tls_client_stop(client_ctx, stream, failure, TLScontext)
-/* SSL_CTX *client_ctx;
+/* void tls_client_stop(app_ctx, stream, failure, TLScontext)
+/* TLS_APPL_STATE *app_ctx;
/* VSTREAM *stream;
/* int failure;
-/* TLScontext_t *TLScontext;
+/* TLS_SESS_STATE *TLScontext;
/* DESCRIPTION
/* This module is the interface between Postfix TLS clients,
/* the OpenSSL library and the TLS entropy and cache manager.
/* tls_client_init() is called once when the SMTP client
/* initializes.
/* Certificate details are also decided during this phase,
-/* so that peer-specific behavior is not possible.
+/* so peer-specific certificate selection is not possible.
/*
/* tls_client_start() activates the TLS session over an established
/* stream. We expect that network buffers are flushed and
/* SSL_shutdown() to the peer and resets all connection specific
/* TLS data. As RFC2487 does not specify a separate shutdown, it
/* is assumed that the underlying TCP connection is shut down
-/* immediately afterwards, so we don't care about additional data
-/* coming through the channel.
+/* immediately afterwards. Any further writes to the channel will
+/* be discarded, and any further reads will report end-of-file.
/* If the failure flag is set, no SSL_shutdown() handshake is performed.
/*
/* Once the TLS connection is initiated, information about the TLS
/* The last two values may differ from each other when export-strength
/* encryption is used.
/*
-/* The status of the peer certificate verification is available in
-/* TLScontext->peer_verified. It is set to 1 when the certificate could
-/* be verified.
/* If the peer offered a certificate, part of the certificate data are
/* available as:
+/* .IP TLScontext->peer_status
+/* A bitmask field that records the status of the peer certificate
+/* verification. This consists of one or more of
+/* TLS_CERT_FLAG_PRESENT, TLS_CERT_FLAG_ALTNAME, TLS_CERT_FLAG_TRUSTED
+/* and TLS_CERT_FLAG_MATCHED.
/* .IP TLScontext->peer_CN
/* Extracted CommonName of the peer, or zero-length string if the
/* information could not be extracted.
/* .IP TLScontext->issuer_CN
-/* extracted CommonName of the issuer, or zero-length string if the
+/* Extracted CommonName of the issuer, or zero-length string if the
/* information could not be extracted.
+/* .IP TLScontext->peer_fingerprint
+/* At the fingerprint security level, if the peer presented a certificate
+/* the fingerprint of the certificate.
/* .PP
-/* Otherwise these fields are set to null pointers.
+/* If no peer certificate is presented the peer_status is set to 0.
/* LICENSE
/* .ad
/* .fi
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
/* load_clnt_session - load session from client cache (non-callback) */
-static SSL_SESSION *load_clnt_session(TLScontext_t *TLScontext)
+static SSL_SESSION *load_clnt_session(TLS_SESS_STATE *TLScontext)
{
const char *myname = "load_clnt_session";
SSL_SESSION *session = 0;
static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
{
const char *myname = "new_client_session_cb";
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
VSTRING *session_data;
/*
/* uncache_session - remove session from the external cache */
-static void uncache_session(TLScontext_t *TLScontext)
+static void uncache_session(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext)
{
+ SSL_SESSION *session = SSL_get_session(TLScontext->con);
+
+ SSL_CTX_remove_session(ctx, session);
if (TLScontext->cache_type == 0 || TLScontext->serverid == 0)
return;
/* tls_client_init - initialize client-side TLS engine */
-SSL_CTX *tls_client_init(const tls_client_init_props *props)
+TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props)
{
long off = 0;
- SSL_CTX *client_ctx;
int cachable;
-
- /* See skeleton in OpenSSL apps/s_client.c. */
+ SSL_CTX *client_ctx;
+ TLS_APPL_STATE *app_ctx;
+ const EVP_MD *md_alg;
+ unsigned int md_len;
if (props->log_level >= 2)
msg_info("initializing the client-side TLS engine");
+ /*
+ * Load (mostly cipher related) TLS-library internal main.cf parameters.
+ */
+ tls_param_init();
+
/*
* Detect mismatch between compile-time headers and run-time library.
*/
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
+ /*
+ * Create an application data index for SSL objects, so that we can
+ * attach TLScontext information; this information is needed inside
+ * tls_verify_certificate_callback().
+ */
+ if (TLScontext_index < 0) {
+ if ((TLScontext_index = SSL_get_ex_new_index(0, 0, 0, 0, 0)) < 0) {
+ msg_warn("Cannot allocate SSL application data index: "
+ "disabling TLS support");
+ return (0);
+ }
+ }
+
+ /*
+ * If the administrator specifies an unsupported digest algorithm, fail
+ * now, rather than in the middle of a TLS handshake.
+ */
+ if ((md_alg = EVP_get_digestbyname(props->fpt_dgst)) == 0) {
+ msg_warn("Digest algorithm \"%s\" not found: disabling TLS support",
+ props->fpt_dgst);
+ return (0);
+ }
+
+ /*
+ * Sanity check: Newer shared libraries may use larger digests.
+ */
+ if ((md_len = EVP_MD_size(md_alg)) > EVP_MAX_MD_SIZE) {
+ msg_warn("Digest algorithm \"%s\" output size %u too large:"
+ " disabling TLS support", props->fpt_dgst, md_len);
+ return (0);
+ }
+
/*
* Initialize the PRNG (Pseudo Random Number Generator) with some seed
* from external and internal sources. Don't enable TLS without some real
* SSLv2 greeting allowing the best we can offer: TLSv1. We can restrict
* this with the options setting later, anyhow.
*/
- client_ctx = SSL_CTX_new(SSLv23_client_method());
- if (client_ctx == NULL) {
+ ERR_clear_error();
+ if ((client_ctx = SSL_CTX_new(SSLv23_client_method())) == 0) {
+ msg_warn("cannot allocate client SSL_CTX: disabling TLS support");
tls_print_errors();
return (0);
}
+ /*
+ * See the verify callback in tls_verify.c
+ */
+ SSL_CTX_set_verify_depth(client_ctx, props->verifydepth + 1);
+
/*
* Protocol selection is destination dependent, so we delay the protocol
* selection options to the per-session SSL object.
*/
if (tls_set_ca_certificate_info(client_ctx,
props->CAfile, props->CApath) < 0) {
+ /* tls_set_ca_certificate_info() already logs a warning. */
SSL_CTX_free(client_ctx); /* 200411 */
return (0);
}
&& tls_set_my_certificate_key_info(client_ctx, props->cert_file,
props->key_file, props->dcert_file,
props->dkey_file) < 0) {
+ /* tls_set_my_certificate_key_info() already logs a warning. */
SSL_CTX_free(client_ctx); /* 200411 */
return (0);
}
* sessions from the external cache, so we must delete them directly (not
* via a callback).
*/
-
- 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;
- }
+ /*
+ * Allocate an application context, and populate with mandatory protocol
+ * and cipher data.
+ */
+ app_ctx = tls_alloc_app_context(client_ctx);
/*
* The external session cache is implemented by the tlsmgr(8) process.
*/
if (cachable) {
+ app_ctx->cache_type = mystrdup(props->cache_type);
+
/*
* OpenSSL does not use callbacks to load sessions from a client
* cache, so we must invoke that function directly. Apparently,
SSL_SESS_CACHE_NO_AUTO_CLEAR);
SSL_CTX_sess_set_new_cb(client_ctx, new_client_session_cb);
}
-
- /*
- * Create a global index so that we can attach TLScontext information to
- * SSL objects; this information is needed inside
- * tls_verify_certificate_callback().
- */
- if (TLScontext_index < 0)
- TLScontext_index = SSL_get_ex_new_index(0, "TLScontext ex_data index",
- NULL, NULL, NULL);
- return (client_ctx);
+ return (app_ctx);
}
/* match_hostname - match hostname against pattern */
-static int match_hostname(const char *peerid, ARGV *cmatch_argv,
- const char *nexthop, const char *hname)
+static int match_hostname(const char *peerid,
+ const TLS_CLIENT_START_PROPS *props)
{
+ const ARGV *cmatch_argv = props->matchargv;
+ const char *nexthop = props->nexthop;
+ const char *hname = props->host;
const char *pattern;
const char *pattern_left;
int sub;
return (0);
}
-/* verify_extract_peer - verify peer name and extract peer information */
+/* verify_extract_name - verify peer name and extract peer information */
-static void verify_extract_peer(const char *nexthop, const char *hname,
- const char *certmatch, X509 *peercert,
- TLScontext_t *TLScontext)
+static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
+ const TLS_CLIENT_START_PROPS *props)
{
int i;
int r;
- int hostname_matched = 0;
- int dNSName_found = 0;
- int verify_peername;
- ARGV *cmatch_argv = 0;
+ int matched = 0;
+ const char *dnsname;
+ const GENERAL_NAME *gn;
STACK_OF(GENERAL_NAME) * gens;
- TLScontext->peer_verified =
- (SSL_get_verify_result(TLScontext->con) == X509_V_OK);
+ /*
+ * On exit both peer_CN and issuer_CN should be set.
+ */
+ TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext);
- verify_peername =
- (TLScontext->enforce_CN != 0 && TLScontext->peer_verified != 0);
+ /*
+ * Is the certificate trust chain valid and trusted?
+ */
+ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
+ TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
- if (verify_peername) {
- cmatch_argv = argv_split(certmatch, "\t\n\r, :");
+ if (TLS_CERT_IS_TRUSTED(TLScontext) && props->tls_level >= TLS_LEV_VERIFY) {
/*
- * Verify the dNSName(s) in the peer certificate against the
- * peername.
+ * Verify the dNSName(s) in the peer certificate against the nexthop
+ * and hostname.
+ *
+ * If DNS names are present, we use the first matching (or else simply
+ * the first) DNS name as the subject CN. The CommonName in the
+ * issuer DN is obsolete when SubjectAltName is available. This
+ * yields much less surprising logs, because we log the name we
+ * verified or a name we checked and failed to match.
*
* 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
*/
gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
if (gens) {
- for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) {
- const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
-
- if (gn->type == GEN_DNS) {
- dNSName_found++;
- if ((hostname_matched =
- match_hostname((char *) gn->d.ia5->data, cmatch_argv,
- nexthop, hname)) != 0)
- break;
+ r = sk_GENERAL_NAME_num(gens);
+ for (i = 0; i < r && !matched; ++i) {
+ gn = sk_GENERAL_NAME_value(gens, i);
+ if (gn->type != GEN_DNS)
+ continue;
+
+ /*
+ * Even if we have an invalid DNS name, we still ultimately
+ * ignore the CommonName, because subjectAltName:DNS is
+ * present (though malformed). Replace any previous peer_CN
+ * if empty or we get a match.
+ *
+ * We always set at least an empty peer_CN if the ALTNAME cert
+ * flag is set. If not, we set peer_CN from the cert
+ * CommonName below, so peer_CN is always non-null on return.
+ */
+ TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME;
+ dnsname = tls_dns_name(gn, TLScontext);
+ if (dnsname && *dnsname) {
+ matched = match_hostname(dnsname, props);
+ if (TLScontext->peer_CN
+ && (matched || *TLScontext->peer_CN == 0)) {
+ myfree(TLScontext->peer_CN);
+ TLScontext->peer_CN = 0;
+ }
}
+ if (TLScontext->peer_CN == 0)
+ TLScontext->peer_CN = mystrdup(dnsname ? dnsname : "");
}
+
+ /*
+ * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME
+ * objects
+ */
sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
}
- }
- if (dNSName_found) {
- if (!hostname_matched)
- msg_info("certificate peer name verification failed for "
- "%s: %d dNSNames in certificate found, "
- "but none match", hname, dNSName_found);
- }
- if ((TLScontext->peer_CN = tls_peer_CN(peercert)) == 0)
- TLScontext->peer_CN = mystrdup("");
-
- if ((TLScontext->issuer_CN = tls_issuer_CN(peercert)) == 0)
- TLScontext->issuer_CN = mystrdup("");
-
- if (!dNSName_found && verify_peername) {
/*
- * Verify the CommonName in the peer certificate against the
- * peername.
+ * No subjectAltNames, peer_CN is taken from CommonName.
*/
- if (TLScontext->peer_CN[0] != '\0') {
- hostname_matched = match_hostname(TLScontext->peer_CN, cmatch_argv,
- nexthop, hname);
- if (!hostname_matched)
- msg_info("certificate peer name verification failed "
- "for nexthop=%s, host=%s: CommonName mis-match: %s",
- nexthop, hname, TLScontext->peer_CN);
+ if (TLScontext->peer_CN == 0) {
+ TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
+ if (*TLScontext->peer_CN)
+ matched = match_hostname(TLScontext->peer_CN, props);
}
- }
- if (cmatch_argv)
- cmatch_argv = argv_free(cmatch_argv);
- TLScontext->hostname_matched = hostname_matched;
-
- if (TLScontext->log_level >= 1) {
- if (TLScontext->peer_verified
- && (!TLScontext->enforce_CN || TLScontext->hostname_matched))
- msg_info("Verified: subject_CN=%s, issuer=%s",
- TLScontext->peer_CN, TLScontext->issuer_CN);
- else
- msg_info("Unverified: subject_CN=%s, issuer=%s",
+ if (matched)
+ TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
+
+ /*
+ * - Matched: Trusted and peername matches - Trusted: Signed by
+ * trusted CA(s), but peername not matched - Untrusted: Can't verify
+ * the trust chain, reason already logged.
+ */
+ if (TLScontext->log_level >= 2)
+ msg_info("%s: %s subject_CN=%s, issuer_CN=%s", props->namaddr,
+ TLS_CERT_IS_MATCHED(TLScontext) ? "Matched" :
+ TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
TLScontext->peer_CN, TLScontext->issuer_CN);
- }
+ } else
+ TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
+
+ /*
+ * Give them a clue. Problems with trust chain verification were logged
+ * when the session was first negotiated, before the session was stored
+ * into the cache. We don't want mystery failures, so log the fact the
+ * real problem is to be found in the past.
+ */
+ if (TLScontext->session_reused
+ && !TLS_CERT_IS_TRUSTED(TLScontext)
+ && TLScontext->log_level >= 1)
+ msg_info("%s: re-using session with untrusted certificate, "
+ "look for details earlier in the log", props->namaddr);
+}
+
+/* verify_extract_print - extract and verify peer fingerprint */
+
+static void verify_extract_print(TLS_SESS_STATE *TLScontext, X509 *peercert,
+ const TLS_CLIENT_START_PROPS *props)
+{
+ char **cpp;
+
+ /* Non-null by contract */
+ TLScontext->peer_fingerprint = tls_fingerprint(peercert, props->fpt_dgst);
+
+ if (props->tls_level != TLS_LEV_FPRINT)
+ return;
+
+ /*
+ * Compare the fingerprint against each acceptable value, ignoring
+ * upper/lower case differences.
+ */
+ for (cpp = props->matchargv->argv; *cpp; ++cpp)
+ if (strcasecmp(TLScontext->peer_fingerprint, *cpp) == 0) {
+ TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
+ break;
+ }
+ if (props->log_level >= 2)
+ msg_info("%s %s%s fingerprint %s", props->namaddr,
+ TLS_CERT_IS_MATCHED(TLScontext) ? "Matched " : "",
+ props->fpt_dgst, TLScontext->peer_fingerprint);
}
/*
* 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(const tls_client_start_props *props)
+TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
{
int sts;
+ int protomask;
+ const char *cipher_list;
SSL_SESSION *session;
SSL_CIPHER *cipher;
X509 *peercert;
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
+ TLS_APPL_STATE *app_ctx = props->ctx;
+ VSTRING *myserverid;
if (props->log_level >= 1)
- msg_info("setting up TLS connection to %s", props->host);
+ msg_info("setting up TLS connection to %s", props->namaddr);
/*
- * Before we create an SSL, update the SSL_CTX cipherlist if necessary.
+ * First make sure we have valid protocol and cipher parameters
+ *
+ * The cipherlist will be applied to the global SSL context, where it can be
+ * repeatedly reset if necessary, but the protocol restrictions will be
+ * is applied to the SSL connection, because protocol restrictions in the
+ * global context cannot be cleared.
*/
- if (tls_set_cipher_list(props->ctx, props->cipherlist) == 0) {
- msg_warn("Invalid cipherlist \"%s\": aborting TLS session",
- props->cipherlist);
+
+ /*
+ * 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 salt the session lookup key with the protocol list, so that
+ * sessions found in the cache are always acceptable.
+ */
+ protomask = tls_protocol_mask(props->protocols);
+ if (protomask == TLS_PROTOCOL_INVALID) {
+ /* tls_protocol_mask() logs no warning. */
+ msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session",
+ props->namaddr, props->protocols);
return (0);
}
+ myserverid = vstring_alloc(100);
+ vstring_sprintf_append(myserverid, "%s&p=%d", props->serverid, protomask);
+
+ /*
+ * 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 salt the
+ * session lookup key with the cipher list, so that sessions found in the
+ * cache are always acceptable.
+ */
+ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade,
+ props->cipher_exclusions);
+ if (cipher_list == 0) {
+ msg_warn("%s: %s: aborting TLS session",
+ props->namaddr, vstring_str(app_ctx->why));
+ vstring_free(myserverid);
+ return (0);
+ }
+ if (props->log_level >= 2)
+ msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list);
+ vstring_sprintf_append(myserverid, "&c=%s", cipher_list);
/*
* Allocate a new TLScontext for the new connection and get an SSL
* If session caching was enabled when TLS was initialized, the cache type
* is stored in the client SSL context.
*/
- 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);
+ TLScontext = tls_alloc_sess_context(props->log_level, props->namaddr);
+ TLScontext->cache_type = app_ctx->cache_type;
+
+ TLScontext->serverid = vstring_export(myserverid);
- if ((TLScontext->con = (SSL *) SSL_new(props->ctx)) == NULL) {
+ if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
tls_free_context(TLScontext);
}
/*
- * Set the verification parameters to be checked in
- * tls_verify_certificate_callback().
+ * Apply session protocol restrictions.
*/
- 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_verify_certificate_callback);
- } else {
- TLScontext->enforce_verify_errors = 0;
- TLScontext->enforce_CN = 0;
- }
- TLScontext->hostname_matched = 0;
+ if (protomask != 0)
+ SSL_set_options(TLScontext->con,
+ ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L)
+ | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L)
+ | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L));
/*
* The TLS connection is realized by a BIO_pair, so obtain the pair.
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);
- }
-
- /*
- * Try to load an existing session from the TLS session cache.
- *
- * 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.
- *
* 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.
- *
- * 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);
sts = tls_bio_connect(vstream_fileno(props->stream), props->timeout,
TLScontext);
if (sts <= 0) {
- msg_info("SSL_connect error to %s: %d", props->host, sts);
+ msg_info("SSL_connect error to %s: %d", props->namaddr, sts);
tls_print_errors();
- uncache_session(TLScontext);
+ uncache_session(app_ctx->ssl_ctx, TLScontext);
tls_free_context(TLScontext);
return (0);
}
-
- /*
- * The TLS engine is active. Switch to the tls_timed_read/write()
- * functions and make the TLScontext available to those functions.
- */
- tls_stream_start(props->stream, TLScontext);
-
/* Only log_level==4 dumps everything */
if (props->log_level < 4)
BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
*/
TLScontext->session_reused = SSL_session_reused(TLScontext->con);
if (props->log_level >= 2 && TLScontext->session_reused)
- msg_info("Reusing old session");
+ msg_info("%s: Reusing old session", TLScontext->namaddr);
/*
* 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(props->nexthop, props->host, props->certmatch,
- peercert, TLScontext);
+ TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT;
+
+ /*
+ * Peer name or fingerprint verification as requested.
+ * Unconditionally set peer_CN, issuer_CN and peer_fingerprint.
+ */
+ verify_extract_name(TLScontext, peercert, props);
+ verify_extract_print(TLScontext, peercert, props);
X509_free(peercert);
- }
- if (TLScontext->enforce_CN && !TLScontext->hostname_matched) {
- msg_info("Server certificate could not be verified for %s:"
- " hostname mismatch", props->host);
- tls_client_stop(props->ctx, props->stream, props->timeout, 0,
- TLScontext);
- return (0);
+ } else {
+ TLScontext->issuer_CN = mystrdup("");
+ TLScontext->peer_CN = mystrdup("");
+ TLScontext->peer_fingerprint = mystrdup("");
}
/*
TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher,
&(TLScontext->cipher_algbits));
+ /*
+ * The TLS engine is active. Switch to the tls_timed_read/write()
+ * functions and make the TLScontext available to those functions.
+ */
+ tls_stream_start(props->stream, TLScontext);
+
+ /*
+ * All the key facts in a single log entry.
+ */
if (props->log_level >= 1)
- msg_info("TLS connection established to %s: %s with cipher %s"
- " (%d/%d bits)", props->host,
- TLScontext->protocol, TLScontext->cipher_name,
+ msg_info("%s TLS connection established to %s: %s with cipher %s "
+ "(%d/%d bits)", TLS_CERT_IS_MATCHED(TLScontext) ? "Verified" :
+ TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
+ props->namaddr, TLScontext->protocol, TLScontext->cipher_name,
TLScontext->cipher_usebits, TLScontext->cipher_algbits);
tls_int_seed();
/* because they evaluate their arguments only once.
/*
/* tls_level_lookup() converts a TLS level from symbolic name
-/* to internal form. The result is TLS_NOTFOUND for an unknown
-/* level.
+/* to internal form. When an unknown level is specified,
+/* tls_level_lookup() logs no warning, and returns TLS_LEV_INVALID.
/*
/* str_tls_level() converts a TLS level from internal form to
/* symbolic name. The result is a null pointer for an unknown
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
/* Application-specific. */
-NAME_CODE tls_level_table[] = {
+ /*
+ * Order is critical:
+ *
+ * Levels > "encrypt" are expected to match a peer certificate.
+ *
+ * Levels >= "verify" are expected to require a valid CA trust-chain
+ *
+ * This forces "fingerprint" between "encrypt" and "verify".
+ */
+const NAME_CODE tls_level_table[] = {
"none", TLS_LEV_NONE,
"may", TLS_LEV_MAY,
"encrypt", TLS_LEV_ENCRYPT,
+ "fingerprint", TLS_LEV_FPRINT,
"verify", TLS_LEV_VERIFY,
"secure", TLS_LEV_SECURE,
- 0, TLS_LEV_NOTFOUND,
+ 0, TLS_LEV_INVALID,
};
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
-/* TLScontext_t *tls_alloc_context(log_level, peername)
+/* 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;
+/* int var_tls_daemon_rand_bytes;
+/*
+/* TLS_APPL_STATE *tls_alloc_app_context(ssl_ctx)
+/* SSL_CTX *ssl_ctx;
+/*
+/* void tls_free_app_context(app_ctx)
+/* void *app_ctx;
+/*
+/* TLS_SESS_STATE *tls_alloc_sess_context(log_level, namaddr)
/* int log_level;
-/* const char *peername;
+/* const char *namaddr;
/*
/* void tls_free_context(TLScontext)
-/* TLScontext_t *TLScontext;
+/* TLS_SESS_STATE *TLScontext;
/*
/* void tls_check_version()
/*
/* long tls_bug_bits()
/*
-/* const char *tls_set_cipher_list(ssl_ctx, cipher_list)
-/* SSL_CTX *ssl_ctx;
-/* char *cipher_list;
+/* void tls_param_init()
+/*
+/* int tls_protocol_mask(plist)
+/* const char *plist;
+/*
+/* int tls_cipher_grade(name)
+/* const char *name;
+/*
+/* const char *str_tls_cipher_grade(grade)
+/* int grade;
/*
-/* const char *tls_cipher_list(cipher_level, ...)
-/* int cipher_level;
+/* const char *tls_set_ciphers(app_ctx, context, grade, exclusions)
+/* TLS_APPL_STATE *app_ctx;
+/* const char *context;
+/* int grade;
+/* const char *exclusions;
/*
/* void tls_print_errors()
/*
-/* void tls_info_callback(ssl, where, ret)
+/* void tls_info_callback(ssl, where, ret)
/* const SSL *ssl; /* unused */
/* int where;
/* int ret;
/* This module implements routines that support the TLS client
/* and server internals.
/*
-/* tls_alloc_context() creates an initialized TLScontext
-/* structure with the specified peer name and logging level.
+/* tls_alloc_app_context() creates an application context that
+/* holds the SSL context for the application and related cached state.
+/*
+/* tls_free_app_context() deallocates the application context and its
+/* contents (the application context is stored outside the TLS library).
+/*
+/* tls_alloc_sess_context() creates an initialized TLS session context
+/* structure with the specified log mask and peer name[addr].
/*
/* tls_free_context() destroys a TLScontext structure
/* together with OpenSSL structures that are attached to it.
/* for the run-time library. Some of the bug work-arounds are
/* not appropriate for some library versions.
/*
-/* tls_set_cipher_list() updates the cipher list of the specified SSL
-/* context. Returns the new cipherlist on success, otherwise logs a
-/* suitable warning and returns 0. The storage for the return value
-/* is overwritted with each call.
+/* tls_param_init() loads main.cf parameters used internally in
+/* TLS library. Any errors are fatal.
+/*
+/* tls_protocol_mask() returns a bitmask of excluded protocols, given
+/* a list (plist) of protocols to include or (preceded by a '!') exclude.
+/* If "plist" contains invalid protocol names, TLS_PROTOCOL_INVALID is
+/* returned and no warning is logged.
+/*
+/* tls_cipher_grade() converts a case-insensitive cipher grade
+/* name (high, medium, low, export, null) to the corresponding
+/* TLS_CIPHER_ constant. When the input specifies an unrecognized
+/* grade, tls_cipher_grade() logs no warning, and returns
+/* TLS_CIPHER_NONE.
+/*
+/* str_tls_cipher_grade() converts a cipher grade to a name.
+/* When the input specifies an undefined grade, str_tls_cipher_grade()
+/* logs no warning, returns a null pointer.
/*
-/* tls_cipher_list() generates a cipher list from the specified
-/* grade, minus any ciphers specified via a null-terminated
-/* list of string-valued exclusions. The result is overwritten
-/* upon each call.
+/* tls_set_ciphers() generates a cipher list from the specified
+/* grade, minus any ciphers specified via a list of exclusions.
+/* The cipherlist is applied to the supplied SSL context if it
+/* is different from the most recently applied value. The return
+/* value is the cipherlist used and is overwritten upon each call.
+/* When the input is invalid, tls_set_ciphers() logs a warning with
+/* the specified context, and returns a null pointer result.
/*
/* tls_print_errors() queries the OpenSSL error stack,
/* logs the error messages, and clears the error stack.
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
#include <stringops.h>
#include <argv.h>
-/* TLS library. */
+ /*
+ * Global library.
+ */
+#include <mail_params.h>
+#include <mail_conf.h>
+ /*
+ * TLS library.
+ */
#define TLS_INTERNAL
#include <tls.h>
/* Application-specific. */
/*
- * Index to attach TLScontext pointers to SSL objects, so that they can be
- * accessed by call-back routines.
+ * Tunable parameters.
*/
-int TLScontext_index = -1;
+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;
+int var_tls_daemon_rand_bytes;
/*
- * Index to attach session cache names SSL_CTX objects.
+ * Index to attach TLScontext pointers to SSL objects, so that they can be
+ * accessed by call-back routines.
*/
-int TLSscache_index = -1;
+int TLScontext_index = -1;
/*
* Protocol name <=> mask conversion.
*/
-NAME_MASK tls_protocol_table[] = {
+static const NAME_CODE protocol_table[] = {
SSL_TXT_SSLV2, TLS_PROTOCOL_SSLv2,
SSL_TXT_SSLV3, TLS_PROTOCOL_SSLv3,
SSL_TXT_TLSV1, TLS_PROTOCOL_TLSv1,
- 0, 0,
+ 0, TLS_PROTOCOL_INVALID,
};
-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[] = {
+const NAME_CODE tls_cipher_grade_table[] = {
"high", TLS_CIPHER_HIGH,
"medium", TLS_CIPHER_MEDIUM,
"low", TLS_CIPHER_LOW,
"export", TLS_CIPHER_EXPORT,
"null", TLS_CIPHER_NULL,
+ "invalid", TLS_CIPHER_NONE,
0, TLS_CIPHER_NONE,
};
* broken ciphers other than AES and CAMELLIA.
*/
typedef struct {
- char *ssl_name;
- int alg_bits;
- char *evp_name;
-} cipher_probe_t;
+ const char *ssl_name;
+ const int alg_bits;
+ const char *evp_name;
+} cipher_probe_t;
-static cipher_probe_t cipher_probes[] = {
+static const cipher_probe_t cipher_probes[] = {
"AES", 256, "AES-256-CBC",
"CAMELLIA", 256, "CAMELLIA-256-CBC",
0, 0, 0,
/* tls_exclude_missing - Append exclusions for missing ciphers */
-static void tls_exclude_missing(SSL_CTX *ctx, VSTRING *buf)
+static const char *tls_exclude_missing(SSL_CTX *ctx, VSTRING *buf)
{
const char *myname = "tls_exclude_missing";
static ARGV *exclude; /* Cached */
STACK_OF(SSL_CIPHER) * ciphers;
SSL_CIPHER *c;
- cipher_probe_t *probe;
+ const cipher_probe_t *probe;
int alg_bits;
int num;
int i;
* An SSL cipher-suite name for a family of ciphers that use the same
* symmetric algorithm at two or more key sizes, typically 128/256 bits.
*
- * The key size (typically 256) that OpenSSL fails check, and assumes is
+ * The key size (typically 256) that OpenSSL fails to check, and assumes
* available when another key size (typically 128) is usable.
*
* The OpenSSL name of the symmetric algorithm associated with the SSL
}
for (i = 0; i < exclude->argc; ++i)
vstring_sprintf_append(buf, ":!%s", exclude->argv[i]);
+ return (vstring_str(buf));
}
-/* tls_set_cipher_list - Set SSL_CTX cipher list */
+/* tls_apply_cipher_list - update SSL_CTX cipher list */
-const char *tls_set_cipher_list(SSL_CTX *ssl_ctx, const char *spec)
+static const char *tls_apply_cipher_list(TLS_APPL_STATE *app_ctx,
+ const char *context, VSTRING *spec)
{
- static VSTRING *buf;
- const char *ex_spec;
+ const char *new = tls_exclude_missing(app_ctx->ssl_ctx, spec);
- if (buf == 0)
- buf = vstring_alloc(10);
+ ERR_clear_error();
+ if (SSL_CTX_set_cipher_list(app_ctx->ssl_ctx, new) == 0) {
+ tls_print_errors();
+ vstring_sprintf(app_ctx->why, "invalid %s cipher list: \"%s\"",
+ context, new);
+ return (0);
+ }
+ return (new);
+}
- vstring_strcpy(buf, spec);
- tls_exclude_missing(ssl_ctx, buf);
- ex_spec = vstring_str(buf);
+/* tls_protocol_mask - Bitmask of protocols to exclude */
- ERR_clear_error();
- if (SSL_CTX_set_cipher_list(ssl_ctx, ex_spec) != 0)
- return (ex_spec);
+int tls_protocol_mask(const char *plist)
+{
+ char *save;
+ char *tok;
+ char *cp;
+ int code;
+ int exclude = 0;
+ int include = 0;
+
+ save = cp = mystrdup(plist);
+ while ((tok = mystrtok(&cp, "\t\n\r ,:")) != 0) {
+ if (*tok == '!')
+ exclude |= code =
+ name_code(protocol_table, NAME_CODE_FLAG_NONE, ++tok);
+ else
+ include |= code =
+ name_code(protocol_table, NAME_CODE_FLAG_NONE, tok);
+ if (code == TLS_PROTOCOL_INVALID)
+ return TLS_PROTOCOL_INVALID;
+ }
+ myfree(save);
- tls_print_errors();
- return (0);
+ /*
+ * When the include list is empty, use only the explicit exclusions.
+ * Otherwise, also exclude the complement of the include list from the
+ * built-in list of known protocols. There is no way to exclude protocols
+ * we don't know about at compile time, and this is unavoidable because
+ * the OpenSSL API works with compile-time *exclusion* bit-masks.
+ */
+ return (include ? (exclude | (TLS_KNOWN_PROTOCOLS & ~include)) : exclude);
+}
+
+/* tls_param_init - Load TLS related config parameters */
+
+void tls_param_init(void)
+{
+ static const CONFIG_STR_TABLE str_table[] = {
+ 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,
+ 0,
+ };
+ static const CONFIG_INT_TABLE int_table[] = {
+ VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 1, 0,
+ 0,
+ };
+ static int init_done;
+
+ if (init_done)
+ return;
+ init_done = 1;
+
+ get_mail_conf_str_table(str_table);
+ get_mail_conf_int_table(int_table);
}
-/* tls_cipher_list - Cipherlist for given grade, less exclusions */
+/* tls_set_ciphers - Set SSL context cipher list */
-const char *tls_cipher_list(int cipher_level,...)
+const char *tls_set_ciphers(TLS_APPL_STATE *app_ctx, const char *context,
+ const char *grade, const char *exclusions)
{
- const char *myname = "tls_cipher_list";
+ const char *myname = "tls_set_ciphers";
static VSTRING *buf;
- va_list ap;
- const char *exclude;
- char *tok;
+ int new_grade;
char *save;
char *cp;
+ char *tok;
+ const char *new_list;
- buf = buf ? buf : vstring_alloc(10);
+ new_grade = tls_cipher_grade(grade);
+ if (new_grade == TLS_CIPHER_NONE) {
+ vstring_sprintf(app_ctx->why, "invalid %s cipher grade: \"%s\"",
+ context, grade);
+ return (0);
+ }
+ if (buf == 0)
+ buf = vstring_alloc(10);
VSTRING_RESET(buf);
- switch (cipher_level) {
+ /*
+ * Given cached state and identical input, we return the same result.
+ */
+ if (app_ctx->cipher_list) {
+ if (new_grade == app_ctx->cipher_grade
+ && strcmp(app_ctx->cipher_exclusions, exclusions) == 0)
+ return (app_ctx->cipher_list);
+
+ /* Change required, flush cached state */
+ app_ctx->cipher_grade = TLS_CIPHER_NONE;
+
+ myfree(app_ctx->cipher_exclusions);
+ app_ctx->cipher_exclusions = 0;
+
+ myfree(app_ctx->cipher_list);
+ app_ctx->cipher_list = 0;
+ }
+ switch (new_grade) {
case TLS_CIPHER_HIGH:
vstring_strcpy(buf, var_tls_high_clist);
break;
case TLS_CIPHER_NULL:
vstring_strcpy(buf, var_tls_null_clist);
break;
- case TLS_CIPHER_NONE:
- return 0;
default:
/*
* The caller MUST provide a valid cipher grade
*/
- msg_panic("%s: invalid cipher grade: %d", myname, cipher_level);
+ msg_panic("invalid %s cipher grade: %d", context, new_grade);
}
/*
* The base lists for each grade can't be empty.
*/
if (VSTRING_LEN(buf) == 0)
- msg_panic("%s: empty cipherlist", myname);
+ msg_panic("%s: empty \"%s\" cipherlist", myname, grade);
- va_start(ap, cipher_level);
- while ((exclude = va_arg(ap, char *)) != 0) {
- if (*exclude == '\0')
- continue;
- save = cp = mystrdup(exclude);
- while ((tok = mystrtok(&cp, "\t\n\r ,:")) != 0) {
+ /*
+ * Apply locally-specified exclusions.
+ */
+#define CIPHER_SEP "\t\n\r ,:"
+ if (exclusions != 0) {
+ cp = save = mystrdup(exclusions);
+ while ((tok = mystrtok(&cp, CIPHER_SEP)) != 0) {
/*
* Can't exclude ciphers that start with modifiers.
*/
if (strchr("!+-@", *tok)) {
- msg_warn("%s: can't exclude '!+-@' modifiers, '%s' ignored",
- myname, tok);
- continue;
+ vstring_sprintf(app_ctx->why,
+ "invalid unary '!+-@' in %s cipher "
+ "exclusion: \"%s\"", context, tok);
+ return (0);
}
vstring_sprintf_append(buf, ":!%s", tok);
}
myfree(save);
}
- va_end(ap);
+ if ((new_list = tls_apply_cipher_list(app_ctx, context, buf)) == 0)
+ return (0);
- return (vstring_str(buf));
+ /* Cache new state */
+ app_ctx->cipher_grade = new_grade;
+ app_ctx->cipher_exclusions = mystrdup(exclusions);
+
+ return (app_ctx->cipher_list = mystrdup(new_list));
}
+/* tls_alloc_app_context - allocate TLS application context */
-/* tls_alloc_context - allocate TLScontext */
+TLS_APPL_STATE *tls_alloc_app_context(SSL_CTX *ssl_ctx)
+{
+ TLS_APPL_STATE *app_ctx;
+
+ app_ctx = (TLS_APPL_STATE *) mymalloc(sizeof(*app_ctx));
+
+ memset((char *) app_ctx, 0, sizeof(*app_ctx));
+ app_ctx->ssl_ctx = ssl_ctx;
+
+ /* See also: cache purging code in tls_set_ciphers(). */
+ app_ctx->cipher_grade = TLS_CIPHER_NONE;
+ app_ctx->cipher_exclusions = 0;
+ app_ctx->cipher_list = 0;
+ app_ctx->cache_type = 0;
+ app_ctx->why = vstring_alloc(1);
+
+ return (app_ctx);
+}
-TLScontext_t *tls_alloc_context(int log_level, const char *peername)
+/* tls_free_app_context - Free TLS application context */
+
+void tls_free_app_context(TLS_APPL_STATE *app_ctx)
+{
+ if (app_ctx->ssl_ctx)
+ SSL_CTX_free(app_ctx->ssl_ctx);
+ if (app_ctx->cache_type)
+ myfree(app_ctx->cache_type);
+ /* See also: cache purging code in tls_set_ciphers(). */
+ if (app_ctx->cipher_exclusions)
+ myfree(app_ctx->cipher_exclusions);
+ if (app_ctx->cipher_list)
+ myfree(app_ctx->cipher_list);
+ if (app_ctx->why)
+ vstring_free(app_ctx->why);
+ myfree((char *) app_ctx);
+}
+
+/* tls_alloc_sess_context - allocate TLS session context */
+
+TLS_SESS_STATE *tls_alloc_sess_context(int log_level, const char *namaddr)
{
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
/*
* PORTABILITY: Do not assume that null pointers are all-zero bits. Use
*
* However, it's OK to use memset() to zero integer values.
*/
- TLScontext = (TLScontext_t *) mymalloc(sizeof(TLScontext_t));
+ TLScontext = (TLS_SESS_STATE *) mymalloc(sizeof(TLS_SESS_STATE));
memset((char *) TLScontext, 0, sizeof(*TLScontext));
TLScontext->con = 0;
TLScontext->internal_bio = 0;
TLScontext->network_bio = 0;
+ TLScontext->cache_type = 0;
TLScontext->serverid = 0;
TLScontext->peer_CN = 0;
TLScontext->issuer_CN = 0;
TLScontext->protocol = 0;
TLScontext->cipher_name = 0;
TLScontext->log_level = log_level;
- TLScontext->peername = lowercase(mystrdup(peername));
+ TLScontext->namaddr = lowercase(mystrdup(namaddr));
return (TLScontext);
}
/* tls_free_context - deallocate TLScontext and members */
-void tls_free_context(TLScontext_t *TLScontext)
+void tls_free_context(TLS_SESS_STATE *TLScontext)
{
/*
if (TLScontext->network_bio)
BIO_free(TLScontext->network_bio);
- if (TLScontext->peername)
- myfree(TLScontext->peername);
+ if (TLScontext->namaddr)
+ myfree(TLScontext->namaddr);
if (TLScontext->serverid)
myfree(TLScontext->serverid);
msg_info("%s:failed in %s",
str, SSL_state_string_long((SSL *) s));
else if (ret < 0) {
- msg_info("%s:error in %s",
- str, SSL_state_string_long((SSL *) s));
+#ifndef LOG_NON_ERROR_STATES
+ switch (SSL_get_error((SSL *) s, ret)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* Don't log non-error states. */
+ break;
+ default:
+#endif
+ msg_info("%s:error in %s",
+ str, SSL_state_string_long((SSL *) s));
+#ifndef LOG_NON_ERROR_STATES
+ }
+#endif
}
}
}
/* SYNOPSIS
/* #include <tls.h>
/*
-/* SSL_CTX *tls_server_init(props)
-/* const tls_server_props *props;
+/* TLS_APPL_STATE *tls_server_init(props)
+/* const TLS_SERVER_INIT_PROPS *props;
/*
-/* TLScontext_t *tls_server_start(props)
-/* const tls_server_start_props *props;
+/* TLS_SESS_STATE *tls_server_start(props)
+/* const TLS_SERVER_START_PROPS *props;
/*
-/* void tls_server_stop(server_ctx, stream, failure, TLScontext)
-/* SSL_CTX *server_ctx;
+/* void tls_server_stop(app_ctx, stream, failure, TLScontext)
+/* TLS_APPL_STATE *app_ctx;
/* VSTREAM *stream;
/* int failure;
-/* TLScontext_t *TLScontext;
+/* TLS_SESS_STATE *TLScontext;
/* DESCRIPTION
/* This module is the interface between Postfix TLS servers,
/* the OpenSSL library, and the TLS entropy and cache manager.
/* SSL_shutdown() to the peer and resets all connection specific
/* TLS data. As RFC2487 does not specify a separate shutdown, it
/* is assumed that the underlying TCP connection is shut down
-/* immediately afterwards, so we don't care about additional data
-/* coming through the channel.
+/* immediately afterwards. Any further writes to the channel will
+/* be discarded, and any further reads will report end-of-file.
/* If the failure flag is set, no SSL_shutdown() handshake is performed.
/*
/* Once the TLS connection is initiated, information about the TLS
/* The last two values may differ from each other when export-strength
/* encryption is used.
/*
-/* The status of the peer certificate verification is available in
-/* TLScontext->peer_verified. It is set to 1 when the certificate could
-/* be verified.
/* If the peer offered a certificate, part of the certificate data are
/* available as:
+/* .IP TLScontext->peer_status
+/* A bitmask field that records the status of the peer certificate
+/* verification. One or more of TLS_CERT_FLAG_PRESENT and
+/* TLS_CERT_FLAG_TRUSTED.
/* .IP TLScontext->peer_CN
/* Extracted CommonName of the peer, or zero-length string
/* when information could not be extracted.
/* Extracted CommonName of the issuer, or zero-length string
/* when information could not be extracted.
/* .IP TLScontext->peer_fingerprint
-/* Fingerprint of the certificate, or null pointer when no
-/* certificate digest is available.
+/* Fingerprint of the certificate, or zero-length string when no peer
+/* certificate is available.
/* .PP
-/* Otherwise these fields are set to null pointers.
+/* If no peer certificate is presented the peer_status is set to 0.
/* LICENSE
/* .ad
/* .fi
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
/* Application-specific. */
-/* We must keep some of the info available */
-static const char hexcodes[] = "0123456789ABCDEF";
-
/*
* The session_id_context indentifies the service that created a session.
* This information is used to distinguish between multiple TLS-based
* servers running on the same server. We use the name of the mail system.
*/
-static char server_session_id_context[] = "Postfix/TLS";
+static const char server_session_id_context[] = "Postfix/TLS";
/* get_server_session_cb - callback to retrieve session from server cache */
int *unused_copy)
{
const char *myname = "get_server_session_cb";
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
VSTRING *cache_id;
VSTRING *session_data = vstring_alloc(2048);
SSL_SESSION *session = 0;
GEN_CACHE_ID(cache_id, session_id, session_id_length, TLScontext->serverid);
if (TLScontext->log_level >= 2)
- msg_info("looking up session %s in %s cache",
+ msg_info("%s: looking up session %s in %s cache", TLScontext->namaddr,
STR(cache_id), TLScontext->cache_type);
/*
session_data) == TLS_MGR_STAT_OK) {
session = tls_session_activate(STR(session_data), LEN(session_data));
if (session && (TLScontext->log_level >= 2))
- msg_info("reloaded session %s from %s cache",
- STR(cache_id), TLScontext->cache_type);
+ msg_info("%s: reloaded session %s from %s cache",
+ TLScontext->namaddr, STR(cache_id),
+ TLScontext->cache_type);
}
/*
/* uncache_session - remove session from internal & external cache */
-static void uncache_session(SSL_CTX *ctx, TLScontext_t *TLScontext)
+static void uncache_session(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext)
{
VSTRING *cache_id;
SSL_SESSION *session = SSL_get_session(TLScontext->con);
TLScontext->serverid);
if (TLScontext->log_level >= 2)
- msg_info("remove session %s from %s cache",
+ msg_info("%s: remove session %s from %s cache", TLScontext->namaddr,
STR(cache_id), TLScontext->cache_type);
tls_mgr_delete(TLScontext->cache_type, STR(cache_id));
{
const char *myname = "new_server_session_cb";
VSTRING *cache_id;
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
VSTRING *session_data;
if ((TLScontext = SSL_get_ex_data(ssl, TLScontext_index)) == 0)
TLScontext->serverid);
if (TLScontext->log_level >= 2)
- msg_info("save session %s to %s cache",
+ msg_info("%s: save session %s to %s cache", TLScontext->namaddr,
STR(cache_id), TLScontext->cache_type);
/*
/* tls_server_init - initialize the server-side TLS engine */
-SSL_CTX *tls_server_init(const tls_server_props *props)
+TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
{
+ SSL_CTX *server_ctx;
long off = 0;
int verify_flags = SSL_VERIFY_NONE;
- SSL_CTX *server_ctx;
int cachable;
-
- /* See skeleton at OpenSSL apps/s_server.c. */
+ int protomask;
+ TLS_APPL_STATE *app_ctx;
+ const EVP_MD *md_alg;
+ unsigned int md_len;
if (props->log_level >= 2)
msg_info("initializing the server-side TLS engine");
+ /*
+ * Load (mostly cipher related) TLS-library internal main.cf parameters.
+ */
+ tls_param_init();
+
/*
* Detect mismatch between compile-time headers and run-time library.
*/
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
+ /*
+ * First validate the protocols. If these are invalid, we can't continue.
+ */
+ protomask = tls_protocol_mask(props->protocols);
+ if (protomask == TLS_PROTOCOL_INVALID) {
+ /* tls_protocol_mask() logs no warning. */
+ msg_warn("Invalid TLS protocol list \"%s\": disabling TLS support",
+ props->protocols);
+ return (0);
+ }
+
+ /*
+ * Create an application data index for SSL objects, so that we can
+ * attach TLScontext information; this information is needed inside
+ * tls_verify_certificate_callback().
+ */
+ if (TLScontext_index < 0) {
+ if ((TLScontext_index = SSL_get_ex_new_index(0, 0, 0, 0, 0)) < 0) {
+ msg_warn("Cannot allocate SSL application data index: "
+ "disabling TLS support");
+ return (0);
+ }
+ }
+
+ /*
+ * If the administrator specifies an unsupported digest algorithm, fail
+ * now, rather than in the middle of a TLS handshake.
+ */
+ if ((md_alg = EVP_get_digestbyname(props->fpt_dgst)) == 0) {
+ msg_warn("Digest algorithm \"%s\" not found: disabling TLS support",
+ props->fpt_dgst);
+ return (0);
+ }
+
+ /*
+ * Sanity check: Newer shared libraries may use larger digests.
+ */
+ if ((md_len = EVP_MD_size(md_alg)) > EVP_MAX_MD_SIZE) {
+ msg_warn("Digest algorithm \"%s\" output size %u too large:"
+ " disabling TLS support", props->fpt_dgst, md_len);
+ return (0);
+ }
+
/*
* Initialize the PRNG (Pseudo Random Number Generator) with some seed
* from external and internal sources. Don't enable TLS without some real
* the protocol level, we can add an option to not use SSLv2/v3/TLSv1
* later.
*/
- server_ctx = SSL_CTX_new(SSLv23_server_method());
- if (server_ctx == NULL) {
+ ERR_clear_error();
+ if ((server_ctx = SSL_CTX_new(SSLv23_server_method())) == 0) {
+ msg_warn("cannot allocate server SSL_CTX: disabling TLS support");
tls_print_errors();
return (0);
}
+ /*
+ * See the verify callback in tls_verify.c
+ */
+ SSL_CTX_set_verify_depth(server_ctx, props->verifydepth + 1);
+
/*
* 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.
+ * Global protocol selection.
*/
- 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);
- }
+ if (protomask != 0)
+ SSL_CTX_set_options(server_ctx,
+ ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L)
+ | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L)
+ | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L));
/*
- * Set the call-back routine for verbose logging.
+ * Set the call-back routine to debug handshake progress.
*/
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 (tls_set_cipher_list(server_ctx, props->cipherlist) == 0) {
- SSL_CTX_free(server_ctx);
- msg_warn("Invalid cipherlist \"%s\": disabling TLS support",
- props->cipherlist);
- return (0); /* Already logged */
- }
-
/*
* Load the CA public key certificates for both the server cert and for
* the verification of client certificates. As provided by OpenSSL we
*/
if (tls_set_ca_certificate_info(server_ctx,
props->CAfile, props->CApath) < 0) {
+ /* tls_set_ca_certificate_info() already logs a warning. */
SSL_CTX_free(server_ctx); /* 200411 */
return (0);
}
if (tls_set_my_certificate_key_info(server_ctx, props->cert_file,
props->key_file, props->dcert_file,
props->dkey_file) < 0) {
+ /* tls_set_my_certificate_key_info() already logs a warning. */
SSL_CTX_free(server_ctx); /* 200411 */
return (0);
}
SSL_CTX_set_client_CA_list(server_ctx,
SSL_load_client_CA_file(props->CAfile));
+ /*
+ * Initialize our own TLS server handle, before diving into the details
+ * of TLS session cache management.
+ */
+ app_ctx = tls_alloc_app_context(server_ctx);
+
/*
* The session cache is implemented by the tlsmgr(8) server.
*
* Victor Duchovni.
*/
- 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 || props->set_sessid) {
/*
SSL_SESS_CACHE_SERVER |
SSL_SESS_CACHE_NO_AUTO_CLEAR);
if (cachable) {
+ app_ctx->cache_type = mystrdup(props->cache_type);
+
SSL_CTX_sess_set_get_cb(server_ctx, get_server_session_cb);
SSL_CTX_sess_set_new_cb(server_ctx, new_server_session_cb);
}
SSL_CTX_set_session_cache_mode(server_ctx, SSL_SESS_CACHE_OFF);
}
- /*
- * Create a global index so that we can attach TLScontext information to
- * SSL objects; this information is needed inside
- * tls_verify_certificate_callback().
- */
- if (TLScontext_index < 0)
- TLScontext_index = SSL_get_ex_new_index(0, "TLScontext ex_data index",
- NULL, NULL, NULL);
-
- return (server_ctx);
+ return (app_ctx);
}
/*
* the SMTP buffers are flushed and the "220 Ready to start TLS" was sent to
* the client, so that we can immediately start the TLS handshake process.
*/
-TLScontext_t *tls_server_start(const tls_server_start_props *props)
+TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props)
{
int sts;
- int j;
- int verify_flags;
- unsigned int n;
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
SSL_CIPHER *cipher;
X509 *peer;
- unsigned char md[EVP_MAX_MD_SIZE];
char buf[CCERT_BUFSIZ];
+ const char *cipher_list;
+ TLS_APPL_STATE *app_ctx = props->ctx;
if (props->log_level >= 1)
- msg_info("setting up TLS connection from %s[%s]",
- props->peername, props->peeraddr);
+ msg_info("setting up TLS connection from %s", props->namaddr);
+
+ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade,
+ props->cipher_exclusions);
+ if (cipher_list == 0) {
+ msg_warn("%s: %s: aborting TLS session", props->namaddr,
+ vstring_str(app_ctx->why));
+ return (0);
+ }
+ if (props->log_level >= 2)
+ msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list);
/*
* 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().
*/
- TLScontext = tls_alloc_context(props->log_level, props->peername);
- TLScontext->cache_type = SSL_CTX_get_ex_data(props->ctx, TLSscache_index);
+ TLScontext = tls_alloc_sess_context(props->log_level, props->namaddr);
+ TLScontext->cache_type = app_ctx->cache_type;
+
TLScontext->serverid = mystrdup(props->serverid);
+ TLScontext->am_server = 1;
- if ((TLScontext->con = (SSL *) SSL_new(props->ctx)) == NULL) {
+ ERR_clear_error();
+ if ((TLScontext->con = (SSL *) SSL_new(app_ctx->ssl_ctx)) == 0) {
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
tls_free_context(TLScontext);
return (0);
}
- /*
- * Set the verification parameters to be checked in
- * tls_verify_certificate_callback().
- */
- if (props->requirecert) {
- verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
- verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- TLScontext->enforce_verify_errors = 1;
- SSL_set_verify(TLScontext->con, verify_flags,
- tls_verify_certificate_callback);
- } else {
- TLScontext->enforce_verify_errors = 0;
- }
- TLScontext->enforce_CN = 0;
-
/*
* The TLS connection is realized by a BIO_pair, so obtain the pair.
*
sts = tls_bio_accept(vstream_fileno(props->stream), props->timeout,
TLScontext);
if (sts <= 0) {
- msg_info("SSL_accept error from %s[%s]: %d",
- props->peername, props->peeraddr, sts);
+ msg_info("SSL_accept error from %s: %d", props->namaddr, sts);
tls_print_errors();
tls_free_context(TLScontext);
return (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");
+ if (TLScontext->log_level >= 2 && TLScontext->session_reused)
+ msg_info("%s: Reusing old session", TLScontext->namaddr);
/*
* Let's see whether a peer certificate is available and what is the
*/
peer = SSL_get_peer_certificate(TLScontext->con);
if (peer != NULL) {
+ TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT;
if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
- TLScontext->peer_verified = 1;
+ TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
if (props->log_level >= 2) {
X509_NAME_oneline(X509_get_subject_name(peer),
buf, sizeof(buf));
msg_info("issuer=%s", buf);
}
- if (X509_digest(peer, EVP_md5(), md, &n) && n > 0) {
- TLScontext->peer_fingerprint = mymalloc(n * 3);
- for (j = 0; j < (int) n; j++) {
- TLScontext->peer_fingerprint[j * 3] =
- hexcodes[(md[j] & 0xf0) >> 4U];
- TLScontext->peer_fingerprint[(j * 3) + 1] =
- hexcodes[(md[j] & 0x0f)];
- if (j + 1 != (int) n)
- TLScontext->peer_fingerprint[(j * 3) + 2] = ':';
- else
- TLScontext->peer_fingerprint[(j * 3) + 2] = '\0';
- }
- if (props->log_level >= 1)
- msg_info("fingerprint=%s", TLScontext->peer_fingerprint);
- }
- if ((TLScontext->peer_CN = tls_peer_CN(peer)) == 0)
- TLScontext->peer_CN = mystrdup("");
- if ((TLScontext->issuer_CN = tls_issuer_CN(peer)) == 0)
- TLScontext->issuer_CN = mystrdup("");
+ TLScontext->peer_CN = tls_peer_CN(peer, TLScontext);
+ TLScontext->issuer_CN = tls_issuer_CN(peer, TLScontext);
+ TLScontext->peer_fingerprint = tls_fingerprint(peer, props->fpt_dgst);
if (props->log_level >= 1) {
- if (TLScontext->peer_verified)
- msg_info("Verified: subject_CN=%s, issuer=%s",
- TLScontext->peer_CN, TLScontext->issuer_CN);
- else
- msg_info("Unverified: subject_CN=%s, issuer=%s",
- TLScontext->peer_CN, TLScontext->issuer_CN);
+ msg_info("%s: %s: subject_CN=%s, issuer=%s, fingerprint=%s",
+ props->namaddr,
+ TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
+ TLScontext->peer_CN, TLScontext->issuer_CN,
+ TLScontext->peer_fingerprint);
}
X509_free(peer);
- }
-
- /*
- * If this is a cached session, we have to check by hand if the cached
- * session peer was verified.
- */
- if (props->requirecert) {
- if (!TLScontext->peer_verified || !TLScontext->peer_CN) {
- if (TLScontext->session_reused == 0)
- msg_panic("tls_server_start: peer was not verified");
- msg_info("Re-used session without peer certificate removed");
- uncache_session(props->ctx, TLScontext);
- tls_free_context(TLScontext);
- return (0);
- }
+ } else {
+ TLScontext->peer_CN = mystrdup("");
+ TLScontext->issuer_CN = mystrdup("");
+ TLScontext->peer_fingerprint = mystrdup("");
}
/*
*/
tls_stream_start(props->stream, TLScontext);
+ /*
+ * All the key facts in a single log entry.
+ */
if (props->log_level >= 1)
- msg_info("TLS connection established from %s[%s]: %s with cipher %s (%d/%d bits)",
- props->peername, props->peeraddr,
- TLScontext->protocol, TLScontext->cipher_name,
+ msg_info("%s TLS connection established from %s: %s with cipher %s "
+ "(%d/%d bits)", !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous"
+ : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
+ props->namaddr, TLScontext->protocol, TLScontext->cipher_name,
TLScontext->cipher_usebits, TLScontext->cipher_algbits);
+
tls_int_seed();
return (TLScontext);
/* #include <tls.h>
/*
/* void tls_session_stop(ctx, stream, timeout, failure, TLScontext)
-/* SSL_CTX *ctx;
+/* TLS_APPL_STATE *ctx;
/* VSTREAM *stream;
/* int timeout;
/* int failure;
-/* TLScontext_t *TLScontext;
+/* TLS_SESS_STATE *TLScontext;
/*
/* VSTRING *tls_session_passivate(session)
/* SSL_SESSION *session;
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
/* tls_session_stop - shut down the TLS connection and reset state */
-void tls_session_stop(SSL_CTX *unused_ctx, VSTREAM *stream, int timeout,
- int failure, TLScontext_t *TLScontext)
+void tls_session_stop(TLS_APPL_STATE *unused_ctx, VSTREAM *stream, int timeout,
+ int failure, TLS_SESS_STATE *TLScontext)
{
const char *myname = "tls_session_stop";
int retval;
/*
/* void tls_stream_start(stream, context)
/* VSTREAM *stream;
-/* TLScontext_t *context;
+/* TLS_SESS_STATE *context;
/*
/* void tls_stream_stop(stream)
/* VSTREAM *stream;
{
const char *myname = "tls_timed_read";
ssize_t ret;
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
- TLScontext = (TLScontext_t *) context;
+ TLScontext = (TLS_SESS_STATE *) context;
if (!TLScontext)
msg_panic("%s: no context", myname);
void *context)
{
const char *myname = "tls_timed_write";
- TLScontext_t *TLScontext;
+ TLS_SESS_STATE *TLScontext;
- TLScontext = (TLScontext_t *) context;
+ TLScontext = (TLS_SESS_STATE *) context;
if (!TLScontext)
msg_panic("%s: no context", myname);
/* tls_stream_start - start VSTREAM over TLS */
-void tls_stream_start(VSTREAM *stream, TLScontext_t *context)
+void tls_stream_start(VSTREAM *stream, TLS_SESS_STATE *context)
{
vstream_control(stream,
VSTREAM_CTL_READ_FN, tls_timed_read,
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
-/* char *tls_peer_CN(peercert)
+/* char *tls_peer_CN(peercert, TLScontext)
/* X509 *peercert;
+/* TLS_SESS_STATE *TLScontext;
/*
-/* char *tls_issuer_CN(peercert)
+/* char *tls_issuer_CN(peercert, TLScontext)
/* X509 *peercert;
+/* TLS_SESS_STATE *TLScontext;
+/*
+/* const char *tls_dns_name(gn, TLScontext)
+/* const GENERAL_NAME *gn;
+/* TLS_SESS_STATE *TLScontext;
+/*
+/* char *tls_fingerprint(peercert, dgst)
+/* X509 *peercert;
+/* const char *dgst;
/*
/* int tls_verify_certificate_callback(ok, ctx)
/* int ok;
/* X509_STORE_CTX *ctx;
/* DESCRIPTION
/* tls_peer_CN() returns the text CommonName for the peer
-/* certificate subject, or a null pointer if no CommonName was
+/* certificate subject, or an empty string if no CommonName was
/* found. The result is allocated with mymalloc() and must be
/* freed by the caller.
/*
/* tls_issuer_CN() returns the text CommonName for the peer
-/* certificate issuer, or a null pointer if no CommonName was
+/* certificate issuer, or an empty string if no CommonName was
/* found. The result is allocated with mymalloc() and must be
/* freed by the caller.
/*
+/* tls_dns_name() returns the string value of a GENERAL_NAME
+/* from a DNS subjectAltName extension. If non-printable characters
+/* are found, a null string is returned instead. Further sanity
+/* checks may be added if the need arises.
+/*
+/* tls_fingerprint() returns a fingerprint of the the given
+/* certificate using the requested message digest. Panics if the
+/* (previously verified) digest algorithm is not found. The return
+/* value is dynamically allocated with mymalloc(), and the caller
+/* must eventually free it with myfree().
+/*
/* tls_verify_callback() is called several times (directly or
/* indirectly) from crypto/x509/x509_vfy.c. It is called as
/* a final check, and if it returns "0", the handshake is
/* are disabled when verification failed because of some
/* earlier problem.
/* .IP ctx
-/* TLS client or server context. This also specifies the
-/* TLScontext with enforcement options.
+/* SSL application context. This links to the Postfix TLScontext
+/* with enforcement and logging options.
+/* .IP gn
+/* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
+/* to be decoded and checked for validity.
+/* .IP peercert
+/* Server or client X.509 certificate.
+/* .IP dgst
+/* Name of a message digest algorithm suitable for computing secure
+/* (1st pre-image resistant) message digests of certificates. For now,
+/* md5, sha1, or member of SHA-2 family if supported by OpenSSL.
+/* .IP TLScontext
+/* Server or client context for warning messages.
/* DIAGNOSTICS
-/* tls_peer_CN() and tls_issuer_CN() log a warning and return
-/* a null pointer when 1) the requested information is not
-/* available in the specified certificate, 2) the result
-/* exceeds a fixed limit, or 3) the result contains null
-/* characters.
+/* tls_peer_CN(), tls_issuer_CN() and tls_dns_name() log a warning
+/* when 1) the requested information is not available in the specified
+/* certificate, 2) the result exceeds a fixed limit, 3) the result
+/* contains NUL characters or the result contains non-printable or
+/* non-ASCII characters.
/* LICENSE
/* .ad
/* .fi
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Victor Duchovni
+/* Morgan Stanley
/*--*/
/* System library. */
#include <sys_defs.h>
+#include <ctype.h>
#ifdef USE_TLS
#include <string.h>
#include <msg.h>
#include <mymalloc.h>
+#include <stringops.h>
/* TLS library. */
#define TLS_INTERNAL
#include <tls.h>
+/* Application-specific. */
+
+static const char hexcodes[] = "0123456789ABCDEF";
+
/* tls_verify_certificate_callback - verify peer certificate info */
int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
{
- char buf[1024];
- X509 *err_cert;
+ char buf[CCERT_BUFSIZ];
+ X509 *cert;
int err;
int depth;
- int verify_depth;
SSL *con;
- TLScontext_t *TLScontext;
-
- /* Adapted from OpenSSL apps/s_cb.c */
+ TLS_SESS_STATE *TLScontext;
- err_cert = X509_STORE_CTX_get_current_cert(ctx);
- err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
-
+ cert = X509_STORE_CTX_get_current_cert(ctx);
con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
TLScontext = SSL_get_ex_data(con, TLScontext_index);
- X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
- if (TLScontext->log_level >= 2)
- msg_info("certificate verification depth=%d subject=%s", depth, buf);
-
/*
- * Test for a too long certificate chain, because that error condition is
- * not handled by the OpenSSL library.
+ * The callback function is called repeatedly, first with the root
+ * certificate, and then with each intermediate certificate ending with
+ * the peer certificate.
+ *
+ * With each call, the validity of the current certificate (usage bits,
+ * attributes, expiration, ... checked by the OpenSSL library) is
+ * available in the "ok" argument. Error details are available via
+ * X509_STORE_CTX API.
+ *
+ * We never terminate the SSL handshake in the verification callback, rather
+ * we allow the TLS handshake to continue, but mark the session as
+ * unverified. The application is responsible for closing any sessions
+ * with unverified credentials.
+ *
+ * Certificate chain depth limit violations are mis-reported by the OpenSSL
+ * library, from SSL_CTX_set_verify(3):
+ *
+ * The certificate verification depth set with SSL[_CTX]_verify_depth()
+ * stops the verification at a certain depth. The error message produced
+ * will be that of an incomplete certificate chain and not
+ * X509_V_ERR_CERT_CHAIN_TOO_LONG as may be expected.
+ *
+ * We set a limit that is one higher than the user requested limit. If this
+ * higher limit is reached, we raise an error even a trusted root CA is
+ * present at this depth. This disambiguates trust chain truncation from
+ * an incomplete trust chain.
*/
- verify_depth = SSL_get_verify_depth(con);
- if (ok && (verify_depth >= 0) && (depth > verify_depth)) {
+ if (depth >= SSL_get_verify_depth(con)) {
ok = 0;
- err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
- X509_STORE_CTX_set_error(ctx, err);
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG);
}
- if (!ok) {
- msg_info("certificate verification failed for %s: num=%d:%s",
- TLScontext->peername, err,
- X509_verify_cert_error_string(err));
+ if (TLScontext->log_level >= 2) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
+ msg_info("%s: certificate verification depth=%d verify=%d subject=%s",
+ TLScontext->namaddr, depth, ok, printable(buf, '?'));
}
/*
- * We delay peername verification until the SSL handshake completes. The
- * peername verification previously done here is now called directly from
- * tls_client_start(). This substantially simplifies the cache interface.
+ * If no errors, or we are not logging verification errors, we are done.
*/
+ if (ok || (TLScontext->peer_status & TLS_CERT_FLAG_LOGGED) != 0)
+ return (1);
/*
- * Other causes for verification failure.
+ * One counter-example is enough.
*/
- switch (ctx->error) {
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ TLScontext->peer_status |= TLS_CERT_FLAG_LOGGED;
+
+#define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server")
+
+ /*
+ * Specific causes for verification failure.
+ */
+ switch (err = X509_STORE_CTX_get_error(ctx)) {
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ msg_info("certificate verification failed for %s: "
+ "self-signed certificate", TLScontext->namaddr);
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+
+ /*
+ * There is no difference between issuing cert not provided and
+ * provided, but not found in CAfile/CApath. Either way, we don't
+ * trust it.
+ */
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),
buf, sizeof(buf));
- msg_info("certificate verification failed for %s:"
- "issuer %s certificate unavailable",
- TLScontext->peername, buf);
+ msg_info("certificate verification failed for %s: untrusted issuer %s",
+ TLScontext->namaddr, printable(buf, '?'));
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
- msg_info("certificate verification failed for %s:"
- "certificate not yet valid",
- TLScontext->peername);
+ msg_info("%s certificate verification failed for %s: certificate not"
+ " yet valid", PURPOSE, TLScontext->namaddr);
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
- msg_info("certificate verification failed for %s:"
- "certificate has expired",
- TLScontext->peername);
+ msg_info("%s certificate verification failed for %s: certificate has"
+ " expired", PURPOSE, TLScontext->namaddr);
+ break;
+ case X509_V_ERR_INVALID_PURPOSE:
+ msg_info("certificate verification failed for %s: not designated for "
+ "use as a %s certificate", TLScontext->namaddr, PURPOSE);
+ break;
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ msg_info("certificate verification failed for %s: "
+ "certificate chain longer than limit(%d)",
+ TLScontext->namaddr, SSL_get_verify_depth(con) - 1);
+ break;
+ default:
+ msg_info("%s certificate verification failed for %s: num=%d:%s",
+ PURPOSE, TLScontext->namaddr, err,
+ X509_verify_cert_error_string(err));
break;
}
- if (TLScontext->log_level >= 2)
- msg_info("verify return: %d", ok);
- /*
- * Never fail in case of opportunistic mode.
- */
- if (TLScontext->enforce_verify_errors)
- return (ok);
- else
- return (1);
+ return (1);
}
#ifndef DONT_GRIPE
/* tls_text_name - extract certificate property value by name */
-static char *tls_text_name(X509_NAME *name, int nid, char *label, int gripe)
+static char *tls_text_name(X509_NAME *name, int nid, const char *label,
+ const TLS_SESS_STATE *TLScontext, int gripe)
{
- int len;
+ const char *myname = "tls_text_name";
int pos;
X509_NAME_ENTRY *entry;
ASN1_STRING *entry_str;
- unsigned char *tmp;
- char *result;
+ int typ;
+ int len;
+ unsigned char *val;
+ unsigned char *utf;
+ char *cp;
- if (name == 0
- || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
+ if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
if (gripe != DONT_GRIPE) {
- msg_warn("peer certificate has no %s", label);
+ msg_warn("%s: %s: peer certificate has no %s",
+ myname, TLScontext->namaddr, label);
tls_print_errors();
}
return (0);
}
-
#if 0
+
/*
- * If the match is required unambiguous, insist that that no
- * other values be present.
+ * If the match is required unambiguous, insist that that no other values
+ * be present.
*/
- if (unique == UNIQUE && X509_NAME_get_index_by_NID(name, nid, pos) >= 0) {
- msg_warn("multiple %ss in peer certificate", label);
+ if (X509_NAME_get_index_by_NID(name, nid, pos) >= 0) {
+ msg_warn("%s: %s: multiple %ss in peer certificate",
+ myname, TLScontext->namaddr, label);
return (0);
}
#endif
if ((entry = X509_NAME_get_entry(name, pos)) == 0) {
/* This should not happen */
- msg_warn("error reading peer certificate %s entry", label);
+ msg_warn("%s: %s: error reading peer certificate %s entry",
+ myname, TLScontext->namaddr, label);
tls_print_errors();
return (0);
}
-
if ((entry_str = X509_NAME_ENTRY_get_data(entry)) == 0) {
/* This should not happen */
- msg_warn("error reading peer certificate %s data", label);
+ msg_warn("%s: %s: error reading peer certificate %s data",
+ myname, TLScontext->namaddr, label);
tls_print_errors();
return (0);
}
- if ((len = ASN1_STRING_to_UTF8(&tmp, entry_str)) < 0) {
- /* This should not happen */
- msg_warn("error decoding peer certificate %s data", label);
- tls_print_errors();
- return (0);
- }
+ /*
+ * Peername checks are security sensitive, carefully scrutinize the
+ * input!
+ */
+ typ = ASN1_STRING_type(entry_str);
+ len = ASN1_STRING_length(entry_str);
+ val = ASN1_STRING_data(entry_str);
/*
- * Since the peer CN is used in peer verification, take care to detect
- * truncation due to excessive length or internal NULs.
+ * http://www.apps.ietf.org/rfc/rfc3280.html#sec-4.1.2.4 Quick Summary:
+ *
+ * The DirectoryString type is defined as a choice of PrintableString,
+ * TeletexString, BMPString, UTF8String, and UniversalString. The
+ * UTF8String encoding is the preferred encoding, and all certificates
+ * issued after December 31, 2003 MUST use the UTF8String encoding of
+ * DirectoryString (except as noted below).
+ *
+ * XXX: 2007, the above has not happened yet (of course), and we continue to
+ * see new certificates with T61STRING (Teletex) attribute values.
+ *
+ * XXX: 2007, at this time there are only two ASN.1 fixed width multi-byte
+ * string encodings, BMPSTRING (16 bit Unicode) and UniversalString
+ * (32-bit Unicode). The only variable width ASN.1 string encoding is
+ * UTF8 with all the other encodings being 1 byte wide subsets or subsets
+ * of ASCII.
+ *
+ * Relying on this could simplify the code, because we would never convert
+ * unexpected single-byte encodings, but is involves too many cases to be
+ * sure that we have a complete set and the assumptions may become false.
+ * So, we pessimistically convert encodings not blessed by RFC 2459, and
+ * filter out all types that are not string types as a side-effect of
+ * UTF8 conversion (the ASN.1 library knows which types are string types
+ * and how wide they are...).
+ *
+ * XXX: Two possible states after switch, either "utf == val" and it MUST
+ * NOT be freed with OPENSSL_free(), or "utf != val" and it MUST be freed
+ * with OPENSSL_free().
*/
+ switch (typ) {
+ case V_ASN1_PRINTABLESTRING: /* X.500 portable ASCII
+ * printables */
+ case V_ASN1_IA5STRING: /* ISO 646 ~ ASCII */
+ case V_ASN1_T61STRING: /* Essentially ISO-Latin */
+ case V_ASN1_UTF8STRING: /* UTF8 */
+ utf = val;
+ break;
+
+ default:
+
+ /*
+ * May shrink in wash, but BMPSTRING only shrinks by 50%. Others may
+ * shrink by up to 75%. We Sanity check the length before bothering
+ * to copy any large strings to convert to UTF8, only to find out
+ * they don't fit. So long as no new MB types are introduced, and
+ * weird string encodings unsanctioned by RFC 3280, are used in the
+ * issuer or subject DN, this "conservative" estimate will be exact.
+ */
+ len >>= (typ == V_ASN1_BMPSTRING) ? 1 : 2;
+ if (len >= CCERT_BUFSIZ) {
+ msg_warn("%s: %s: peer %s too long: %d",
+ myname, TLScontext->namaddr, label, len);
+ return (0);
+ }
+ if ((len = ASN1_STRING_to_UTF8(&utf, entry_str)) < 0) {
+ msg_warn("%s: %s: error decoding peer %s of ASN.1 type=%d",
+ myname, TLScontext->namaddr, label, typ);
+ tls_print_errors();
+ return (0);
+ }
+ }
+#define RETURN(x) do { if (utf!=val) OPENSSL_free(utf); return (x); } while (0)
+
if (len >= CCERT_BUFSIZ) {
- OPENSSL_free(tmp);
- msg_warn("peer %s too long: %d", label, (int) len);
- return (0);
+ msg_warn("%s: %s: peer %s too long: %d",
+ myname, TLScontext->namaddr, label, len);
+ RETURN(0);
+ }
+ if (len != strlen((char *) utf)) {
+ msg_warn("%s: %s: internal NUL in peer %s",
+ myname, TLScontext->namaddr, label);
+ RETURN(0);
}
+ for (cp = (char *) utf; *cp; cp++) {
+ if (!ISASCII(*cp) || !ISPRINT(*cp)) {
+ msg_warn("%s: %s: non-printable characters in peer %s",
+ myname, TLScontext->namaddr, label);
+ RETURN(0);
+ }
+ }
+ cp = mystrdup((char *) utf);
+ RETURN(cp);
+}
+
+/* tls_dns_name - Extract valid DNS name from subjectAltName value */
+
+const char *tls_dns_name(const GENERAL_NAME * gn,
+ const TLS_SESS_STATE *TLScontext)
+{
+ const char *myname = "tls_dns_name";
+ char *cp;
+ const char *dnsname;
+
+ /*
+ * Peername checks are security sensitive, carefully scrutinize the
+ * input!
+ */
+ if (gn->type != GEN_DNS)
+ msg_panic("%s: Non DNS input argument", myname);
/*
- * Standard UTF8 does not encode NUL as 0b11000000, that is
- * a Java "feature". So we need to check for embedded NULs.
+ * We expect the OpenSSL library to construct GEN_DNS extesion objects as
+ * ASN1_IA5STRING values. Check we got the right union member.
*/
- if (strlen((char *) tmp) != len) {
- msg_warn("internal NUL in peer %s", label);
- OPENSSL_free(tmp);
+ if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) {
+ msg_warn("%s: %s: invalid ASN1 value type in subjectAltName",
+ myname, TLScontext->namaddr);
return (0);
}
- result = mystrdup((char *) tmp);
- OPENSSL_free(tmp);
- return (result);
+ /*
+ * Safe to treat as an ASCII string possibly holding a DNS name
+ */
+ dnsname = (char *) ASN1_STRING_data(gn->d.ia5);
+
+ /*
+ * Per Dr. Steven Henson of the OpenSSL development team, ASN1_IA5STRING
+ * values can have internal ASCII NUL values in this context because
+ * their length is taken from the decoded ASN1 buffer, a trailing NUL is
+ * always appended to make sure that the string is terminated, but the
+ * ASN.1 length may differ from strlen().
+ */
+ if (ASN1_STRING_length(gn->d.ia5) != strlen(dnsname)) {
+ msg_warn("%s: %s: internal NUL in subjectAltName",
+ myname, TLScontext->namaddr);
+ return 0;
+ }
+
+ /*
+ * XXX: Should we be more strict and call valid_hostname()? So long as
+ * the name is safe to handle, if it is not a valid hostname, it will not
+ * compare equal to the expected peername, so being more strict than
+ * "printable" is likely excessive...
+ */
+ for (cp = (char *) dnsname; cp && *cp; cp++)
+ if (!ISASCII(*cp) || !ISPRINT(*cp)) {
+ cp = mystrdup(dnsname);
+ msg_warn("%s: %s: non-printable characters in subjectAltName: %s",
+ myname, TLScontext->namaddr, printable(cp, '?'));
+ myfree(cp);
+ return 0;
+ }
+ return (dnsname);
}
/* tls_peer_CN - extract peer common name from certificate */
-char *tls_peer_CN(X509 *peercert)
+char *tls_peer_CN(X509 *peercert, const TLS_SESS_STATE *TLScontext)
{
char *cn;
- cn = tls_text_name(X509_get_subject_name(peercert),
- NID_commonName, "subject CN", DO_GRIPE);
- return (cn);
+ cn = tls_text_name(X509_get_subject_name(peercert), NID_commonName,
+ "subject CN", TLScontext, DO_GRIPE);
+ return (cn ? cn : mystrdup(""));
}
/* tls_issuer_CN - extract issuer common name from certificate */
-char *tls_issuer_CN(X509 *peer)
+char *tls_issuer_CN(X509 *peer, const TLS_SESS_STATE *TLScontext)
{
X509_NAME *name;
char *cn;
* If no issuer CN field, use Organization instead. CA certs without a CN
* are common, so we only complain if the organization is also missing.
*/
- if ((cn = tls_text_name(name, NID_commonName, "issuer CN", DONT_GRIPE)) == 0)
+ if ((cn = tls_text_name(name, NID_commonName,
+ "issuer CN", TLScontext, DONT_GRIPE)) == 0)
cn = tls_text_name(name, NID_organizationName,
- "issuer Organization", DO_GRIPE);
- return (cn);
+ "issuer Organization", TLScontext, DO_GRIPE);
+ return (cn ? cn : mystrdup(""));
+}
+
+/* tls_fingerprint - extract fingerprint from certificate */
+
+char *tls_fingerprint(X509 *peercert, const char *dgst)
+{
+ const char *myname = "tls_fingerprint";
+ const EVP_MD *md_alg;
+ unsigned char md_buf[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
+ int i;
+ char *result = 0;
+
+ /* Previously available in "init" routine. */
+ if ((md_alg = EVP_get_digestbyname(dgst)) == 0)
+ msg_panic("%s: digest algorithm \"%s\" not found", myname, dgst);
+
+ /* Fails when serialization to ASN.1 runs out of memory */
+ if (X509_digest(peercert, md_alg, md_buf, &md_len) == 0)
+ msg_fatal("%s: error computing certificate %s digest (out of memory?)",
+ myname, dgst);
+
+ /* Check for OpenSSL contract violation */
+ if (md_len > EVP_MAX_MD_SIZE || md_len >= INT_MAX / 3)
+ msg_panic("%s: unexpectedly large %s digest size: %u",
+ myname, dgst, md_len);
+
+ result = mymalloc(md_len * 3);
+ for (i = 0; i < md_len; i++) {
+ result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U];
+ result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)];
+ result[(i * 3) + 2] = (i + 1 != md_len) ? ':' : '\0';
+ }
+ return (result);
}
#endif
* Tunables.
*/
char *var_tls_rand_source;
-int var_tls_daemon_rand_bytes;
int var_tls_rand_bytes;
int var_tls_reseed_period;
int var_tls_prng_exch_period;
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const 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_LMTP_TLS_SCACHE_DB, DEF_LMTP_TLS_SCACHE_DB, &var_lmtp_tls_scache_db, 0, 0,
0,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const 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_LMTP_TLS_SCACHTIME, DEF_LMTP_TLS_SCACHTIME, &var_lmtp_tls_scache_timeout, 0, 0,
0,
};
- static CONFIG_INT_TABLE int_table[] = {
+ static const 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,
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport, 1, 0,
VAR_VIRT_TRANSPORT, DEF_VIRT_TRANSPORT, &var_virt_transport, 1, 0,
VAR_VRFY_RELAY_MAPS, DEF_VRFY_RELAY_MAPS, &var_vrfy_relay_maps, 0, 0,
0,
};
- static CONFIG_BOOL_TABLE bool_table[] = {
+ static const CONFIG_BOOL_TABLE bool_table[] = {
VAR_SWAP_BANGPATH, DEF_SWAP_BANGPATH, &var_swap_bangpath,
VAR_APP_DOT_MYDOMAIN, DEF_APP_DOT_MYDOMAIN, &var_append_dot_mydomain,
VAR_APP_AT_MYORIGIN, DEF_APP_AT_MYORIGIN, &var_append_at_myorigin,
/*
* Mapping between flag names and flag values.
*/
-static NAME_MASK dict_mask[] = {
+static const NAME_MASK dict_mask[] = {
"warn_dup", (1 << 0), /* if file, warn about dups */
"ignore_dup", (1 << 1), /* if file, ignore dups */
"try0null", (1 << 2), /* do not append 0 to key/value */
if (r < 0)
msg_fatal("error writing %s: %m", dict_cdbm->tmp_path);
else if (r > 0) {
- if (dict->flags & (DICT_FLAG_DUP_IGNORE | DICT_FLAG_DUP_REPLACE));
+ if (dict->flags & (DICT_FLAG_DUP_IGNORE | DICT_FLAG_DUP_REPLACE))
+ /* void */ ;
else if (dict->flags & DICT_FLAG_DUP_WARN)
msg_warn("%s: duplicate entry: \"%s\"",
dict_cdbm->dict.name, name);
struct DICT *(*open) (const char *, int, int);
} DICT_OPEN_INFO;
-static DICT_OPEN_INFO dict_open_info[] = {
+static const DICT_OPEN_INFO dict_open_info[] = {
#ifdef HAS_CDB
DICT_TYPE_CDB, dict_cdb_open,
#endif
static void dict_open_init(void)
{
const char *myname = "dict_open_init";
- DICT_OPEN_INFO *dp;
+ const DICT_OPEN_INFO *dp;
if (dict_open_hash != 0)
msg_panic("%s: multiple initialization", myname);
#define INET_PROTO_MASK_IPV4 (1<<0)
#define INET_PROTO_MASK_IPV6 (1<<1)
-static NAME_MASK proto_table[] = {
+static const NAME_MASK proto_table[] = {
#ifdef HAS_IPV6
INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4 | INET_PROTO_MASK_IPV6,
INET_PROTO_NAME_IPV6, INET_PROTO_MASK_IPV6,
int main(int argc, char **argv)
{
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0,
VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0,
0,
};
- static CONFIG_TIME_TABLE time_table[] = {
+ static const CONFIG_TIME_TABLE time_table[] = {
VAR_VERIFY_POS_EXP, DEF_VERIFY_POS_EXP, &var_verify_pos_exp, 1, 0,
VAR_VERIFY_POS_TRY, DEF_VERIFY_POS_TRY, &var_verify_pos_try, 1, 0,
VAR_VERIFY_NEG_EXP, DEF_VERIFY_NEG_EXP, &var_verify_neg_exp, 1, 0,
int main(int argc, char **argv)
{
- static CONFIG_INT_TABLE int_table[] = {
+ static const CONFIG_INT_TABLE int_table[] = {
VAR_VIRT_MINUID, DEF_VIRT_MINUID, &var_virt_minimum_uid, 1, 0,
VAR_VIRT_MAILBOX_LIMIT, DEF_VIRT_MAILBOX_LIMIT, &var_virt_mailbox_limit, 0, 0,
0,
};
- static CONFIG_STR_TABLE str_table[] = {
+ static const CONFIG_STR_TABLE str_table[] = {
VAR_MAIL_SPOOL_DIR, DEF_MAIL_SPOOL_DIR, &var_mail_spool_dir, 0, 0,
VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
VAR_VIRT_UID_MAPS, DEF_VIRT_UID_MAPS, &var_virt_uid_maps, 0, 0,
struct XSASL_CLIENT_IMPL *(*client_init) (const char *, const char *);
} XSASL_CLIENT_IMPL_INFO;
-static XSASL_CLIENT_IMPL_INFO client_impl_info[] = {
+static const XSASL_CLIENT_IMPL_INFO client_impl_info[] = {
#ifdef XSASL_TYPE_CYRUS
XSASL_TYPE_CYRUS, xsasl_cyrus_client_init,
#endif
XSASL_CLIENT_IMPL *xsasl_client_init(const char *client_type,
const char *path_info)
{
- XSASL_CLIENT_IMPL_INFO *xp;
+ const XSASL_CLIENT_IMPL_INFO *xp;
for (xp = client_impl_info; xp->client_type; xp++)
if (strcmp(client_type, xp->client_type) == 0)
ARGV *xsasl_client_types(void)
{
- XSASL_CLIENT_IMPL_INFO *xp;
+ const XSASL_CLIENT_IMPL_INFO *xp;
ARGV *argv = argv_alloc(1);
for (xp = client_impl_info; xp->client_type; xp++)
/*
* SASL Security options.
*/
-static NAME_MASK xsasl_cyrus_sec_mask[] = {
+static const NAME_MASK xsasl_cyrus_sec_mask[] = {
"noplaintext", SASL_SEC_NOPLAINTEXT,
"noactive", SASL_SEC_NOACTIVE,
"nodictionary", SASL_SEC_NODICTIONARY,
/*
* Security properties as specified in the Postfix main.cf file.
*/
-static NAME_MASK xsasl_dovecot_conf_sec_props[] = {
+static const NAME_MASK xsasl_dovecot_conf_sec_props[] = {
"noplaintext", SEC_PROPS_NOPLAINTEXT,
"noactive", SEC_PROPS_NOACTIVE,
"nodictionary", SEC_PROPS_NODICTIONARY,
* Security properties as specified in the Dovecot protocol. See
* http://wiki.dovecot.org/Authentication_Protocol.
*/
-static NAME_MASK xsasl_dovecot_serv_sec_props[] = {
+static const NAME_MASK xsasl_dovecot_serv_sec_props[] = {
"plaintext", SEC_PROPS_NOPLAINTEXT,
"active", SEC_PROPS_NOACTIVE,
"dictionary", SEC_PROPS_NODICTIONARY,
struct XSASL_SERVER_IMPL *(*server_init) (const char *, const char *);
} XSASL_SERVER_IMPL_INFO;
-static XSASL_SERVER_IMPL_INFO server_impl_info[] = {
+static const XSASL_SERVER_IMPL_INFO server_impl_info[] = {
#ifdef XSASL_TYPE_CYRUS
{XSASL_TYPE_CYRUS, xsasl_cyrus_server_init},
#endif
XSASL_SERVER_IMPL *xsasl_server_init(const char *server_type,
const char *path_info)
{
- XSASL_SERVER_IMPL_INFO *xp;
+ const XSASL_SERVER_IMPL_INFO *xp;
for (xp = server_impl_info; xp->server_type; xp++)
if (strcmp(server_type, xp->server_type) == 0)
ARGV *xsasl_server_types(void)
{
- XSASL_SERVER_IMPL_INFO *xp;
+ const XSASL_SERVER_IMPL_INFO *xp;
ARGV *argv = argv_alloc(1);
for (xp = server_impl_info; xp->server_type; xp++)