cleanup/cleanup_map11.c, cleanup/cleanup_map1n.c,
cleanup/cleanup_masquerade.c, cleanup/cleanup_message.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.
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
member for the case-folded address.
limit is exceeded. Setting the limit to 1 ensures
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>
- This section describes an interface that is deprecated as
- of Postfix 2.2. It is replaced by the more general <b>query</b>
- interface described above. If the <b>query</b> parameter is
- defined, the legacy parameters described here ignored.
- Please migrate to the new interface as the legacy inter-
+ This section describes an interface that is deprecated as
+ of Postfix 2.2. It is replaced by the more general <b>query</b>
+ interface described above. If the <b>query</b> parameter is
+ defined, the legacy parameters described here ignored.
+ Please migrate to the new interface as the legacy inter-
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:
SELECT [<b>select_field</b>]
WHERE [<b>where_field</b>] = '%s'
[<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-
acters, it will not cause a parse error, or worse, a secu-
rity problem.
<a href="MYSQL_README.html">MYSQL_README</a>, Postfix MYSQL client guide
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>HISTORY</b>
pcre_table - format of Postfix PCRE tables
<b>SYNOPSIS</b>
- <b>postmap -q "<i></b>string</i><b>" <a href="pcre_table.5.html">pcre</a>:/etc/postfix/<i></b>filename\e[0m
+ <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> <<i>inputfile</i>
+ <b>postmap -q - <a href="pcre_table.5.html">pcre</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
<b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address
<b>TABLE FORMAT</b>
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-
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
the corresponding <i>result</i> value.
- <b>if /<i></b>pattern</i><b>/<i></b>flags\e[0m
+ <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
<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
This feature is available in Postfix 2.1 and later.
- <b>if !/<i></b>pattern</i><b>/<i></b>flags\e[0m
+ <b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
<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
a whitespace character as part of the pattern,
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)
Toggles the PCRE_ANCHORED flag. When this flag 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
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
mechanisms will not be used for just the "_tcp" subdomain of a host.
Suppressing the TLSA RRset lookup reduces latency and avoids potential
<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>
<DT><b><a name="tls_dane_trust_anchor_digest_enable">tls_dane_trust_anchor_digest_enable</a>
regexp_table - format of Postfix regular expression tables
<b>SYNOPSIS</b>
- <b>postmap -q "<i></b>string</i><b>" <a href="regexp_table.5.html">regexp</a>:/etc/postfix/<i></b>filename\e[0m
+ <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> <<i>inputfile</i>
+ <b>postmap -q - <a href="regexp_table.5.html">regexp</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
<b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address
<b>TABLE FORMAT</b>
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-
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
the corresponding <i>result</i> value.
- <b>if /<i></b>pattern</i><b>/<i></b>flags\e[0m
+ <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
<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
This feature is available in Postfix 2.1 and later.
- <b>if !/<i></b>pattern</i><b>/<i></b>flags\e[0m
+ <b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
<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>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>unix:<i></b>pathname backlog</i>
+ <b>smtp-sink</b> [<i>options</i>] <b>unix:</b><i>pathname backlog</i>
<b>DESCRIPTION</b>
<b>smtp-sink</b> listens on the named host (or address) and port.
<b>-a</b> Do not announce SASL authentication support.
- <b>-A <i></b>delay\e[0m
+ <b>-A</b> <i>delay</i>
Wait <i>delay</i> seconds after responding to DATA, then
abort prematurely with a 550 reply status. Do not
read further input from the client; this is an
attempt to block the client before it sends ".".
Specify a zero delay value to abort immediately.
- <b>-b <i></b>soft-bounce-reply\e[0m
+ <b>-b</b> <i>soft-bounce-reply</i>
Use <i>soft-bounce-reply</i> for soft reject responses.
The default reply is "450 4.3.0 Error: command
failed".
- <b>-B <i></b>hard-bounce-reply\e[0m
+ <b>-B</b> <i>hard-bounce-reply</i>
Use <i>hard-bounce-reply</i> for hard reject responses.
The default reply is "500 5.3.0 Error: command
failed".
<b>-C</b> Disable XCLIENT support.
- <b>-d <i></b>dump-template\e[0m
+ <b>-d</b> <i>dump-template</i>
Dump each mail transaction to a single-message file
whose name is created by expanding the <i>dump-tem-</i>
<i>plate</i> via strftime(3) and appending a pseudo-random
Note: this option keeps one capture file open for
every mail transaction in progress.
- <b>-D <i></b>dump-template\e[0m
+ <b>-D</b> <i>dump-template</i>
Append mail transactions to a multi-message dump
file whose name is created by expanding the <i>dump-</i>
<i>template</i> via strftime(3). If the template contains
<b>-E</b> Do not announce ENHANCEDSTATUSCODES support.
- <b>-f <i></b>command,command,...\e[0m
+ <b>-f</b> <i>command,command,...</i>
Reject the specified commands with a hard (5xx)
error code. This option implies <b>-p</b>.
<b>-F</b> Disable XFORWARD support.
- <b>-h <i></b>hostname\e[0m
+ <b>-h</b> <i>hostname</i>
Use <i>hostname</i> in the SMTP greeting, in the HELO
response, and in the EHLO response. The default
hostname is "smtp-sink".
<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-
ous connections that <b>smtp-sink</b> will handle. This
prevents the process from running out of file
descriptors. Excess connections will stay queued in
the TCP/IP stack.
- <b>-M <i></b>count\e[0m
+ <b>-M</b> <i>count</i>
Terminate after receiving <i>count</i> messages.
- <b>-n <i></b>count\e[0m
+ <b>-n</b> <i>count</i>
Terminate after <i>count</i> sessions.
<b>-p</b> Do not announce support for ESMTP command pipelin-
<b>-P</b> Change the server greeting so that it appears to
come through a CISCO PIX system. Implies <b>-e</b>.
- <b>-q <i></b>command,command,...\e[0m
+ <b>-q</b> <i>command,command,...</i>
Disconnect (without replying) after receiving one
of the specified commands.
and use quotes to protect white space from the
shell. Command names are case-insensitive.
- <b>-Q <i></b>command,command,...\e[0m
+ <b>-Q</b> <i>command,command,...</i>
Send a 421 reply and disconnect after receiving one
of the specified commands.
and use quotes to protect white space from the
shell. Command names are case-insensitive.
- <b>-r <i></b>command,command,...\e[0m
+ <b>-r</b> <i>command,command,...</i>
Reject the specified commands with a soft (4xx)
error code. This option implies <b>-p</b>.
and use quotes to protect white space from the
shell. Command names are case-insensitive.
- <b>-R <i></b>root-directory\e[0m
+ <b>-R</b> <i>root-directory</i>
Change the process root directory to the specified
location. This option requires super-user privi-
leges. See also the <b>-u</b> option.
- <b>-s <i></b>command,command,...\e[0m
+ <b>-s</b> <i>command,command,...</i>
Log the named commands to syslogd.
Examples of commands are CONNECT, HELO, EHLO, LHLO,
tab), \<i>ddd</i> (up to three octal digits) and \\ (the
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
response. The time limit is specified in seconds.
- <b>-T <i></b>windowsize\e[0m
+ <b>-T</b> <i>windowsize</i>
Override the default TCP window size. To work
around broken TCP window scaling implementations,
specify a value > 0 and < 65536.
- <b>-u <i></b>username\e[0m
+ <b>-u</b> <i>username</i>
Switch to the specified user privileges after open-
ing the network socket and optionally changing the
process root directory. This option is required
<b>-v</b> Show the SMTP conversations.
- <b>-w <i></b>delay\e[0m
+ <b>-w</b> <i>delay</i>
Wait <i>delay</i> seconds before responding to a DATA com-
mand.
- <b>-W <i></b>command:delay[:odds]\e[0m
+ <b>-W</b> <i>command:delay[:odds]</i>
Wait <i>delay</i> seconds before responding to <i>command</i>.
If <i>odds</i> is also specified (a number between 1-99
inclusive), wait for a random multiple of <i>delay</i>.
interface) TCP port <i>port</i>. Both <i>host</i> and <i>port</i> may be
specified in numeric or symbolic form.
- <b>unix:<i></b>pathname\e[0m
+ <b>unix:</b><i>pathname</i>
Listen on the UNIX-domain socket at <i>pathname</i>.
<i>backlog</i>
The format of the <b>smtp-sink</b> generated headers is as fol-
lows:
- <b>X-Client-Addr: <i></b>text\e[0m
+ <b>X-Client-Addr:</b> <i>text</i>
The client IP address without enclosing []. An IPv6
address is prefixed with "ipv6:". This record is
always present.
- <b>X-Client-Proto: <i></b>text\e[0m
+ <b>X-Client-Proto:</b> <i>text</i>
The client protocol: SMTP, ESMTP or LMTP. This
record is always present.
- <b>X-Helo-Args: <i></b>text\e[0m
+ <b>X-Helo-Args:</b> <i>text</i>
The arguments of the last HELO or EHLO command
before this mail delivery transaction. This record
is present only if the client sent a recognizable
HELO or EHLO command before the DATA command.
- <b>X-Mail-Args: <i></b>text\e[0m
+ <b>X-Mail-Args:</b> <i>text</i>
The arguments of the MAIL command that started this
mail delivery transaction. This record is present
exactly once.
- <b>X-Rcpt-Args: <i></b>text\e[0m
+ <b>X-Rcpt-Args:</b> <i>text</i>
The arguments of an RCPT command within this mail
delivery transaction. There is one record for each
RCPT command, and they are in the order as sent by
the client.
- <b>Received: <i></b>text\e[0m
+ <b>Received:</b> <i>text</i>
A message header for compatibility with mail pro-
cessing software. This three-line header marks the
end of the headers provided by <b>smtp-sink</b>, and is
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
IP address. If the client did not send HELO
or EHLO, the client IP address is used
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 client protocol (see <b>X-Client-Proto</b>
above), and the pseudo-random portion of the
temporary error if the limit is exceeded. Setting the
limit to 1 ensures that lookups do not return multiple
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"
.na
.nf
.ft C
/etc/postfix/main.cf:
postscreen_access_list = permit_mynetworks,
- cidr:/etc/postfix/postscreen_access.cidr
+ cidr:/etc/postfix/postscreen_access.cidr
postscreen_blacklist_action = enforce
.fi
.ad
.na
.ft C
/etc/postfix/dnsbl_reply:
- secret.zen.spamhaus.org zen.spamhaus.org
+ secret.zen.spamhaus.org zen.spamhaus.org
.fi
.ad
.ft R
.na
.ft C
/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=EC:3B:2D:B0:5B:B1:FB:6D:20:A3:9D:72:F6:8D:12:35
.fi
Lookup the associated DANE TLSA RRset even when a hostname is
not an alias and its address records lie in an unsigned zone. This
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
mechanisms will not be used for just the "_tcp" subdomain of a host.
Suppressing the TLSA RRset lookup reduces latency and avoids potential
[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
+ 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
(or 168bit) session key.
.PP
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)
RFC 6698 trust-anchor digest support in the Postfix TLS library.
Specify zero or more of the following options, separated by comma or
s;'$ESC'\[4m;<i>;g
s;'$ESC'\[24m;</i>;g
# Undo gratuitous whitespace changes.
- s;\( *\)\(</[bi]>\);\2\1;g
+ #s;\( *\)\(</[bi]>\);\2\1;g
# End nroff ANSI escape sequence workarounds.
s;</i>\( *\)<i>;\1;g
s;</b>\( *\)<b>;\1;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_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;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;
# temporary error if the limit is exceeded. Setting the
# limit to 1 ensures that lookups do not return multiple
# 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
# .ad
# .fi
<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
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
mechanisms will not be used for just the "_tcp" subdomain of a host.
Suppressing the TLSA RRset lookup reduces latency and avoids potential
are not prepared to handle the new TLSA RRset. </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>
/* releases.
/* .IP hosts
/* 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
/* For example, if you want the map to reference databases of
/* the name "your_db" and execute a query like this: select
CFG_PARSER *parser;
char *query;
char *result_format;
+ char *option_file;
+ char *option_group;
void *ctx;
int expansion_limit;
char *username;
PLMYSQL *pldb;
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
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
} DICT_MYSQL;
/* internal function declarations */
static PLMYSQL *plmysql_init(ARGV *);
-static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *, char *,
- char *, char *);
+static MYSQL_RES *plmysql_query(DICT_MYSQL *, const char *, VSTRING *);
static void plmysql_dealloc(PLMYSQL *);
static void plmysql_close_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 *);
DICT *dict_mysql_open(const char *, int, int);
static void dict_mysql_close(DICT *);
return (0);
/* do the query - set dict->error & cleanup if there's an error */
- if ((query_res = plmysql_query(dict_mysql, name, query,
- dict_mysql->dbname,
- dict_mysql->username,
- dict_mysql->password)) == 0) {
+ if ((query_res = plmysql_query(dict_mysql, name, query)) == 0) {
dict->error = DICT_ERR_RETRY;
return (0);
}
/* dict_mysql_get_active - get an active connection */
-static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname,
- char *username, char *password)
+static HOST *dict_mysql_get_active(DICT_MYSQL *dict_mysql)
{
const char *myname = "dict_mysql_get_active";
+ PLMYSQL *PLDB = dict_mysql->pldb;
HOST *host;
int count = RETRY_CONN_MAX;
if (msg_verbose)
msg_info("%s: attempting to connect to host %s", myname,
host->hostname);
- plmysql_connect_single(host, dbname, username, password);
+ plmysql_connect_single(dict_mysql, host);
if (host->stat == STATACTIVE)
return host;
}
static MYSQL_RES *plmysql_query(DICT_MYSQL *dict_mysql,
const char *name,
- VSTRING *query,
- char *dbname,
- char *username,
- char *password)
+ VSTRING *query)
{
- PLMYSQL *PLDB = dict_mysql->pldb;
HOST *host;
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
/*
* 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
*/
-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)
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,
(host->type == TYPEINET ? host->name : 0),
- username,
- password,
- dbname,
+ dict_mysql->username,
+ dict_mysql->password,
+ dict_mysql->dbname,
host->port,
(host->type == TYPEUNIX ? host->name : 0),
0)) {
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;
VSTRING *buf;
char *hosts;
dict_mysql->password = cfg_get_str(p, "password", "", 0, 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->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
myfree(dict_mysql->dbname);
myfree(dict_mysql->query);
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)
argv_free(dict_mysql->hosts);
if (dict_mysql->ctx)
#define DEF_TLS_BC_PKEY_FPRINT 0
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
* when the corresponding certificate is missing from the peer chain (this
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20131114"
+#define MAIL_RELEASE_DATE "20131117"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT
{
#ifdef USE_TLS
- argc -= optind;
- argv += optind;
-
switch (state->level) {
- case TLS_LEV_SECURE:
+ case TLS_LEV_SECURE:
state->match = argv_alloc(2);
while (*argv)
argv_split_append(state->match, *argv++, "");
mail_conf_suck();
parse_options(&state, argc, argv);
mail_params_init();
-
- parse_match(&state, argc, argv);
parse_tas(&state);
+ argc -= optind;
+ argv += optind;
+
/* The first non-option argument is the destination. */
- if (argc < optind)
+ if (!argc)
usage();
- state.dest = mystrdup(argv[optind]);
+
+ state.dest = mystrdup(argv[0]);
+ parse_match(&state, --argc, ++argv);
/* Don't talk to remote systems as root */
if (!geteuid())
#endif
static const EVP_MD *signmd;
+static const char *signalg;
static EVP_PKEY *danekey;
static ASN1_OBJECT *serverAuth;
-static const char *sha256 = "sha256";
-static const EVP_MD *sha256md;
-static int sha256len;
-
-static const char *sha512 = "sha512";
-static const EVP_MD *sha512md;
-static int sha512len;
+/*
+ * https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml
+ */
+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;
+
+#define MAXDIGESTS 256 /* RFC limit */
+digest_info digest_table[] = {
+ {"full", 0, 0, 0, DNS_TLSA_MATCHING_TYPE_NO_HASH_USED},
+ {"sha256", 0, 0, -1, DNS_TLSA_MATCHING_TYPE_SHA256},
+ {"sha512", 0, 0, -1, DNS_TLSA_MATCHING_TYPE_SHA512},
+ {0, 0, 0, 0, 0}
+};
static int digest_mask;
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 */
static EVP_PKEY *gencakey(void)
EVP_PKEY *key = 0;
#ifdef WRAP_SIGNED
- int len;
- unsigned char *p;
EC_KEY *eckey;
EC_GROUP *group;
TLS_DANE_TAA, TLS_DANE_ENABLE_TAA,
0,
};
+ int digest_pref = 0;
+ char *cp;
+ char *save;
+ char *tok;
+ digest_info *di;
digest_mask =
name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst,
NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
- if ((sha256md = EVP_get_digestbyname(sha256)) != 0)
- sha256len = EVP_MD_size(sha256md);
- if ((sha512md = EVP_get_digestbyname(sha512)) != 0)
- sha512len = EVP_MD_size(sha512md);
- signmd = sha256md ? sha256md : EVP_sha1();
+ save = cp = mystrdup(var_tls_dane_digests);
+ while ((tok = mystrtok(&cp, "\t\n\r ,")) != 0) {
+ for (di = digest_table; di->alg; ++di)
+ if (strcasecmp(tok, di->alg) == 0)
+ 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 */
ERR_clear_error();
dane_init();
#ifdef DANE_TLSA_SUPPORT
- return (sha256md && sha512md && serverAuth);
+ return (signalg && serverAuth);
#else
return (0);
#endif
static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b)
{
- if (a->data_len == b->data_len)
- return (memcmp(a->data, b->data, a->data_len));
- return ((a->data_len > b->data_len) ? 1 : -1);
+ int cmp;
+
+ /*
+ * 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 */
uint8_t mtype;
int mlen;
const unsigned char *p;
+ uint32_t prev_usage_selector; /* Previous (usage<<8|selector) */
+ uint32_t prev_mtype; /* Previous valid mtype for above */
+
+#define NO_PREV 0xffffffff /* Not any usage|selector or
+ * mtype */
+ prev_usage_selector = NO_PREV;
if (rr == 0)
msg_panic("null TLSA rr");
for ( /* nop */ ; rr; rr = rr->next) {
const char *mdalg = 0;
- int mdlen;
char *digest;
int same = (strcasecmp(rr->rname, rr->qname) == 0);
uint8_t *ip = (uint8_t *) rr->data;
X509 *x = 0; /* OpenSSL tries to re-use *x if x!=0 */
EVP_PKEY *k = 0; /* OpenSSL tries to re-use *k if k!=0 */
+ digest_info *di;
#define rcname(rr) (same ? "" : rr->qname)
#define rarrow(rr) (same ? "" : " -> ")
break;
}
- switch (mtype = *ip++) {
- default:
- 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);
+ /*
+ * Skip all but the most preferred matching type for any given
+ * (usage, selector) combination.
+ */
+ mtype = *ip++;
+ if (prev_usage_selector != (usage << 8 | selector))
+ prev_mtype = NO_PREV;
+ else if (prev_mtype != NO_PREV && prev_mtype != mtype)
continue;
- case DNS_TLSA_MATCHING_TYPE_SHA256:
- if (!mdalg) {
- mdalg = sha256;
- mdlen = sha256len;
+
+ switch (mtype) {
+ default:
+ if ((di = dane_digest_info(mtype)) == 0) {
+ msg_warn("unsupported matching type %u in RR: "
+ "%s%s%s IN TLSA %u %u %u ...", mtype, rcname(rr),
+ rarrow(rr), rr->rname, usage, selector, mtype);
+ continue;
}
- /* FALLTHROUGH */
- case DNS_TLSA_MATCHING_TYPE_SHA512:
- if (!mdalg) {
- mdalg = sha512;
- mdlen = sha512len;
+ if (di->pref < 0) {
+ msg_warn("digest algorithm %s locally disabled, in RR: "
+ "%s%s%s IN TLSA %u %u %u ...", di->alg,
+ rcname(rr), rarrow(rr), rr->rname,
+ usage, selector, mtype);
+ continue;
}
- if (mlen != mdlen) {
+ if (mlen != di->len) {
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,
usage, selector, mtype);
continue;
if (!(digest_mask & TLS_DANE_ENABLE_CC)) {
msg_warn("%s trust-anchor %s digests disabled, in RR: "
"%s%s%s IN TLSA %u %u %u ...", TLS_DANE_CC,
- mdalg, rcname(rr), rarrow(rr), rr->rname,
+ di->alg, rcname(rr), rarrow(rr), rr->rname,
usage, selector, mtype);
continue;
}
if (!(digest_mask & TLS_DANE_ENABLE_TAA)) {
msg_warn("%s trust-anchor %s digests disabled, in RR: "
"%s%s%s IN TLSA %u %u %u ...", TLS_DANE_TAA,
- mdalg, rcname(rr), rarrow(rr), rr->rname,
+ di->alg, rcname(rr), rarrow(rr), rr->rname,
usage, selector, mtype);
continue;
}
break;
}
- dane_add(dane, usage, selector, mdalg,
- digest = tls_digest_encode((unsigned char *) ip, mdlen));
+ digest = tls_digest_encode((unsigned char *) ip, di->len);
+ dane_add(dane, usage, selector, di->alg, digest);
break;
case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
}
X509_free(x);
break;
+
case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
if (!d2i_PUBKEY(&k, &p, mlen)) {
msg_warn("malformed %s in RR: "
/*
* 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, sha256));
+ digest = tls_data_fprint((char *) ip, mlen, signalg);
+ dane_add(dane, usage, selector, mdalg = signalg, digest);
break;
}
+
+ /*
+ * Save state
+ */
+ prev_usage_selector = (usage << 8 | selector);
+ prev_mtype = mtype;
+
if (msg_verbose || dane_verbose) {
switch (mtype) {
default:
dane->expires = 1 + event_time() + rrs->ttl;
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);
parse_tlsa_rrs(dane, rrs);
} else
msg_warn("trust-anchor files not supported");
return (0);
}
- mdalg = sha256md ? sha256 : "sha1";
+ mdalg = signalg ? signalg : "sha1";
/*
* On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio,
char **dgst;
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) {
char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg);
for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst)
if (strcasecmp(pkey_dgst, *dgst) == 0)
- matched = 1;
+ matched = MATCHED_PKEY;
if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
&& matched)
msg_info("%s: depth=%d matched %s public-key %s digest=%s",
for (dgst = certs->argv; !matched && *dgst; ++dgst)
if (strcasecmp(cert_dgst, *dgst) == 0)
- matched = 1;
+ matched = MATCHED_CERT;
if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)
&& matched)
msg_info("%s: depth=%d matched %s certificate %s digest %s",
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) {
int i;
general_name_stack_t *gens = akid->issuer;
GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
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)));
}
/* wrap_key - wrap TA "key" as issuer of "subject" */
-static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject,
- int depth)
+static int wrap_key(TLS_SESS_STATE *TLScontext, int depth,
+ EVP_PKEY *key, X509 *subject)
{
int ret = 1;
+ int selfsigned = 0;
X509 *cert = 0;
AUTHORITY_KEYID *akid;
X509_NAME *name = X509_get_issuer_name(subject);
+ X509_NAME *akid_name;
/*
* Record the depth of the intermediate wrapper certificate, logged in
- * the verify callback, unlike the parent root CA.
+ * the verify callback.
*/
- if (!key)
- TLScontext->tadepth = depth;
- else if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
- msg_info("%s: depth=%d chain is trust-anchor signed",
- TLScontext->namaddr, depth);
+ if (TLScontext->tadepth < 0) {
+ TLScontext->tadepth = depth + 1;
+ if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))
+ msg_info("%s: depth=%d chain is trust-anchor signed",
+ TLScontext->namaddr, depth);
+ }
/*
* If key is NULL generate a self-signed root CA, with key "danekey",
return (0);
akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0);
+ if ((akid_name = akid_issuer_name(akid)) == 0
+ || X509_NAME_cmp(name, akid_name) == 0)
+ selfsigned = 1;
ERR_clear_error();
|| !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L)
|| !X509_set_pubkey(cert, key ? key : danekey)
|| !add_ext(0, cert, NID_basic_constraints, "CA:TRUE")
- || (key && !add_akid(cert, akid))
+ || (key && !selfsigned && !add_akid(cert, akid))
|| !add_skid(cert, akid)
|| (wrap_signed
&& (!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");
tls_print_errors();
ret = 0;
if (akid)
AUTHORITY_KEYID_free(akid);
if (ret) {
- if (key && wrap_signed)
+ if (key && !selfsigned && wrap_signed)
grow_chain(&TLScontext->untrusted, cert, 0);
else
grow_chain(&TLScontext->trusted, cert, serverAuth);
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 */
static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth)
continue;
/* Check signature, since some other TA may work if not this. */
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);
}
}
*/
for (k = dane->pkeys; !done && k; k = k->next)
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);
}
{
int n;
int i;
+ int match;
int depth = 0;
EVP_PKEY *takey;
X509 *ca;
ca = sk_X509_delete(in, i);
/* Is it a trust anchor? */
- if (tls_dane_match(TLScontext, TLS_DANE_TA, ca, depth + 1)) {
- if ((takey = X509_get_pubkey(ca)) != 0
- && wrap_key(TLScontext, takey, cert, depth))
+ match = tls_dane_match(TLScontext, TLS_DANE_TA, ca, depth + 1);
+ if (match) {
+ 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);
+ break;
+ default:
+ msg_panic("unexpected tls_dane_match result: %d", match);
+ }
cert = 0;
break;
}
/* char *var_tls_eecdh_strong;
/* char *var_tls_eecdh_ultra;
/* char *var_tls_dane_ta_dgst;
+/* char *var_tls_dane_digests;
/* int var_tls_daemon_rand_bytes;
/* bool var_tls_append_def_CA;
/* bool var_tls_preempt_clist;
int var_tls_daemon_rand_bytes;
char *var_tls_eecdh_strong;
char *var_tls_eecdh_ultra;
+char *var_tls_dane_digests;
char *var_tls_dane_ta_dgst;
bool var_tls_append_def_CA;
char *var_tls_bug_tweaks;
bool var_tls_bc_pkey_fprint;
bool var_tls_multi_wildcard;
char *var_tls_mgr_service;
+char *tls_dane_digests;
#ifdef VAR_TLS_PREEMPT_CLIST
bool var_tls_preempt_clist;
VAR_TLS_EECDH_ULTRA, DEF_TLS_EECDH_ULTRA, &var_tls_eecdh_ultra, 1, 0,
VAR_TLS_BUG_TWEAKS, DEF_TLS_BUG_TWEAKS, &var_tls_bug_tweaks, 0, 0,
VAR_TLS_SSL_OPTIONS, DEF_TLS_SSL_OPTIONS, &var_tls_ssl_options, 0, 0,
+ VAR_TLS_DANE_DIGESTS, DEF_TLS_DANE_DIGESTS, &var_tls_dane_digests, 1, 0,
VAR_TLS_DANE_TA_DGST, DEF_TLS_DANE_TA_DGST, &var_tls_dane_ta_dgst, 0, 0,
VAR_TLS_MGR_SERVICE, DEF_TLS_MGR_SERVICE, &var_tls_mgr_service, 1, 0,
0,
/*
- * Proof-of-concept test program. Create, update or read a database. When
- * the input is a name=value pair, the database is updated, otherwise the
- * program assumes that the input specifies a lookup key and prints the
- * corresponding value.
+ * Proof-of-concept test program. Create, update or read a database. Type
+ * '?' for a list of commands.
*/
/* System library. */