2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-2.11-20131117

This commit is contained in:
Wietse Venema 2013-11-17 00:00:00 -05:00 committed by Viktor Dukhovni
parent 897f730f14
commit e96f9e5c3d
20 changed files with 737 additions and 164 deletions

View File

@ -19111,3 +19111,36 @@ Apologies for any names omitted.
cleanup/cleanup_map11.c, cleanup/cleanup_map1n.c, cleanup/cleanup_map11.c, cleanup/cleanup_map1n.c,
cleanup/cleanup_masquerade.c, cleanup/cleanup_message.c, cleanup/cleanup_masquerade.c, cleanup/cleanup_message.c,
cleanup/cleanup_milter.c. cleanup/cleanup_milter.c.
20131116
Feature: MySQL client support for option_file, option_group,
tls_cert_file, tls_key_file, tls_CAfile, tls_CApath,
tls_verify_cert. See mysql_table(5). Code by Gareth Palmer.
Files: proto/mysql_table, global/dict_mysql.c.
Cleanup: DANE support. Keep the attributes of TA certificates
obtained via "IN TLSA 2 0 X" RRs, while continuing to only
use the key from "IN TLSA 2 1 X" RRs. This means in the
"2 0 X" case that we re-sign the TA certificate in place,
rather than synthesize a vanilla cert around just the key.
Viktor Dukhovni. File: tls/tls_dane.c.
Bugfix: posttls-finger parsing of destination and optional
match values. Viktor Dukhovni. File:
posttls-finger/posttls-finger.c.
Cleanup: When wrap_signed is false (OpenSSL 1.0.2 some day),
we don't have to sign trust anchors, and don't generate a
key to do so. Thus don't attempt to re-sign trust-anchor
certificates (IN TLSA 2 0 X) in this case. Viktor Dukhovni.
File: tls/tls_dane.c.
Feature: configurable DANE digest algorithm priority. Use
only the most-preferred, shared, digest algorithm for any
give (usage, selector) combination. Viktor Dukhovni.
mantools/postlink, proto/postconf.proto, global/mail_params.h,
tls/tls_dane.c, tls/tls_misc.c.
Bugfix: FreeBSD nroff workaround messed up. File:
mantools/postlink.

View File

@ -4,6 +4,9 @@ Wish list:
Make been_here flag BH_FLAG_FOLD configurable for masochists. Make been_here flag BH_FLAG_FOLD configurable for masochists.
Allow LMDB-based caches to be shared. This requires a new
flag that says a database is concurrency-safe.
Preserve case in smtpd_resolve_addr() and add a structure Preserve case in smtpd_resolve_addr() and add a structure
member for the case-folded address. member for the case-folded address.

View File

@ -256,15 +256,63 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
limit is exceeded. Setting the limit to 1 ensures limit is exceeded. Setting the limit to 1 ensures
that lookups do not return multiple values. that lookups do not return multiple values.
<b>option_file</b>
Read options from the given file instead of the
default my.cnf location.
This parameter is available with Postfix 2.11 and
later.
<b>option_group</b>
Read options from the given group.
This parameter is available with Postfix 2.11 and
later.
<b>tls_cert_file</b>
File containing client's X509 certificate.
This parameter is available with Postfix 2.11 and
later.
<b>tls_key_file</b>
File containing the private key corresponding to
<b>tls_cert_file</b>.
This parameter is available with Postfix 2.11 and
later.
<b>tls_CAfile</b>
File containing certificates for all of the X509
Certificate Authorities the client will recognize.
Takes precedence over <b>tls_CApath</b>.
This parameter is available with Postfix 2.11 and
later.
<b>tls_CApath</b>
Directory containing X509 Certificate Authority
certificates in separate individual files.
This parameter is available with Postfix 2.11 and
later.
<b>tls_verify_cert (default: no)</b>
Verify that the server's name matches the common
name in the certficate.
This parameter is available with Postfix 2.11 and
later.
<b>OBSOLETE QUERY INTERFACE</b> <b>OBSOLETE QUERY INTERFACE</b>
This section describes an interface that is deprecated as This section describes an interface that is deprecated as
of Postfix 2.2. It is replaced by the more general <b>query</b> of Postfix 2.2. It is replaced by the more general <b>query</b>
interface described above. If the <b>query</b> parameter is interface described above. If the <b>query</b> parameter is
defined, the legacy parameters described here ignored. defined, the legacy parameters described here ignored.
Please migrate to the new interface as the legacy inter- Please migrate to the new interface as the legacy inter-
face may be removed in a future release. face may be removed in a future release.
The following parameters can be used to fill in a SELECT The following parameters can be used to fill in a SELECT
template statement of the form: template statement of the form:
SELECT [<b>select_field</b>] SELECT [<b>select_field</b>]
@ -272,7 +320,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
WHERE [<b>where_field</b>] = '%s' WHERE [<b>where_field</b>] = '%s'
[<b>additional_conditions</b>] [<b>additional_conditions</b>]
The specifier %s is replaced by the search string, and is The specifier %s is replaced by the search string, and is
escaped so if it contains single quotes or other odd char- escaped so if it contains single quotes or other odd char-
acters, it will not cause a parse error, or worse, a secu- acters, it will not cause a parse error, or worse, a secu-
rity problem. rity problem.
@ -304,7 +352,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5)
<a href="MYSQL_README.html">MYSQL_README</a>, Postfix MYSQL client guide <a href="MYSQL_README.html">MYSQL_README</a>, Postfix MYSQL client guide
<b>LICENSE</b> <b>LICENSE</b>
The Secure Mailer license must be distributed with this The Secure Mailer license must be distributed with this
software. software.
<b>HISTORY</b> <b>HISTORY</b>

View File

@ -10,9 +10,9 @@ PCRE_TABLE(5) PCRE_TABLE(5)
pcre_table - format of Postfix PCRE tables pcre_table - format of Postfix PCRE tables
<b>SYNOPSIS</b> <b>SYNOPSIS</b>
<b>postmap -q "<i></b>string</i><b>" <a href="pcre_table.5.html">pcre</a>:/etc/postfix/<i></b>filename <b>postmap -q "</b><i>string</i><b>" <a href="pcre_table.5.html">pcre</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="pcre_table.5.html">pcre</a>:/etc/postfix/<i></b>filename</i> &lt;<i>inputfile</i> <b>postmap -q - <a href="pcre_table.5.html">pcre</a>:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i>
<b>DESCRIPTION</b> <b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address The Postfix mail system uses optional tables for address
@ -39,15 +39,15 @@ PCRE_TABLE(5) PCRE_TABLE(5)
<b>TABLE FORMAT</b> <b>TABLE FORMAT</b>
The general form of a PCRE table is: The general form of a PCRE table is:
<b>/<i></b>pattern</i><b>/<i></b>flags result</i> <b>/</b><i>pattern</i><b>/</b><i>flags result</i>
When <i>pattern</i> matches the input string, use the cor- When <i>pattern</i> matches the input string, use the cor-
responding <i>result</i> value. responding <i>result</i> value.
<b>!/<i></b>pattern</i><b>/<i></b>flags result</i> <b>!/</b><i>pattern</i><b>/</b><i>flags result</i>
When <i>pattern</i> does <b>not</b> match the input string, use When <i>pattern</i> does <b>not</b> match the input string, use
the corresponding <i>result</i> value. the corresponding <i>result</i> value.
<b>if /<i></b>pattern</i><b>/<i></b>flags <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
<b>endif</b> Match the input string against the patterns between <b>endif</b> Match the input string against the patterns between
<b>if</b> and <b>endif</b>, if and only if that same input string <b>if</b> and <b>endif</b>, if and only if that same input string
@ -58,7 +58,7 @@ PCRE_TABLE(5) PCRE_TABLE(5)
This feature is available in Postfix 2.1 and later. This feature is available in Postfix 2.1 and later.
<b>if !/<i></b>pattern</i><b>/<i></b>flags <b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
<b>endif</b> Match the input string against the patterns between <b>endif</b> Match the input string against the patterns between
<b>if</b> and <b>endif</b>, if and only if that same input string <b>if</b> and <b>endif</b>, if and only if that same input string
@ -115,7 +115,7 @@ PCRE_TABLE(5) PCRE_TABLE(5)
a whitespace character as part of the pattern, a whitespace character as part of the pattern,
escape it with backslash. escape it with backslash.
Note: do not use <b>#<i></b>comment</i> after patterns. Note: do not use <b>#</b><i>comment</i> after patterns.
<b>A</b> (default: off) <b>A</b> (default: off)
Toggles the PCRE_ANCHORED flag. When this flag is Toggles the PCRE_ANCHORED flag. When this flag is

View File

@ -11311,7 +11311,7 @@ to Postfix 2.9.6 or later. </p>
<p> Lookup the associated DANE TLSA RRset even when a hostname is <p> Lookup the associated DANE TLSA RRset even when a hostname is
not an alias and its address records lie in an unsigned zone. This not an alias and its address records lie in an unsigned zone. This
is unlikely to ever yield DNSSEC validated results, since child is unlikely to ever yield DNSSEC validated results, since child
zones of unsigned zones are also unsigned in the absense of DLV or zones of unsigned zones are also unsigned in the absence of DLV or
locally configured non-root trust-anchors. We anticipate that such locally configured non-root trust-anchors. We anticipate that such
mechanisms will not be used for just the "_tcp" subdomain of a host. mechanisms will not be used for just the "_tcp" subdomain of a host.
Suppressing the TLSA RRset lookup reduces latency and avoids potential Suppressing the TLSA RRset lookup reduces latency and avoids potential
@ -16211,6 +16211,55 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
<p> This feature is available in Postfix 2.2 and later. </p> <p> This feature is available in Postfix 2.2 and later. </p>
</DD>
<DT><b><a name="tls_dane_digests">tls_dane_digests</a>
(default: sha512 sha256)</b></DT><DD>
<p> <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> TLSA resource-record "matching type" digest algorithms
in descending preference order. All the specified algorithms must
be supported by the underlying OpenSSL library, otherwise the Postfix
SMTP client will not support DANE TLSA security. </p>
<p> When for a particular combination of "certificate usage" and
"selector" the TLSA RRset contains a well-formed record with a
matching type of "0", i.e. a full value of the associated certificate
or public key, the Postfix SMTP client will ignore all other matching
types for the same certificate usage and selector. In this case
the first algorithm listed in <a href="postconf.5.html#tls_dane_digests">tls_dane_digests</a> will be used to
compute a digest of the full value, which will then be used to match
certificates or public keys in the server's certificate chain. </p>
<p> Otherwise, when for a particular combination of "certificate
usage" and "selector" the TLSA RRset contains a records with more
than one non-zero matching type, i.e. multiple digest algorithms,
only records with the highest preference digest are used after
discarding any records with an incorrect digest length as unusable. </p>
<p> This strategy ensures that the strongest digest supported by
both the Postfix SMTP client and the remote server is used, and
weaker digests are ignored. This supports non-disruptive deprecation
of outdated digest algorithms. </p>
<p> The strategy requires that when a TLSA RRset provides association
data for multiple certificates or public keys, all RRs with the same
"certificate usage" and "selector" be published with the same set
of digests. In particular, during key rotation, when a certificate
or public key is being replaced with another (and both are published
during the transition) both the old and the new certificate MUST be
specified with the same set of digests. One can change the list of
digest algorithms later, once old keys are retired. At any given
time change either the list of digests without changing the list of
certificates or public keys or the list of certificates or public
keys without changing the list of digests. </p>
<p> It is expected that this algorithm agility mechanism will be
published in a standards track RFC for SMTP with DANE, and perhaps
in an eventual update to <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a>. </p>
<p> This feature is available in Postfix 2.11. </p>
</DD> </DD>
<DT><b><a name="tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a> <DT><b><a name="tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>

View File

@ -10,9 +10,9 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
regexp_table - format of Postfix regular expression tables regexp_table - format of Postfix regular expression tables
<b>SYNOPSIS</b> <b>SYNOPSIS</b>
<b>postmap -q "<i></b>string</i><b>" <a href="regexp_table.5.html">regexp</a>:/etc/postfix/<i></b>filename <b>postmap -q "</b><i>string</i><b>" <a href="regexp_table.5.html">regexp</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="regexp_table.5.html">regexp</a>:/etc/postfix/<i></b>filename</i> &lt;<i>inputfile</i> <b>postmap -q - <a href="regexp_table.5.html">regexp</a>:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i>
<b>DESCRIPTION</b> <b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address The Postfix mail system uses optional tables for address
@ -39,15 +39,15 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
<b>TABLE FORMAT</b> <b>TABLE FORMAT</b>
The general form of a Postfix regular expression table is: The general form of a Postfix regular expression table is:
<b>/<i></b>pattern</i><b>/<i></b>flags result</i> <b>/</b><i>pattern</i><b>/</b><i>flags result</i>
When <i>pattern</i> matches the input string, use the cor- When <i>pattern</i> matches the input string, use the cor-
responding <i>result</i> value. responding <i>result</i> value.
<b>!/<i></b>pattern</i><b>/<i></b>flags result</i> <b>!/</b><i>pattern</i><b>/</b><i>flags result</i>
When <i>pattern</i> does <b>not</b> match the input string, use When <i>pattern</i> does <b>not</b> match the input string, use
the corresponding <i>result</i> value. the corresponding <i>result</i> value.
<b>if /<i></b>pattern</i><b>/<i></b>flags <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
<b>endif</b> Match the input string against the patterns between <b>endif</b> Match the input string against the patterns between
<b>if</b> and <b>endif</b>, if and only if that same input string <b>if</b> and <b>endif</b>, if and only if that same input string
@ -58,7 +58,7 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
This feature is available in Postfix 2.1 and later. This feature is available in Postfix 2.1 and later.
<b>if !/<i></b>pattern</i><b>/<i></b>flags <b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
<b>endif</b> Match the input string against the patterns between <b>endif</b> Match the input string against the patterns between
<b>if</b> and <b>endif</b>, if and only if that same input string <b>if</b> and <b>endif</b>, if and only if that same input string

View File

@ -12,7 +12,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>SYNOPSIS</b> <b>SYNOPSIS</b>
<b>smtp-sink</b> [<i>options</i>] [<b>inet:</b>][<i>host</i>]:<i>port backlog</i> <b>smtp-sink</b> [<i>options</i>] [<b>inet:</b>][<i>host</i>]:<i>port backlog</i>
<b>smtp-sink</b> [<i>options</i>] <b>unix:<i></b>pathname backlog</i> <b>smtp-sink</b> [<i>options</i>] <b>unix:</b><i>pathname backlog</i>
<b>DESCRIPTION</b> <b>DESCRIPTION</b>
<b>smtp-sink</b> listens on the named host (or address) and port. <b>smtp-sink</b> listens on the named host (or address) and port.
@ -47,19 +47,19 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-a</b> Do not announce SASL authentication support. <b>-a</b> Do not announce SASL authentication support.
<b>-A <i></b>delay <b>-A</b> <i>delay</i>
Wait <i>delay</i> seconds after responding to DATA, then Wait <i>delay</i> seconds after responding to DATA, then
abort prematurely with a 550 reply status. Do not abort prematurely with a 550 reply status. Do not
read further input from the client; this is an read further input from the client; this is an
attempt to block the client before it sends ".". attempt to block the client before it sends ".".
Specify a zero delay value to abort immediately. Specify a zero delay value to abort immediately.
<b>-b <i></b>soft-bounce-reply <b>-b</b> <i>soft-bounce-reply</i>
Use <i>soft-bounce-reply</i> for soft reject responses. Use <i>soft-bounce-reply</i> for soft reject responses.
The default reply is "450 4.3.0 Error: command The default reply is "450 4.3.0 Error: command
failed". failed".
<b>-B <i></b>hard-bounce-reply <b>-B</b> <i>hard-bounce-reply</i>
Use <i>hard-bounce-reply</i> for hard reject responses. Use <i>hard-bounce-reply</i> for hard reject responses.
The default reply is "500 5.3.0 Error: command The default reply is "500 5.3.0 Error: command
failed". failed".
@ -70,7 +70,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-C</b> Disable XCLIENT support. <b>-C</b> Disable XCLIENT support.
<b>-d <i></b>dump-template <b>-d</b> <i>dump-template</i>
Dump each mail transaction to a single-message file Dump each mail transaction to a single-message file
whose name is created by expanding the <i>dump-tem-</i> whose name is created by expanding the <i>dump-tem-</i>
<i>plate</i> via strftime(3) and appending a pseudo-random <i>plate</i> via strftime(3) and appending a pseudo-random
@ -83,7 +83,7 @@ SMTP-SINK(1) SMTP-SINK(1)
Note: this option keeps one capture file open for Note: this option keeps one capture file open for
every mail transaction in progress. every mail transaction in progress.
<b>-D <i></b>dump-template <b>-D</b> <i>dump-template</i>
Append mail transactions to a multi-message dump Append mail transactions to a multi-message dump
file whose name is created by expanding the <i>dump-</i> file whose name is created by expanding the <i>dump-</i>
<i>template</i> via strftime(3). If the template contains <i>template</i> via strftime(3). If the template contains
@ -98,7 +98,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-E</b> Do not announce ENHANCEDSTATUSCODES support. <b>-E</b> Do not announce ENHANCEDSTATUSCODES support.
<b>-f <i></b>command,command,... <b>-f</b> <i>command,command,...</i>
Reject the specified commands with a hard (5xx) Reject the specified commands with a hard (5xx)
error code. This option implies <b>-p</b>. error code. This option implies <b>-p</b>.
@ -110,24 +110,24 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-F</b> Disable XFORWARD support. <b>-F</b> Disable XFORWARD support.
<b>-h <i></b>hostname <b>-h</b> <i>hostname</i>
Use <i>hostname</i> in the SMTP greeting, in the HELO Use <i>hostname</i> in the SMTP greeting, in the HELO
response, and in the EHLO response. The default response, and in the EHLO response. The default
hostname is "smtp-sink". hostname is "smtp-sink".
<b>-L</b> Enable LMTP instead of SMTP. <b>-L</b> Enable LMTP instead of SMTP.
<b>-m <i></b>count</i> (default: 256) <b>-m</b> <i>count</i> (default: 256)
An upper bound on the maximal number of simultane- An upper bound on the maximal number of simultane-
ous connections that <b>smtp-sink</b> will handle. This ous connections that <b>smtp-sink</b> will handle. This
prevents the process from running out of file prevents the process from running out of file
descriptors. Excess connections will stay queued in descriptors. Excess connections will stay queued in
the TCP/IP stack. the TCP/IP stack.
<b>-M <i></b>count <b>-M</b> <i>count</i>
Terminate after receiving <i>count</i> messages. Terminate after receiving <i>count</i> messages.
<b>-n <i></b>count <b>-n</b> <i>count</i>
Terminate after <i>count</i> sessions. Terminate after <i>count</i> sessions.
<b>-p</b> Do not announce support for ESMTP command pipelin- <b>-p</b> Do not announce support for ESMTP command pipelin-
@ -136,7 +136,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-P</b> Change the server greeting so that it appears to <b>-P</b> Change the server greeting so that it appears to
come through a CISCO PIX system. Implies <b>-e</b>. come through a CISCO PIX system. Implies <b>-e</b>.
<b>-q <i></b>command,command,... <b>-q</b> <i>command,command,...</i>
Disconnect (without replying) after receiving one Disconnect (without replying) after receiving one
of the specified commands. of the specified commands.
@ -146,7 +146,7 @@ SMTP-SINK(1) SMTP-SINK(1)
and use quotes to protect white space from the and use quotes to protect white space from the
shell. Command names are case-insensitive. shell. Command names are case-insensitive.
<b>-Q <i></b>command,command,... <b>-Q</b> <i>command,command,...</i>
Send a 421 reply and disconnect after receiving one Send a 421 reply and disconnect after receiving one
of the specified commands. of the specified commands.
@ -156,7 +156,7 @@ SMTP-SINK(1) SMTP-SINK(1)
and use quotes to protect white space from the and use quotes to protect white space from the
shell. Command names are case-insensitive. shell. Command names are case-insensitive.
<b>-r <i></b>command,command,... <b>-r</b> <i>command,command,...</i>
Reject the specified commands with a soft (4xx) Reject the specified commands with a soft (4xx)
error code. This option implies <b>-p</b>. error code. This option implies <b>-p</b>.
@ -166,12 +166,12 @@ SMTP-SINK(1) SMTP-SINK(1)
and use quotes to protect white space from the and use quotes to protect white space from the
shell. Command names are case-insensitive. shell. Command names are case-insensitive.
<b>-R <i></b>root-directory <b>-R</b> <i>root-directory</i>
Change the process root directory to the specified Change the process root directory to the specified
location. This option requires super-user privi- location. This option requires super-user privi-
leges. See also the <b>-u</b> option. leges. See also the <b>-u</b> option.
<b>-s <i></b>command,command,... <b>-s</b> <i>command,command,...</i>
Log the named commands to syslogd. Log the named commands to syslogd.
Examples of commands are CONNECT, HELO, EHLO, LHLO, Examples of commands are CONNECT, HELO, EHLO, LHLO,
@ -190,16 +190,16 @@ SMTP-SINK(1) SMTP-SINK(1)
tab), \<i>ddd</i> (up to three octal digits) and \\ (the tab), \<i>ddd</i> (up to three octal digits) and \\ (the
backslash character). backslash character).
<b>-t <i></b>timeout</i> (default: 100) <b>-t</b> <i>timeout</i> (default: 100)
Limit the time for receiving a command or sending a Limit the time for receiving a command or sending a
response. The time limit is specified in seconds. response. The time limit is specified in seconds.
<b>-T <i></b>windowsize <b>-T</b> <i>windowsize</i>
Override the default TCP window size. To work Override the default TCP window size. To work
around broken TCP window scaling implementations, around broken TCP window scaling implementations,
specify a value &gt; 0 and &lt; 65536. specify a value &gt; 0 and &lt; 65536.
<b>-u <i></b>username <b>-u</b> <i>username</i>
Switch to the specified user privileges after open- Switch to the specified user privileges after open-
ing the network socket and optionally changing the ing the network socket and optionally changing the
process root directory. This option is required process root directory. This option is required
@ -208,11 +208,11 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-v</b> Show the SMTP conversations. <b>-v</b> Show the SMTP conversations.
<b>-w <i></b>delay <b>-w</b> <i>delay</i>
Wait <i>delay</i> seconds before responding to a DATA com- Wait <i>delay</i> seconds before responding to a DATA com-
mand. mand.
<b>-W <i></b>command:delay[:odds] <b>-W</b> <i>command:delay[:odds]</i>
Wait <i>delay</i> seconds before responding to <i>command</i>. Wait <i>delay</i> seconds before responding to <i>command</i>.
If <i>odds</i> is also specified (a number between 1-99 If <i>odds</i> is also specified (a number between 1-99
inclusive), wait for a random multiple of <i>delay</i>. inclusive), wait for a random multiple of <i>delay</i>.
@ -226,7 +226,7 @@ SMTP-SINK(1) SMTP-SINK(1)
interface) TCP port <i>port</i>. Both <i>host</i> and <i>port</i> may be interface) TCP port <i>port</i>. Both <i>host</i> and <i>port</i> may be
specified in numeric or symbolic form. specified in numeric or symbolic form.
<b>unix:<i></b>pathname <b>unix:</b><i>pathname</i>
Listen on the UNIX-domain socket at <i>pathname</i>. Listen on the UNIX-domain socket at <i>pathname</i>.
<i>backlog</i> <i>backlog</i>
@ -251,45 +251,45 @@ SMTP-SINK(1) SMTP-SINK(1)
The format of the <b>smtp-sink</b> generated headers is as fol- The format of the <b>smtp-sink</b> generated headers is as fol-
lows: lows:
<b>X-Client-Addr: <i></b>text <b>X-Client-Addr:</b> <i>text</i>
The client IP address without enclosing []. An IPv6 The client IP address without enclosing []. An IPv6
address is prefixed with "ipv6:". This record is address is prefixed with "ipv6:". This record is
always present. always present.
<b>X-Client-Proto: <i></b>text <b>X-Client-Proto:</b> <i>text</i>
The client protocol: SMTP, ESMTP or LMTP. This The client protocol: SMTP, ESMTP or LMTP. This
record is always present. record is always present.
<b>X-Helo-Args: <i></b>text <b>X-Helo-Args:</b> <i>text</i>
The arguments of the last HELO or EHLO command The arguments of the last HELO or EHLO command
before this mail delivery transaction. This record before this mail delivery transaction. This record
is present only if the client sent a recognizable is present only if the client sent a recognizable
HELO or EHLO command before the DATA command. HELO or EHLO command before the DATA command.
<b>X-Mail-Args: <i></b>text <b>X-Mail-Args:</b> <i>text</i>
The arguments of the MAIL command that started this The arguments of the MAIL command that started this
mail delivery transaction. This record is present mail delivery transaction. This record is present
exactly once. exactly once.
<b>X-Rcpt-Args: <i></b>text <b>X-Rcpt-Args:</b> <i>text</i>
The arguments of an RCPT command within this mail The arguments of an RCPT command within this mail
delivery transaction. There is one record for each delivery transaction. There is one record for each
RCPT command, and they are in the order as sent by RCPT command, and they are in the order as sent by
the client. the client.
<b>Received: <i></b>text <b>Received:</b> <i>text</i>
A message header for compatibility with mail pro- A message header for compatibility with mail pro-
cessing software. This three-line header marks the cessing software. This three-line header marks the
end of the headers provided by <b>smtp-sink</b>, and is end of the headers provided by <b>smtp-sink</b>, and is
formatted as follows: formatted as follows:
<b>from <i></b>helo</i> <b>([<i></b>addr</i><b>])</b> <b>from</b> <i>helo</i> <b>([</b><i>addr</i><b>])</b>
The HELO or EHLO command argument and client The HELO or EHLO command argument and client
IP address. If the client did not send HELO IP address. If the client did not send HELO
or EHLO, the client IP address is used or EHLO, the client IP address is used
instead. instead.
<b>by <i></b>host</i> <b>(smtp-sink) with <i></b>proto</i> <b>id <i></b>random</i><b>;</b> <b>by</b> <i>host</i> <b>(smtp-sink) with</b> <i>proto</i> <b>id</b> <i>random</i><b>;</b>
The hostname specified with the <b>-h</b> option, The hostname specified with the <b>-h</b> option,
the client protocol (see <b>X-Client-Proto</b> the client protocol (see <b>X-Client-Proto</b>
above), and the pseudo-random portion of the above), and the pseudo-random portion of the

View File

@ -256,6 +256,39 @@ A setting of zero disables the limit. Lookups fail with a
temporary error if the limit is exceeded. Setting the temporary error if the limit is exceeded. Setting the
limit to 1 ensures that lookups do not return multiple limit to 1 ensures that lookups do not return multiple
values. values.
.IP "\fBoption_file\fR"
Read options from the given file instead of the default my.cnf
location.
.sp
This parameter is available with Postfix 2.11 and later.
.IP "\fBoption_group\fR"
Read options from the given group.
.sp
This parameter is available with Postfix 2.11 and later.
.IP "\fBtls_cert_file\fR"
File containing client's X509 certificate.
.sp
This parameter is available with Postfix 2.11 and later.
.IP "\fBtls_key_file\fR"
File containing the private key corresponding to \fBtls_cert_file\fR.
.sp
This parameter is available with Postfix 2.11 and later.
.IP "\fBtls_CAfile\fR"
File containing certificates for all of the X509 Certificate
Authorities the client will recognize. Takes precedence over
\fBtls_CApath\fR.
.sp
This parameter is available with Postfix 2.11 and later.
.IP "\fBtls_CApath\fR"
Directory containing X509 Certificate Authority certificates
in separate individual files.
.sp
This parameter is available with Postfix 2.11 and later.
.IP "\fBtls_verify_cert (default: no)\fR"
Verify that the server's name matches the common name in the
certficate.
.sp
This parameter is available with Postfix 2.11 and later.
.SH "OBSOLETE QUERY INTERFACE" .SH "OBSOLETE QUERY INTERFACE"
.na .na
.nf .nf

View File

@ -4173,7 +4173,7 @@ Example:
.ft C .ft C
/etc/postfix/main.cf: /etc/postfix/main.cf:
postscreen_access_list = permit_mynetworks, postscreen_access_list = permit_mynetworks,
cidr:/etc/postfix/postscreen_access.cidr cidr:/etc/postfix/postscreen_access.cidr
postscreen_blacklist_action = enforce postscreen_blacklist_action = enforce
.fi .fi
.ad .ad
@ -4393,7 +4393,7 @@ Example:
.na .na
.ft C .ft C
/etc/postfix/dnsbl_reply: /etc/postfix/dnsbl_reply:
secret.zen.spamhaus.org zen.spamhaus.org secret.zen.spamhaus.org zen.spamhaus.org
.fi .fi
.ad .ad
.ft R .ft R
@ -6962,7 +6962,7 @@ As in the example above, we show two matching fingerprints:
.na .na
.ft C .ft C
/etc/postfix/tls_policy: /etc/postfix/tls_policy:
example.com fingerprint example.com fingerprint
match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1 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 match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
.fi .fi
@ -7079,7 +7079,7 @@ This feature is available in Postfix 2.5 and later.
Lookup the associated DANE TLSA RRset even when a hostname is Lookup the associated DANE TLSA RRset even when a hostname is
not an alias and its address records lie in an unsigned zone. This not an alias and its address records lie in an unsigned zone. This
is unlikely to ever yield DNSSEC validated results, since child is unlikely to ever yield DNSSEC validated results, since child
zones of unsigned zones are also unsigned in the absense of DLV or zones of unsigned zones are also unsigned in the absence of DLV or
locally configured non-root trust-anchors. We anticipate that such locally configured non-root trust-anchors. We anticipate that such
mechanisms will not be used for just the "_tcp" subdomain of a host. mechanisms will not be used for just the "_tcp" subdomain of a host.
Suppressing the TLSA RRset lookup reduces latency and avoids potential Suppressing the TLSA RRset lookup reduces latency and avoids potential
@ -7506,8 +7506,8 @@ Example:
[mail.example.org]:587 secure match=nexthop [mail.example.org]:587 secure match=nexthop
# Postfix 2.5 and later # Postfix 2.5 and later
[thumb.example.org] fingerprint [thumb.example.org] fingerprint
match=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35 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 match=3D:95:34:51:24:66:33:B9:D2:40:99:C0:C1:17:0B:D1
.fi .fi
.ad .ad
.ft R .ft R
@ -11006,6 +11006,49 @@ bytes (equivalent to 256 bits) is sufficient to generate a 128bit
(or 168bit) session key. (or 168bit) session key.
.PP .PP
This feature is available in Postfix 2.2 and later. This feature is available in Postfix 2.2 and later.
.SH tls_dane_digests (default: sha512 sha256)
RFC 6698 TLSA resource-record "matching type" digest algorithms
in descending preference order. All the specified algorithms must
be supported by the underlying OpenSSL library, otherwise the Postfix
SMTP client will not support DANE TLSA security.
.PP
When for a particular combination of "certificate usage" and
"selector" the TLSA RRset contains a well-formed record with a
matching type of "0", i.e. a full value of the associated certificate
or public key, the Postfix SMTP client will ignore all other matching
types for the same certificate usage and selector. In this case
the first algorithm listed in tls_dane_digests will be used to
compute a digest of the full value, which will then be used to match
certificates or public keys in the server's certificate chain.
.PP
Otherwise, when for a particular combination of "certificate
usage" and "selector" the TLSA RRset contains a records with more
than one non-zero matching type, i.e. multiple digest algorithms,
only records with the highest preference digest are used after
discarding any records with an incorrect digest length as unusable.
.PP
This strategy ensures that the strongest digest supported by
both the Postfix SMTP client and the remote server is used, and
weaker digests are ignored. This supports non-disruptive deprecation
of outdated digest algorithms.
.PP
The strategy requires that when a TLSA RRset provides association
data for multiple certificates or public keys, all RRs with the same
"certificate usage" and "selector" be published with the same set
of digests. In particular, during key rotation, when a certificate
or public key is being replaced with another (and both are published
during the transition) both the old and the new certificate MUST be
specified with the same set of digests. One can change the list of
digest algorithms later, once old keys are retired. At any given
time change either the list of digests without changing the list of
certificates or public keys or the list of certificates or public
keys without changing the list of digests.
.PP
It is expected that this algorithm agility mechanism will be
published in a standards track RFC for SMTP with DANE, and perhaps
in an eventual update to RFC 6698.
.PP
This feature is available in Postfix 2.11.
.SH tls_dane_trust_anchor_digest_enable (default: trust-anchor-assertion) .SH tls_dane_trust_anchor_digest_enable (default: trust-anchor-assertion)
RFC 6698 trust-anchor digest support in the Postfix TLS library. RFC 6698 trust-anchor digest support in the Postfix TLS library.
Specify zero or more of the following options, separated by comma or Specify zero or more of the following options, separated by comma or

View File

@ -47,7 +47,7 @@ sed '
s;'$ESC'\[4m;<i>;g s;'$ESC'\[4m;<i>;g
s;'$ESC'\[24m;</i>;g s;'$ESC'\[24m;</i>;g
# Undo gratuitous whitespace changes. # Undo gratuitous whitespace changes.
s;\( *\)\(</[bi]>\);\2\1;g #s;\( *\)\(</[bi]>\);\2\1;g
# End nroff ANSI escape sequence workarounds. # End nroff ANSI escape sequence workarounds.
s;</i>\( *\)<i>;\1;g s;</i>\( *\)<i>;\1;g
s;</b>\( *\)<b>;\1;g s;</b>\( *\)<b>;\1;g

View File

@ -716,6 +716,7 @@ while (<>) {
s;\btls_append_default_CA\b;<a href="postconf.5.html#tls_append_default_CA">$&</a>;g; s;\btls_append_default_CA\b;<a href="postconf.5.html#tls_append_default_CA">$&</a>;g;
s;\btls_legacy_public_key_fingerprints\b;<a href="postconf.5.html#tls_legacy_public_key_fingerprint">$&</a>;g; s;\btls_legacy_public_key_fingerprints\b;<a href="postconf.5.html#tls_legacy_public_key_fingerprint">$&</a>;g;
s;\btls_dane_trust_anchor_digest_enable\b;<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">$&</a>;g; s;\btls_dane_trust_anchor_digest_enable\b;<a href="postconf.5.html#tls_dane_trust_anchor_digest_enable">$&</a>;g;
s;\btls_dane_digests\b;<a href="postconf.5.html#tls_dane_digests">$&</a>;g;
s;\btls_wildcard_matches_multiple_labels\b;<a href="postconf.5.html#tls_wildcard_matches_multiple_labels">$&</a>;g; s;\btls_wildcard_matches_multiple_labels\b;<a href="postconf.5.html#tls_wildcard_matches_multiple_labels">$&</a>;g;
s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g; s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;

View File

@ -244,6 +244,39 @@
# temporary error if the limit is exceeded. Setting the # temporary error if the limit is exceeded. Setting the
# limit to 1 ensures that lookups do not return multiple # limit to 1 ensures that lookups do not return multiple
# values. # values.
# .IP "\fBoption_file\fR"
# Read options from the given file instead of the default my.cnf
# location.
# .sp
# This parameter is available with Postfix 2.11 and later.
# .IP "\fBoption_group\fR"
# Read options from the given group.
# .sp
# This parameter is available with Postfix 2.11 and later.
# .IP "\fBtls_cert_file\fR"
# File containing client's X509 certificate.
# .sp
# This parameter is available with Postfix 2.11 and later.
# .IP "\fBtls_key_file\fR"
# File containing the private key corresponding to \fBtls_cert_file\fR.
# .sp
# This parameter is available with Postfix 2.11 and later.
# .IP "\fBtls_CAfile\fR"
# File containing certificates for all of the X509 Certificate
# Authorities the client will recognize. Takes precedence over
# \fBtls_CApath\fR.
# .sp
# This parameter is available with Postfix 2.11 and later.
# .IP "\fBtls_CApath\fR"
# Directory containing X509 Certificate Authority certificates
# in separate individual files.
# .sp
# This parameter is available with Postfix 2.11 and later.
# .IP "\fBtls_verify_cert (default: no)\fR"
# Verify that the server's name matches the common name in the
# certficate.
# .sp
# This parameter is available with Postfix 2.11 and later.
# OBSOLETE QUERY INTERFACE # OBSOLETE QUERY INTERFACE
# .ad # .ad
# .fi # .fi

View File

@ -15438,7 +15438,7 @@ configuration parameter. See there for details. </p>
<p> Lookup the associated DANE TLSA RRset even when a hostname is <p> Lookup the associated DANE TLSA RRset even when a hostname is
not an alias and its address records lie in an unsigned zone. This not an alias and its address records lie in an unsigned zone. This
is unlikely to ever yield DNSSEC validated results, since child is unlikely to ever yield DNSSEC validated results, since child
zones of unsigned zones are also unsigned in the absense of DLV or zones of unsigned zones are also unsigned in the absence of DLV or
locally configured non-root trust-anchors. We anticipate that such locally configured non-root trust-anchors. We anticipate that such
mechanisms will not be used for just the "_tcp" subdomain of a host. mechanisms will not be used for just the "_tcp" subdomain of a host.
Suppressing the TLSA RRset lookup reduces latency and avoids potential Suppressing the TLSA RRset lookup reduces latency and avoids potential
@ -15446,3 +15446,48 @@ interoperability problems with nameservers for unsigned zones that
are not prepared to handle the new TLSA RRset. </p> are not prepared to handle the new TLSA RRset. </p>
<p> This feature is available in Postfix 2.11. </p> <p> This feature is available in Postfix 2.11. </p>
%PARAM tls_dane_digests sha512 sha256
<p> RFC 6698 TLSA resource-record "matching type" digest algorithms
in descending preference order. All the specified algorithms must
be supported by the underlying OpenSSL library, otherwise the Postfix
SMTP client will not support DANE TLSA security. </p>
<p> When for a particular combination of "certificate usage" and
"selector" the TLSA RRset contains a well-formed record with a
matching type of "0", i.e. a full value of the associated certificate
or public key, the Postfix SMTP client will ignore all other matching
types for the same certificate usage and selector. In this case
the first algorithm listed in tls_dane_digests will be used to
compute a digest of the full value, which will then be used to match
certificates or public keys in the server's certificate chain. </p>
<p> Otherwise, when for a particular combination of "certificate
usage" and "selector" the TLSA RRset contains a records with more
than one non-zero matching type, i.e. multiple digest algorithms,
only records with the highest preference digest are used after
discarding any records with an incorrect digest length as unusable. </p>
<p> This strategy ensures that the strongest digest supported by
both the Postfix SMTP client and the remote server is used, and
weaker digests are ignored. This supports non-disruptive deprecation
of outdated digest algorithms. </p>
<p> The strategy requires that when a TLSA RRset provides association
data for multiple certificates or public keys, all RRs with the same
"certificate usage" and "selector" be published with the same set
of digests. In particular, during key rotation, when a certificate
or public key is being replaced with another (and both are published
during the transition) both the old and the new certificate MUST be
specified with the same set of digests. One can change the list of
digest algorithms later, once old keys are retired. At any given
time change either the list of digests without changing the list of
certificates or public keys or the list of certificates or public
keys without changing the list of digests. </p>
<p> It is expected that this algorithm agility mechanism will be
published in a standards track RFC for SMTP with DANE, and perhaps
in an eventual update to RFC 6698. </p>
<p> This feature is available in Postfix 2.11. </p>

View File

@ -91,6 +91,25 @@
/* releases. /* releases.
/* .IP hosts /* .IP hosts
/* List of hosts to connect to. /* List of hosts to connect to.
/* .IP option_file
/* Read options from the given file instead of the default my.cnf
/* location.
/* .IP option_group
/* Read options from the given group.
/* .IP tls_cert_file
/* File containing client's X509 certificate.
/* .IP tls_key_file
/* File containing the private key corresponding to \fItls_cert_file\fR.
/* .IP tls_CAfile
/* File containing certificates for all of the X509 Certificate
/* Authorities the client will recognize. Takes precedence over
/* \fItls_CApath\fR.
/* .IP tls_CApath
/* Directory containing X509 Certificate Authority certificates
/* in separate individual files.
/* .IP tls_verify_cert
/* Verify that the server's name matches the common name of the
/* certficate.
/* .PP /* .PP
/* For example, if you want the map to reference databases of /* For example, if you want the map to reference databases of
/* the name "your_db" and execute a query like this: select /* the name "your_db" and execute a query like this: select
@ -217,6 +236,8 @@ typedef struct {
CFG_PARSER *parser; CFG_PARSER *parser;
char *query; char *query;
char *result_format; char *result_format;
char *option_file;
char *option_group;
void *ctx; void *ctx;
int expansion_limit; int expansion_limit;
char *username; char *username;
@ -226,6 +247,14 @@ typedef struct {
PLMYSQL *pldb; PLMYSQL *pldb;
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
HOST *active_host; HOST *active_host;
char *tls_cert_file;
char *tls_key_file;
char *tls_CAfile;
char *tls_CApath;
char *tls_ciphers;
#if MYSQL_VERSION_ID >= 50023
int tls_verify_cert;
#endif
#endif #endif
} DICT_MYSQL; } DICT_MYSQL;
@ -242,12 +271,11 @@ typedef struct {
/* internal function declarations */ /* internal function declarations */
static PLMYSQL *plmysql_init(ARGV *); static PLMYSQL *plmysql_init(ARGV *);
static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *, char *, static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *);
char *, char *);
static void plmysql_dealloc(PLMYSQL *); static void plmysql_dealloc(PLMYSQL *);
static void plmysql_close_host(HOST *); static void plmysql_close_host(HOST *);
static void plmysql_down_host(HOST *); static void plmysql_down_host(HOST *);
static void plmysql_connect_single(HOST *, char *, char *, char *); static void plmysql_connect_single(DICT_MYSQL *, HOST *);
static const char *dict_mysql_lookup(DICT *, const char *); static const char *dict_mysql_lookup(DICT *, const char *);
DICT *dict_mysql_open(const char *, int, int); DICT *dict_mysql_open(const char *, int, int);
static void dict_mysql_close(DICT *); static void dict_mysql_close(DICT *);
@ -349,10 +377,7 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
return (0); return (0);
/* do the query - set dict->error & cleanup if there's an error */ /* do the query - set dict->error & cleanup if there's an error */
if ((query_res = plmysql_query(dict_mysql, name, query, if ((query_res = plmysql_query(dict_mysql, name, query)) == 0) {
dict_mysql->dbname,
dict_mysql->username,
dict_mysql->password)) == 0) {
dict->error = DICT_ERR_RETRY; dict->error = DICT_ERR_RETRY;
return (0); return (0);
} }
@ -428,10 +453,10 @@ static HOST *dict_mysql_find_host(PLMYSQL *PLDB, unsigned stat, unsigned type)
/* dict_mysql_get_active - get an active connection */ /* dict_mysql_get_active - get an active connection */
static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname, static HOST *dict_mysql_get_active(DICT_MYSQL *dict_mysql)
char *username, char *password)
{ {
const char *myname = "dict_mysql_get_active"; const char *myname = "dict_mysql_get_active";
PLMYSQL *PLDB = dict_mysql->pldb;
HOST *host; HOST *host;
int count = RETRY_CONN_MAX; int count = RETRY_CONN_MAX;
@ -457,7 +482,7 @@ static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname,
if (msg_verbose) if (msg_verbose)
msg_info("%s: attempting to connect to host %s", myname, msg_info("%s: attempting to connect to host %s", myname,
host->hostname); host->hostname);
plmysql_connect_single(host, dbname, username, password); plmysql_connect_single(dict_mysql, host);
if (host->stat == STATACTIVE) if (host->stat == STATACTIVE)
return host; return host;
} }
@ -485,17 +510,12 @@ static void dict_mysql_event(int unused_event, char *context)
static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql, static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql,
const char *name, const char *name,
VSTRING *query, VSTRING *query)
char *dbname,
char *username,
char *password)
{ {
PLMYSQL *PLDB = dict_mysql->pldb;
HOST *host; HOST *host;
MYSQL_RES *res = 0; MYSQL_RES *res = 0;
while ((host = dict_mysql_get_active(PLDB, dbname, username, password)) != NULL) { while ((host = dict_mysql_get_active(dict_mysql)) != NULL) {
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000 #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
/* /*
@ -534,15 +554,32 @@ static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql,
* used to reconnect to a single database when one is down or none is * used to reconnect to a single database when one is down or none is
* connected yet. Log all errors and set the stat field of host accordingly * connected yet. Log all errors and set the stat field of host accordingly
*/ */
static void plmysql_connect_single(HOST *host, char *dbname, char *username, char *password) static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
{ {
if ((host->db = mysql_init(NULL)) == NULL) if ((host->db = mysql_init(NULL)) == NULL)
msg_fatal("dict_mysql: insufficient memory"); msg_fatal("dict_mysql: insufficient memory");
if (dict_mysql->option_file)
mysql_options(host->db, MYSQL_READ_DEFAULT_FILE, dict_mysql->option_file);
if (dict_mysql->option_group)
mysql_options(host->db, MYSQL_READ_DEFAULT_GROUP, dict_mysql->option_group);
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
if (dict_mysql->tls_key_file || dict_mysql->tls_cert_file ||
dict_mysql->tls_CAfile || dict_mysql->tls_CApath || dict_mysql->tls_ciphers)
mysql_ssl_set(host->db,
dict_mysql->tls_key_file, dict_mysql->tls_cert_file,
dict_mysql->tls_CAfile, dict_mysql->tls_CApath,
dict_mysql->tls_ciphers);
#if MYSQL_VERSION_ID >= 50023
if (dict_mysql->tls_verify_cert != -1)
mysql_options(host->db, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&dict_mysql->tls_verify_cert);
#endif
#endif
if (mysql_real_connect(host->db, if (mysql_real_connect(host->db,
(host->type == TYPEINET ? host->name : 0), (host->type == TYPEINET ? host->name : 0),
username, dict_mysql->username,
password, dict_mysql->password,
dbname, dict_mysql->dbname,
host->port, host->port,
(host->type == TYPEUNIX ? host->name : 0), (host->type == TYPEUNIX ? host->name : 0),
0)) { 0)) {
@ -582,7 +619,7 @@ static void plmysql_down_host(HOST *host)
static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf) static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
{ {
const char *myname = "mysqlname_parse"; const char *myname = "mysql_parse_config";
CFG_PARSER *p = dict_mysql->parser; CFG_PARSER *p = dict_mysql->parser;
VSTRING *buf; VSTRING *buf;
char *hosts; char *hosts;
@ -591,6 +628,18 @@ static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
dict_mysql->password = cfg_get_str(p, "password", "", 0, 0); dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0); dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0); dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
dict_mysql->option_file = cfg_get_str(p, "option_file", NULL, 0, 0);
dict_mysql->option_group = cfg_get_str(p, "option_group", NULL, 0, 0);
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
dict_mysql->tls_key_file = cfg_get_str(p, "tls_key_file", NULL, 0, 0);
dict_mysql->tls_cert_file = cfg_get_str(p, "tls_cert_file", NULL, 0, 0);
dict_mysql->tls_CAfile = cfg_get_str(p, "tls_CAfile", NULL, 0, 0);
dict_mysql->tls_CApath = cfg_get_str(p, "tls_CApath", NULL, 0, 0);
dict_mysql->tls_ciphers = cfg_get_str(p, "tls_ciphers", NULL, 0, 0);
#if MYSQL_VERSION_ID >= 50023
dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1);
#endif
#endif
/* /*
* XXX: The default should be non-zero for safety, but that is not * XXX: The default should be non-zero for safety, but that is not
@ -759,6 +808,22 @@ static void dict_mysql_close(DICT *dict)
myfree(dict_mysql->dbname); myfree(dict_mysql->dbname);
myfree(dict_mysql->query); myfree(dict_mysql->query);
myfree(dict_mysql->result_format); myfree(dict_mysql->result_format);
if (dict_mysql->option_file)
myfree(dict_mysql->option_file);
if (dict_mysql->option_group)
myfree(dict_mysql->option_group);
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
if (dict_mysql->tls_key_file)
myfree(dict_mysql->tls_key_file);
if (dict_mysql->tls_cert_file)
myfree(dict_mysql->tls_cert_file);
if (dict_mysql->tls_CAfile)
myfree(dict_mysql->tls_CAfile);
if (dict_mysql->tls_CApath)
myfree(dict_mysql->tls_CApath);
if (dict_mysql->tls_ciphers)
myfree(dict_mysql->tls_ciphers);
#endif
if (dict_mysql->hosts) if (dict_mysql->hosts)
argv_free(dict_mysql->hosts); argv_free(dict_mysql->hosts);
if (dict_mysql->ctx) if (dict_mysql->ctx)

View File

@ -3103,6 +3103,13 @@ extern char *var_tls_ssl_options;
#define DEF_TLS_BC_PKEY_FPRINT 0 #define DEF_TLS_BC_PKEY_FPRINT 0
extern bool var_tls_bc_pkey_fprint; extern bool var_tls_bc_pkey_fprint;
/*
* Ordered list of DANE digest algorithms.
*/
#define VAR_TLS_DANE_DIGESTS "tls_dane_digests"
#define DEF_TLS_DANE_DIGESTS "sha512 sha256"
extern char *var_tls_dane_digests;
/* /*
* External interface for enabling trust-anchor digests, which are risky * External interface for enabling trust-anchor digests, which are risky
* when the corresponding certificate is missing from the peer chain (this * when the corresponding certificate is missing from the peer chain (this

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20131114" #define MAIL_RELEASE_DATE "20131117"
#define MAIL_VERSION_NUMBER "2.11" #define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@ -1745,11 +1745,8 @@ static void parse_match(STATE *state, int argc, char *argv[])
{ {
#ifdef USE_TLS #ifdef USE_TLS
argc -= optind;
argv += optind;
switch (state->level) { switch (state->level) {
case TLS_LEV_SECURE: case TLS_LEV_SECURE:
state->match = argv_alloc(2); state->match = argv_alloc(2);
while (*argv) while (*argv)
argv_split_append(state->match, *argv++, ""); argv_split_append(state->match, *argv++, "");
@ -1826,14 +1823,17 @@ int main(int argc, char *argv[])
mail_conf_suck(); mail_conf_suck();
parse_options(&state, argc, argv); parse_options(&state, argc, argv);
mail_params_init(); mail_params_init();
parse_match(&state, argc, argv);
parse_tas(&state); parse_tas(&state);
argc -= optind;
argv += optind;
/* The first non-option argument is the destination. */ /* The first non-option argument is the destination. */
if (argc < optind) if (!argc)
usage(); usage();
state.dest = mystrdup(argv[optind]);
state.dest = mystrdup(argv[0]);
parse_match(&state, --argc, ++argv);
/* Don't talk to remote systems as root */ /* Don't talk to remote systems as root */
if (!geteuid()) if (!geteuid())

View File

@ -235,17 +235,29 @@ static int wrap_signed = 0;
#endif #endif
static const EVP_MD *signmd; static const EVP_MD *signmd;
static const char *signalg;
static EVP_PKEY *danekey; static EVP_PKEY *danekey;
static ASN1_OBJECT *serverAuth; static ASN1_OBJECT *serverAuth;
static const char *sha256 = "sha256"; /*
static const EVP_MD *sha256md; * https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml
static int sha256len; */
typedef struct digest_info {
const char *alg; /* OpenSSL name */
const EVP_MD *md; /* OpenSSL EVP handle */
int len; /* digest octet length */
int pref; /* tls_dane_digests index or -1 */
uint8_t dane_id; /* IANA id */
} digest_info;
static const char *sha512 = "sha512"; #define MAXDIGESTS 256 /* RFC limit */
static const EVP_MD *sha512md; digest_info digest_table[] = {
static int sha512len; {"full", 0, 0, 0, DNS_TLSA_MATCHING_TYPE_NO_HASH_USED},
{"sha256", 0, 0, -1, DNS_TLSA_MATCHING_TYPE_SHA256},
{"sha512", 0, 0, -1, DNS_TLSA_MATCHING_TYPE_SHA512},
{0, 0, 0, 0, 0}
};
static int digest_mask; static int digest_mask;
@ -271,6 +283,46 @@ void tls_dane_verbose(int on)
dane_verbose = on; dane_verbose = on;
} }
/* digest_info_cmp - qsort() comparator for digest_table */
static int digest_info_cmp(const void *a, const void *b)
{
register const digest_info *ai = (const digest_info *) a;
register const digest_info *bi = (const digest_info *) b;
/*
* Negative preferences sort last. Otherwise, sort in ascending order.
*/
if (ai->pref == bi->pref)
return (0);
if (ai->pref < 0 || bi->pref < 0)
return bi->pref - ai->pref;
return ai->pref - bi->pref;
}
/* dane_digest_info - locate digest_table entry for given IANA id */
static digest_info *dane_digest_info(uint8_t dane_id)
{
digest_info *di;
for (di = digest_table; di->alg; ++di)
if (di->dane_id == dane_id)
return (di);
return (0);
}
/* dane_digest_pref - digest preference by IANA id */
static int dane_digest_pref(uint8_t dane_id)
{
digest_info *di = dane_digest_info(dane_id);
if (di && di->pref >= 0)
return (di->pref);
return (MAXDIGESTS + dane_id);
}
/* gencakey - generate interal DANE root CA key */ /* gencakey - generate interal DANE root CA key */
static EVP_PKEY *gencakey(void) static EVP_PKEY *gencakey(void)
@ -278,8 +330,6 @@ static EVP_PKEY *gencakey(void)
EVP_PKEY *key = 0; EVP_PKEY *key = 0;
#ifdef WRAP_SIGNED #ifdef WRAP_SIGNED
int len;
unsigned char *p;
EC_KEY *eckey; EC_KEY *eckey;
EC_GROUP *group; EC_GROUP *group;
@ -312,16 +362,44 @@ static void dane_init(void)
TLS_DANE_TAA, TLS_DANE_ENABLE_TAA, TLS_DANE_TAA, TLS_DANE_ENABLE_TAA,
0, 0,
}; };
int digest_pref = 0;
char *cp;
char *save;
char *tok;
digest_info *di;
digest_mask = digest_mask =
name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst, name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst,
NAME_MASK_ANY_CASE | NAME_MASK_FATAL); NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
if ((sha256md = EVP_get_digestbyname(sha256)) != 0) save = cp = mystrdup(var_tls_dane_digests);
sha256len = EVP_MD_size(sha256md); while ((tok = mystrtok(&cp, "\t\n\r ,")) != 0) {
if ((sha512md = EVP_get_digestbyname(sha512)) != 0) for (di = digest_table; di->alg; ++di)
sha512len = EVP_MD_size(sha512md); if (strcasecmp(tok, di->alg) == 0)
signmd = sha256md ? sha256md : EVP_sha1(); break;
if (di->alg != 0
&& (di->md = EVP_get_digestbyname(di->alg)) != 0
&& (di->len = EVP_MD_size(di->md)) > 0
&& di->len <= EVP_MAX_MD_SIZE) {
/*
* The most preferred digest will be used for cert signing and
* digesting for comparison.
*/
if ((di->pref = ++digest_pref) == 1) {
signalg = di->alg;
signmd = di->md;
}
} else {
msg_warn("Unsupported DANE digest algorithm: %s", tok);
continue;
}
}
myfree(save);
if (digest_pref > 0)
qsort(digest_table, digest_pref, sizeof(digest_table[0]),
digest_info_cmp);
/* Don't report old news */ /* Don't report old news */
ERR_clear_error(); ERR_clear_error();
@ -344,7 +422,7 @@ int tls_dane_avail(void)
dane_init(); dane_init();
#ifdef DANE_TLSA_SUPPORT #ifdef DANE_TLSA_SUPPORT
return (sha256md && sha512md && serverAuth); return (signalg && serverAuth);
#else #else
return (0); return (0);
#endif #endif
@ -574,9 +652,27 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector,
static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b) static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b)
{ {
if (a->data_len == b->data_len) int cmp;
return (memcmp(a->data, b->data, a->data_len));
return ((a->data_len > b->data_len) ? 1 : -1); /*
* Sort in ascending order, by usage, selector, matching type preference
* and payload. The usage, selector and matching type are the first
* three unsigned octets of the RR data.
*/
if (a->data_len > 2 && b->data_len > 2) {
uint8_t *ai = (uint8_t *) a->data;
uint8_t *bi = (uint8_t *) b->data;
#define signedcmp(x, y) (((int)(y)) - ((int)(x)))
if ((cmp = signedcmp(ai[0], bi[0])) != 0
|| (cmp = signedcmp(ai[1], bi[1])) != 0
|| (cmp = dane_digest_pref(ai[2]) - dane_digest_pref(bi[2])) != 0)
return (cmp);
}
if ((cmp = a->data_len - b->data_len) != 0)
return (cmp);
return (memcmp(a->data, b->data, a->data_len));
} }
/* parse_tlsa_rrs - parse a validated TLSA RRset */ /* parse_tlsa_rrs - parse a validated TLSA RRset */
@ -588,18 +684,24 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
uint8_t mtype; uint8_t mtype;
int mlen; int mlen;
const unsigned char *p; const unsigned char *p;
uint32_t prev_usage_selector; /* Previous (usage<<8|selector) */
uint32_t prev_mtype; /* Previous valid mtype for above */
#define NO_PREV 0xffffffff /* Not any usage|selector or
* mtype */
prev_usage_selector = NO_PREV;
if (rr == 0) if (rr == 0)
msg_panic("null TLSA rr"); msg_panic("null TLSA rr");
for ( /* nop */ ; rr; rr = rr->next) { for ( /* nop */ ; rr; rr = rr->next) {
const char *mdalg = 0; const char *mdalg = 0;
int mdlen;
char *digest; char *digest;
int same = (strcasecmp(rr->rname, rr->qname) == 0); int same = (strcasecmp(rr->rname, rr->qname) == 0);
uint8_t *ip = (uint8_t *) rr->data; uint8_t *ip = (uint8_t *) rr->data;
X509 *x = 0; /* OpenSSL tries to re-use *x if x!=0 */ X509 *x = 0; /* OpenSSL tries to re-use *x if x!=0 */
EVP_PKEY *k = 0; /* OpenSSL tries to re-use *k if k!=0 */ EVP_PKEY *k = 0; /* OpenSSL tries to re-use *k if k!=0 */
digest_info *di;
#define rcname(rr) (same ? "" : rr->qname) #define rcname(rr) (same ? "" : rr->qname)
#define rarrow(rr) (same ? "" : " -> ") #define rarrow(rr) (same ? "" : " -> ")
@ -638,26 +740,34 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
break; break;
} }
switch (mtype = *ip++) { /*
default: * Skip all but the most preferred matching type for any given
msg_warn("unsupported matching type %u in RR: " * (usage, selector) combination.
"%s%s%s IN TLSA %u %u %u ...", mtype, rcname(rr), */
rarrow(rr), rr->rname, usage, selector, mtype); mtype = *ip++;
if (prev_usage_selector != (usage << 8 | selector))
prev_mtype = NO_PREV;
else if (prev_mtype != NO_PREV && prev_mtype != mtype)
continue; continue;
case DNS_TLSA_MATCHING_TYPE_SHA256:
if (!mdalg) { switch (mtype) {
mdalg = sha256; default:
mdlen = sha256len; if ((di = dane_digest_info(mtype)) == 0) {
msg_warn("unsupported matching type %u in RR: "
"%s%s%s IN TLSA %u %u %u ...", mtype, rcname(rr),
rarrow(rr), rr->rname, usage, selector, mtype);
continue;
} }
/* FALLTHROUGH */ if (di->pref < 0) {
case DNS_TLSA_MATCHING_TYPE_SHA512: msg_warn("digest algorithm %s locally disabled, in RR: "
if (!mdalg) { "%s%s%s IN TLSA %u %u %u ...", di->alg,
mdalg = sha512; rcname(rr), rarrow(rr), rr->rname,
mdlen = sha512len; usage, selector, mtype);
continue;
} }
if (mlen != mdlen) { if (mlen != di->len) {
msg_warn("malformed %s digest, length %u, in RR: " msg_warn("malformed %s digest, length %u, in RR: "
"%s%s%s IN TLSA %u %u %u ...", mdalg, mlen, "%s%s%s IN TLSA %u %u %u ...", di->alg, mlen,
rcname(rr), rarrow(rr), rr->rname, rcname(rr), rarrow(rr), rr->rname,
usage, selector, mtype); usage, selector, mtype);
continue; continue;
@ -667,7 +777,7 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
if (!(digest_mask & TLS_DANE_ENABLE_CC)) { if (!(digest_mask & TLS_DANE_ENABLE_CC)) {
msg_warn("%s trust-anchor %s digests disabled, in RR: " msg_warn("%s trust-anchor %s digests disabled, in RR: "
"%s%s%s IN TLSA %u %u %u ...", TLS_DANE_CC, "%s%s%s IN TLSA %u %u %u ...", TLS_DANE_CC,
mdalg, rcname(rr), rarrow(rr), rr->rname, di->alg, rcname(rr), rarrow(rr), rr->rname,
usage, selector, mtype); usage, selector, mtype);
continue; continue;
} }
@ -676,14 +786,14 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
if (!(digest_mask & TLS_DANE_ENABLE_TAA)) { if (!(digest_mask & TLS_DANE_ENABLE_TAA)) {
msg_warn("%s trust-anchor %s digests disabled, in RR: " msg_warn("%s trust-anchor %s digests disabled, in RR: "
"%s%s%s IN TLSA %u %u %u ...", TLS_DANE_TAA, "%s%s%s IN TLSA %u %u %u ...", TLS_DANE_TAA,
mdalg, rcname(rr), rarrow(rr), rr->rname, di->alg, rcname(rr), rarrow(rr), rr->rname,
usage, selector, mtype); usage, selector, mtype);
continue; continue;
} }
break; break;
} }
dane_add(dane, usage, selector, mdalg, digest = tls_digest_encode((unsigned char *) ip, di->len);
digest = tls_digest_encode((unsigned char *) ip, mdlen)); dane_add(dane, usage, selector, di->alg, digest);
break; break;
case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED: case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
@ -726,6 +836,7 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
} }
X509_free(x); X509_free(x);
break; break;
case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO: case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
if (!d2i_PUBKEY(&k, &p, mlen)) { if (!d2i_PUBKEY(&k, &p, mlen)) {
msg_warn("malformed %s in RR: " msg_warn("malformed %s in RR: "
@ -747,12 +858,19 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
/* /*
* The cert or key was valid, just digest the raw object, and * The cert or key was valid, just digest the raw object, and
* encode the digest value. We choose SHA256. * encode the digest value.
*/ */
dane_add(dane, usage, selector, mdalg = sha256, digest = tls_data_fprint((char *) ip, mlen, signalg);
digest = tls_data_fprint((char *) ip, mlen, sha256)); dane_add(dane, usage, selector, mdalg = signalg, digest);
break; break;
} }
/*
* Save state
*/
prev_usage_selector = (usage << 8 | selector);
prev_mtype = mtype;
if (msg_verbose || dane_verbose) { if (msg_verbose || dane_verbose) {
switch (mtype) { switch (mtype) {
default: default:
@ -803,7 +921,13 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
dane->expires = 1 + event_time() + rrs->ttl; dane->expires = 1 + event_time() + rrs->ttl;
if (rrs->dnssec_valid) { if (rrs->dnssec_valid) {
/* Sort for deterministic digest in session cache lookup key */
/*
* Sort for deterministic digest in session cache lookup key. In
* addition we must arrange for more preferred matching types
* (full value or digest) to precede less preferred ones for the
* same usage and selector.
*/
rrs = dns_rr_sort(rrs, tlsa_rr_cmp); rrs = dns_rr_sort(rrs, tlsa_rr_cmp);
parse_tlsa_rrs(dane, rrs); parse_tlsa_rrs(dane, rrs);
} else } else
@ -905,7 +1029,7 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
msg_warn("trust-anchor files not supported"); msg_warn("trust-anchor files not supported");
return (0); return (0);
} }
mdalg = sha256md ? sha256 : "sha1"; mdalg = signalg ? signalg : "sha1";
/* /*
* On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio, * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio,
@ -1001,12 +1125,20 @@ int tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
char **dgst; char **dgst;
ARGV *certs; ARGV *certs;
/*
* Note, set_trust() needs to know whether the match was for a pkey
* digest or a certificate digest. We return MATCHED_PKEY or
* MATCHED_CERT accordingly.
*/
#define MATCHED_CERT 1
#define MATCHED_PKEY 2
if (tlsa->pkeys) { if (tlsa->pkeys) {
char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg); char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg);
for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst) for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst)
if (strcasecmp(pkey_dgst, *dgst) == 0) if (strcasecmp(pkey_dgst, *dgst) == 0)
matched = 1; matched = MATCHED_PKEY;
if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH) if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
&& matched) && matched)
msg_info("%s: depth=%d matched %s public-key %s digest=%s", msg_info("%s: depth=%d matched %s public-key %s digest=%s",
@ -1034,7 +1166,7 @@ int tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
for (dgst = certs->argv; !matched && *dgst; ++dgst) for (dgst = certs->argv; !matched && *dgst; ++dgst)
if (strcasecmp(cert_dgst, *dgst) == 0) if (strcasecmp(cert_dgst, *dgst) == 0)
matched = 1; matched = MATCHED_CERT;
if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH) if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
&& matched) && matched)
msg_info("%s: depth=%d matched %s certificate %s digest %s", msg_info("%s: depth=%d matched %s certificate %s digest %s",
@ -1139,15 +1271,10 @@ static int add_skid(X509 *cert, AUTHORITY_KEYID *akid)
return (ret); return (ret);
} }
/* set_issuer - set issuer DN to match akid if specified */ /* akid_issuer_name - get akid issuer directory name */
static int set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid) static X509_NAME *akid_issuer_name(AUTHORITY_KEYID *akid)
{ {
/*
* If subject's akid specifies an authority key identifer issuer name, we
* must use that.
*/
if (akid && akid->issuer) { if (akid && akid->issuer) {
int i; int i;
general_name_stack_t *gens = akid->issuer; general_name_stack_t *gens = akid->issuer;
@ -1156,9 +1283,24 @@ static int set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid)
GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
if (gn->type == GEN_DIRNAME) if (gn->type == GEN_DIRNAME)
return (X509_set_issuer_name(cert, gn->d.dirn)); return (gn->d.dirn);
} }
} }
return (0);
}
/* set_issuer - set issuer DN to match akid if specified */
static int set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid)
{
X509_NAME *name = akid_issuer_name(akid);
/*
* If subject's akid specifies an authority key identifer issuer name, we
* must use that.
*/
if (name)
return (X509_set_issuer_name(cert, name));
return (X509_set_issuer_name(cert, X509_get_subject_name(cert))); return (X509_set_issuer_name(cert, X509_get_subject_name(cert)));
} }
@ -1179,23 +1321,26 @@ static void grow_chain(x509_stack_t **skptr, X509 *cert, ASN1_OBJECT *trust)
/* wrap_key - wrap TA "key" as issuer of "subject" */ /* wrap_key - wrap TA "key" as issuer of "subject" */
static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject, static int wrap_key(TLS_SESS_STATE *TLScontext, int depth,
int depth) EVP_PKEY *key, X509 *subject)
{ {
int ret = 1; int ret = 1;
int selfsigned = 0;
X509 *cert = 0; X509 *cert = 0;
AUTHORITY_KEYID *akid; AUTHORITY_KEYID *akid;
X509_NAME *name = X509_get_issuer_name(subject); X509_NAME *name = X509_get_issuer_name(subject);
X509_NAME *akid_name;
/* /*
* Record the depth of the intermediate wrapper certificate, logged in * Record the depth of the intermediate wrapper certificate, logged in
* the verify callback, unlike the parent root CA. * the verify callback.
*/ */
if (!key) if (TLScontext->tadepth < 0) {
TLScontext->tadepth = depth; TLScontext->tadepth = depth + 1;
else if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)) if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
msg_info("%s: depth=%d chain is trust-anchor signed", msg_info("%s: depth=%d chain is trust-anchor signed",
TLScontext->namaddr, depth); TLScontext->namaddr, depth);
}
/* /*
* If key is NULL generate a self-signed root CA, with key "danekey", * If key is NULL generate a self-signed root CA, with key "danekey",
@ -1205,6 +1350,9 @@ static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject,
return (0); return (0);
akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0); akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0);
if ((akid_name = akid_issuer_name(akid)) == 0
|| X509_NAME_cmp(name, akid_name) == 0)
selfsigned = 1;
ERR_clear_error(); ERR_clear_error();
@ -1217,11 +1365,12 @@ static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject,
|| !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L) || !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L)
|| !X509_set_pubkey(cert, key ? key : danekey) || !X509_set_pubkey(cert, key ? key : danekey)
|| !add_ext(0, cert, NID_basic_constraints, "CA:TRUE") || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE")
|| (key && !add_akid(cert, akid)) || (key && !selfsigned && !add_akid(cert, akid))
|| !add_skid(cert, akid) || !add_skid(cert, akid)
|| (wrap_signed || (wrap_signed
&& (!X509_sign(cert, danekey, signmd) && (!X509_sign(cert, danekey, signmd)
|| (key && !wrap_key(TLScontext, 0, cert, depth + 1))))) { || (key && !selfsigned
&& !wrap_key(TLScontext, depth + 1, 0, cert))))) {
msg_warn("error generating DANE wrapper certificate"); msg_warn("error generating DANE wrapper certificate");
tls_print_errors(); tls_print_errors();
ret = 0; ret = 0;
@ -1229,7 +1378,7 @@ static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject,
if (akid) if (akid)
AUTHORITY_KEYID_free(akid); AUTHORITY_KEYID_free(akid);
if (ret) { if (ret) {
if (key && wrap_signed) if (key && !selfsigned && wrap_signed)
grow_chain(&TLScontext->untrusted, cert, 0); grow_chain(&TLScontext->untrusted, cert, 0);
else else
grow_chain(&TLScontext->trusted, cert, serverAuth); grow_chain(&TLScontext->trusted, cert, serverAuth);
@ -1239,6 +1388,56 @@ static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject,
return (ret); return (ret);
} }
/* wrap_cert - wrap "tacert" as issuer of "subject" */
static int wrap_cert(TLS_SESS_STATE *TLScontext, int depth,
X509 *tacert, X509 *subject)
{
int ret = 1;
X509 *cert;
int len;
unsigned char *asn1;
unsigned char *buf;
TLScontext->tadepth = depth;
if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
msg_info("%s: depth=%d trust-anchor certificate",
TLScontext->namaddr, depth);
/*
* If the TA certificate is self-issued, use it directly.
*/
if (!wrap_signed
|| X509_check_issued(tacert, tacert) == X509_V_OK) {
grow_chain(&TLScontext->trusted, tacert, serverAuth);
return (ret);
}
/* Deep-copy tacert by converting to ASN.1 and back */
len = i2d_X509(tacert, NULL);
asn1 = buf = (unsigned char *) mymalloc(len);
i2d_X509(tacert, &buf);
if (buf - asn1 != len)
msg_panic("i2d_X509 failed to encode TA certificate");
buf = asn1;
cert = d2i_X509(0, (unsigned const char **) &buf, len);
if (!cert || (buf - asn1) != len)
msg_panic("d2i_X509 failed to decode TA certificate");
myfree((char *) asn1);
grow_chain(&TLScontext->untrusted, cert, 0);
/* Sign and wrap TA cert with internal "danekey" */
if (!X509_sign(cert, danekey, signmd)
|| !wrap_key(TLScontext, depth + 1, danekey, cert)) {
msg_warn("error generating DANE wrapper certificate");
tls_print_errors();
ret = 0;
}
X509_free(cert);
return (ret);
}
/* ta_signed - is certificate signed by a TLSA cert or pkey */ /* ta_signed - is certificate signed by a TLSA cert or pkey */
static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth) static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
@ -1262,7 +1461,7 @@ static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
continue; continue;
/* Check signature, since some other TA may work if not this. */ /* Check signature, since some other TA may work if not this. */
if (X509_verify(cert, pk) > 0) if (X509_verify(cert, pk) > 0)
done = wrap_key(TLScontext, pk, cert, depth); done = wrap_cert(TLScontext, depth + 1, x->cert, cert);
EVP_PKEY_free(pk); EVP_PKEY_free(pk);
} }
} }
@ -1286,7 +1485,7 @@ static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
*/ */
for (k = dane->pkeys; !done && k; k = k->next) for (k = dane->pkeys; !done && k; k = k->next)
if (X509_verify(cert, k->pkey) > 0) if (X509_verify(cert, k->pkey) > 0)
done = wrap_key(TLScontext, k->pkey, cert, depth); done = wrap_key(TLScontext, depth, k->pkey, cert);
return (done); return (done);
} }
@ -1297,6 +1496,7 @@ static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx)
{ {
int n; int n;
int i; int i;
int match;
int depth = 0; int depth = 0;
EVP_PKEY *takey; EVP_PKEY *takey;
X509 *ca; X509 *ca;
@ -1331,10 +1531,21 @@ static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx)
ca = sk_X509_delete(in, i); ca = sk_X509_delete(in, i);
/* Is it a trust anchor? */ /* Is it a trust anchor? */
if (tls_dane_match(TLScontext, TLS_DANE_TA, ca, depth + 1)) { match = tls_dane_match(TLScontext, TLS_DANE_TA, ca, depth + 1);
if ((takey = X509_get_pubkey(ca)) != 0 if (match) {
&& wrap_key(TLScontext, takey, cert, depth)) switch (match) {
case MATCHED_CERT:
wrap_cert(TLScontext, depth, ca, cert);
break;
case MATCHED_PKEY:
if ((takey = X509_get_pubkey(ca)) == 0)
msg_panic("trust-anchor certificate has null pkey");
wrap_key(TLScontext, depth, takey, cert);
EVP_PKEY_free(takey); EVP_PKEY_free(takey);
break;
default:
msg_panic("unexpected tls_dane_match result: %d", match);
}
cert = 0; cert = 0;
break; break;
} }

View File

@ -15,6 +15,7 @@
/* char *var_tls_eecdh_strong; /* char *var_tls_eecdh_strong;
/* char *var_tls_eecdh_ultra; /* char *var_tls_eecdh_ultra;
/* char *var_tls_dane_ta_dgst; /* char *var_tls_dane_ta_dgst;
/* char *var_tls_dane_digests;
/* int var_tls_daemon_rand_bytes; /* int var_tls_daemon_rand_bytes;
/* bool var_tls_append_def_CA; /* bool var_tls_append_def_CA;
/* bool var_tls_preempt_clist; /* bool var_tls_preempt_clist;
@ -220,6 +221,7 @@ char *var_tls_null_clist;
int var_tls_daemon_rand_bytes; int var_tls_daemon_rand_bytes;
char *var_tls_eecdh_strong; char *var_tls_eecdh_strong;
char *var_tls_eecdh_ultra; char *var_tls_eecdh_ultra;
char *var_tls_dane_digests;
char *var_tls_dane_ta_dgst; char *var_tls_dane_ta_dgst;
bool var_tls_append_def_CA; bool var_tls_append_def_CA;
char *var_tls_bug_tweaks; char *var_tls_bug_tweaks;
@ -227,6 +229,7 @@ char *var_tls_ssl_options;
bool var_tls_bc_pkey_fprint; bool var_tls_bc_pkey_fprint;
bool var_tls_multi_wildcard; bool var_tls_multi_wildcard;
char *var_tls_mgr_service; char *var_tls_mgr_service;
char *tls_dane_digests;
#ifdef VAR_TLS_PREEMPT_CLIST #ifdef VAR_TLS_PREEMPT_CLIST
bool var_tls_preempt_clist; bool var_tls_preempt_clist;
@ -594,6 +597,7 @@ void tls_param_init(void)
VAR_TLS_EECDH_ULTRA, DEF_TLS_EECDH_ULTRA, &var_tls_eecdh_ultra, 1, 0, VAR_TLS_EECDH_ULTRA, DEF_TLS_EECDH_ULTRA, &var_tls_eecdh_ultra, 1, 0,
VAR_TLS_BUG_TWEAKS, DEF_TLS_BUG_TWEAKS, &var_tls_bug_tweaks, 0, 0, VAR_TLS_BUG_TWEAKS, DEF_TLS_BUG_TWEAKS, &var_tls_bug_tweaks, 0, 0,
VAR_TLS_SSL_OPTIONS, DEF_TLS_SSL_OPTIONS, &var_tls_ssl_options, 0, 0, VAR_TLS_SSL_OPTIONS, DEF_TLS_SSL_OPTIONS, &var_tls_ssl_options, 0, 0,
VAR_TLS_DANE_DIGESTS, DEF_TLS_DANE_DIGESTS, &var_tls_dane_digests, 1, 0,
VAR_TLS_DANE_TA_DGST, DEF_TLS_DANE_TA_DGST, &var_tls_dane_ta_dgst, 0, 0, VAR_TLS_DANE_TA_DGST, DEF_TLS_DANE_TA_DGST, &var_tls_dane_ta_dgst, 0, 0,
VAR_TLS_MGR_SERVICE, DEF_TLS_MGR_SERVICE, &var_tls_mgr_service, 1, 0, VAR_TLS_MGR_SERVICE, DEF_TLS_MGR_SERVICE, &var_tls_mgr_service, 1, 0,
0, 0,

View File

@ -1,8 +1,6 @@
/* /*
* Proof-of-concept test program. Create, update or read a database. When * Proof-of-concept test program. Create, update or read a database. Type
* the input is a name=value pair, the database is updated, otherwise the * '?' for a list of commands.
* program assumes that the input specifies a lookup key and prints the
* corresponding value.
*/ */
/* System library. */ /* System library. */