util/dict.c, util/dict.h, util/dict_cidr.c, util/dict_file.c,
util/dict_inline.c, util/dict_pcre.c, util/dict_random.c,
util/dict_regexp.c, util/dict_static.c.
+
+20181106
+
+ Bugfix (introduced: 3.0): smtpd_discard_ehlo_keywords could
+ not disable "SMTPUTF8". because the lookup table was using
+ "EHLO_MASK_SMTPUTF8" instead. File: global/ehlo_mask.c.
+
+ Documentation: the postmap(1) manpage no longer refers to
+ compatibility with Sendmail's makemap command. File:
+ postmap/postmap.c.
+
+ Cleanup: don't use ssize_t for boolean result. File:
+ global/smtp_stream.c. Memory leak: the Berkeley DB client
+ leaked a small amount of memory asfter failing to open a
+ table. File: util/dict_db.c.
+
+ Cleanup: memory leak caused by missing dbenv->close() call
+ after failing to open a Berkeley DB table. File: util/dict_db.c.
+
+20181112
+
+ Improved logging of TLS 1.3 summary information, and improved
+ reporting of the same info in Received: message headers.
+ Viktor Dukhovni. Files: proto/FORWARD_SECRECY_README.html,
+ smtpd/smtpd.c, tls/tls.h, tls/tls_client.c, tls/tls_misc.c,
+ tls/tls_proxy.h, tls/tls_proxy_context_print.c,
+ tls/tls_proxy_context_scan.c, tls/tls_server.c.
* With "smtp_tls_loglevel = 1" and "smtpd_tls_loglevel = 1", the Postfix SMTP
client and server will log TLS connection information to the maillog file.
- The general logfile format is:
+ The general logfile format is shown below. With TLS 1.3 there may be
+ additional properties logged after the cipher name and bits.
postfix/smtp[process-id]: Untrusted TLS connection established
to host.example.com[192.168.0.2]:25: TLSv1 with cipher cipher-name
* With "smtpd_tls_received_header = yes", the Postfix SMTP server will record
TLS connection information in the Received: header in the form of comments
(text inside parentheses). The general format depends on the
- smtpd_tls_ask_ccert setting:
+ smtpd_tls_ask_ccert setting. With TLS 1.3 there may be additional
+ properties logged after the cipher name and bits.
Received: from host.example.com (host.example.com [192.168.0.2])
(using TLSv1 with cipher cipher-name
(actual-key-size/raw-key-size bits))
(No client certificate requested)
+ TLS 1.3 examples. Some of the new attributes may not appear when not
+ applicable or not available in an older versions of the OpenSSL library.
+
+ Received: from localhost (localhost [127.0.0.1])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256
+ bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits)
+ server-digest SHA256)
+ (No client certificate requested)
+
+ Received: from localhost (localhost [127.0.0.1])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256
+ bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits)
+ server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256)
+ (Client CN "example.org", Issuer "example.org" (not verified))
+
+ o The "key-exchange" attribute records the type of "Diffie-Hellman" group
+ used for key agreement. Possible values include "DHE", "ECDHE",
+ "X25519" and "X448". With "DHE", the bit size of the prime will be
+ reported in parentheses after the algorithm name, with "ECDHE", the
+ curve name.
+
+ o The "server-signature" attribute shows the public key signature
+ algorithm used by the server. With "RSA-PSS", the bit size of the
+ modulus will be reported in parentheses. With "ECDSA", the curve name.
+ If, for example, the server has both an RSA and an ECDSA private key
+ and certificate, it will be possible to track which one was used for a
+ given connection.
+
+ o The new "server-digest" attribute records the digest algorithm used by
+ the server to prepare handshake messages for signing. The Ed25519 and
+ Ed448 signature algorithms do not make use of such a digest, so no
+ "server-digest" will be shown for these signature algorithms.
+
+ o When a client certificate is requested with "smtpd_tls_ask_ccert" and
+ the client uses a TLS client-certificate, the "client-signature" and
+ "client-digest" attributes will record the corresponding properties of
+ the client's TLS handshake signature.
+
The next sections will explain what cipher-name, key-size, and peer
verification status information to expect.
non-export ciphers, but may they differ for the legacy export ciphers where the
actual key is artificially shortened.
+Starting with TLS 1.3 the cipher name no longer contains enough information to
+determine which forward-secrecy scheme was employed, but TLS 1.3 a\bal\blw\bwa\bay\bys\bs uses
+forward-secrecy. On the client side, up-to-date Postfix releases log additional
+information for TLS 1.3 connections, reporting the signature and key exchange
+algorithms. Two examples below (the long single line messages are folded across
+multiple lines for readability):
+
+ postfix/smtp[process-id]:
+ Untrusted TLS connection established to 127.0.0.1[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest
+ SHA256
+ client-signature ECDSA (P-256) client-digest SHA256
+
+ postfix/smtp[process-id]:
+ Untrusted TLS connection established to 127.0.0.1[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange ECDHE (P-256) server-signature ECDSA (P-256) server-digest
+ SHA256
+
+In the above connections, the "key-exchange" value records the "Diffie-Hellman"
+algorithm used for key agreement. The "server-signature" value records the
+public key algoritm used by the server to sign the key exchange. The "server-
+digest" value records any hash algorithm used to prepare the data for signing.
+With "ED25519" and "ED448", no separate hash algorithm is used.
+
+Examples of Postfix SMTP server logging:
+
+ postfix/smtpd[process-id]:
+ Untrusted TLS connection established from localhost[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest
+ SHA256
+ client-signature ECDSA (P-256) client-digest SHA256
+
+ postfix/smtpd[process-id]:
+ Anonymous TLS connection established from localhost[127.0.0.1]:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ server-signature RSA-PSS (2048 bits) server-digest SHA256
+
+ postfix/smtpd[process-id]:
+ Anonymous TLS connection established from localhost[127.0.0.1]:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ server-signature ED25519
+
W\bWh\bha\bat\bt d\bdo\bo "\b"A\bAn\bno\bon\bny\bym\bmo\bou\bus\bs"\b",\b, "\b"U\bUn\bnt\btr\bru\bus\bst\bte\bed\bd"\b",\b, e\bet\btc\bc.\b. i\bin\bn P\bPo\bos\bst\btf\bfi\bix\bx l\blo\bog\bgg\bgi\bin\bng\bg m\bme\bea\ban\bn?\b?
The verification levels below are subject to man-in-the-middle attacks to
Wish list:
- In dict_db.h, call dbenv->close() before bailing out
- with a surrogate map.
+ With DICT_FLAG_RHS_IS_FILE, should dict_update() open a
+ file? base64-encode the value?
In smtpd(8) and postscreen(8), set the ehlo_discard_mask
- to ~0 so that STARTTLS, BDAT, DSN, etc. won't work.
+ to ~0 so that STARTTLS, BDAT, DSN, etc. work only for clients
+ that send EHLO.
In postscreen, don't fork after 'postfix reload' when
psc_check_queue_length (and psc_post_queue_length?) is zero.
- In smtp_stream.c, replace `ssize_t err' with `int err'.
-
- In ehlo_mask.c, replace "EHLO_MASK_SMTPUTF8" with "SMTPUTF8".
-
With smtpd_reject_footer=$foo in master.cf, and foo defined
in main.cf, postconf complains about an unused setting in
main.cf. Note that "postconf -Px" will expand $name in
<li> <p> With "<a href="postconf.5.html#smtp_tls_loglevel">smtp_tls_loglevel</a> = 1" and "<a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> = 1",
the Postfix SMTP client and server will log TLS connection information
-to the maillog file. The general logfile format is: </p>
+to the maillog file. The general logfile format is shown below.
+With TLS 1.3 there may be additional properties logged after the
+cipher name and bits. </p>
<blockquote>
<pre>
<li> <p> With "<a href="postconf.5.html#smtpd_tls_received_header">smtpd_tls_received_header</a> = yes", the Postfix SMTP
server will record TLS connection information in the Received:
header in the form of comments (text inside parentheses). The general
-format depends on the <a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> setting:
+format depends on the <a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> setting. With TLS 1.3 there
+may be additional properties logged after the cipher name and bits. </p>
<blockquote>
<pre>
</pre>
</blockquote>
+<p> TLS 1.3 examples. Some of the new attributes may not appear when not
+applicable or not available in an older versions of the OpenSSL library. </p>
+
+<blockquote>
+<pre>
+Received: from localhost (localhost [127.0.0.1])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
+ (No client certificate requested)
+
+Received: from localhost (localhost [127.0.0.1])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256)
+ (Client CN "example.org", Issuer "example.org" (not verified))
+</pre>
+</blockquote>
+
+<ul>
+<li> <p> The "key-exchange" attribute records the type of "Diffie-Hellman"
+group used for key agreement. Possible values include "DHE", "ECDHE", "X25519"
+and "X448". With "DHE", the bit size of the prime will be reported in
+parentheses after the algorithm name, with "ECDHE", the curve name. </p>
+
+<li> <p> The "server-signature" attribute shows the public key signature
+algorithm used by the server. With "RSA-PSS", the bit size of the modulus will
+be reported in parentheses. With "ECDSA", the curve name. If, for example,
+the server has both an RSA and an ECDSA private key and certificate, it will be
+possible to track which one was used for a given connection. </p>
+
+<li> <p> The new "server-digest" attribute records the digest algorithm used by
+the server to prepare handshake messages for signing. The Ed25519 and Ed448
+signature algorithms do not make use of such a digest, so no "server-digest"
+will be shown for these signature algorithms. </p>
+
+<li> <p> When a client certificate is requested with "<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a>" and
+the client uses a TLS client-certificate, the "client-signature" and
+"client-digest" attributes will record the corresponding properties of the
+client's TLS handshake signature. </p> </ul>
+
</ul>
<p> The next sections will explain what <i>cipher-name</i>,
differ for the legacy export ciphers where the actual key
is artificially shortened. </p>
+<p> Starting with TLS 1.3 the cipher name no longer contains enough
+information to determine which forward-secrecy scheme was employed,
+but TLS 1.3 <b>always</b> uses forward-secrecy. On the client side,
+up-to-date Postfix releases log additional information for TLS 1.3
+connections, reporting the signature and key exchange algorithms.
+Two examples below (the long single line messages are folded across
+multiple lines for readability): </p>
+
+<blockquote>
+<pre>
+postfix/smtp[<i>process-id</i>]:
+ Untrusted TLS connection established to 127.0.0.1[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256
+
+postfix/smtp[<i>process-id</i>]:
+ Untrusted TLS connection established to 127.0.0.1[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange ECDHE (P-256) server-signature ECDSA (P-256) server-digest SHA256
+</pre>
+</blockquote>
+
+<p> In the above connections, the "key-exchange" value records the
+"Diffie-Hellman" algorithm used for key agreement. The "server-signature" value
+records the public key algoritm used by the server to sign the key exchange.
+The "server-digest" value records any hash algorithm used to prepare the data
+for signing. With "ED25519" and "ED448", no separate hash algorithm is used.
+</p>
+
+<p> Examples of Postfix SMTP server logging: </p>
+
+<blockquote>
+<pre>
+postfix/smtpd[<i>process-id</i>]:
+ Untrusted TLS connection established from localhost[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256
+
+postfix/smtpd[<i>process-id</i>]:
+ Anonymous TLS connection established from localhost[127.0.0.1]:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ server-signature RSA-PSS (2048 bits) server-digest SHA256
+
+postfix/smtpd[<i>process-id</i>]:
+ Anonymous TLS connection established from localhost[127.0.0.1]:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ server-signature ED25519
+</pre>
+</blockquote>
+
<h2><a name="status"> What do "Anonymous", "Untrusted", etc. in
Postfix logging mean? </a> </h2>
<b>DESCRIPTION</b>
The <a href="postmap.1.html"><b>postmap</b>(1)</a> command creates or queries one or more Postfix lookup
- tables, or updates an existing one. The input and output file formats
- are expected to be compatible with:
+ tables, or updates an existing one.
- <b>makemap</b> <i>file</i><b>_</b><i>type file</i><b>_</b><i>name</i> < <i>file</i><b>_</b><i>name</i>
-
- If the result files do not exist they will be created with the same
+ If the result files do not exist they will be created with the same
group and other read permissions as their source file.
- While the table update is in progress, signal delivery is postponed,
- and an exclusive, advisory, lock is placed on the entire table, in
+ While the table update is in progress, signal delivery is postponed,
+ and an exclusive, advisory, lock is placed on the entire table, in
order to avoid surprises in spectator processes.
<b>INPUT FILE FORMAT</b>
<i>key</i> whitespace <i>value</i>
- <b>o</b> Empty lines and whitespace-only lines are ignored, as are lines
+ <b>o</b> Empty lines and whitespace-only lines are ignored, as are lines
whose first non-whitespace character is a `#'.
- <b>o</b> A logical line starts with non-whitespace text. A line that
+ <b>o</b> A logical line starts with non-whitespace text. A line that
starts with whitespace continues a logical line.
- The <i>key</i> and <i>value</i> are processed as is, except that surrounding white
- space is stripped off. Whitespace in lookup keys is supported as of
+ The <i>key</i> and <i>value</i> are processed as is, except that surrounding white
+ space is stripped off. Whitespace in lookup keys is supported as of
Postfix 3.2.
- When the <b>-F</b> option is given, the <i>value</i> must specify a filename;
- <a href="postmap.1.html"><b>postmap</b>(1)</a> will store the base64-encoded content of that file instead
+ When the <b>-F</b> option is given, the <i>value</i> must specify a filename;
+ <a href="postmap.1.html"><b>postmap</b>(1)</a> will store the base64-encoded content of that file instead
of the <i>value</i> itself.
- When the <i>key</i> specifies email address information, the localpart should
+ When the <i>key</i> specifies email address information, the localpart should
be enclosed with double quotes if required by <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>. For example, an
address localpart that contains ";", or a localpart that starts or ends
with ".".
- By default the lookup key is mapped to lowercase to make the lookups
+ By default the lookup key is mapped to lowercase to make the lookups
case insensitive; as of Postfix 2.3 this case folding happens only with
tables whose lookup keys are fixed-case strings such as <a href="DATABASE_README.html#types">btree</a>:, <a href="DATABASE_README.html#types">dbm</a>: or
<a href="DATABASE_README.html#types">hash</a>:. With earlier versions, the lookup key is folded even with tables
- where a lookup field can match both upper and lower case text, such as
- <a href="regexp_table.5.html">regexp</a>: and <a href="pcre_table.5.html">pcre</a>:. This resulted in loss of information with $<i>number</i>
+ where a lookup field can match both upper and lower case text, such as
+ <a href="regexp_table.5.html">regexp</a>: and <a href="pcre_table.5.html">pcre</a>:. This resulted in loss of information with $<i>number</i>
substitutions.
<b>COMMAND-LINE ARGUMENTS</b>
- <b>-b</b> Enable message body query mode. When reading lookup keys from
- standard input with "<b>-q -</b>", process the input as if it is an
- email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each line of body content
+ <b>-b</b> Enable message body query mode. When reading lookup keys from
+ standard input with "<b>-q -</b>", process the input as if it is an
+ email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each line of body content
becomes one lookup key.
- By default, the <b>-b</b> option starts generating lookup keys at the
- first non-header line, and stops when the end of the message is
- reached. To simulate <a href="header_checks.5.html"><b>body_checks</b>(5)</a> processing, enable MIME
- parsing with <b>-m</b>. With this, the <b>-b</b> option generates no
- body-style lookup keys for attachment MIME headers and for
+ By default, the <b>-b</b> option starts generating lookup keys at the
+ first non-header line, and stops when the end of the message is
+ reached. To simulate <a href="header_checks.5.html"><b>body_checks</b>(5)</a> processing, enable MIME
+ parsing with <b>-m</b>. With this, the <b>-b</b> option generates no
+ body-style lookup keys for attachment MIME headers and for
attached message/* headers.
- NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
- ables UTF-8 syntax checks on query keys and lookup results.
+ NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
+ ables UTF-8 syntax checks on query keys and lookup results.
Specify the <b>-U</b> option to force UTF-8 syntax checks anyway.
This feature is available in Postfix version 2.6 and later.
<b>-c</b> <i>config</i><b>_</b><i>dir</i>
- Read the <a href="postconf.5.html"><b>main.cf</b></a> configuration file in the named directory
+ Read the <a href="postconf.5.html"><b>main.cf</b></a> configuration file in the named directory
instead of the default configuration directory.
- <b>-d</b> <i>key</i> Search the specified maps for <i>key</i> and remove one entry per map.
- The exit status is zero when the requested information was
+ <b>-d</b> <i>key</i> Search the specified maps for <i>key</i> and remove one entry per map.
+ The exit status is zero when the requested information was
found.
- If a key value of <b>-</b> is specified, the program reads key values
- from the standard input stream. The exit status is zero when at
+ If a key value of <b>-</b> is specified, the program reads key values
+ from the standard input stream. The exit status is zero when at
least one of the requested keys was found.
- <b>-f</b> Do not fold the lookup key to lower case while creating or
+ <b>-f</b> Do not fold the lookup key to lower case while creating or
querying a table.
- With Postfix version 2.3 and later, this option has no effect
+ With Postfix version 2.3 and later, this option has no effect
for regular expression tables. There, case folding is controlled
by appending a flag to a pattern.
- <b>-F</b> When creating a map from source file, replace each value with
- the base64-encoded content of the named file. When querying a
+ <b>-F</b> When creating a map from source file, replace each value with
+ the base64-encoded content of the named file. When querying a
map, or listing a map, base64-decode each value.
- <b>-h</b> Enable message header query mode. When reading lookup keys from
- standard input with "<b>-q -</b>", process the input as if it is an
- email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each logical header line
- becomes one lookup key. A multi-line header becomes one lookup
+ <b>-h</b> Enable message header query mode. When reading lookup keys from
+ standard input with "<b>-q -</b>", process the input as if it is an
+ email message in <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a> format. Each logical header line
+ becomes one lookup key. A multi-line header becomes one lookup
key with one or more embedded newline characters.
- By default, the <b>-h</b> option generates lookup keys until the first
- non-header line is reached. To simulate <a href="header_checks.5.html"><b>header_checks</b>(5)</a> pro-
- cessing, enable MIME parsing with <b>-m</b>. With this, the <b>-h</b> option
- also generates header-style lookup keys for attachment MIME
+ By default, the <b>-h</b> option generates lookup keys until the first
+ non-header line is reached. To simulate <a href="header_checks.5.html"><b>header_checks</b>(5)</a> pro-
+ cessing, enable MIME parsing with <b>-m</b>. With this, the <b>-h</b> option
+ also generates header-style lookup keys for attachment MIME
headers and for attached message/* headers.
- NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
- ables UTF-8 syntax checks on query keys and lookup results.
+ NOTE: with "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", the <b>-b</b> option option dis-
+ ables UTF-8 syntax checks on query keys and lookup results.
Specify the <b>-U</b> option to force UTF-8 syntax checks anyway.
This feature is available in Postfix version 2.6 and later.
- <b>-i</b> Incremental mode. Read entries from standard input and do not
- truncate an existing database. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> creates a
+ <b>-i</b> Incremental mode. Read entries from standard input and do not
+ truncate an existing database. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> creates a
new database from the entries in <b>file_name</b>.
<b>-m</b> Enable MIME parsing with "<b>-b</b>" and "<b>-h</b>".
This feature is available in Postfix version 2.6 and later.
- <b>-N</b> Include the terminating null character that terminates lookup
- keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is the
+ <b>-N</b> Include the terminating null character that terminates lookup
+ keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is the
default for the host operating system.
- <b>-n</b> Don't include the terminating null character that terminates
- lookup keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is
+ <b>-n</b> Don't include the terminating null character that terminates
+ lookup keys and values. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> does whatever is
the default for the host operating system.
- <b>-o</b> Do not release root privileges when processing a non-root input
- file. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> drops root privileges and runs as
+ <b>-o</b> Do not release root privileges when processing a non-root input
+ file. By default, <a href="postmap.1.html"><b>postmap</b>(1)</a> drops root privileges and runs as
the source file owner instead.
- <b>-p</b> Do not inherit the file access permissions from the input file
- when creating a new file. Instead, create a new file with
+ <b>-p</b> Do not inherit the file access permissions from the input file
+ when creating a new file. Instead, create a new file with
default access permissions (mode 0644).
- <b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
- found to the standard output stream. The exit status is zero
+ <b>-q</b> <i>key</i> Search the specified maps for <i>key</i> and write the first value
+ found to the standard output stream. The exit status is zero
when the requested information was found.
- Note: this performs a single query with the key as specified,
- and does not make iterative queries with substrings of the key
- as described for <a href="access.5.html">access(5)</a>, <a href="canonical.5.html">canonical(5)</a>, <a href="transport.5.html">transport(5)</a>, <a href="virtual.5.html">vir-</a>
+ Note: this performs a single query with the key as specified,
+ and does not make iterative queries with substrings of the key
+ as described for <a href="access.5.html">access(5)</a>, <a href="canonical.5.html">canonical(5)</a>, <a href="transport.5.html">transport(5)</a>, <a href="virtual.5.html">vir-</a>
<a href="virtual.5.html">tual(5)</a> and other Postfix table-driven features.
- If a key value of <b>-</b> is specified, the program reads key values
- from the standard input stream and writes one line of <i>key value</i>
+ If a key value of <b>-</b> is specified, the program reads key values
+ from the standard input stream and writes one line of <i>key value</i>
output for each key that was found. The exit status is zero when
at least one of the requested keys was found.
- <b>-r</b> When updating a table, do not complain about attempts to update
+ <b>-r</b> When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
- <b>-s</b> Retrieve all database elements, and write one line of <i>key value</i>
- output for each element. The elements are printed in database
- order, which is not necessarily the same as the original input
+ <b>-s</b> Retrieve all database elements, and write one line of <i>key value</i>
+ output for each element. The elements are printed in database
+ order, which is not necessarily the same as the original input
order.
- This feature is available in Postfix version 2.2 and later, and
+ This feature is available in Postfix version 2.2 and later, and
is not available for all database types.
- <b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
- "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
+ <b>-u</b> Disable UTF-8 support. UTF-8 support is enabled by default when
+ "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes". It requires that keys and values are
valid UTF-8 strings.
<b>-U</b> With "<a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> = yes", force UTF-8 syntax checks with the
<b>-b</b> and <b>-h</b> options.
- <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
+ <b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
options make the software increasingly verbose.
- <b>-w</b> When updating a table, do not complain about attempts to update
+ <b>-w</b> When updating a table, do not complain about attempts to update
existing entries, and ignore those attempts.
Arguments:
The <a href="postmap.1.html"><b>postmap</b>(1)</a> command can query any supported file type, but it
can create only the following file types:
- <b>btree</b> The output file is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
- This is available on systems with support for <b>db</b> data-
+ <b>btree</b> The output file is a btree file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
+ This is available on systems with support for <b>db</b> data-
bases.
- <b>cdb</b> The output consists of one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
- This is available on systems with support for <b>cdb</b> data-
+ <b>cdb</b> The output consists of one file, named <i>file</i><b>_</b><i>name</i><b>.cdb</b>.
+ This is available on systems with support for <b>cdb</b> data-
bases.
<b>dbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>dbm</b> databases.
- <b>hash</b> The output file is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
- This is available on systems with support for <b>db</b> data-
+ <b>hash</b> The output file is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
+ This is available on systems with support for <b>db</b> data-
bases.
- <b>fail</b> A table that reliably fails all requests. The lookup ta-
- ble name is used for logging only. This table exists to
+ <b>fail</b> A table that reliably fails all requests. The lookup ta-
+ ble name is used for logging only. This table exists to
simplify Postfix error tests.
<b>sdbm</b> The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
<i>file</i><b>_</b><i>name</i><b>.dir</b>. This is available on systems with support
for <b>sdbm</b> databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
- type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses the database
+ type specified via the <b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration
parameter.
<i>file</i><b>_</b><i>name</i>
<b>DIAGNOSTICS</b>
Problems are logged to the standard error stream and to <b>syslogd</b>(8). No
- output means that no problems were detected. Duplicate entries are
+ output means that no problems were detected. Duplicate entries are
skipped and are flagged with a warning.
<a href="postmap.1.html"><b>postmap</b>(1)</a> terminates with zero exit status in case of success (includ-
- ing successful "<b>postmap -q</b>" lookup) and terminates with non-zero exit
+ ing successful "<b>postmap -q</b>" lookup) and terminates with non-zero exit
status in case of failure.
<b>ENVIRONMENT</b>
Enable verbose logging for debugging purposes.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
- gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+ gram. The text below provides only a parameter summary. See <a href="postconf.5.html"><b>post-</b></a>
<a href="postconf.5.html"><b>conf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that create Berkeley
+ The per-table I/O buffer size for programs that create Berkeley
DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
hash or btree tables.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
<b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
- The list of environment parameters that a privileged Postfix
- process will import from a non-Postfix parent process, or
+ The list of environment parameters that a privileged Postfix
+ process will import from a non-Postfix parent process, or
name=value environment overrides.
<b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in <a href="http://tools.ietf.org/html/rfc6531">RFC 6531</a>..6533.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
<b>SEE ALSO</b>
.ad
.fi
The \fBpostmap\fR(1) command creates or queries one or more Postfix
-lookup tables, or updates an existing one. The input and output
-file formats are expected to be compatible with:
-
-.nf
- \fBmakemap \fIfile_type\fR \fIfile_name\fR < \fIfile_name\fR
-.fi
+lookup tables, or updates an existing one.
If the result files do not exist they will be created with the
same group and other read permissions as their source file.
<li> <p> With "smtp_tls_loglevel = 1" and "smtpd_tls_loglevel = 1",
the Postfix SMTP client and server will log TLS connection information
-to the maillog file. The general logfile format is: </p>
+to the maillog file. The general logfile format is shown below.
+With TLS 1.3 there may be additional properties logged after the
+cipher name and bits. </p>
<blockquote>
<pre>
<li> <p> With "smtpd_tls_received_header = yes", the Postfix SMTP
server will record TLS connection information in the Received:
header in the form of comments (text inside parentheses). The general
-format depends on the smtpd_tls_ask_ccert setting:
+format depends on the smtpd_tls_ask_ccert setting. With TLS 1.3 there
+may be additional properties logged after the cipher name and bits. </p>
<blockquote>
<pre>
</pre>
</blockquote>
+<p> TLS 1.3 examples. Some of the new attributes may not appear when not
+applicable or not available in an older versions of the OpenSSL library. </p>
+
+<blockquote>
+<pre>
+Received: from localhost (localhost [127.0.0.1])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
+ (No client certificate requested)
+
+Received: from localhost (localhost [127.0.0.1])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256)
+ (Client CN "example.org", Issuer "example.org" (not verified))
+</pre>
+</blockquote>
+
+<ul>
+<li> <p> The "key-exchange" attribute records the type of "Diffie-Hellman"
+group used for key agreement. Possible values include "DHE", "ECDHE", "X25519"
+and "X448". With "DHE", the bit size of the prime will be reported in
+parentheses after the algorithm name, with "ECDHE", the curve name. </p>
+
+<li> <p> The "server-signature" attribute shows the public key signature
+algorithm used by the server. With "RSA-PSS", the bit size of the modulus will
+be reported in parentheses. With "ECDSA", the curve name. If, for example,
+the server has both an RSA and an ECDSA private key and certificate, it will be
+possible to track which one was used for a given connection. </p>
+
+<li> <p> The new "server-digest" attribute records the digest algorithm used by
+the server to prepare handshake messages for signing. The Ed25519 and Ed448
+signature algorithms do not make use of such a digest, so no "server-digest"
+will be shown for these signature algorithms. </p>
+
+<li> <p> When a client certificate is requested with "smtpd_tls_ask_ccert" and
+the client uses a TLS client-certificate, the "client-signature" and
+"client-digest" attributes will record the corresponding properties of the
+client's TLS handshake signature. </p> </ul>
+
</ul>
<p> The next sections will explain what <i>cipher-name</i>,
differ for the legacy export ciphers where the actual key
is artificially shortened. </p>
+<p> Starting with TLS 1.3 the cipher name no longer contains enough
+information to determine which forward-secrecy scheme was employed,
+but TLS 1.3 <b>always</b> uses forward-secrecy. On the client side,
+up-to-date Postfix releases log additional information for TLS 1.3
+connections, reporting the signature and key exchange algorithms.
+Two examples below (the long single line messages are folded across
+multiple lines for readability): </p>
+
+<blockquote>
+<pre>
+postfix/smtp[<i>process-id</i>]:
+ Untrusted TLS connection established to 127.0.0.1[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256
+
+postfix/smtp[<i>process-id</i>]:
+ Untrusted TLS connection established to 127.0.0.1[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange ECDHE (P-256) server-signature ECDSA (P-256) server-digest SHA256
+</pre>
+</blockquote>
+
+<p> In the above connections, the "key-exchange" value records the
+"Diffie-Hellman" algorithm used for key agreement. The "server-signature" value
+records the public key algoritm used by the server to sign the key exchange.
+The "server-digest" value records any hash algorithm used to prepare the data
+for signing. With "ED25519" and "ED448", no separate hash algorithm is used.
+</p>
+
+<p> Examples of Postfix SMTP server logging: </p>
+
+<blockquote>
+<pre>
+postfix/smtpd[<i>process-id</i>]:
+ Untrusted TLS connection established from localhost[127.0.0.1]:25:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
+ client-signature ECDSA (P-256) client-digest SHA256
+
+postfix/smtpd[<i>process-id</i>]:
+ Anonymous TLS connection established from localhost[127.0.0.1]:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ server-signature RSA-PSS (2048 bits) server-digest SHA256
+
+postfix/smtpd[<i>process-id</i>]:
+ Anonymous TLS connection established from localhost[127.0.0.1]:
+ TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ server-signature ED25519
+</pre>
+</blockquote>
+
<h2><a name="status"> What do "Anonymous", "Untrusted", etc. in
Postfix logging mean? </a> </h2>
"ENHANCEDSTATUSCODES", EHLO_MASK_ENHANCEDSTATUSCODES,
"DSN", EHLO_MASK_DSN,
"EHLO_MASK_SMTPUTF8", EHLO_MASK_SMTPUTF8,
+ "SMTPUTF8", EHLO_MASK_SMTPUTF8,
"CHUNKING", EHLO_MASK_CHUNKING,
"SILENT-DISCARD", EHLO_MASK_SILENT, /* XXX In-band signaling */
0,
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20181105"
+#define MAIL_RELEASE_DATE "20181113"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
void smtp_fputs(const char *cp, ssize_t todo, VSTREAM *stream)
{
- ssize_t err;
+ int err;
if (todo < 0)
msg_panic("smtp_fputs: negative todo %ld", (long) todo);
void smtp_fwrite(const char *cp, ssize_t todo, VSTREAM *stream)
{
- ssize_t err;
+ int err;
if (todo < 0)
msg_panic("smtp_fwrite: negative todo %ld", (long) todo);
/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
/* The \fBpostmap\fR(1) command creates or queries one or more Postfix
-/* lookup tables, or updates an existing one. The input and output
-/* file formats are expected to be compatible with:
-/*
-/* .nf
-/* \fBmakemap \fIfile_type\fR \fIfile_name\fR < \fIfile_name\fR
-/* .fi
+/* lookup tables, or updates an existing one.
/*
/* If the result files do not exist they will be created with the
/* same group and other read permissions as their source file.
#ifdef USE_TLS
if (var_smtpd_tls_received_header && state->tls_context) {
- out_fprintf(out_stream, REC_TYPE_NORM,
- "\t(using %s with cipher %s (%d/%d bits))",
- state->tls_context->protocol,
- state->tls_context->cipher_name,
- state->tls_context->cipher_usebits,
- state->tls_context->cipher_algbits);
+ int cont = 0;
+
+ vstring_sprintf(state->buffer,
+ "\t(using %s with cipher %s (%d/%d bits)",
+ state->tls_context->protocol,
+ state->tls_context->cipher_name,
+ state->tls_context->cipher_usebits,
+ state->tls_context->cipher_algbits);
+ if (state->tls_context->kex_name && *state->tls_context->kex_name) {
+ out_record(out_stream, REC_TYPE_NORM, STR(state->buffer),
+ LEN(state->buffer));
+ vstring_sprintf(state->buffer, "\t key-exchange %s",
+ state->tls_context->kex_name);
+ if (state->tls_context->kex_curve
+ && *state->tls_context->kex_curve)
+ vstring_sprintf_append(state->buffer, " (%s)",
+ state->tls_context->kex_curve);
+ else if (state->tls_context->kex_bits > 0)
+ vstring_sprintf_append(state->buffer, " (%d bits)",
+ state->tls_context->kex_bits);
+ cont = 1;
+ }
+ if (state->tls_context->locl_sig_name
+ && *state->tls_context->locl_sig_name) {
+ if (cont) {
+ vstring_sprintf_append(state->buffer, " server-signature %s",
+ state->tls_context->locl_sig_name);
+ } else {
+ out_record(out_stream, REC_TYPE_NORM, STR(state->buffer),
+ LEN(state->buffer));
+ vstring_sprintf(state->buffer, "\t server-signature %s",
+ state->tls_context->locl_sig_name);
+ }
+ if (state->tls_context->locl_sig_curve
+ && *state->tls_context->locl_sig_curve)
+ vstring_sprintf_append(state->buffer, " (%s)",
+ state->tls_context->locl_sig_curve);
+ else if (state->tls_context->locl_sig_bits > 0)
+ vstring_sprintf_append(state->buffer, " (%d bits)",
+ state->tls_context->locl_sig_bits);
+ if (state->tls_context->locl_sig_dgst
+ && *state->tls_context->locl_sig_dgst)
+ vstring_sprintf_append(state->buffer, " server-digest %s",
+ state->tls_context->locl_sig_dgst);
+ }
+ if (state->tls_context->peer_sig_name
+ && *state->tls_context->peer_sig_name) {
+ out_record(out_stream, REC_TYPE_NORM, STR(state->buffer),
+ LEN(state->buffer));
+ vstring_sprintf(state->buffer, "\t client-signature %s",
+ state->tls_context->peer_sig_name);
+ if (state->tls_context->peer_sig_curve
+ && *state->tls_context->peer_sig_curve)
+ vstring_sprintf_append(state->buffer, " (%s)",
+ state->tls_context->peer_sig_curve);
+ else if (state->tls_context->peer_sig_bits > 0)
+ vstring_sprintf_append(state->buffer, " (%d bits)",
+ state->tls_context->peer_sig_bits);
+ if (state->tls_context->peer_sig_dgst
+ && *state->tls_context->peer_sig_dgst)
+ vstring_sprintf_append(state->buffer, " client-digest %s",
+ state->tls_context->peer_sig_dgst);
+ }
+ out_fprintf(out_stream, REC_TYPE_NORM, "%s)", STR(state->buffer));
if (TLS_CERT_IS_PRESENT(state->tls_context)) {
peer_CN = VSTRING_STRDUP(state->tls_context->peer_CN);
comment_sanitize(peer_CN);
/* Backwards compatibility with OpenSSL < 1.1.1 */
#if OPENSSL_VERSION_NUMBER < 0x1010100fUL
#define SSL_CTX_set_num_tickets(ctx, num) ((void)0)
+#endif
+
+ /*-
+ * Backwards compatibility with OpenSSL < 1.1.1a (or some later version).
+ *
+ * The client-only interface SSL_get_server_tmp_key() is slated to be made to
+ * work on both client and server, and renamed to SSL_get_peer_tmp_key(), with
+ * the original name left behind as an alias. We'll use the new name if/when
+ * available.
+ *
+ * XXX: Set corresponding OpenSSL version floor below when OpenSSL pull
+ * request:
+ *
+ * <https://github.com/openssl/openssl/pull/7608>
+ *
+ * is merged, perhaps in the upcoming 1.1.1a release (at which point the XXX
+ * part of this comment can be deleted).
+ */
+#if OPENSSL_VERSION_NUMBER < 0x1010101fUL
+#undef SSL_get_signature_nid
+#define SSL_get_signature_nid(ssl, pnid) (NID_undef)
+#define tls_get_peer_dh_pubkey SSL_get_server_tmp_key
+#else
+#define tls_get_peer_dh_pubkey SSL_get_peer_tmp_key
#endif
/* SSL_CIPHER_get_name() got constified in 0.9.7g */
const char *cipher_name;
int cipher_usebits;
int cipher_algbits;
+ const char *kex_name; /* shared key-exchange algorithm */
+ const char *kex_curve; /* shared key-exchange ECDHE curve */
+ int kex_bits; /* shared FFDHE key exchange bits */
+ const char *locl_sig_name; /* local signature key algorithm */
+ const char *locl_sig_curve; /* local ECDSA curve name */
+ int locl_sig_bits; /* local RSA signature key bits */
+ const char *locl_sig_dgst; /* local signature digest */
+ const char *peer_sig_name; /* peer's signature key algorithm */
+ const char *peer_sig_curve; /* peer's ECDSA curve name */
+ int peer_sig_bits; /* peer's RSA signature key bits */
+ const char *peer_sig_dgst; /* peer's signature digest */
/* Private. */
SSL *con;
char *cache_type; /* tlsmgr(8) cache type if enabled */
#endif
/*
- * OpenSSL 1.1.1 does not define a TXT macro for TLS 1.3, so we roll our own.
+ * OpenSSL 1.1.1 does not define a TXT macro for TLS 1.3, so we roll our
+ * own.
*/
#define TLS_PROTOCOL_TXT_TLSV1_3 "TLSv1.3"
extern const char *tls_set_ciphers(TLS_APPL_STATE *, const char *,
const char *, const char *);
-#endif
+ /*
+ * Populate TLS context with TLS 1.3-related signature parameters.
+ */
+extern void tls_get_signature_params(TLS_SESS_STATE *);
+
+#endif /* TLS_INTERNAL */
/*
* tls_client.c
TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
}
+/* log_summary - TLS loglevel 1 one-liner, embellished with TLS 1.3 details */
+
+static void log_summary(TLS_SESS_STATE *TLScontext,
+ const TLS_CLIENT_START_PROPS *props)
+{
+ VSTRING *msg = vstring_alloc(100);
+
+ vstring_sprintf(msg, "%s TLS connection established to %s: %s"
+ " with cipher %s (%d/%d bits)",
+ !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" :
+ TLS_CERT_IS_SECURED(TLScontext) ? "Verified" :
+ TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
+ props->namaddr, TLScontext->protocol,
+ TLScontext->cipher_name, TLScontext->cipher_usebits,
+ TLScontext->cipher_algbits);
+
+ if (TLScontext->kex_name && *TLScontext->kex_name) {
+ vstring_sprintf_append(msg, " key-exchange %s",
+ TLScontext->kex_name);
+ if (TLScontext->kex_curve && *TLScontext->kex_curve)
+ vstring_sprintf_append(msg, " (%s)",
+ TLScontext->kex_curve);
+ else if (TLScontext->kex_bits > 0)
+ vstring_sprintf_append(msg, " (%d bits)",
+ TLScontext->kex_bits);
+ }
+ if (TLScontext->peer_sig_name && *TLScontext->peer_sig_name) {
+ vstring_sprintf_append(msg, " server-signature %s",
+ TLScontext->peer_sig_name);
+ if (TLScontext->peer_sig_curve && *TLScontext->peer_sig_curve)
+ vstring_sprintf_append(msg, " (%s)",
+ TLScontext->peer_sig_curve);
+ else if (TLScontext->peer_sig_bits > 0)
+ vstring_sprintf_append(msg, " (%d bits)",
+ TLScontext->peer_sig_bits);
+ if (TLScontext->peer_sig_dgst && *TLScontext->peer_sig_dgst)
+ vstring_sprintf_append(msg, " server-digest %s",
+ TLScontext->peer_sig_dgst);
+ }
+ if (TLScontext->locl_sig_name && *TLScontext->locl_sig_name) {
+ vstring_sprintf_append(msg, " client-signature %s",
+ TLScontext->locl_sig_name);
+ if (TLScontext->locl_sig_curve && *TLScontext->locl_sig_curve)
+ vstring_sprintf_append(msg, " (%s)",
+ TLScontext->locl_sig_curve);
+ else if (TLScontext->locl_sig_bits > 0)
+ vstring_sprintf_append(msg, " (%d bits)",
+ TLScontext->locl_sig_bits);
+ if (TLScontext->locl_sig_dgst && *TLScontext->locl_sig_dgst)
+ vstring_sprintf_append(msg, " client-digest %s",
+ TLScontext->locl_sig_dgst);
+ }
+ msg_info("%s", vstring_str(msg));
+ vstring_free(msg);
+}
+
/*
* This is the actual startup routine for the connection. We expect that the
* buffers are flushed and the "220 Ready to start TLS" was received by us,
&& !TLS_NEVER_SECURED(props->tls_level))
TLScontext->peer_status |= TLS_CERT_FLAG_SECURED;
- /*
- * All the key facts in a single log entry.
- */
+ tls_get_signature_params(TLScontext);
+
if (TLScontext->log_mask & TLS_LOG_SUMMARY)
- msg_info("%s TLS connection established to %s: %s with cipher %s "
- "(%d/%d bits)",
- !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" :
- TLS_CERT_IS_SECURED(TLScontext) ? "Verified" :
- TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
- props->namaddr, TLScontext->protocol, TLScontext->cipher_name,
- TLScontext->cipher_usebits, TLScontext->cipher_algbits);
+ log_summary(TLScontext, props);
tls_int_seed();
/* int grade;
/* const char *exclusions;
/*
+/* void tls_get_signature_params(TLScontext)
+/* TLS_SESS_STATE *TLScontext;
+/*
/* void tls_print_errors()
/*
/* void tls_info_callback(ssl, where, ret)
/* When the input is invalid, tls_set_ciphers() logs a warning with
/* the specified context, and returns a null pointer result.
/*
+/* tls_get_signature_params() updates the "TLScontext" with handshake
+/* signature parameters pertaining to TLS 1.3, where the ciphersuite
+/* no longer describes the asymmetric algorithms employed in the
+/* handshake, which are negotiated separately. This function
+/* has no effect for TLS 1.2 and earlier.
+/*
/* tls_print_errors() queries the OpenSSL error stack,
/* logs the error messages, and clears the error stack.
/*
NAMEBUG(TLSEXT_PADDING),
#if 0
- /*
- * XXX: New with OpenSSL 1.1.1, this is turned on implicitly in SSL_CTX_new()
- * and is not included in SSL_OP_ALL. Allowing users to disable this would
- * thus a code change that would clearing bug work-around bits in SSL_CTX,
- * after setting SSL_OP_ALL. Since this is presumably required for TLS 1.3 on
- * today's Internet, the code change will be done separately later. For now
- * this implicit bug work-around cannot be disabled via supported Postfix
- * mechanisms.
- */
+
+ /*
+ * XXX: New with OpenSSL 1.1.1, this is turned on implicitly in
+ * SSL_CTX_new() and is not included in SSL_OP_ALL. Allowing users to
+ * disable this would thus be a code change that would require clearing
+ * bug work-around bits in SSL_CTX, after setting SSL_OP_ALL. Since this
+ * is presumably required for TLS 1.3 on today's Internet, the code
+ * change will be done separately later. For now this implicit bug
+ * work-around cannot be disabled via supported Postfix mechanisms.
+ */
#ifndef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
#define SSL_OP_ENABLE_MIDDLEBOX_COMPAT 0
#endif
return (app_ctx->cipher_list = mystrdup(new_list));
}
+/* tls_get_signature_params - TLS 1.3 signature details */
+
+void tls_get_signature_params(TLS_SESS_STATE *TLScontext)
+{
+ const char *kex_name = 0;
+ const char *kex_curve = 0;
+ const char *locl_sig_name = 0;
+ const char *locl_sig_curve = 0;
+ const char *locl_sig_dgst = 0;
+ const char *peer_sig_name = 0;
+ const char *peer_sig_curve = 0;
+ const char *peer_sig_dgst = 0;
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fUL && defined(TLS1_3_VERSION)
+#ifndef OPENSSL_NO_EC
+ EC_KEY *eckey;
+
+#endif
+ int nid;
+ int got_kex_key;
+ SSL *ssl = TLScontext->con;
+ X509 *cert;
+ EVP_PKEY *pkey = 0;
+
+ if (SSL_version(ssl) != TLS1_3_VERSION)
+ return;
+
+ if (tls_get_peer_dh_pubkey(ssl, &pkey)) {
+ switch (nid = EVP_PKEY_id(pkey)) {
+ default:
+ kex_name = OBJ_nid2sn(EVP_PKEY_type(nid));
+ break;
+
+ case EVP_PKEY_DH:
+ kex_name = "DHE";
+ TLScontext->kex_bits = EVP_PKEY_bits(pkey);
+ break;
+
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ kex_name = "ECDHE";
+ eckey = EVP_PKEY_get0_EC_KEY(pkey);
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey));
+ kex_curve = EC_curve_nid2nist(nid);
+ if (!kex_curve)
+ kex_curve = OBJ_nid2sn(nid);
+ break;
+#endif
+ }
+ EVP_PKEY_free(pkey);
+ }
+
+ /*
+ * On the client end, the certificate may be preset, but not used, so we
+ * check via SSL_get_signature_nid(). This means that local signature
+ * data on clients requires at least 1.1.1a.
+ */
+ if (SSL_is_server(ssl) || SSL_get_signature_nid(ssl, &nid))
+ cert = SSL_get_certificate(ssl);
+ else
+ cert = 0;
+
+ /* Signature algorithms for the local end of the connection */
+ if (cert) {
+ pkey = X509_get0_pubkey(cert);
+
+ /*
+ * Override the built-in name for the "ECDSA" algorithms OID, with
+ * the more familiar name. For "RSA" keys report "RSA-PSS", which
+ * must be used with TLS 1.3.
+ */
+ if ((nid = EVP_PKEY_type(EVP_PKEY_id(pkey))) != NID_undef) {
+ switch (nid) {
+ default:
+ locl_sig_name = OBJ_nid2sn(nid);
+ break;
+
+ case EVP_PKEY_RSA:
+ /* For RSA, TLS 1.3 mandates PSS signatures */
+ locl_sig_name = "RSA-PSS";
+ TLScontext->locl_sig_bits = EVP_PKEY_bits(pkey);
+ break;
+
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ locl_sig_name = "ECDSA";
+ eckey = EVP_PKEY_get0_EC_KEY(pkey);
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey));
+ locl_sig_curve = EC_curve_nid2nist(nid);
+ if (!locl_sig_curve)
+ locl_sig_curve = OBJ_nid2sn(nid);
+ break;
+#endif
+ }
+ }
+
+ /*
+ * With Ed25519 and Ed448 there is no pre-signature digest, but the
+ * accessor does not fail, rather we get NID_undef.
+ */
+ if (SSL_get_signature_nid(ssl, &nid) && nid != NID_undef)
+ locl_sig_dgst = OBJ_nid2sn(nid);
+ }
+ /* Signature algorithms for the peer end of the connection */
+ if ((cert = SSL_get_peer_certificate(ssl)) != 0) {
+ pkey = X509_get0_pubkey(cert);
+
+ /*
+ * Override the built-in name for the "ECDSA" algorithms OID, with
+ * the more familiar name. For "RSA" keys report "RSA-PSS", which
+ * must be used with TLS 1.3.
+ */
+ if ((nid = EVP_PKEY_type(EVP_PKEY_id(pkey))) != NID_undef) {
+ switch (nid) {
+ default:
+ peer_sig_name = OBJ_nid2sn(nid);
+ break;
+
+ case EVP_PKEY_RSA:
+ /* For RSA, TLS 1.3 mandates PSS signatures */
+ peer_sig_name = "RSA-PSS";
+ TLScontext->peer_sig_bits = EVP_PKEY_bits(pkey);
+ break;
+
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ peer_sig_name = "ECDSA";
+ eckey = EVP_PKEY_get0_EC_KEY(pkey);
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey));
+ peer_sig_curve = EC_curve_nid2nist(nid);
+ if (!peer_sig_curve)
+ peer_sig_curve = OBJ_nid2sn(nid);
+ break;
+#endif
+ }
+ }
+
+ /*
+ * With Ed25519 and Ed448 there is no pre-signature digest, but the
+ * accessor does not fail, rather we get NID_undef.
+ */
+ if (SSL_get_peer_signature_nid(ssl, &nid) && nid != NID_undef)
+ peer_sig_dgst = OBJ_nid2sn(nid);
+ }
+#endif /* OPENSSL_VERSION_NUMBER >=
+ * 0x1010100fUL &&
+ * defined(TLS1_3_VERSION) */
+
+ if (kex_name) {
+ TLScontext->kex_name = mystrdup(kex_name);
+ if (kex_curve)
+ TLScontext->kex_curve = mystrdup(kex_curve);
+ }
+ if (locl_sig_name) {
+ TLScontext->locl_sig_name = mystrdup(locl_sig_name);
+ if (locl_sig_curve)
+ TLScontext->locl_sig_curve = mystrdup(locl_sig_curve);
+ if (locl_sig_dgst)
+ TLScontext->locl_sig_dgst = mystrdup(locl_sig_dgst);
+ }
+ if (peer_sig_name) {
+ TLScontext->peer_sig_name = mystrdup(peer_sig_name);
+ if (peer_sig_curve)
+ TLScontext->peer_sig_curve = mystrdup(peer_sig_curve);
+ if (peer_sig_dgst)
+ TLScontext->peer_sig_dgst = mystrdup(peer_sig_dgst);
+ }
+}
+
/* tls_alloc_app_context - allocate TLS application context */
TLS_APPL_STATE *tls_alloc_app_context(SSL_CTX *ssl_ctx, int log_mask)
TLScontext->peer_pkey_fprint = 0;
TLScontext->protocol = 0;
TLScontext->cipher_name = 0;
+ TLScontext->kex_name = 0;
+ TLScontext->kex_curve = 0;
+ TLScontext->locl_sig_name = 0;
+ TLScontext->locl_sig_curve = 0;
+ TLScontext->locl_sig_dgst = 0;
+ TLScontext->peer_sig_name = 0;
+ TLScontext->peer_sig_curve = 0;
+ TLScontext->peer_sig_dgst = 0;
TLScontext->log_mask = log_mask;
TLScontext->namaddr = lowercase(mystrdup(namaddr));
TLScontext->mdalg = 0; /* Alias for props->mdalg */
#define TLS_ATTR_CIPHER_NAME "cipher_name"
#define TLS_ATTR_CIPHER_USEBITS "cipher_usebits"
#define TLS_ATTR_CIPHER_ALGBITS "cipher_algbits"
+#define TLS_ATTR_KEX_NAME "key_exchange"
+#define TLS_ATTR_KEX_CURVE "key_exchange_curve"
+#define TLS_ATTR_KEX_BITS "key_exchange_bits"
+#define TLS_ATTR_LOCL_SIG_NAME "locl_signature"
+#define TLS_ATTR_LOCL_SIG_CURVE "locl_signature_curve"
+#define TLS_ATTR_LOCL_SIG_BITS "locl_signature_bits"
+#define TLS_ATTR_LOCL_SIG_DGST "locl_signature_digest"
+#define TLS_ATTR_PEER_SIG_NAME "peer_signature"
+#define TLS_ATTR_PEER_SIG_CURVE "peer_signature_curve"
+#define TLS_ATTR_PEER_SIG_BITS "peer_signature_bits"
+#define TLS_ATTR_PEER_SIG_DGST "peer_signature_digest"
/*
* TLS_SERVER_INIT_PROPS attributes.
tp->cipher_usebits),
SEND_ATTR_INT(TLS_ATTR_CIPHER_ALGBITS,
tp->cipher_algbits),
+ SEND_ATTR_STR(TLS_ATTR_KEX_NAME,
+ STRING_OR_EMPTY(tp->kex_name)),
+ SEND_ATTR_STR(TLS_ATTR_KEX_CURVE,
+ STRING_OR_EMPTY(tp->kex_curve)),
+ SEND_ATTR_INT(TLS_ATTR_KEX_BITS,
+ tp->kex_bits),
+ SEND_ATTR_STR(TLS_ATTR_LOCL_SIG_NAME,
+ STRING_OR_EMPTY(tp->locl_sig_name)),
+ SEND_ATTR_STR(TLS_ATTR_LOCL_SIG_CURVE,
+ STRING_OR_EMPTY(tp->locl_sig_curve)),
+ SEND_ATTR_INT(TLS_ATTR_LOCL_SIG_BITS,
+ tp->locl_sig_bits),
+ SEND_ATTR_STR(TLS_ATTR_LOCL_SIG_DGST,
+ STRING_OR_EMPTY(tp->locl_sig_dgst)),
+ SEND_ATTR_STR(TLS_ATTR_PEER_SIG_NAME,
+ STRING_OR_EMPTY(tp->peer_sig_name)),
+ SEND_ATTR_STR(TLS_ATTR_PEER_SIG_CURVE,
+ STRING_OR_EMPTY(tp->peer_sig_curve)),
+ SEND_ATTR_INT(TLS_ATTR_PEER_SIG_BITS,
+ tp->peer_sig_bits),
+ SEND_ATTR_STR(TLS_ATTR_PEER_SIG_DGST,
+ STRING_OR_EMPTY(tp->peer_sig_dgst)),
ATTR_TYPE_END);
/* Do not flush the stream. */
return (ret);
VSTRING *peer_pkey_fprint = vstring_alloc(60); /* 60 for SHA-1 */
VSTRING *protocol = vstring_alloc(25);
VSTRING *cipher_name = vstring_alloc(25);
+ VSTRING *kex_name = vstring_alloc(25);
+ VSTRING *kex_curve = vstring_alloc(25);
+ VSTRING *locl_sig_name = vstring_alloc(25);
+ VSTRING *locl_sig_curve = vstring_alloc(25);
+ VSTRING *locl_sig_dgst = vstring_alloc(25);
+ VSTRING *peer_sig_name = vstring_alloc(25);
+ VSTRING *peer_sig_curve = vstring_alloc(25);
+ VSTRING *peer_sig_dgst = vstring_alloc(25);
if (msg_verbose)
msg_info("begin tls_proxy_context_scan");
&tls_context->cipher_usebits),
RECV_ATTR_INT(TLS_ATTR_CIPHER_ALGBITS,
&tls_context->cipher_algbits),
+ RECV_ATTR_STR(TLS_ATTR_KEX_NAME, kex_name),
+ RECV_ATTR_STR(TLS_ATTR_KEX_CURVE, kex_curve),
+ RECV_ATTR_INT(TLS_ATTR_KEX_BITS, &tls_context->kex_bits),
+ RECV_ATTR_STR(TLS_ATTR_LOCL_SIG_NAME, locl_sig_name),
+ RECV_ATTR_STR(TLS_ATTR_LOCL_SIG_CURVE, locl_sig_curve),
+ RECV_ATTR_INT(TLS_ATTR_LOCL_SIG_BITS, &tls_context->locl_sig_bits),
+ RECV_ATTR_STR(TLS_ATTR_LOCL_SIG_DGST, locl_sig_dgst),
+ RECV_ATTR_STR(TLS_ATTR_PEER_SIG_NAME, peer_sig_name),
+ RECV_ATTR_STR(TLS_ATTR_PEER_SIG_CURVE, peer_sig_curve),
+ RECV_ATTR_INT(TLS_ATTR_PEER_SIG_BITS, &tls_context->peer_sig_bits),
+ RECV_ATTR_STR(TLS_ATTR_PEER_SIG_DGST, peer_sig_dgst),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
tls_context->peer_CN = vstring_export(peer_CN);
tls_context->peer_pkey_fprint = vstring_export(peer_pkey_fprint);
tls_context->protocol = vstring_export(protocol);
tls_context->cipher_name = vstring_export(cipher_name);
- ret = (ret == 9 ? 1 : -1);
+ tls_context->kex_name = vstring_export(kex_name);
+ tls_context->kex_curve = vstring_export(kex_curve);
+ tls_context->locl_sig_name = vstring_export(locl_sig_name);
+ tls_context->locl_sig_curve = vstring_export(locl_sig_curve);
+ tls_context->locl_sig_dgst = vstring_export(locl_sig_dgst);
+ tls_context->peer_sig_name = vstring_export(peer_sig_name);
+ tls_context->peer_sig_curve = vstring_export(peer_sig_curve);
+ tls_context->peer_sig_dgst = vstring_export(peer_sig_dgst);
+ ret = (ret == 20 ? 1 : -1);
if (ret != 1) {
tls_proxy_context_free(tls_context);
tls_context = 0;
myfree((void *) tls_context->protocol);
if (tls_context->cipher_name)
myfree((void *) tls_context->cipher_name);
+ if (tls_context->kex_name)
+ myfree((void *) tls_context->kex_name);
+ if (tls_context->kex_curve)
+ myfree((void *) tls_context->kex_curve);
+ if (tls_context->locl_sig_name)
+ myfree((void *) tls_context->locl_sig_name);
+ if (tls_context->locl_sig_curve)
+ myfree((void *) tls_context->locl_sig_curve);
+ if (tls_context->locl_sig_dgst)
+ myfree((void *) tls_context->locl_sig_dgst);
+ if (tls_context->peer_sig_name)
+ myfree((void *) tls_context->peer_sig_name);
+ if (tls_context->peer_sig_curve)
+ myfree((void *) tls_context->peer_sig_curve);
+ if (tls_context->peer_sig_dgst)
+ myfree((void *) tls_context->peer_sig_dgst);
myfree((void *) tls_context);
}
#endif
+/* log_summary - TLS loglevel 1 one-liner, embellished with TLS 1.3 details */
+
+static void log_summary(TLS_SESS_STATE *TLScontext)
+{
+ VSTRING *msg = vstring_alloc(100);
+
+ vstring_sprintf(msg, "%s TLS connection established from %s: %s"
+ " with cipher %s (%d/%d bits)",
+ !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" :
+ TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
+ TLScontext->namaddr, TLScontext->protocol,
+ TLScontext->cipher_name, TLScontext->cipher_usebits,
+ TLScontext->cipher_algbits);
+
+ if (TLScontext->kex_name && *TLScontext->kex_name) {
+ vstring_sprintf_append(msg, " key-exchange %s",
+ TLScontext->kex_name);
+ if (TLScontext->kex_curve && *TLScontext->kex_curve)
+ vstring_sprintf_append(msg, " (%s)",
+ TLScontext->kex_curve);
+ else if (TLScontext->kex_bits > 0)
+ vstring_sprintf_append(msg, " (%d bits)",
+ TLScontext->kex_bits);
+ }
+ if (TLScontext->locl_sig_name && *TLScontext->locl_sig_name) {
+ vstring_sprintf_append(msg, " server-signature %s",
+ TLScontext->locl_sig_name);
+ if (TLScontext->locl_sig_curve && *TLScontext->locl_sig_curve)
+ vstring_sprintf_append(msg, " (%s)",
+ TLScontext->locl_sig_curve);
+ else if (TLScontext->locl_sig_bits > 0)
+ vstring_sprintf_append(msg, " (%d bits)",
+ TLScontext->locl_sig_bits);
+ if (TLScontext->locl_sig_dgst && *TLScontext->locl_sig_dgst)
+ vstring_sprintf_append(msg, " server-digest %s",
+ TLScontext->locl_sig_dgst);
+ }
+ if (TLScontext->peer_sig_name && *TLScontext->peer_sig_name) {
+ vstring_sprintf_append(msg, " client-signature %s",
+ TLScontext->peer_sig_name);
+ if (TLScontext->peer_sig_curve && *TLScontext->peer_sig_curve)
+ vstring_sprintf_append(msg, " (%s)",
+ TLScontext->peer_sig_curve);
+ else if (TLScontext->peer_sig_bits > 0)
+ vstring_sprintf_append(msg, " (%d bits)",
+ TLScontext->peer_sig_bits);
+ if (TLScontext->peer_sig_dgst && *TLScontext->peer_sig_dgst)
+ vstring_sprintf_append(msg, " client-digest %s",
+ TLScontext->peer_sig_dgst);
+ }
+ msg_info("%s", vstring_str(msg));
+ vstring_free(msg);
+}
+
/* tls_server_init - initialize the server-side TLS engine */
TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props)
}
if (ticketable) {
SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_cb);
- /*
- * OpenSSL 1.1.1 introduces support for TLS 1.3, which can issue more
- * than one ticket per handshake. While this may be appropriate for
- * communication between browsers and webservers, it is not terribly
- * useful for MTAs, many of which other than Postfix don't do TLS
- * session caching at all, and Postfix has no mechanism for storing
- * multiple session tickets, if more than one sent, the second clobbers
- * the first. OpenSSL 1.1.1 servers default to issuing two tickets for
- * non-resumption handshakes, we reduce this to one. Our ticket
- * decryption callback already (since 2.11) asks OpenSSL to avoid
- * issuing new tickets when the presented ticket is re-usable.
- */
+
+ /*
+ * OpenSSL 1.1.1 introduces support for TLS 1.3, which can issue more
+ * than one ticket per handshake. While this may be appropriate for
+ * communication between browsers and webservers, it is not terribly
+ * useful for MTAs, many of which other than Postfix don't do TLS
+ * session caching at all, and Postfix has no mechanism for storing
+ * multiple session tickets, if more than one sent, the second
+ * clobbers the first. OpenSSL 1.1.1 servers default to issuing two
+ * tickets for non-resumption handshakes, we reduce this to one. Our
+ * ticket decryption callback already (since 2.11) asks OpenSSL to
+ * avoid issuing new tickets when the presented ticket is re-usable.
+ */
SSL_CTX_set_num_tickets(server_ctx, 1);
}
#endif
if (TLScontext->stream != 0)
tls_stream_start(TLScontext->stream, TLScontext);
+ /*
+ * With the handshake done, extract TLS 1.3 signature metadata.
+ */
+ tls_get_signature_params(TLScontext);
+
/*
* All the key facts in a single log entry.
*/
if (TLScontext->log_mask & TLS_LOG_SUMMARY)
- msg_info("%s TLS connection established from %s: %s with cipher %s "
- "(%d/%d bits)", !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous"
- : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted",
- TLScontext->namaddr, TLScontext->protocol, TLScontext->cipher_name,
- TLScontext->cipher_usebits, TLScontext->cipher_algbits);
+ log_summary(TLScontext);
tls_int_seed();
#endif
#if DB_VERSION_MAJOR > 2
- DB_ENV *dbenv;
+ DB_ENV *dbenv = 0;
#endif
* db_open() create a non-existent file for us.
*/
#define LOCK_OPEN_FLAGS(f) ((f) & ~(O_CREAT|O_TRUNC))
+#if DB_VERSION_MAJOR <= 2
#define FREE_RETURN(e) do { \
DICT *_dict = (e); if (db) DICT_DB_CLOSE(db); \
if (lock_fd >= 0) (void) close(lock_fd); \
if (db_base_buf) vstring_free(db_base_buf); \
if (db_path) myfree(db_path); return (_dict); \
} while (0)
+#else
+#define FREE_RETURN(e) do { \
+ DICT *_dict = (e); if (db) DICT_DB_CLOSE(db); \
+ if (dbenv) dbenv->close(dbenv, 0); \
+ if (lock_fd >= 0) (void) close(lock_fd); \
+ if (db_base_buf) vstring_free(db_base_buf); \
+ if (db_path) myfree(db_path); return (_dict); \
+ } while (0)
+#endif
if (dict_flags & DICT_FLAG_LOCK) {
if ((lock_fd = open(db_path, LOCK_OPEN_FLAGS(open_flags), 0644)) < 0) {