From c04936c1b0c355bb583933e7383b8f21e4c2682d Mon Sep 17 00:00:00 2001
From: Wietse Venema
Date: Tue, 29 Mar 2005 00:00:00 -0500
Subject: [PATCH] postfix-2.3-20050329
---
postfix/.indent.pro | 1 +
postfix/ENHANCED_STATUS_README | 13 +-
postfix/HISTORY | 21 +-
postfix/README_FILES/TLS_README | 9 +-
postfix/RELEASE_NOTES | 10 +
postfix/conf/header_checks | 100 +++++----
postfix/html/TLS_README.html | 8 +-
postfix/html/header_checks.5.html | 98 ++++----
postfix/man/man5/header_checks.5 | 22 +-
postfix/proto/TLS_README.html | 8 +-
postfix/proto/header_checks | 22 +-
postfix/src/cleanup/cleanup_api.c | 6 +-
postfix/src/cleanup/cleanup_message.c | 88 ++++----
postfix/src/discard/discard.c | 4 +-
postfix/src/error/error.c | 2 +-
postfix/src/global/dsn_util.c | 47 +++-
postfix/src/global/dsn_util.h | 31 ++-
postfix/src/global/mail_version.h | 2 +-
postfix/src/global/pipe_command.c | 4 +-
postfix/src/lmtp/lmtp.c | 8 +-
postfix/src/lmtp/lmtp.h | 2 +-
postfix/src/lmtp/lmtp_chat.c | 10 +-
postfix/src/lmtp/lmtp_proto.c | 20 +-
postfix/src/local/command.c | 5 +-
postfix/src/local/file.c | 5 +-
postfix/src/local/mailbox.c | 5 +-
postfix/src/local/maildir.c | 5 +-
postfix/src/oqmgr/qmgr_deliver.c | 2 +-
postfix/src/pipe/pipe.c | 2 +-
postfix/src/qmgr/qmgr_deliver.c | 2 +-
postfix/src/smtp/smtp.h | 2 +-
postfix/src/smtp/smtp_chat.c | 10 +-
postfix/src/smtp/smtp_connect.c | 10 +-
postfix/src/smtp/smtp_proto.c | 73 ++----
postfix/src/smtpd/smtpd_check.c | 14 +-
postfix/src/tls/tls.h | 21 +-
postfix/src/tls/tls_client.c | 312 +++++++++++++++-----------
postfix/src/tls/tls_mgr.c | 42 +---
postfix/src/tls/tls_mgr.h | 9 +-
postfix/src/tls/tls_misc.c | 60 +++++
postfix/src/tls/tls_scache.c | 178 +++------------
postfix/src/tls/tls_scache.h | 17 +-
postfix/src/tls/tls_server.c | 95 ++++----
postfix/src/tls/tls_session.c | 16 +-
postfix/src/tls/tls_verify.c | 101 +--------
postfix/src/tlsmgr/tlsmgr.c | 21 +-
postfix/src/virtual/mailbox.c | 6 +-
postfix/src/virtual/maildir.c | 6 +-
48 files changed, 765 insertions(+), 790 deletions(-)
diff --git a/postfix/.indent.pro b/postfix/.indent.pro
index 06d5e2538..08e5e99d6 100644
--- a/postfix/.indent.pro
+++ b/postfix/.indent.pro
@@ -77,6 +77,7 @@
-TDNS_REPLY
-TDNS_RR
-TDOMAIN_LIST
+-TDSN_BUF
-TDSN_SPLIT
-TDSN_VSTRING
-TEXPAND_ATTR
diff --git a/postfix/ENHANCED_STATUS_README b/postfix/ENHANCED_STATUS_README
index 684591e48..bf0207f62 100644
--- a/postfix/ENHANCED_STATUS_README
+++ b/postfix/ENHANCED_STATUS_README
@@ -48,12 +48,13 @@ EACCES. The latter happens to work because mbox_open() does not
need to unlock the output file, so it won't clobber the errno value.
- Avoid passing around strings that combine enhanced status code
-and diagnostic text, because the compiler can't help to enforce
-that everything has a status code. Currently there are two exceptions
-to this rule: the cleanup server status reply, and the delivery
-agent status reply. Once these protocols are updated we can remove
-the dns_prepend() routine. The third exception, enhanced status
-codes in external command output, is a feature.
+and diagnostic text. Instead, use separate variables for status
+code and text, so that the compiler can enforce that everything has
+a status code. Currently there are two exceptions to this rule:
+the cleanup server status reply, and the delivery agent status
+reply. Once these protocols are updated we can remove the dns_prepend()
+routine. The third exception, enhanced status codes in external
+command output, is a feature.
- The bounce/defer/sent library modules will catch the cases where
an enhanced status code does not match the reject/defer/success
diff --git a/postfix/HISTORY b/postfix/HISTORY
index 22219e598..7a23da1d9 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -10488,7 +10488,7 @@ Apologies for any names omitted.
20050321-27
Support for RFC 3463 enhanced status codes. See also the
- ENHANCED_STATUS_README file for background.
+ ENHANCED_STATUS_README (a hacker's guide) for background.
New module to pass around (status code + text) instead of
just text. File: Files: global/dsn_util.c.
@@ -10548,6 +10548,25 @@ Apologies for any names omitted.
server IP address, TCP Port, and server HELO hostname
if available. File: smtp/smtp_proto.c.
+20050328
+
+ Cleanup: the REPLACE action is no longer implemented as
+ PREPEND+IGNORE. The result remains in the input stream,
+ and is subject to address rewriting and other processing
+ where applicable. File: cleanup/cleanup_message.c.
+
+ Feature: the TLS server name verification status is moved
+ out of the TLS session cache. This not only simplifies the
+ client-side TLS cache implementation, but also provides
+ better cache support for clients that connect to multiple
+ independent MTAs under the same DNS hostname or IP address,
+ provided that each MTA replies with a unique name in the
+ EHLO response. Patch by Victor Duchovni. Files: tlsmgr/tlsmgr.c,
+ tls/tls_verify.c, tls/tls_session.c, tls/tls_server.c,
+ tls/tls_scache.h, tls/tls_scache.c, tls/tls_misc.c,
+ tls/tls_mgr.h, tls/tls_mgr.c, tls/tls_client.c, tls/tls.h,
+ smtp/smtp_proto.c.
+
Open problems:
Med: disable header address rewriting after XCLIENT?
diff --git a/postfix/README_FILES/TLS_README b/postfix/README_FILES/TLS_README
index fd270b9cb..1e1a75383 100644
--- a/postfix/README_FILES/TLS_README
+++ b/postfix/README_FILES/TLS_README
@@ -364,7 +364,9 @@ between multiple smtpd(8) processes, a persistent session cache can be used.
You can specify any database type that can store objects of several kbytes and
that supports the sequence operator. DBM databases are not suitable because
they can only store small objects. The cache is maintained by the tlsmgr(8)
-process, so there is no problem with concurrent access.
+process, so there is no problem with concurrent access. Session caching is
+highly recommended, because the cost of repeatedly negotiating TLS session keys
+is high.
Example:
@@ -608,7 +610,10 @@ between multiple smtp(8) processes, a persistent session cache can be used. You
can specify any database type that can store objects of several kbytes and that
supports the sequence operator. DBM databases are not suitable because they can
only store small objects. The cache is maintained by the tlsmgr(8) process, so
-there is no problem with concurrent access.
+there is no problem with concurrent access. Session caching is highly
+recommended, because the cost of repeatedly negotiating TLS session keys is
+high. Future Postfix SMTP servers may limit the number of sessions that a
+client is allowed to negotiate per unit time.
Example:
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 5f1fcb08d..8ea1805bc 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -17,9 +17,19 @@ Incompatibility with Postfix 2.1 and earlier
If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
before proceeding.
+Incompatibility with snapshot 20050329
+======================================
+
+If you use TLS, you need to execute "postfix reload" because the
+TLS manager protocol has changed.
+
Incompatibility with snapshot 20050328
======================================
+The logging format has changed. Postfix delivery agents now log the
+RFC 3463 enhanced status code as "dsn=x.y.z" where y and z can be
+up to three digits each.
+
After you upgrade from Postfix 2.2 or 2.3 you need to execute
"postfix reload", otherwise you will keep running the old Postfix
queue manager, which gives no special treatment to the enhanced
diff --git a/postfix/conf/header_checks b/postfix/conf/header_checks
index b48bac2e2..10dcea1d0 100644
--- a/postfix/conf/header_checks
+++ b/postfix/conf/header_checks
@@ -202,8 +202,15 @@
# line, immediately before the input that
# triggered the PREPEND action.
#
+# o The prepended text is not considered part of
+# the input stream. Unlike the result from the
+# REPLACE action, prepended text is not sub-
+# ject to header/body checks or address
+# rewriting, and does not affect the way that
+# Postfix adds missing message headers.
+#
# o When prepending text before a message header
-# line, the prepended text must begin with a
+# line, the prepended text must begin with a
# valid message header label.
#
# o This action cannot be used to prepend multi-
@@ -212,35 +219,46 @@
# This feature is available in Postfix 2.1 and later.
#
# REDIRECT user@domain
-# Write a message redirection request to the queue
-# file and inspect the next input line. After the
+# Write a message redirection request to the queue
+# file and inspect the next input line. After the
# message is queued, it will be sent to the specified
# address instead of the intended recipient(s).
#
-# Note: this action overrides the FILTER action, and
-# affects all recipients of the message. If multiple
-# REDIRECT actions fire, only the last one is exe-
+# Note: this action overrides the FILTER action, and
+# affects all recipients of the message. If multiple
+# REDIRECT actions fire, only the last one is exe-
# cuted.
#
# This feature is available in Postfix 2.1 and later.
#
# REPLACE text...
-# Replace the current line with the specified text
+# Replace the current line with the specified text
# and inspect the next input line.
#
-# Note: when replacing a message header line, the
-# replacement text must begin with a valid header
-# label.
-#
# This feature is available in Postfix 2.2 and later.
+# The description below applies to Postfix 2.2.2 and
+# later.
+#
+# Notes:
+#
+# o When replacing a message header line, the
+# replacement text must begin with a valid
+# header label.
+#
+# o The replaced text remains part of the input
+# stream. Unlike the result from the PREPEND
+# action, a replaced message header may be
+# subject to address rewriting and may affect
+# the way that Postfix adds missing message
+# headers.
#
# REJECT optional text...
-# Reject the entire message. Reply with optional
+# Reject the entire message. Reply with optional
# text... when the optional text is specified, other-
# wise reply with a generic error message.
#
-# Note: this action disables further header or
-# body_checks inspection of the current message and
+# Note: this action disables further header or
+# body_checks inspection of the current message and
# affects all recipients.
#
# Postfix version 2.3 and later support enhanced sta-
@@ -249,26 +267,26 @@
# enhanced status code of "5.7.1".
#
# WARN optional text...
-# Log a warning with the optional text... (or log a
-# generic message) and inspect the next input line.
+# Log a warning with the optional text... (or log a
+# generic message) and inspect the next input line.
# This action is useful for debugging and for testing
# a pattern before applying more drastic actions.
#
# BUGS
-# Many people overlook the main limitations of header and
-# body_checks rules. These rules operate on one logical
-# message header or one body line at a time, and a decision
-# made for one line is not carried over to the next line.
+# Many people overlook the main limitations of header and
+# body_checks rules. These rules operate on one logical
+# message header or one body line at a time, and a decision
+# made for one line is not carried over to the next line.
# If text in the message body is encoded (RFC 2045) then the
-# rules have to specified for the encoded form. Likewise,
+# rules have to specified for the encoded form. Likewise,
# when message headers are encoded (RFC 2047) then the rules
# need to be specified for the encoded form.
#
-# Message headers added by the cleanup(8) daemon itself are
+# Message headers added by the cleanup(8) daemon itself are
# excluded from inspection. Examples of such message headers
# are From:, To:, Message-ID:, Date:.
#
-# Message headers deleted by the cleanup(8) daemon will be
+# Message headers deleted by the cleanup(8) daemon will be
# examined before they are deleted. Examples are: Bcc:, Con-
# tent-Length:, Return-Path:.
#
@@ -276,11 +294,11 @@
# body_checks
# Lookup tables with content filter rules for message
# body lines. These filters see one physical line at
-# a time, in chunks of at most $line_length_limit
+# a time, in chunks of at most $line_length_limit
# bytes.
#
# body_checks_size_limit
-# The amount of content per message body segment
+# The amount of content per message body segment
# (attachment) that is subjected to $body_checks fil-
# tering.
#
@@ -290,32 +308,32 @@
#
# nested_header_checks (default: $header_checks)
# Lookup tables with content filter rules for message
-# header lines: respectively, these are applied to
-# the initial message headers (not including MIME
-# headers), to the MIME headers anywhere in the mes-
-# sage, and to the initial headers of attached mes-
+# header lines: respectively, these are applied to
+# the initial message headers (not including MIME
+# headers), to the MIME headers anywhere in the mes-
+# sage, and to the initial headers of attached mes-
# sages.
#
-# Note: these filters see one logical message header
-# at a time, even when a message header spans multi-
-# ple lines. Message headers that are longer than
+# Note: these filters see one logical message header
+# at a time, even when a message header spans multi-
+# ple lines. Message headers that are longer than
# $header_size_limit characters are truncated.
#
# disable_mime_input_processing
-# While receiving mail, give no special treatment to
-# MIME related message headers; all text after the
+# While receiving mail, give no special treatment to
+# MIME related message headers; all text after the
# initial message headers is considered to be part of
-# the message body. This means that header_checks is
-# applied to all the initial message headers, and
+# the message body. This means that header_checks is
+# applied to all the initial message headers, and
# that body_checks is applied to the remainder of the
# message.
#
-# Note: when used in this manner, body_checks will
-# process a multi-line message header one line at a
+# Note: when used in this manner, body_checks will
+# process a multi-line message header one line at a
# time.
#
# EXAMPLES
-# Header pattern to block attachments with bad file name
+# Header pattern to block attachments with bad file name
# extensions.
#
# /etc/postfix/main.cf:
@@ -347,7 +365,7 @@
# RFC 2047, message header encoding for non-ASCII text
#
# README FILES
-# Use "postconf readme_directory" or "postconf html_direc-
+# Use "postconf readme_directory" or "postconf html_direc-
# tory" to locate this information.
# DATABASE_README, Postfix lookup table overview
# CONTENT_INSPECTION_README, Postfix content inspection overview
@@ -355,7 +373,7 @@
# BACKSCATTER_README, blocking returned forged mail
#
# LICENSE
-# The Secure Mailer license must be distributed with this
+# The Secure Mailer license must be distributed with this
# software.
#
# AUTHOR(S)
diff --git a/postfix/html/TLS_README.html b/postfix/html/TLS_README.html
index 8030a819d..054bb40b2 100644
--- a/postfix/html/TLS_README.html
+++ b/postfix/html/TLS_README.html
@@ -553,7 +553,8 @@ can specify any database type that can store objects of several
kbytes and that supports the sequence operator. DBM databases are
not suitable because they can only store small objects. The cache
is maintained by the tlsmgr(8) process, so there is no problem with
-concurrent access.
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high.
Example:
@@ -904,7 +905,10 @@ can specify any database type that can store objects of several
kbytes and that supports the sequence operator. DBM databases are
not suitable because they can only store small objects. The cache
is maintained by the tlsmgr(8) process, so there is no problem with
-concurrent access.
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high. Future
+Postfix SMTP servers may limit the number of sessions that a client
+is allowed to negotiate per unit time.
Example:
diff --git a/postfix/html/header_checks.5.html b/postfix/html/header_checks.5.html
index d372a9019..89e533ecb 100644
--- a/postfix/html/header_checks.5.html
+++ b/postfix/html/header_checks.5.html
@@ -208,8 +208,15 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
line, immediately before the input that
triggered the PREPEND action.
+ o The prepended text is not considered part of
+ the input stream. Unlike the result from the
+ REPLACE action, prepended text is not sub-
+ ject to header/body checks or address
+ rewriting, and does not affect the way that
+ Postfix adds missing message headers.
+
o When prepending text before a message header
- line, the prepended text must begin with a
+ line, the prepended text must begin with a
valid message header label.
o This action cannot be used to prepend multi-
@@ -218,35 +225,46 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
This feature is available in Postfix 2.1 and later.
REDIRECT user@domain
- Write a message redirection request to the queue
- file and inspect the next input line. After the
+ Write a message redirection request to the queue
+ file and inspect the next input line. After the
message is queued, it will be sent to the specified
address instead of the intended recipient(s).
- Note: this action overrides the FILTER action, and
- affects all recipients of the message. If multiple
- REDIRECT actions fire, only the last one is exe-
+ Note: this action overrides the FILTER action, and
+ affects all recipients of the message. If multiple
+ REDIRECT actions fire, only the last one is exe-
cuted.
This feature is available in Postfix 2.1 and later.
REPLACE text...
- Replace the current line with the specified text
+ Replace the current line with the specified text
and inspect the next input line.
- Note: when replacing a message header line, the
- replacement text must begin with a valid header
- label.
-
This feature is available in Postfix 2.2 and later.
+ The description below applies to Postfix 2.2.2 and
+ later.
+
+ Notes:
+
+ o When replacing a message header line, the
+ replacement text must begin with a valid
+ header label.
+
+ o The replaced text remains part of the input
+ stream. Unlike the result from the PREPEND
+ action, a replaced message header may be
+ subject to address rewriting and may affect
+ the way that Postfix adds missing message
+ headers.
REJECT optional text...
- Reject the entire message. Reply with optional
+ Reject the entire message. Reply with optional
text... when the optional text is specified, other-
wise reply with a generic error message.
- Note: this action disables further header or
- body_checks inspection of the current message and
+ Note: this action disables further header or
+ body_checks inspection of the current message and
affects all recipients.
Postfix version 2.3 and later support enhanced sta-
@@ -255,26 +273,26 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
enhanced status code of "5.7.1".
WARN optional text...
- Log a warning with the optional text... (or log a
- generic message) and inspect the next input line.
+ Log a warning with the optional text... (or log a
+ generic message) and inspect the next input line.
This action is useful for debugging and for testing
a pattern before applying more drastic actions.
BUGS
- Many people overlook the main limitations of header and
- body_checks rules. These rules operate on one logical
- message header or one body line at a time, and a decision
- made for one line is not carried over to the next line.
+ Many people overlook the main limitations of header and
+ body_checks rules. These rules operate on one logical
+ message header or one body line at a time, and a decision
+ made for one line is not carried over to the next line.
If text in the message body is encoded (RFC 2045) then the
- rules have to specified for the encoded form. Likewise,
+ rules have to specified for the encoded form. Likewise,
when message headers are encoded (RFC 2047) then the rules
need to be specified for the encoded form.
- Message headers added by the cleanup(8) daemon itself are
+ Message headers added by the cleanup(8) daemon itself are
excluded from inspection. Examples of such message headers
are From:, To:, Message-ID:, Date:.
- Message headers deleted by the cleanup(8) daemon will be
+ Message headers deleted by the cleanup(8) daemon will be
examined before they are deleted. Examples are: Bcc:, Con-
tent-Length:, Return-Path:.
@@ -282,11 +300,11 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
body_checks
Lookup tables with content filter rules for message
body lines. These filters see one physical line at
- a time, in chunks of at most $line_length_limit
+ a time, in chunks of at most $line_length_limit
bytes.
body_checks_size_limit
- The amount of content per message body segment
+ The amount of content per message body segment
(attachment) that is subjected to $body_checks fil-
tering.
@@ -296,32 +314,32 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
nested_header_checks (default: $header_checks)
Lookup tables with content filter rules for message
- header lines: respectively, these are applied to
- the initial message headers (not including MIME
- headers), to the MIME headers anywhere in the mes-
- sage, and to the initial headers of attached mes-
+ header lines: respectively, these are applied to
+ the initial message headers (not including MIME
+ headers), to the MIME headers anywhere in the mes-
+ sage, and to the initial headers of attached mes-
sages.
- Note: these filters see one logical message header
- at a time, even when a message header spans multi-
- ple lines. Message headers that are longer than
+ Note: these filters see one logical message header
+ at a time, even when a message header spans multi-
+ ple lines. Message headers that are longer than
$header_size_limit characters are truncated.
disable_mime_input_processing
- While receiving mail, give no special treatment to
- MIME related message headers; all text after the
+ While receiving mail, give no special treatment to
+ MIME related message headers; all text after the
initial message headers is considered to be part of
- the message body. This means that header_checks is
- applied to all the initial message headers, and
+ the message body. This means that header_checks is
+ applied to all the initial message headers, and
that body_checks is applied to the remainder of the
message.
- Note: when used in this manner, body_checks will
- process a multi-line message header one line at a
+ Note: when used in this manner, body_checks will
+ process a multi-line message header one line at a
time.
EXAMPLES
- Header pattern to block attachments with bad file name
+ Header pattern to block attachments with bad file name
extensions.
/etc/postfix/main.cf:
@@ -359,7 +377,7 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
BACKSCATTER_README, blocking returned forged mail
LICENSE
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
AUTHOR(S)
diff --git a/postfix/man/man5/header_checks.5 b/postfix/man/man5/header_checks.5
index bd794663d..e8954116d 100644
--- a/postfix/man/man5/header_checks.5
+++ b/postfix/man/man5/header_checks.5
@@ -194,6 +194,12 @@ Notes:
The prepended text is output on a separate line, immediately
before the input that triggered the \fBPREPEND\fR action.
.IP \(bu
+The prepended text is not considered part of the input
+stream. Unlike the result from the \fBREPLACE\fR action,
+prepended text is not subject to header/body checks or
+address rewriting, and does not affect the way that Postfix
+adds missing message headers.
+.IP \(bu
When prepending text before a message header line, the prepended
text must begin with a valid message header label.
.IP \(bu
@@ -216,10 +222,20 @@ This feature is available in Postfix 2.1 and later.
Replace the current line with the specified text and inspect the next
input line.
.sp
-Note: when replacing a message header line, the replacement text
-must begin with a valid header label.
+This feature is available in Postfix 2.2 and later. The
+description below applies to Postfix 2.2.2 and later.
.sp
-This feature is available in Postfix 2.2 and later.
+Notes:
+.RS
+.IP \(bu
+When replacing a message header line, the replacement text
+must begin with a valid header label.
+.IP \(bu
+The replaced text remains part of the input stream. Unlike
+the result from the \fBPREPEND\fR action, a replaced message
+header may be subject to address rewriting and may affect
+the way that Postfix adds missing message headers.
+.RE
.IP "\fBREJECT \fIoptional text...\fR
Reject the entire message. Reply with \fIoptional text...\fR when
the optional text is specified, otherwise reply with a generic error
diff --git a/postfix/proto/TLS_README.html b/postfix/proto/TLS_README.html
index 6b2165685..23f51d28b 100644
--- a/postfix/proto/TLS_README.html
+++ b/postfix/proto/TLS_README.html
@@ -553,7 +553,8 @@ can specify any database type that can store objects of several
kbytes and that supports the sequence operator. DBM databases are
not suitable because they can only store small objects. The cache
is maintained by the tlsmgr(8) process, so there is no problem with
-concurrent access.
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high.
Example:
@@ -904,7 +905,10 @@ can specify any database type that can store objects of several
kbytes and that supports the sequence operator. DBM databases are
not suitable because they can only store small objects. The cache
is maintained by the tlsmgr(8) process, so there is no problem with
-concurrent access.
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high. Future
+Postfix SMTP servers may limit the number of sessions that a client
+is allowed to negotiate per unit time.
Example:
diff --git a/postfix/proto/header_checks b/postfix/proto/header_checks
index 51220d4e6..45133981c 100644
--- a/postfix/proto/header_checks
+++ b/postfix/proto/header_checks
@@ -180,6 +180,12 @@
# The prepended text is output on a separate line, immediately
# before the input that triggered the \fBPREPEND\fR action.
# .IP \(bu
+# The prepended text is not considered part of the input
+# stream. Unlike the result from the \fBREPLACE\fR action,
+# prepended text is not subject to header/body checks or
+# address rewriting, and does not affect the way that Postfix
+# adds missing message headers.
+# .IP \(bu
# When prepending text before a message header line, the prepended
# text must begin with a valid message header label.
# .IP \(bu
@@ -202,10 +208,20 @@
# Replace the current line with the specified text and inspect the next
# input line.
# .sp
-# Note: when replacing a message header line, the replacement text
-# must begin with a valid header label.
+# This feature is available in Postfix 2.2 and later. The
+# description below applies to Postfix 2.2.2 and later.
# .sp
-# This feature is available in Postfix 2.2 and later.
+# Notes:
+# .RS
+# .IP \(bu
+# When replacing a message header line, the replacement text
+# must begin with a valid header label.
+# .IP \(bu
+# The replaced text remains part of the input stream. Unlike
+# the result from the \fBPREPEND\fR action, a replaced message
+# header may be subject to address rewriting and may affect
+# the way that Postfix adds missing message headers.
+# .RE
# .IP "\fBREJECT \fIoptional text...\fR
# Reject the entire message. Reply with \fIoptional text...\fR when
# the optional text is specified, otherwise reply with a generic error
diff --git a/postfix/src/cleanup/cleanup_api.c b/postfix/src/cleanup/cleanup_api.c
index fdceb52b3..15fbdc577 100644
--- a/postfix/src/cleanup/cleanup_api.c
+++ b/postfix/src/cleanup/cleanup_api.c
@@ -259,7 +259,7 @@ DSN_SPLIT dp;
if (state->errs != 0) {
if (CAN_BOUNCE()) {
- if (state->reason)
+ if (state->reason)
dsn_split(&dp, "5.0.0", state->reason);
else
detail = cleanup_stat_detail(state->errs);
@@ -267,8 +267,8 @@ DSN_SPLIT dp;
state->recip ? state->recip : "unknown",
state->recip ? state->recip : "unknown",
(long) 0, "none",
- detail ? detail->dsn : dp.dsn,
- state->time, "%s",
+ detail ? detail->dsn : DSN_CODE(dp.dsn),
+ state->time, "%s",
detail ? detail->text : dp.text) == 0
&& bounce_flush(BOUNCE_FLAG_CLEAN, state->queue_name,
state->queue_id,
diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c
index 3819363cd..51be87e99 100644
--- a/postfix/src/cleanup/cleanup_message.c
+++ b/postfix/src/cleanup/cleanup_message.c
@@ -296,22 +296,22 @@ static void cleanup_act_log(CLEANUP_STATE *state,
/* cleanup_act - act upon a header/body match */
-static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
- const char *value, const char *map_class)
+static const char *cleanup_act(CLEANUP_STATE *state, char *context,
+ const char *buf, const char *value,
+ const char *map_class)
{
const char *optional_text = value + strcspn(value, " \t");
int command_len = optional_text - value;
- VSTRING *bp;
- CLEANUP_STAT_DETAIL *detail;
while (*optional_text && ISSPACE(*optional_text))
optional_text++;
#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
-#define CLEANUP_ACT_KEEP 1
#define CLEANUP_ACT_DROP 0
if (STREQUAL(value, "REJECT", command_len)) {
+ CLEANUP_STAT_DETAIL *detail;
+
if (state->reason == 0) {
if (*optional_text) {
state->reason = dsn_prepend("5.7.1", optional_text);
@@ -323,17 +323,18 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
state->errs |= CLEANUP_STAT_CONT;
state->flags &= ~CLEANUP_FLAG_FILTER;
cleanup_act_log(state, "reject", context, buf, state->reason);
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
if (STREQUAL(value, "WARN", command_len)) {
cleanup_act_log(state, "warning", context, buf, optional_text);
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
if (STREQUAL(value, "FILTER", command_len)) {
if (*optional_text == 0) {
msg_warn("missing FILTER command argument in %s map", map_class);
} else if (strchr(optional_text, ':') == 0) {
- msg_warn("bad FILTER command %s in %s, need transport:destination",
+ msg_warn("bad FILTER command %s in %s -- "
+ "need transport:destination",
optional_text, map_class);
} else {
if (state->filter)
@@ -341,57 +342,52 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
state->filter = mystrdup(optional_text);
cleanup_act_log(state, "filter", context, buf, optional_text);
}
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
if (STREQUAL(value, "DISCARD", command_len)) {
cleanup_act_log(state, "discard", context, buf, optional_text);
state->flags |= CLEANUP_FLAG_DISCARD;
state->flags &= ~CLEANUP_FLAG_FILTER;
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
if (STREQUAL(value, "HOLD", command_len)) {
cleanup_act_log(state, "hold", context, buf, optional_text);
state->flags |= CLEANUP_FLAG_HOLD;
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
if (STREQUAL(value, "PREPEND", command_len)) {
if (*optional_text == 0) {
msg_warn("PREPEND action without text in %s map", map_class);
} else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0
&& !is_header(optional_text)) {
- msg_warn("bad PREPEND header text \"%s\" in %s map, "
+ msg_warn("bad PREPEND header text \"%s\" in %s map -- "
"need \"headername: headervalue\"",
optional_text, map_class);
} else {
cleanup_act_log(state, "prepend", context, buf, optional_text);
cleanup_out_string(state, REC_TYPE_NORM, optional_text);
}
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
if (STREQUAL(value, "REPLACE", command_len)) {
if (*optional_text == 0) {
msg_warn("REPLACE action without text in %s map", map_class);
- return (CLEANUP_ACT_KEEP);
- } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0) {
- if (!is_header(optional_text)) {
- msg_warn("bad REPLACE header text \"%s\" in %s map, "
- "need \"headername: headervalue\"",
- optional_text, map_class);
- return (CLEANUP_ACT_KEEP);
- }
- /* XXX Impedance mismatch. */
- bp = vstring_strcpy(vstring_alloc(100), optional_text);
- cleanup_out_header(state, bp);
- vstring_free(bp);
+ return (buf);
+ } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0
+ && !is_header(optional_text)) {
+ msg_warn("bad REPLACE header text \"%s\" in %s map -- "
+ "need \"headername: headervalue\"",
+ optional_text, map_class);
+ return (buf);
} else {
- cleanup_out_string(state, REC_TYPE_NORM, optional_text);
+ cleanup_act_log(state, "replace", context, buf, optional_text);
+ return (mystrdup(optional_text));
}
- cleanup_act_log(state, "replace", context, buf, optional_text);
- return (CLEANUP_ACT_DROP);
}
if (STREQUAL(value, "REDIRECT", command_len)) {
if (strchr(optional_text, '@') == 0) {
- msg_warn("bad REDIRECT target \"%s\" in %s map, need user@domain",
+ msg_warn("bad REDIRECT target \"%s\" in %s map -- "
+ "need user@domain",
optional_text, map_class);
} else {
if (state->redirect)
@@ -400,7 +396,7 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
cleanup_act_log(state, "redirect", context, buf, optional_text);
state->flags &= ~CLEANUP_FLAG_FILTER;
}
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
/* Allow and ignore optional text after the action. */
@@ -408,13 +404,13 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
return (CLEANUP_ACT_DROP);
if (STREQUAL(value, "DUNNO", command_len)) /* preferred */
- return (CLEANUP_ACT_KEEP);
+ return (buf);
if (STREQUAL(value, "OK", command_len)) /* compat */
- return (CLEANUP_ACT_KEEP);
+ return (buf);
msg_warn("unknown command in %s map: %s", map_class, value);
- return (CLEANUP_ACT_KEEP);
+ return (buf);
}
/* cleanup_header_callback - process one complete header line */
@@ -463,10 +459,17 @@ static void cleanup_header_callback(void *context, int header_class,
const char *value;
if ((value = maps_find(checks, header, 0)) != 0) {
- if (cleanup_act(state, CLEANUP_ACT_CTXT_HEADER,
- header, value, map_class)
- == CLEANUP_ACT_DROP)
+ const char *result;
+
+ if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_HEADER,
+ header, value, map_class))
+ == CLEANUP_ACT_DROP) {
return;
+ } else if (result != header) {
+ vstring_strcpy(header_buf, result);
+ hdr_opts = header_opts_find(result);
+ myfree((char *) result);
+ }
}
}
@@ -672,10 +675,17 @@ static void cleanup_body_callback(void *context, int type,
const char *value;
if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) {
- if (cleanup_act(state, CLEANUP_ACT_CTXT_BODY,
- buf, value, VAR_BODY_CHECKS)
- == CLEANUP_ACT_DROP)
+ const char *result;
+
+ if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_BODY,
+ buf, value, VAR_BODY_CHECKS))
+ == CLEANUP_ACT_DROP) {
+ return;
+ } else if (result != buf) {
+ cleanup_out(state, type, result, strlen(result));
+ myfree((char *) result);
return;
+ }
}
}
cleanup_out(state, type, buf, len);
diff --git a/postfix/src/discard/discard.c b/postfix/src/discard/discard.c
index 6f7fd1869..d5156c191 100644
--- a/postfix/src/discard/discard.c
+++ b/postfix/src/discard/discard.c
@@ -171,8 +171,8 @@ static int deliver_message(DELIVER_REQUEST *request)
if (rcpt->offset >= 0) {
status = sent(BOUNCE_FLAGS(request), request->queue_id,
rcpt->orig_addr, rcpt->address, rcpt->offset,
- "none", dp.dsn, request->arrival_time,
- "%s", dp.text);
+ "none", DSN_CODE(dp.dsn),
+ request->arrival_time, "%s", dp.text);
if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
deliver_completed(src, rcpt->offset);
result |= status;
diff --git a/postfix/src/error/error.c b/postfix/src/error/error.c
index 348772af0..9fc83cc14 100644
--- a/postfix/src/error/error.c
+++ b/postfix/src/error/error.c
@@ -173,7 +173,7 @@ static int deliver_message(DELIVER_REQUEST *request)
if (rcpt->offset >= 0) {
status = bounce_append(BOUNCE_FLAGS(request), request->queue_id,
rcpt->orig_addr, rcpt->address,
- rcpt->offset, "none", dp.dsn,
+ rcpt->offset, "none", DSN_CODE(dp.dsn),
request->arrival_time,
"%s", dp.text);
if (status == 0)
diff --git a/postfix/src/global/dsn_util.c b/postfix/src/global/dsn_util.c
index 677dabdae..740de2cb9 100644
--- a/postfix/src/global/dsn_util.c
+++ b/postfix/src/global/dsn_util.c
@@ -6,9 +6,24 @@
/* SYNOPSIS
/* #include
/*
+/* #define DSN_SIZE ...
+/*
+/* typedef struct { ... } DSN_BUF;
+/*
+/* void DSN_UPDATE(dsn_buf, dsn, len)
+/* DSN_BUF dsn_buf;
+/* const char *dsn;
+/* size_t len;
+/*
+/* const char *DSN_CODE(dsn_buf)
+/* DSN_BUF dsn_buf;
+/*
+/* char *DSN_CLASS(dsn_buf)
+/* DSN_BUF dsn_buf;
+/*
/* typedef struct {
/* .in +4
-/* char dsn[...]; /* RFC 3463 */
+/* DSN_BUF dsn; /* RFC 3463 detail */
/* const char *text; /* Free text */
/* .in -4
/* } DSN_SPLIT;
@@ -24,7 +39,7 @@
/*
/* typedef struct {
/* .in +4
-/* char dsn[...]; /* RFC 3463 */
+/* DSN_BUF dsn; /* RFC 3463 detail */
/* VSTRING *text; /* Free text */
/* .in -4
/* } DSN_VSTRING;
@@ -72,6 +87,18 @@
/* dsn_vstring_free() recycles the storage that was allocated
/* by dsn_vstring_alloc() and dsn_vstring_update().
/*
+/* DSN_UPDATE() is a helper macro to safely update an
+/* RFC 3463 detail code.
+/*
+/* DSN_CODE() is a helper macro to safely read an
+/* RFC 3463 detail code.
+/*
+/* DSN_CLASS() is a helper macro to safely read or update an
+/* RFC 3463 detail code class (i.e. the first digit).
+/*
+/* DSN_SIZE is the maximal length of an enhanced status
+/* code including the null string terminator.
+/*
/* dsn_valid() returns the length of the RFC 3463 detail code
/* at the beginning of text, or zero. It does not skip initial
/* whitespace.
@@ -167,15 +194,11 @@ DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
while (ISSPACE(*cp))
cp++;
if ((len = dsn_valid(cp)) > 0) {
- if (len >= sizeof(dp->dsn))
- msg_panic("dsn_split: bad DSN code length %d", len);
- DSN_BUF_UPDATE(dp->dsn, cp, len);
+ DSN_UPDATE(dp->dsn, cp, len);
cp += len + 1;
} else {
len = strlen(def_dsn);
- if (len >= sizeof(dp->dsn))
- msg_panic("dsn_split: bad default DSN code length %d", len);
- DSN_BUF_UPDATE(dp->dsn, def_dsn, len);
+ DSN_UPDATE(dp->dsn, def_dsn, len);
}
/*
@@ -195,7 +218,7 @@ char *dsn_prepend(const char *def_dsn, const char *text)
DSN_SPLIT dp;
dsn_split(&dp, def_dsn, text);
- return (concatenate(dp.dsn, " ", dp.text, (char *) 0));
+ return (concatenate(DSN_CODE(dp.dsn), " ", dp.text, (char *) 0));
}
/* dsn_vstring_alloc - create DSN+string storage */
@@ -205,7 +228,7 @@ DSN_VSTRING *dsn_vstring_alloc(int len)
DSN_VSTRING *dv;
dv = (DSN_VSTRING *) mymalloc(sizeof(*dv));
- dv->dsn[0] = 0;
+ DSN_CLASS(dv->dsn) = 0;
dv->vstring = vstring_alloc(len);
return(dv);
}
@@ -227,9 +250,9 @@ DSN_VSTRING *dsn_vstring_update(DSN_VSTRING *dv, const char *dsn,
size_t len;
if (dsn && *dsn) {
- if ((len = dsn_valid(dsn)) == 0 || len >= sizeof(dv->dsn))
+ if ((len = dsn_valid(dsn)) == 0)
msg_panic("dsn_vstring_update: bad dsn: \"%s\"", dsn);
- DSN_BUF_UPDATE(dv->dsn, dsn, len);
+ DSN_UPDATE(dv->dsn, dsn, len);
}
if (format && *format) {
va_start(ap, format);
diff --git a/postfix/src/global/dsn_util.h b/postfix/src/global/dsn_util.h
index fcd3622c4..f49aff9e2 100644
--- a/postfix/src/global/dsn_util.h
+++ b/postfix/src/global/dsn_util.h
@@ -23,21 +23,36 @@
#define DSN_DIGS2 3 /* middle digits */
#define DSN_DIGS3 3 /* trailing digits */
#define DSN_LEN (DSN_DIGS1 + 1 + DSN_DIGS2 + 1 + DSN_DIGS3)
-#define DSN_BUFSIZE (DSN_LEN + 1)
+#define DSN_SIZE (DSN_LEN + 1)
+
+ /*
+ * Storage for an enhanced status code. Avoid using malloc for itty-bitty
+ * strings with a known size limit.
+ */
+typedef struct {
+ char data[DSN_SIZE]; /* NOT a public interface */
+} DSN_BUF;
+
+#define DSN_UPDATE(dsn_buf, dsn, len) do { \
+ if (len >= sizeof((dsn_buf).data)) \
+ msg_panic("DSN_UPDATE: bad DSN code \"%.*s...\" length %d", \
+ sizeof((dsn_buf).data) - 1, dsn, len); \
+ strncpy((dsn_buf).data, (dsn), (len)); \
+ (dsn_buf).data[len] = 0; \
+ } while (0)
+
+#define DSN_CODE(dsn_buf) ((const char *) (dsn_buf).data)
+
+#define DSN_CLASS(dsn_buf) ((dsn_buf).data[0])
/*
* Split flat text into detail code and free text.
*/
typedef struct {
- char dsn[DSN_BUFSIZE]; /* RFC 3463 X.XXX.XXX detail */
+ DSN_BUF dsn; /* RFC 3463 X.XXX.XXX detail */
const char *text; /* free text */
} DSN_SPLIT;
-#define DSN_BUF_UPDATE(buf, text, len) do { \
- strncpy((buf), (text), (len)); \
- (buf)[len] = 0; \
- } while (0)
-
extern DSN_SPLIT *dsn_split(DSN_SPLIT *, const char *, const char *);
extern size_t dsn_valid(const char *);
@@ -50,7 +65,7 @@ extern char *dsn_prepend(const char *, const char *);
* Easy to update pair of detail code and free text.
*/
typedef struct {
- char dsn[DSN_LEN + 1]; /* RFC 3463 X.XXX.XXX detail */
+ DSN_BUF dsn; /* RFC 3463 X.XXX.XXX detail */
VSTRING *vstring; /* free text */
} DSN_VSTRING;
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 8a74d56bf..578989cf6 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change the patchlevel and the release date. Snapshots change the
* release date only.
*/
-#define MAIL_RELEASE_DATE "20050328"
+#define MAIL_RELEASE_DATE "20050329"
#define MAIL_VERSION_NUMBER "2.3"
#define VAR_MAIL_VERSION "mail_version"
diff --git a/postfix/src/global/pipe_command.c b/postfix/src/global/pipe_command.c
index fca7632fa..2653056c6 100644
--- a/postfix/src/global/pipe_command.c
+++ b/postfix/src/global/pipe_command.c
@@ -575,8 +575,8 @@ int pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
else if (dsn_valid(log_buf) > 0) {
/* XXX Assumes dsn_split() does not require 5.x.x in log_buf */
dsn_split(&dp, "5.3.0", log_buf);
- dsn_vstring_update(why, dp.dsn, "%s", dp.text);
- return (dp.dsn[0] == '4' ?
+ dsn_vstring_update(why, DSN_CODE(dp.dsn), "%s", dp.text);
+ return (DSN_CLASS(dp.dsn) == '4' ?
PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
}
/* Use compatible exit status. */
diff --git a/postfix/src/lmtp/lmtp.c b/postfix/src/lmtp/lmtp.c
index 79b7ee3c5..9eb6d87cb 100644
--- a/postfix/src/lmtp/lmtp.c
+++ b/postfix/src/lmtp/lmtp.c
@@ -401,12 +401,12 @@ static int deliver_message(DELIVER_REQUEST *request, char **unused_argv)
*/
if ((state->session = lmtp_connect(request->nexthop, why)) == 0) {
if (lmtp_errno == LMTP_RETRY) {
- why->dsn[0] = '4';
- lmtp_site_fail(state, why->dsn, 450,
+ DSN_CLASS(why->dsn) = '4';
+ lmtp_site_fail(state, DSN_CODE(why->dsn), 450,
"%s", vstring_str(why->vstring));
} else {
- why->dsn[0] = '5';
- lmtp_site_fail(state, why->dsn, 550,
+ DSN_CLASS(why->dsn) = '5';
+ lmtp_site_fail(state, DSN_CODE(why->dsn), 550,
"%s", vstring_str(why->vstring));
}
}
diff --git a/postfix/src/lmtp/lmtp.h b/postfix/src/lmtp/lmtp.h
index 560a712c7..6e21fac01 100644
--- a/postfix/src/lmtp/lmtp.h
+++ b/postfix/src/lmtp/lmtp.h
@@ -105,7 +105,7 @@ extern int lmtp_rset(LMTP_STATE *);
*/
typedef struct LMTP_RESP { /* server response */
int code; /* status */
- char dsn[DSN_BUFSIZE]; /* DSN detail */
+ DSN_BUF dsn; /* DSN detail */
char *str; /* text */
VSTRING *buf; /* origin of text */
} LMTP_RESP;
diff --git a/postfix/src/lmtp/lmtp_chat.c b/postfix/src/lmtp/lmtp_chat.c
index a5c1f5d8c..0a0c7e0fa 100644
--- a/postfix/src/lmtp/lmtp_chat.c
+++ b/postfix/src/lmtp/lmtp_chat.c
@@ -233,16 +233,16 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
* Extract RFC 821 reply code and RFC 2034 detail code. Use a default
* detail code if none was given.
*/
- rdata.dsn[0] = 0;
+ DSN_CLASS(rdata.dsn) = 0;
if (three_digs != 0) {
rdata.code = atoi(STR(state->buffer));
for (cp = STR(state->buffer) + 4; *cp == ' '; cp++)
/* void */ ;
- if ((len = dsn_valid(cp)) > 0 && len < sizeof(rdata.dsn)) {
- DSN_BUF_UPDATE(rdata.dsn, cp, len);
+ if ((len = dsn_valid(cp)) > 0 && len < sizeof(DSN_SIZE)) {
+ DSN_UPDATE(rdata.dsn, cp, len);
} else if (strchr("245", STR(state->buffer)[0]) != 0) {
- DSN_BUF_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
- rdata.dsn[0] = STR(state->buffer)[0];
+ DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
+ DSN_CLASS(rdata.dsn) = STR(state->buffer)[0];
}
} else {
rdata.code = 0;
diff --git a/postfix/src/lmtp/lmtp_proto.c b/postfix/src/lmtp/lmtp_proto.c
index f446f1f74..077c5951a 100644
--- a/postfix/src/lmtp/lmtp_proto.c
+++ b/postfix/src/lmtp/lmtp_proto.c
@@ -224,7 +224,7 @@ int lmtp_lhlo(LMTP_STATE *state)
* Read and parse the server's LMTP greeting banner.
*/
if (((resp = lmtp_chat_resp(state))->code / 100) != 2)
- return (lmtp_site_fail(state, resp->dsn, resp->code,
+ return (lmtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
"host %s refused to talk to me: %s",
session->namaddr, translit(resp->str, "\n", " ")));
@@ -233,7 +233,7 @@ int lmtp_lhlo(LMTP_STATE *state)
*/
lmtp_chat_cmd(state, "LHLO %s", var_myhostname);
if ((resp = lmtp_chat_resp(state))->code / 100 != 2)
- return (lmtp_site_fail(state, resp->dsn, resp->code,
+ return (lmtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -594,7 +594,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
*/
case LMTP_STATE_MAIL:
if (resp->code / 100 != 2) {
- lmtp_mesg_fail(state, resp->dsn, resp->code,
+ lmtp_mesg_fail(state, DSN_CODE(resp->dsn),
+ resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -634,7 +635,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
&& sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, rcpt->orig_addr,
rcpt->address, rcpt->offset,
- session->namaddr, resp->dsn,
+ session->namaddr,
+ DSN_CODE(resp->dsn),
request->arrival_time, "%s",
translit(resp->str, "\n", " ")) == 0) {
if (request->flags & DEL_REQ_FLAG_SUCCESS)
@@ -642,7 +644,7 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
rcpt->offset = 0; /* in case deferred */
}
} else {
- lmtp_rcpt_fail(state, resp->dsn,
+ lmtp_rcpt_fail(state, DSN_CODE(resp->dsn),
resp->code, rcpt,
"host %s said: %s (in reply to %s)",
session->namaddr,
@@ -665,7 +667,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
case LMTP_STATE_DATA:
if (resp->code / 100 != 3) {
if (nrcpt > 0)
- lmtp_mesg_fail(state, resp->dsn, resp->code,
+ lmtp_mesg_fail(state, DSN_CODE(resp->dsn),
+ resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -692,7 +695,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
if (sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, rcpt->orig_addr,
rcpt->address, rcpt->offset,
- session->namaddr, resp->dsn,
+ session->namaddr,
+ DSN_CODE(resp->dsn),
request->arrival_time,
"%s", resp->str) == 0) {
if (request->flags & DEL_REQ_FLAG_SUCCESS)
@@ -701,7 +705,7 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
}
}
} else {
- lmtp_rcpt_fail(state, resp->dsn,
+ lmtp_rcpt_fail(state, DSN_CODE(resp->dsn),
resp->code, rcpt,
"host %s said: %s (in reply to %s)",
session->namaddr,
diff --git a/postfix/src/local/command.c b/postfix/src/local/command.c
index a06cf0208..f419a0d3f 100644
--- a/postfix/src/local/command.c
+++ b/postfix/src/local/command.c
@@ -232,9 +232,10 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
break;
case PIPE_STAT_BOUNCE:
case PIPE_STAT_DEFER:
- deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+ deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+ defer_append : bounce_append)
(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr, why->dsn),
+ BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
"%s", vstring_str(why->vstring));
break;
case PIPE_STAT_CORRUPT:
diff --git a/postfix/src/local/file.c b/postfix/src/local/file.c
index 55e72e94e..b64474833 100644
--- a/postfix/src/local/file.c
+++ b/postfix/src/local/file.c
@@ -183,9 +183,10 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
deliver_status = DEL_STAT_DEFER;
} else if (mail_copy_status != 0) {
- deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+ deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+ defer_append : bounce_append)
(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr, why->dsn),
+ BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
"cannot append message to destination file %s: %s",
path, vstring_str(why->vstring));
} else {
diff --git a/postfix/src/local/mailbox.c b/postfix/src/local/mailbox.c
index a3f2723e2..a289300cd 100644
--- a/postfix/src/local/mailbox.c
+++ b/postfix/src/local/mailbox.c
@@ -212,9 +212,10 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
deliver_status = DEL_STAT_DEFER;
} else if (mail_copy_status != 0) {
- deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+ deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+ defer_append : bounce_append)
(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr, why->dsn),
+ BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
"cannot update mailbox %s for user %s. %s",
mailbox, state.msg_attr.user, vstring_str(why->vstring));
} else {
diff --git a/postfix/src/local/maildir.c b/postfix/src/local/maildir.c
index 6d6611e92..b634332e4 100644
--- a/postfix/src/local/maildir.c
+++ b/postfix/src/local/maildir.c
@@ -225,9 +225,10 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
deliver_status = DEL_STAT_DEFER;
} else if (mail_copy_status != 0) {
- deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+ deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+ defer_append : bounce_append)
(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr, why->dsn),
+ BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
"maildir delivery failed: %s", vstring_str(why->vstring));
if (errno == EACCES) {
msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
diff --git a/postfix/src/oqmgr/qmgr_deliver.c b/postfix/src/oqmgr/qmgr_deliver.c
index 67be2d78e..6da11329a 100644
--- a/postfix/src/oqmgr/qmgr_deliver.c
+++ b/postfix/src/oqmgr/qmgr_deliver.c
@@ -260,7 +260,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
if (VSTRING_LEN(reason)) {
/* Sanitize the DSN status from delivery agent. */
dsn_split(&dp, "4.0.0", printable(vstring_str(reason), '?'));
- qmgr_queue_throttle(queue, dp.dsn, *dp.text ?
+ qmgr_queue_throttle(queue, DSN_CODE(dp.dsn), *dp.text ?
dp.text : "unknown problem");
if (queue->window == 0)
qmgr_defer_todo(queue, queue->dsn, queue->reason);
diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c
index d4f6107d4..4c40d6597 100644
--- a/postfix/src/pipe/pipe.c
+++ b/postfix/src/pipe/pipe.c
@@ -1076,7 +1076,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
argv_free(export_env);
deliver_status = eval_command_status(command_status, service, request,
- request->fp, why->dsn,
+ request->fp, DSN_CODE(why->dsn),
vstring_str(why->vstring));
/*
diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c
index 486963d53..fc698e551 100644
--- a/postfix/src/qmgr/qmgr_deliver.c
+++ b/postfix/src/qmgr/qmgr_deliver.c
@@ -265,7 +265,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
if (VSTRING_LEN(reason)) {
/* Sanitize the DSN status from delivery agent. */
dsn_split(&dp, "4.0.0", printable(vstring_str(reason), '?'));
- qmgr_queue_throttle(queue, dp.dsn, *dp.text ?
+ qmgr_queue_throttle(queue, DSN_CODE(dp.dsn), *dp.text ?
dp.text : "unknown problem");
if (queue->window == 0)
qmgr_defer_todo(queue, queue->dsn, queue->reason);
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index 4328e6426..2302be217 100644
--- a/postfix/src/smtp/smtp.h
+++ b/postfix/src/smtp/smtp.h
@@ -248,7 +248,7 @@ extern int smtp_quit(SMTP_STATE *);
*/
typedef struct SMTP_RESP { /* server response */
int code; /* status */
- char dsn[DSN_BUFSIZE]; /* DSN detail */
+ DSN_BUF dsn; /* DSN detail */
char *str; /* text */
VSTRING *buf; /* origin of text */
} SMTP_RESP;
diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c
index b25647a6d..ff9f219ad 100644
--- a/postfix/src/smtp/smtp_chat.c
+++ b/postfix/src/smtp/smtp_chat.c
@@ -256,16 +256,16 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
* Extract RFC 821 reply code and RFC 2034 detail. Use a default detail
* code if none was given.
*/
- rdata.dsn[0] = 0;
+ DSN_CLASS(rdata.dsn) = 0;
if (three_digs != 0) {
rdata.code = atoi(STR(session->buffer));
for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
/* void */ ;
- if ((len = dsn_valid(cp)) > 0 && len < sizeof(rdata.dsn)) {
- DSN_BUF_UPDATE(rdata.dsn, cp, len);
+ if ((len = dsn_valid(cp)) > 0 && len < sizeof(DSN_SIZE)) {
+ DSN_UPDATE(rdata.dsn, cp, len);
} else if (strchr("245", STR(session->buffer)[0]) != 0) {
- DSN_BUF_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
- rdata.dsn[0] = STR(session->buffer)[0];
+ DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
+ DSN_CLASS(rdata.dsn) = STR(session->buffer)[0];
}
} else {
rdata.code = 0;
diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
index b01c1201f..db25f3fc8 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -757,11 +757,13 @@ int smtp_connect(SMTP_STATE *state)
*/
state->final_server = 1; /* XXX */
if (smtp_errno == SMTP_ERR_RETRY) {
- why->dsn[0] = '4';
- smtp_site_fail(state, why->dsn, 450, "%s", STR(why->vstring));
+ DSN_CLASS(why->dsn) = '4';
+ smtp_site_fail(state, DSN_CODE(why->dsn), 450,
+ "%s", STR(why->vstring));
} else {
- why->dsn[0] = '5';
- smtp_site_fail(state, why->dsn, 550, "%s", STR(why->vstring));
+ DSN_CLASS(why->dsn) = '5';
+ smtp_site_fail(state, DSN_CODE(why->dsn), 550,
+ "%s", STR(why->vstring));
}
/*
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index e98ebed78..1cfbb6815 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -275,10 +275,10 @@ int smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
case 5:
if (var_smtp_skip_5xx_greeting) {
resp->code = 400;
- resp->dsn[0] = '4';
+ DSN_CLASS(resp->dsn) = '4';
}
default:
- return (smtp_site_fail(state, resp->dsn, resp->code,
+ return (smtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -336,7 +336,7 @@ int smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2)
- return (smtp_site_fail(state, resp->dsn, resp->code,
+ return (smtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -520,7 +520,8 @@ int smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
*/
session->features &= ~SMTP_FEATURE_STARTTLS;
if (session->tls_enforce_tls)
- return (smtp_site_fail(state, resp->dsn, resp->code,
+ return (smtp_site_fail(state, DSN_CODE(resp->dsn),
+ resp->code,
"TLS is required, but host %s refused to start TLS: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@@ -600,9 +601,8 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
* use TLS session caching???
*/
serverid = vstring_alloc(10);
- vstring_sprintf(serverid, "%s:%u", session->addr, ntohs(session->port));
- if (session->helo != 0)
- vstring_sprintf_append(serverid, ":%s", session->helo);
+ vstring_sprintf(serverid, "%s:%u:%s", session->addr,
+ ntohs(session->port), session->helo ? session->helo : "");
session->tls_context =
tls_client_start(smtp_tls_ctx, session->stream,
var_smtp_starttls_tmout,
@@ -615,49 +615,6 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
return (smtp_site_fail(state, "4.7.5", 450,
"Cannot start TLS: handshake failure"));
- /*
- * Give up when TLS is required, we can parse the server certificate's
- * CommonName field, but server certificate verification failed.
- *
- * In enforce_peername state, the handshake would already have been
- * terminated by the certificate verification call-back routine, so the
- * check here is for logging only.
- *
- * XXX It appears that the CommonName field is used as an indicator that a
- * server certificate is available. If the latter is what we want, then
- * we should test for that instead.
- */
- if (session->tls_info.peer_CN != NULL) {
- if (!session->tls_info.peer_verified) {
- msg_info("Server certificate could not be verified");
- if (session->tls_enforce_tls) {
- tls_client_stop(smtp_tls_ctx, session->stream,
- var_smtp_starttls_tmout, 1,
- &(session->tls_info));
- return (smtp_site_fail(state, "4.7.5", 450,
- "TLS failure: Cannot verify server certificate"));
- }
- }
- }
-
- /*
- * Give up when TLS is required but no server certificate is available
- * (or we could not parse the certificate's CommonName) field.
- *
- * XXX The test below is not accurate: the server hostname verification may
- * use the dNSNames instead of the CommonName. We really should be
- * testing if a certificate is available.
- */
- else {
- if (session->tls_enforce_tls) {
- tls_client_stop(smtp_tls_ctx, session->stream,
- var_smtp_starttls_tmout, 1,
- &(session->tls_info));
- return (smtp_site_fail(state, "4.7.5", 450,
- "TLS failure: Cannot verify server hostname"));
- }
- }
-
/*
* At this point we have to re-negotiate the "EHLO" to reget the
* feature-list.
@@ -1206,7 +1163,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
*/
case SMTP_STATE_MAIL:
if (resp->code / 100 != 2) {
- smtp_mesg_fail(state, resp->dsn, resp->code,
+ smtp_mesg_fail(state, DSN_CODE(resp->dsn), resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1233,7 +1190,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
#ifdef notdef
if (resp->code == 552) {
resp->code = 452;
- resp->dsn[0] = '4';
+ DSN_CLASS(resp->dsn) = '4';
}
#endif
rcpt = request->rcpt_list.info + recv_rcpt;
@@ -1241,10 +1198,10 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
++nrcpt;
/* If trace-only, mark the recipient done. */
if (DEL_REQ_TRACE_ONLY(request->flags))
- smtp_rcpt_done(state, resp->dsn,
+ smtp_rcpt_done(state, DSN_CODE(resp->dsn),
resp->str, rcpt);
} else {
- smtp_rcpt_fail(state, resp->dsn,
+ smtp_rcpt_fail(state, DSN_CODE(resp->dsn),
resp->code, rcpt,
"host %s said: %s (in reply to %s)",
session->namaddr,
@@ -1267,7 +1224,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
case SMTP_STATE_DATA:
if (resp->code / 100 != 3) {
if (nrcpt > 0)
- smtp_mesg_fail(state, resp->dsn, resp->code,
+ smtp_mesg_fail(state, DSN_CODE(resp->dsn),
+ resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1289,7 +1247,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
case SMTP_STATE_DOT:
if (nrcpt > 0) {
if (resp->code / 100 != 2) {
- smtp_mesg_fail(state, resp->dsn, resp->code,
+ smtp_mesg_fail(state, DSN_CODE(resp->dsn),
+ resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@@ -1298,7 +1257,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (!SMTP_RCPT_ISMARKED(rcpt))
- smtp_rcpt_done(state, resp->dsn,
+ smtp_rcpt_done(state, DSN_CODE(resp->dsn),
resp->str, rcpt);
}
}
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index ee3c6d7b1..05069e5e9 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -1873,7 +1873,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
if (STREQUAL(value, "REJECT", cmd_len)) {
dsn_split(&dp, "5.7.1", cmd_text);
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
- var_access_map_code, dp.dsn,
+ var_access_map_code, DSN_CODE(dp.dsn),
"<%s>: %s rejected: %s",
reply_name, reply_class,
*dp.text ? dp.text : "Access denied"));
@@ -1982,7 +1982,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
if (STREQUAL(value, DEFER_IF_PERMIT, cmd_len)) {
dsn_split(&dp, "4.7.1", cmd_text);
DEFER_IF_PERMIT3(state, MAIL_ERROR_POLICY,
- 450, dp.dsn,
+ 450, DSN_CODE(dp.dsn),
"<%s>: %s rejected: %s",
reply_name, reply_class,
*dp.text ? dp.text : "Service unavailable");
@@ -1996,7 +1996,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
if (STREQUAL(value, DEFER_IF_REJECT, cmd_len)) {
dsn_split(&dp, "4.7.1", cmd_text);
DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
- 450, dp.dsn,
+ 450, DSN_CODE(dp.dsn),
"<%s>: %s rejected: %s",
reply_name, reply_class,
*dp.text ? dp.text : "Service unavailable");
@@ -2045,9 +2045,10 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
def_dsn[0] = value[0];
dsn_split(&dp, def_dsn, cmd_text);
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
- code, dp.dsn,
+ code, DSN_CODE(dp.dsn),
"<%s>: %s rejected: %s",
- reply_name, reply_class, dp.text));
+ reply_name, reply_class,
+ *dp.text ? dp.text : "Access denied"));
}
/*
@@ -2892,7 +2893,8 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl,
code = atoi(STR(why));
dsn_split(&dp, "4.7.1", STR(why) + 4);
result = smtpd_check_reject(state, MAIL_ERROR_POLICY,
- code, dp.dsn, "%s", *dp.text ?
+ code, DSN_CODE(dp.dsn),
+ "%s", *dp.text ?
dp.text : "Service unavailable");
}
diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h
index a6514d776..26d6efba2 100644
--- a/postfix/src/tls/tls.h
+++ b/postfix/src/tls/tls.h
@@ -59,7 +59,7 @@ typedef struct {
char issuer_CN[CCERT_BUFSIZ];
unsigned char md[EVP_MAX_MD_SIZE];
char fingerprint[EVP_MAX_MD_SIZE * 3];
- char peername_save[HOST_BUFSIZ + 1];
+ char *peername;
int enforce_verify_errors;
int enforce_CN;
int hostname_matched;
@@ -68,18 +68,6 @@ typedef struct {
#define TLS_BIO_BUFSIZE 8192
-#define NEW_TLS_CONTEXT(p) do { \
- p = (TLScontext_t *) mymalloc(sizeof(*p)); \
- memset((char *) p, 0, sizeof(*p)); \
- p->serverid = 0; \
- } while (0)
-
-#define FREE_TLS_CONTEXT(p) do { \
- if ((p)->serverid) \
- myfree((p)->serverid); \
- myfree((char *) (p)); \
- } while (0)
-
typedef struct {
int peer_verified;
int hostname_matched;
@@ -179,10 +167,7 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
/*
* tls_verify.c
*/
-extern int tls_verify_certificate_callback(int, X509_STORE_CTX *, int);
-
-#define TLS_VERIFY_DEFAULT (0)
-#define TLS_VERIFY_PEERNAME (1<<0)
+extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
/*
* tls_certkey.c
@@ -198,6 +183,8 @@ extern int tls_set_my_certificate_key_info(SSL_CTX *, const char *,
*/
extern int TLScontext_index;
+extern TLScontext_t *tls_alloc_context(int, const char *);
+extern void tls_free_context(TLScontext_t *);
extern void tls_print_errors(void);
extern void tls_info_callback(const SSL *, int, int);
extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long);
diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
index a47e322da..61b04eadb 100644
--- a/postfix/src/tls/tls_client.c
+++ b/postfix/src/tls/tls_client.c
@@ -9,8 +9,8 @@
/* SSL_CTX *tls_client_init(verifydepth)
/* int verifydepth; /* unused */
/*
-/* TLScontext_t *tls_client_start(client_ctx, stream, timeout,
-/* enforce_peername, peername,
+/* TLScontext_t *tls_client_start(client_ctx, stream, timeout,
+/* enforce_peername, peername,
/* serverid, tls_info)
/* SSL_CTX *client_ctx;
/* VSTREAM *stream;
@@ -140,38 +140,25 @@ static const char hexcodes[] = "0123456789ABCDEF";
*/
static int tls_client_cache = 0;
-/* client_verify_callback - certificate verification wrapper */
-
-static int client_verify_callback(int ok, X509_STORE_CTX *ctx)
-{
- return (tls_verify_certificate_callback(ok, ctx, TLS_VERIFY_PEERNAME));
-}
-
/* load_clnt_session - load session from client cache (non-callback) */
-static SSL_SESSION *load_clnt_session(const char *cache_id,
- int enforce_peername)
+static SSL_SESSION *load_clnt_session(const char *cache_id)
{
SSL_SESSION *session = 0;
VSTRING *session_data = vstring_alloc(2048);
- int flags = 0;
-
-#define TLS_FLAG_ENFORCE_PEERNAME (1<<0)
/*
* Prepare the query.
*/
if (var_smtp_tls_loglevel >= 3)
msg_info("looking for session %s in client cache", cache_id);
- if (enforce_peername)
- flags |= TLS_FLAG_ENFORCE_PEERNAME;
/*
* Look up and activate the SSL_SESSION object. Errors are non-fatal,
* since caching is only an optimization.
*/
- if (tls_mgr_lookup(tls_client_cache, cache_id, OPENSSL_VERSION_NUMBER,
- flags, session_data) == TLS_MGR_STAT_OK) {
+ if (tls_mgr_lookup(tls_client_cache, cache_id,
+ session_data) == TLS_MGR_STAT_OK) {
session = tls_session_activate(STR(session_data), LEN(session_data));
if (session) {
if (var_smtp_tls_loglevel >= 3)
@@ -194,7 +181,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
TLScontext_t *TLScontext;
VSTRING *session_data;
const char *cache_id;
- int flags = 0;
/*
* Look up the cache ID string for this session object.
@@ -205,14 +191,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
if (var_smtp_tls_loglevel >= 3)
msg_info("save session %s to client cache", cache_id);
- /*
- * Remember whether peername matching was enforced when the session was
- * created. If later enforce mode is enabled, we do not want to reuse a
- * session that was not sufficiently checked.
- */
- if (TLScontext->enforce_verify_errors && TLScontext->enforce_CN)
- flags |= TLS_FLAG_ENFORCE_PEERNAME;
-
#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
/*
@@ -235,7 +213,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
session_data = tls_session_passivate(session);
if (session_data)
tls_mgr_update(tls_client_cache, cache_id,
- OPENSSL_VERSION_NUMBER, flags,
STR(session_data), LEN(session_data));
/*
@@ -248,12 +225,24 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
return (1);
}
+/* uncache_session - remove session from the external cache */
+
+static void uncache_session(TLScontext_t *TLScontext)
+{
+ if (TLScontext->serverid == 0)
+ return;
+
+ if (var_smtp_tls_loglevel >= 3)
+ msg_info("remove session %s from client cache", TLScontext->serverid);
+
+ tls_mgr_delete(tls_client_cache, TLScontext->serverid);
+}
+
/* tls_client_init - initialize client-side TLS engine */
SSL_CTX *tls_client_init(int unused_verifydepth)
{
int off = 0;
- int verify_flags = SSL_VERIFY_NONE;
SSL_CTX *client_ctx;
int cache_types;
@@ -376,14 +365,20 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
* Finally, the setup for the server certificate checking, done "by the
* book".
*/
- SSL_CTX_set_verify(client_ctx, verify_flags, client_verify_callback);
+ SSL_CTX_set_verify(client_ctx, SSL_VERIFY_NONE,
+ tls_verify_certificate_callback);
/*
- * Initialize the session cache. In order to share cached sessions among
- * multiple SMTP client processes, we use an external cache and set the
- * internal cache size to a minimum value of 1.
+ * Initialize the session cache.
+ *
+ * Since the client does not search an internal cache, we simply disable it.
+ * It is only useful for expiring old sessions, but we do that in the
+ * tlsmgr(8).
+ *
+ * This makes SSL_CTX_remove_session() not useful for flushing broken
+ * sessions from the external cache, so we must delete them directly (not
+ * via a callback).
*/
- SSL_CTX_sess_set_cache_size(client_ctx, 1);
SSL_CTX_set_timeout(client_ctx, var_smtp_tls_scache_timeout);
/*
@@ -401,13 +396,13 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
* OpenSSL can, however, automatically save newly created sessions for
* us by callback (we create the session name in the call-back
* function).
- *
- * Disable automatic clearing of cache entries, as the client process
- * has limited lifetime anyway and we can call the cleanup routine
- * directly.
*/
SSL_CTX_set_session_cache_mode(client_ctx,
- SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+ SSL_SESS_CACHE_CLIENT |
+#ifdef SSL_SESS_CACHE_NO_INTERNAL_STORE
+ SSL_SESS_CACHE_NO_INTERNAL_STORE |
+#endif
+ SSL_SESS_CACHE_NO_AUTO_CLEAR);
SSL_CTX_sess_set_new_cb(client_ctx, new_client_session_cb);
}
@@ -422,6 +417,121 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
return (client_ctx);
}
+/* match_hostname - match hostname against pattern */
+
+static int match_hostname(const char *pattern, const char *hostname)
+{
+ char *peername_left;
+
+ return (strcasecmp(hostname, pattern) == 0
+ || (pattern[0] == '*' && pattern[1] == '.' && pattern[2] != 0
+ && (peername_left = strchr(hostname, '.')) != 0
+ && strcasecmp(peername_left + 1, pattern + 2) == 0));
+}
+
+/* verify_extract_peer - verify peer name and extract peer information */
+
+static void verify_extract_peer(const char *peername, X509 * peercert,
+ TLScontext_t *TLScontext, tls_info_t *tls_info)
+{
+ char buf[1024];
+ int i;
+ int r;
+ int hostname_matched;
+ int dNSName_found;
+
+ STACK_OF(GENERAL_NAME) * gens;
+
+ tls_info->peer_verified =
+ (SSL_get_verify_result(TLScontext->con) == X509_V_OK);
+
+ if (TLScontext->enforce_CN != 0 && tls_info->peer_verified != 0) {
+
+ /*
+ * Verify the name(s) in the peer certificate against the peer
+ * hostname. Log peer hostname/certificate mis-matches. If a match is
+ * required but fails, bail out with a verification error.
+ */
+ hostname_matched = dNSName_found = 0;
+
+ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
+ if (gens) {
+ for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) {
+ const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
+
+ if (gn->type == GEN_DNS) {
+ dNSName_found++;
+ if ((hostname_matched =
+ match_hostname((char *) gn->d.ia5->data, peername)))
+ break;
+ }
+ }
+ sk_GENERAL_NAME_free(gens);
+ }
+ if (dNSName_found) {
+ if (!hostname_matched)
+ msg_info("certificate peer name verification failed for "
+ "%s: %d dNSNames in certificate found, "
+ "but none matches", peername, dNSName_found);
+ } else {
+ buf[0] = '\0';
+ if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peercert),
+ NID_commonName, buf,
+ sizeof(buf))) {
+ msg_info("certificate peer name verification failed for"
+ " %s: cannot parse subject CommonName", peername);
+ tls_print_errors();
+ } else {
+ hostname_matched = match_hostname(buf, peername);
+ if (!hostname_matched)
+ msg_info("certificate peer name verification failed "
+ "for %s: CommonName mis-match: %s",
+ peername, buf);
+ }
+ }
+
+ TLScontext->hostname_matched = hostname_matched;
+ }
+ tls_info->hostname_matched = TLScontext->hostname_matched;
+
+ TLScontext->peer_CN[0] = '\0';
+ if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peercert),
+ NID_commonName, TLScontext->peer_CN,
+ sizeof(TLScontext->peer_CN))) {
+ msg_info("Could not parse server's subject CN");
+ tls_print_errors();
+ }
+ tls_info->peer_CN = TLScontext->peer_CN;
+
+ TLScontext->issuer_CN[0] = '\0';
+ if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peercert),
+ NID_commonName, TLScontext->issuer_CN,
+ sizeof(TLScontext->issuer_CN))) {
+ msg_info("Could not parse server's issuer CN");
+ tls_print_errors();
+ }
+ if (!TLScontext->issuer_CN[0]) {
+ /* No issuer CN field, use Organization instead */
+ if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peercert),
+ NID_organizationName, TLScontext->issuer_CN,
+ sizeof(TLScontext->issuer_CN))) {
+ msg_info("Could not parse server's issuer Organization");
+ tls_print_errors();
+ }
+ }
+ tls_info->issuer_CN = TLScontext->issuer_CN;
+
+ if (var_smtp_tls_loglevel >= 1) {
+ if (tls_info->peer_verified
+ && (!TLScontext->enforce_CN || TLScontext->hostname_matched))
+ msg_info("Verified: subject_CN=%s, issuer=%s",
+ TLScontext->peer_CN, TLScontext->issuer_CN);
+ else
+ msg_info("Unverified: subject_CN=%s, issuer=%s",
+ TLScontext->peer_CN, TLScontext->issuer_CN);
+ }
+}
+
/*
* 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,
@@ -435,10 +545,9 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
tls_info_t *tls_info)
{
int sts;
- SSL_SESSION *session, *old_session;
+ SSL_SESSION *session;
SSL_CIPHER *cipher;
- X509 *peer;
- int verify_flags;
+ X509 *peercert;
TLScontext_t *TLScontext;
if (var_smtp_tls_loglevel >= 1)
@@ -448,30 +557,20 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
* Allocate a new TLScontext for the new connection and get an SSL
* structure. Add the location of TLScontext to the SSL to later retrieve
* the information inside the tls_verify_certificate_callback().
- *
- * XXX Need a dedicated procedure for consistent initialization of all the
- * fields in this structure.
*/
-#define PEERNAME_SIZE sizeof(TLScontext->peername_save)
-
- NEW_TLS_CONTEXT(TLScontext);
- TLScontext->log_level = var_smtp_tls_loglevel;
- strncpy(TLScontext->peername_save, peername, PEERNAME_SIZE - 1);
- TLScontext->peername_save[PEERNAME_SIZE - 1] = 0;
- (void) lowercase(TLScontext->peername_save);
+ TLScontext = tls_alloc_context(var_smtp_tls_loglevel, peername);
TLScontext->serverid = mystrdup(serverid);
if ((TLScontext->con = (SSL *) SSL_new(client_ctx)) == NULL) {
msg_info("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
msg_info("Could not set application data for 'TLScontext->con'");
tls_print_errors();
- SSL_free(TLScontext->con);
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
@@ -480,10 +579,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
* tls_verify_certificate_callback().
*/
if (enforce_peername) {
- verify_flags = SSL_VERIFY_PEER;
TLScontext->enforce_verify_errors = 1;
TLScontext->enforce_CN = 1;
- SSL_set_verify(TLScontext->con, verify_flags, client_verify_callback);
+ SSL_set_verify(TLScontext->con, SSL_VERIFY_PEER,
+ tls_verify_certificate_callback);
} else {
TLScontext->enforce_verify_errors = 0;
TLScontext->enforce_CN = 0;
@@ -501,11 +600,9 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
&TLScontext->network_bio, TLS_BIO_BUFSIZE)) {
msg_info("Could not obtain BIO_pair");
tls_print_errors();
- SSL_free(TLScontext->con);
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
- old_session = NULL;
/*
* Try to load an existing session from the TLS session cache.
@@ -515,10 +612,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
* will be reused.
*/
if (tls_client_cache) {
- old_session = load_clnt_session(serverid, enforce_peername);
- if (old_session) {
- SSL_set_session(TLScontext->con, old_session);
- SSL_SESSION_free(old_session); /* 200411 */
+ session = load_clnt_session(serverid);
+ if (session) {
+ SSL_set_session(TLScontext->con, session);
+ SSL_SESSION_free(session); /* 200411 */
#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
/*
@@ -532,7 +629,7 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
* The development version of 0.9.7 can have this bug, too. It
* has been fixed on 2000/11/29.
*/
- SSL_set_verify_result(TLScontext->con, old_session->verify_result);
+ SSL_set_verify_result(TLScontext->con, session->verify_result);
#endif
}
@@ -581,17 +678,17 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
if (sts <= 0) {
msg_info("SSL_connect error to %s: %d", peername, sts);
tls_print_errors();
- session = SSL_get_session(TLScontext->con);
- if (session) {
- SSL_CTX_remove_session(client_ctx, session);
- if (var_smtp_tls_loglevel >= 2)
- msg_info("SSL session removed");
- }
- SSL_free(TLScontext->con);
- BIO_free(TLScontext->network_bio); /* 200411 */
- FREE_TLS_CONTEXT(TLScontext);
+ uncache_session(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
+
+ /*
+ * The TLS engine is active. Switch to the tls_timed_read/write()
+ * functions and make the TLScontext available to those functions.
+ */
+ tls_stream_start(stream, TLScontext);
+
if (var_smtp_tls_loglevel >= 3 && SSL_session_reused(TLScontext->con))
msg_info("Reusing old session");
@@ -600,52 +697,18 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
/*
- * Let's see whether a peer certificate is available and what is the
- * actual information. We want to save it for later use.
+ * Do peername verification if requested and extract useful information
+ * from the certificate for later use.
*/
- peer = SSL_get_peer_certificate(TLScontext->con);
- if (peer != NULL) {
- if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
- tls_info->peer_verified = 1;
-
- tls_info->hostname_matched = TLScontext->hostname_matched;
-
- TLScontext->peer_CN[0] = '\0';
- if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
- NID_commonName, TLScontext->peer_CN,
- sizeof(TLScontext->peer_CN))) {
- msg_info("Could not parse server's subject CN");
- tls_print_errors();
- }
- tls_info->peer_CN = TLScontext->peer_CN;
-
- TLScontext->issuer_CN[0] = '\0';
- if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
- NID_commonName, TLScontext->issuer_CN,
- sizeof(TLScontext->issuer_CN))) {
- msg_info("Could not parse server's issuer CN");
- tls_print_errors();
- }
- if (!TLScontext->issuer_CN[0]) {
- /* No issuer CN field, use Organization instead */
- if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
- NID_organizationName, TLScontext->issuer_CN,
- sizeof(TLScontext->issuer_CN))) {
- msg_info("Could not parse server's issuer Organization");
- tls_print_errors();
- }
- }
- tls_info->issuer_CN = TLScontext->issuer_CN;
-
- if (var_smtp_tls_loglevel >= 1) {
- if (tls_info->peer_verified)
- msg_info("Verified: subject_CN=%s, issuer=%s",
- TLScontext->peer_CN, TLScontext->issuer_CN);
- else
- msg_info("Unverified: subject_CN=%s, issuer=%s",
- TLScontext->peer_CN, TLScontext->issuer_CN);
- }
- X509_free(peer);
+ if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) {
+ verify_extract_peer(peername, peercert, TLScontext, tls_info);
+ X509_free(peercert);
+ }
+ if (enforce_peername && !TLScontext->hostname_matched) {
+ msg_info("Server certificate could not be verified for %s:"
+ " hostname mismatch", peername);
+ tls_client_stop(client_ctx, stream, timeout, 0, tls_info);
+ return (0);
}
/*
@@ -657,15 +720,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
tls_info->cipher_usebits = SSL_CIPHER_get_bits(cipher,
&(tls_info->cipher_algbits));
- /*
- * The TLS engine is active. Switch to the tls_timed_read/write()
- * functions and make the TLScontext available to those functions.
- */
- tls_stream_start(stream, TLScontext);
-
if (var_smtp_tls_loglevel >= 1)
- msg_info("TLS connection established to %s: %s with cipher %s (%d/%d bits)",
- peername, tls_info->protocol, tls_info->cipher_name,
+ msg_info("TLS connection established to %s: %s with cipher %s"
+ " (%d/%d bits)", peername,
+ tls_info->protocol, tls_info->cipher_name,
tls_info->cipher_usebits, tls_info->cipher_algbits);
tls_int_seed();
diff --git a/postfix/src/tls/tls_mgr.c b/postfix/src/tls/tls_mgr.c
index 3572b1254..61f45f820 100644
--- a/postfix/src/tls/tls_mgr.c
+++ b/postfix/src/tls/tls_mgr.c
@@ -13,21 +13,15 @@
/* int tls_mgr_policy(cache_types)
/* int *cache_types;
/*
-/* int tls_mgr_update(cache_type, cache_id,
-/* openssl_version, flags, buf, len)
+/* int tls_mgr_update(cache_type, cache_id, buf, len)
/* int cache_type;
/* const char *cache_id;
-/* long openssl_version;
-/* int flags;
/* const char *buf;
/* int len;
/*
-/* int tls_mgr_lookup(cache_type, cache_id,
-/* openssl_version, flags, buf)
+/* int tls_mgr_lookup(cache_type, cache_id, buf)
/* int cache_type;
/* const char *cache_id;
-/* long openssl_version;
-/* int flags;
/* VSTRING *buf;
/*
/* int tls_mgr_delete(cache_type, cache_id)
@@ -66,12 +60,6 @@
/* One of TLS_MGR_SCACHE_CLIENT or TLS_MGR_SCACHE_SERVER (see above).
/* .IP cache_id
/* The session cache lookup key.
-/* .IP openssl_version
-/* The OpenSSL version. Sessions saved by the wrong OpenSSL version are
-/* deleted, to avoid compatibility problems.
-/* .IP flags
-/* Flags that must be set in the retrieved cache entry; it not,
-/* the cache entry is deleted.
/* .IP buf
/* The result or input buffer.
/* .IP len
@@ -211,8 +199,7 @@ int tls_mgr_policy(int *policy)
/* tls_mgr_lookup - request cached session */
-int tls_mgr_lookup(int cache_type, const char *cache_id,
- long openssl_vsn, int flags, VSTRING *buf)
+int tls_mgr_lookup(int cache_type, const char *cache_id, VSTRING *buf)
{
int status;
@@ -230,8 +217,6 @@ int tls_mgr_lookup(int cache_type, const char *cache_id,
ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP,
ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
- ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, openssl_vsn,
- ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, flags,
ATTR_TYPE_END,
ATTR_FLAG_MISSING, /* Reply */
ATTR_TYPE_NUM, TLS_MGR_ATTR_STATUS, &status,
@@ -244,7 +229,6 @@ int tls_mgr_lookup(int cache_type, const char *cache_id,
/* tls_mgr_update - save session to cache */
int tls_mgr_update(int cache_type, const char *cache_id,
- long openssl_vsn, int flags,
const char *buf, int len)
{
int status;
@@ -263,8 +247,6 @@ int tls_mgr_update(int cache_type, const char *cache_id,
ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_UPDATE,
ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
- ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, openssl_vsn,
- ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, flags,
ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, len, buf,
ATTR_TYPE_END,
ATTR_FLAG_MISSING, /* Reply */
@@ -363,24 +345,18 @@ int main(int unused_ac, char **av)
vstream_printf("status=%d seed=%s\n", status, STR(hex));
vstring_free(hex);
vstring_free(buf);
- } else if (COMMAND(argv, "lookup", 5)) {
+ } else if (COMMAND(argv, "lookup", 3)) {
VSTRING *buf = vstring_alloc(10);
int cache_type = atoi(argv->argv[1]);
- long openssl_vsn = atol(argv->argv[3]);
- int flags = atoi(argv->argv[4]);
- status = tls_mgr_lookup(cache_type, argv->argv[2],
- openssl_vsn, flags, buf);
+ status = tls_mgr_lookup(cache_type, argv->argv[2], buf);
vstream_printf("status=%d session=%.*s\n",
status, LEN(buf), STR(buf));
- } else if (COMMAND(argv, "update", 6)) {
+ } else if (COMMAND(argv, "update", 4)) {
int cache_type = atoi(argv->argv[1]);
- long openssl_vsn = atol(argv->argv[3]);
- int flags = atoi(argv->argv[4]);
status = tls_mgr_update(cache_type, argv->argv[2],
- openssl_vsn, flags,
- argv->argv[5], strlen(argv->argv[5]));
+ argv->argv[3], strlen(argv->argv[3]));
vstream_printf("status=%d\n", status);
} else if (COMMAND(argv, "delete", 3)) {
int cache_type = atoi(argv->argv[1]);
@@ -391,8 +367,8 @@ int main(int unused_ac, char **av)
vstream_printf("usage:\n"
"seed byte_count\n"
"policy\n"
- "lookup cache_type cache_id openssl_version flags\n"
- "update cache_type cache_id openssl_version flags session\n"
+ "lookup cache_type cache_id\n"
+ "update cache_type cache_id session\n"
"delete cache_type cache_id\n");
}
vstream_fflush(VSTREAM_OUT);
diff --git a/postfix/src/tls/tls_mgr.h b/postfix/src/tls/tls_mgr.h
index 3b108a448..92aa5f50d 100644
--- a/postfix/src/tls/tls_mgr.h
+++ b/postfix/src/tls/tls_mgr.h
@@ -27,12 +27,9 @@
#define TLS_MGR_ATTR_CACHE_TYPE "cache_type"
#define TLS_MGR_ATTR_SEED "seed"
#define TLS_MGR_ATTR_CACHE_ID "cache_id"
-#define TLS_MGR_ATTR_VERSION "version"
-#define TLS_MGR_ATTR_FLAGS "flags"
#define TLS_MGR_ATTR_SESSION "session"
#define TLS_MGR_ATTR_SIZE "size"
#define TLS_MGR_ATTR_STATUS "status"
-#define TLS_MGR_ATTR_FLAGS "flags"
/*
* TLS manager request status codes.
@@ -52,12 +49,10 @@
*/
extern int tls_mgr_seed(VSTRING *, int);
extern int tls_mgr_policy(int *);
-extern int tls_mgr_lookup(int, const char *, long, int, VSTRING *);
-extern int tls_mgr_update(int, const char *, long, int, const char *, int);
+extern int tls_mgr_lookup(int, const char *, VSTRING *);
+extern int tls_mgr_update(int, const char *, const char *, int);
extern int tls_mgr_delete(int, const char *);
-#define TLS_MGR_NO_FLAGS 0
-
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
index 03976abc7..2efb820d2 100644
--- a/postfix/src/tls/tls_misc.c
+++ b/postfix/src/tls/tls_misc.c
@@ -7,6 +7,13 @@
/* #define TLS_INTERNAL
/* #include
/*
+/* TLScontext_t *tls_alloc_context(log_level, peername)
+/* int log_level;
+/* const char *peername;
+/*
+/* void tls_free_context(TLScontext)
+/* TLScontext_t *TLScontext;
+/*
/* void tls_print_errors()
/*
/* void tls_info_callback(ssl, where, ret)
@@ -25,6 +32,12 @@
/* This module implements routines that support the TLS client
/* and server internals.
/*
+/* tls_alloc_context() creates an initialized TLScontext
+/* structure with the specified peer name and logging level.
+/*
+/* tls_free_context() destroys a TLScontext structure
+/* together with OpenSSL structures that are attached to it.
+/*
/* tls_print_errors() queries the OpenSSL error stack,
/* logs the error messages, and clears the error stack.
/*
@@ -69,6 +82,7 @@
#include
#include
#include
+#include
/* TLS library. */
@@ -83,6 +97,52 @@
*/
int TLScontext_index = -1;
+/* tls_alloc_context - allocate TLScontext */
+
+TLScontext_t *tls_alloc_context(int log_level, const char *peername)
+{
+ TLScontext_t *TLScontext;
+
+ /*
+ * PORTABILITY: Do not assume that null pointers are all-zero bits.
+ * Use explicit assignments to initialize pointers.
+ *
+ * See the C language FAQ item 5.17, or if you have time to burn,
+ * http://www.google.com/search?q=zero+bit+null+pointer
+ */
+ TLScontext = (TLScontext_t *) mymalloc(sizeof(TLScontext_t));
+ memset((char *) TLScontext, 0, sizeof(*TLScontext));
+ TLScontext->con = 0;
+ TLScontext->internal_bio = 0;
+ TLScontext->network_bio = 0;
+ TLScontext->serverid = 0;
+ TLScontext->log_level = log_level;
+ TLScontext->peername = lowercase(mystrdup(peername));
+
+ return (TLScontext);
+}
+
+/* tls_free_context - deallocate TLScontext and members */
+
+void tls_free_context(TLScontext_t *TLScontext)
+{
+
+ /*
+ * Free the SSL structure and the BIOs. Warning: the internal_bio is
+ * connected to the SSL structure and is automatically freed with it. Do
+ * not free it again (core dump)!! Only free the network_bio.
+ */
+ if (TLScontext->con != 0)
+ SSL_free(TLScontext->con);
+ if (TLScontext->network_bio)
+ BIO_free(TLScontext->network_bio);
+ if (TLScontext->peername)
+ myfree(TLScontext->peername);
+ if (TLScontext->serverid)
+ myfree(TLScontext->serverid);
+ myfree((char *) TLScontext);
+}
+
/* tls_print_errors - print and clear the error stack */
void tls_print_errors(void)
diff --git a/postfix/src/tls/tls_scache.c b/postfix/src/tls/tls_scache.c
index c00789cc3..f8a314db4 100644
--- a/postfix/src/tls/tls_scache.c
+++ b/postfix/src/tls/tls_scache.c
@@ -15,36 +15,22 @@
/* void tls_scache_close(cache)
/* TLS_SCACHE *cache;
/*
-/* int tls_scache_lookup(cache, cache_id, openssl_version,
-/* flags, out_openssl_version, out_flags,
-/* out_session)
+/* int tls_scache_lookup(cache, cache_id, out_session)
/* TLS_SCACHE *cache;
/* const char *cache_id;
-/* long openssl_version;
-/* int flags;
-/* long *out_openssl_version;
-/* int *out_flags;
/* VSTRING *out_session;
/*
-/* int tls_scache_update(cache, cache_id, openssl_version,
-/* flags, session, session_len)
+/* int tls_scache_update(cache, cache_id, session, session_len)
/* TLS_SCACHE *cache;
/* const char *cache_id;
-/* long openssl_version;
-/* int flags;
/* const char *session;
/* int session_len;
/*
-/* int tls_scache_sequence(cache, first_next, openssl_version, flags,
-/* out_cache_id, out_openssl_version, out_flags,
+/* int tls_scache_sequence(cache, first_next, out_cache_id,
/* VSTRING *out_session)
/* TLS_SCACHE *cache;
/* int first_next;
-/* long openssl_version;
-/* int flags;
/* char **out_cache_id;
-/* long *out_openssl_version;
-/* int *out_flags;
/* VSTRING *out_session;
/*
/* int tls_scache_delete(cache, cache_id)
@@ -53,11 +39,7 @@
/* DESCRIPTION
/* This module maintains Postfix TLS session cache files.
/* each session is stored under a lookup key (hostname or
-/* session ID) together with the OpenSSL version that
-/* created the session and application-specific flags.
-/* Upon lookup, the OpenSSL version and flags can be
-/* specified as optional filters. Entries that don't
-/* satisfy the filter requirements are silently deleted.
+/* session ID).
/*
/* tls_scache_open() opens the specified TLS session cache
/* and returns a handle that must be used for subsequent
@@ -67,25 +49,20 @@
/* and releases memory that was allocated by tls_scache_open().
/*
/* tls_scache_lookup() looks up the specified session in the
-/* specified cache, and applies the session timeout, openssl
-/* version and flags restrictions. Entries that don't satisfy
-/* the requirements are silently deleted.
+/* specified cache, and applies session timeout restrictions.
+/* Entries that are too old are silently deleted.
/*
/* tls_scache_update() updates the specified TLS session cache
/* with the specified session information.
/*
/* tls_scache_sequence() iterates over the specified TLS session
-/* cache and looks up the first or next entry. If that entry
-/* matches the session timeout, OpenSSL version and flags
-/* restrictions, tls_scache_sequence() saves the entry by
-/* updating the result parameters; otherwise it deletes the
-/* entry and does not update the result parameters. Specify
-/* TLS_SCACHE_SEQUENCE_NOTHING
-/* as the third and last argument to disable OpenSSL version
-/* and flags restrictions, and to disable saving of cache
-/* entry content or cache entry ID information. This is useful
-/* when purging expired entries. A result value of zero means
-/* that the end of the cache was reached.
+/* cache and either returns the first or next entry that has not
+/* timed out, or returns no data. Entries that are too old are
+/* silently deleted. Specify TLS_SCACHE_SEQUENCE_NOTHING as the
+/* third and last argument to disable saving of cache entry
+/* content or cache entry ID information. This is useful when
+/* purging expired entries. A result value of zero means that
+/* the end of the cache was reached.
/*
/* tls_scache_delete() removes the specified cache entry from
/* the specified TLS session cache.
@@ -104,38 +81,18 @@
/* (next cache element).
/* .IP cache_id
/* Session cache lookup key.
-/* .IP openssl_version
-/* When storing information, the OpenSSL version that generated a
-/* session. When retrieving information, delete cache entries that
-/* don't match the specified OpenSSL version.
-/*
-/* Specify TLS_SCACHE_ANY_OPENSSL_VSN to match any OpenSSL version.
-/* .IP flags
-/* When storing information, application flags that specify properties
-/* of a session. When retrieving information, delete cache entries that
-/* have the specified flags set.
-/*
-/* Specify TLS_SCACHE_ANY_FLAGS to match any flags value.
/* .IP session
/* Storage for session information.
/* .IP session_len
/* The size of the session information in bytes.
/* .IP out_cache_id
-/* .IP out_openssl_version
-/* .IP out_flags
/* .IP out_session
-/* Storage for saving the cache_id, openssl_version, flags
-/* or session information of the current cache entry.
+/* Storage for saving the cache_id or session information of the
+/* current cache entry.
/*
/* Specify TLS_SCACHE_DONT_NEED_CACHE_ID to avoid saving
/* the session cache ID of the cache entry.
/*
-/* Specify TLS_SCACHE_DONT_NEED_OPENSSL_VSN to avoid
-/* saving the OpenSSL version in the cache entry.
-/*
-/* Specify TLS_SCACHE_DONT_NEED_FLAGS to avoid
-/* saving the flags information in the cache entry.
-/*
/* Specify TLS_SCACHE_DONT_NEED_SESSION to avoid
/* saving the session information in the cache entry.
/* DIAGNOSTICS
@@ -192,15 +149,10 @@
* database when it is opened.
*/
typedef struct {
- long scache_db_version; /* obsolete */
- long openssl_version; /* clients may differ... */
time_t timestamp; /* time when saved */
- int flags; /* enforcement etc. */
char session[1]; /* actually a bunch of bytes */
} TLS_SCACHE_ENTRY;
-#define TLS_SCACHE_DB_VERSION 0x00000003L
-
/*
* SLMs.
*/
@@ -210,7 +162,6 @@ typedef struct {
/* tls_scache_encode - encode TLS session cache entry */
static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
- long openssl_version, int flags,
const char *session,
int session_len)
{
@@ -226,10 +177,7 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
*/
binary_data_len = session_len + offsetof(TLS_SCACHE_ENTRY, session);
entry = (TLS_SCACHE_ENTRY *) mymalloc(binary_data_len);
- entry->scache_db_version = TLS_SCACHE_DB_VERSION;
- entry->openssl_version = openssl_version;
entry->timestamp = time((time_t *) 0);
- entry->flags = flags;
memcpy(entry->session, session, session_len);
/*
@@ -242,13 +190,8 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
* Logging.
*/
if (cp->log_level >= 3)
- msg_info("write %s TLS cache entry %s: cache_version=%ld"
- " openssl_version=0x%lx flags=0x%x time=%ld [data %d bytes]",
- cp->cache_label, cache_id,
- (long) entry->scache_db_version,
- (long) entry->openssl_version,
- entry->flags,
- (long) entry->timestamp,
+ msg_info("write %s TLS cache entry %s: time=%ld [data %d bytes]",
+ cp->cache_label, cache_id, (long) entry->timestamp,
session_len);
/*
@@ -263,9 +206,6 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
const char *hex_data, int hex_data_len,
- long openssl_version, int flags,
- long *out_openssl_version,
- int *out_flags,
VSTRING *out_session)
{
TLS_SCACHE_ENTRY *entry;
@@ -281,8 +221,8 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
}
/*
- * Disassemble the TLS session cache entry and enforce the restrictions
- * specified as version numbers or flags.
+ * Disassemble the TLS session cache entry and enforce version number
+ * restrictions.
*
* No early returns or we have a memory leak.
*/
@@ -294,28 +234,14 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
cp->cache_label, cache_id, hex_data);
FREE_AND_RETURN(bin_data, 0);
}
-
- /*
- * Before doing anything else, verify that the database format version
- * matches this program.
- */
entry = (TLS_SCACHE_ENTRY *) STR(bin_data);
- if (entry->scache_db_version != TLS_SCACHE_DB_VERSION) {
- msg_warn("%s TLS cache: cache version mis-match for %s: 0x%lx != 0x%lx",
- cp->cache_label, cache_id, entry->scache_db_version,
- TLS_SCACHE_DB_VERSION);
- FREE_AND_RETURN(bin_data, 0);
- }
/*
* Logging.
*/
if (cp->log_level >= 3)
- msg_info("read %s TLS cache entry %s: cache_version=%ld"
- " openssl_version=0x%lx time=%ld flags=0x%x [data %d bytes]",
- cp->cache_label, cache_id, (long) entry->scache_db_version,
- (long) entry->openssl_version, (long) entry->timestamp,
- entry->flags,
+ msg_info("read %s TLS cache entry %s: time=%ld [data %d bytes]",
+ cp->cache_label, cache_id, (long) entry->timestamp,
LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
/*
@@ -324,28 +250,9 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
if (entry->timestamp + cp->timeout < time((time_t *) 0))
FREE_AND_RETURN(bin_data, 0);
- /*
- * Optional restrictions.
- */
- if (openssl_version != 0 && entry->openssl_version != openssl_version) {
- msg_warn("%s TLS cache: openssl version mis-match for %s: 0x%lx != 0x%lx",
- cp->cache_label, cache_id, entry->openssl_version,
- openssl_version);
- FREE_AND_RETURN(bin_data, 0);
- }
- if (flags != 0 && (entry->flags & flags) != flags) {
- msg_warn("%s TLS cache: flags mis-match for %s: 0x%x is not subset of 0x%x",
- cp->cache_label, cache_id, entry->flags, flags);
- FREE_AND_RETURN(bin_data, 0);
- }
-
/*
* Optional output.
*/
- if (out_openssl_version != 0)
- *out_openssl_version = entry->openssl_version;
- if (out_flags != 0)
- *out_flags = entry->flags;
if (out_session != 0)
vstring_memcpy(out_session, entry->session,
LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
@@ -359,8 +266,6 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
/* tls_scache_lookup - load session from cache */
int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
- long openssl_version, int flags,
- long *out_openssl_version, int *out_flags,
VSTRING *session)
{
const char *hex_data;
@@ -369,8 +274,7 @@ int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
* Logging.
*/
if (cp->log_level >= 3)
- msg_info("lookup %s session id=%s ssl=0x%lx flags=0x%x",
- cp->cache_label, cache_id, openssl_version, flags);
+ msg_info("lookup %s session id=%s", cp->cache_label, cache_id);
/*
* Initialize. Don't leak data.
@@ -385,15 +289,10 @@ int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
return (0);
/*
- * Decode entry and verify version and flags information.
- *
- * XXX We throw away sessions when flags don't match. If we want to allow
- * for co-existing cache entries with different flags, the flags would
- * have to be encoded in the cache lookup key.
+ * Decode entry and verify version information.
*/
if (tls_scache_decode(cp, cache_id, hex_data, strlen(hex_data),
- openssl_version, flags, out_openssl_version,
- out_flags, session) == 0) {
+ session) == 0) {
tls_scache_delete(cp, cache_id);
return (0);
} else {
@@ -404,7 +303,6 @@ int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
/* tls_scache_update - save session to cache */
int tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
- long openssl_version, int flags,
const char *buf, int len)
{
VSTRING *hex_data;
@@ -413,14 +311,13 @@ int tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
* Logging.
*/
if (cp->log_level >= 3)
- msg_info("put %s session id=%s ssl=0x%lx flags=0x%x [data %d bytes]",
- cp->cache_label, cache_id, openssl_version, flags, len);
+ msg_info("put %s session id=%s [data %d bytes]",
+ cp->cache_label, cache_id, len);
/*
* Encode the cache entry.
*/
- hex_data =
- tls_scache_encode(cp, cache_id, openssl_version, flags, buf, len);
+ hex_data = tls_scache_encode(cp, cache_id, buf, len);
/*
* Store the cache entry.
@@ -441,11 +338,7 @@ int tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
/* tls_scache_sequence - get first/next TLS session cache entry */
int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
- long openssl_version,
- int flags,
char **out_cache_id,
- long *out_openssl_version,
- int *out_flags,
VSTRING *out_session)
{
const char *member;
@@ -468,8 +361,8 @@ int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
/*
* Find the first or next database entry. Activate the passivated entry
- * and check the version, time stamp and flags information. Schedule the
- * entry for deletion if it is bad or too old.
+ * and check the time stamp. Schedule the entry for deletion if it is too
+ * old.
*
* Save the member (cache id) so that it will not be clobbered by the
* tls_scache_lookup() call below.
@@ -477,9 +370,7 @@ int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
found_entry = (dict_seq(cp->db, first_next, &member, &value) == 0);
if (found_entry) {
keep_entry = tls_scache_decode(cp, member, value, strlen(value),
- openssl_version, flags,
- out_openssl_version,
- out_flags, out_session);
+ out_session);
if (keep_entry && out_cache_id)
*out_cache_id = mystrdup(member);
saved_member = mystrdup(member);
@@ -494,9 +385,7 @@ int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
cp->flags &= ~TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
saved_cursor = cp->saved_cursor;
cp->saved_cursor = 0;
- tls_scache_lookup(cp, saved_cursor, cp->saved_openssl_version,
- cp->saved_flags, (long *) 0, (int *) 0,
- (VSTRING *) 0);
+ tls_scache_lookup(cp, saved_cursor, (VSTRING *) 0);
myfree(saved_cursor);
}
@@ -517,11 +406,8 @@ int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
*/
if (found_entry) {
cp->saved_cursor = saved_member;
- if (keep_entry == 0) {
+ if (keep_entry == 0)
cp->flags |= TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
- cp->saved_openssl_version = openssl_version;
- cp->saved_flags = flags;
- }
}
return (found_entry);
}
diff --git a/postfix/src/tls/tls_scache.h b/postfix/src/tls/tls_scache.h
index 2e51d2803..dd545d656 100644
--- a/postfix/src/tls/tls_scache.h
+++ b/postfix/src/tls/tls_scache.h
@@ -27,31 +27,22 @@ typedef struct {
int log_level; /* smtp(d)_tls_log_level */
int timeout; /* smtp(d)_tls_session_cache_timeout */
char *saved_cursor; /* cursor cache ID */
- long saved_openssl_version; /* cursor OpenSSL version */
- int saved_flags; /* cursor lookup flags */
} TLS_SCACHE;
#define TLS_SCACHE_FLAG_DEL_SAVED_CURSOR (1<<0)
extern TLS_SCACHE *tls_scache_open(const char *, const char *, int, int);
extern void tls_scache_close(TLS_SCACHE *);
-extern int tls_scache_lookup(TLS_SCACHE *, const char *, long, int, long *, int *, VSTRING *);
-extern int tls_scache_update(TLS_SCACHE *, const char *, long, int, const char *, int);
+extern int tls_scache_lookup(TLS_SCACHE *, const char *, VSTRING *);
+extern int tls_scache_update(TLS_SCACHE *, const char *, const char *, int);
extern int tls_scache_delete(TLS_SCACHE *, const char *);
-extern int tls_scache_sequence(TLS_SCACHE *, int, long, int, char **, long *, int *, VSTRING *);
-
-#define TLS_SCACHE_ANY_OPENSSL_VSN ((long) 0)
-#define TLS_SCACHE_ANY_FLAGS (0)
+extern int tls_scache_sequence(TLS_SCACHE *, int, char **, VSTRING *);
#define TLS_SCACHE_DONT_NEED_CACHE_ID ((char **) 0)
-#define TLS_SCACHE_DONT_NEED_OPENSSL_VSN ((long *) 0)
-#define TLS_SCACHE_DONT_NEED_FLAGS ((int *) 0)
#define TLS_SCACHE_DONT_NEED_SESSION ((VSTRING *) 0)
#define TLS_SCACHE_SEQUENCE_NOTHING \
- TLS_SCACHE_ANY_FLAGS, TLS_SCACHE_ANY_OPENSSL_VSN, \
- TLS_SCACHE_DONT_NEED_CACHE_ID, TLS_SCACHE_DONT_NEED_OPENSSL_VSN, \
- TLS_SCACHE_DONT_NEED_FLAGS, TLS_SCACHE_DONT_NEED_SESSION
+ TLS_SCACHE_DONT_NEED_CACHE_ID, TLS_SCACHE_DONT_NEED_SESSION
/* LICENSE
/* .ad
diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c
index d71955eab..488d116b8 100644
--- a/postfix/src/tls/tls_server.c
+++ b/postfix/src/tls/tls_server.c
@@ -143,13 +143,6 @@ static char server_session_id_context[] = "Postfix/TLS";
static int tls_server_cache = 0;
-/* server_verify_callback - server verification wrapper */
-
-static int server_verify_callback(int ok, X509_STORE_CTX *ctx)
-{
- return (tls_verify_certificate_callback(ok, ctx, TLS_VERIFY_DEFAULT));
-}
-
/* get_server_session_cb - callback to retrieve session from server cache */
static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
@@ -161,13 +154,10 @@ static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
VSTRING *session_data = vstring_alloc(2048);
SSL_SESSION *session = 0;
-#define MAKE_SERVER_CACHE_ID(id, len) \
+#define HEX_CACHE_ID(id, len) \
hex_encode(vstring_alloc(2 * (len) + 1), (char *) (id), (len))
- /*
- * Encode the session ID.
- */
- cache_id = MAKE_SERVER_CACHE_ID(session_id, session_id_length);
+ cache_id = HEX_CACHE_ID(session_id, session_id_length);
if (var_smtpd_tls_loglevel >= 3)
msg_info("looking up session %s in server cache", STR(cache_id));
@@ -175,7 +165,6 @@ static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
* Load the session from cache and decode it.
*/
if (tls_mgr_lookup(tls_server_cache, STR(cache_id),
- OPENSSL_VERSION_NUMBER, TLS_MGR_NO_FLAGS,
session_data) == TLS_MGR_STAT_OK) {
session = tls_session_activate(STR(session_data), LEN(session_data));
if (session && (var_smtpd_tls_loglevel >= 3))
@@ -191,6 +180,23 @@ static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
return (session);
}
+/* uncache_session - remove session from internal & external cache */
+
+static void uncache_session(SSL_CTX *ctx, TLScontext_t *TLScontext)
+{
+ VSTRING *cache_id;
+ SSL_SESSION *session = SSL_get_session(TLScontext->con);
+
+ SSL_CTX_remove_session(ctx, session);
+
+ cache_id = HEX_CACHE_ID(session->session_id, session->session_id_length);
+ if (var_smtpd_tls_loglevel >= 3)
+ msg_info("remove session %s from server cache", STR(cache_id));
+
+ tls_mgr_delete(tls_server_cache, STR(cache_id));
+ vstring_free(cache_id);
+}
+
/* new_server_session_cb - callback to save session to server cache */
static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
@@ -198,11 +204,7 @@ static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
VSTRING *cache_id;
VSTRING *session_data;
- /*
- * Encode the session ID.
- */
- cache_id =
- MAKE_SERVER_CACHE_ID(session->session_id, session->session_id_length);
+ cache_id = HEX_CACHE_ID(session->session_id, session->session_id_length);
if (var_smtpd_tls_loglevel >= 3)
msg_info("save session %s to server cache", STR(cache_id));
@@ -212,7 +214,6 @@ static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
session_data = tls_session_passivate(session);
if (session_data)
tls_mgr_update(tls_server_cache, STR(cache_id),
- OPENSSL_VERSION_NUMBER, TLS_MGR_NO_FLAGS,
STR(session_data), LEN(session_data));
/*
@@ -381,16 +382,25 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
*/
if (askcert)
verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
- SSL_CTX_set_verify(server_ctx, verify_flags, server_verify_callback);
+ SSL_CTX_set_verify(server_ctx, verify_flags,
+ tls_verify_certificate_callback);
if (*var_smtpd_tls_CAfile)
SSL_CTX_set_client_CA_list(server_ctx,
SSL_load_client_CA_file(var_smtpd_tls_CAfile));
/*
- * Initialize the session cache. In order to share cached sessions among
- * multiple SMTP server processes, we use an external cache and set the
- * internal cache size to a minimum value of 1. Access to the external
- * cache is handled by the appropriate callback functions.
+ * Initialize the session cache.
+ *
+ * With a large number of concurrent smtpd(8) processes, it is not a good
+ * idea to cache multiple large session objects in each process. We set
+ * the internal cache size to 1, and don't register a "remove_cb" so as
+ * to avoid deleting good sessions from the external cache prematurely
+ * (when the internal cache is full, OpenSSL removes sessions from the
+ * external cache also)!
+ *
+ * This makes SSL_CTX_remove_session() not useful for flushing broken
+ * sessions from the external cache, so we must delete them directly (not
+ * via a callback).
*
* Set a session id context to identify to what type of server process
* created a session. In our case, the context is simply the name of the
@@ -415,7 +425,8 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
if (tls_mgr_policy(&cache_types) == TLS_MGR_STAT_OK
&& (tls_server_cache = (cache_types & TLS_MGR_SCACHE_SERVER)) != 0) {
SSL_CTX_set_session_cache_mode(server_ctx,
- SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+ SSL_SESS_CACHE_SERVER |
+ SSL_SESS_CACHE_NO_AUTO_CLEAR);
SSL_CTX_sess_set_get_cb(server_ctx, get_server_session_cb);
SSL_CTX_sess_set_new_cb(server_ctx, new_server_session_cb);
}
@@ -448,7 +459,6 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
int verify_flags;
unsigned int n;
TLScontext_t *TLScontext;
- SSL_SESSION *session;
SSL_CIPHER *cipher;
X509 *peer;
@@ -459,29 +469,19 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
* Allocate a new TLScontext for the new connection and get an SSL
* structure. Add the location of TLScontext to the SSL to later retrieve
* the information inside the tls_verify_certificate_callback().
- *
- * XXX Need a dedicated procedure for consistent initialization of all the
- * fields in this structure.
*/
-#define PEERNAME_SIZE sizeof(TLScontext->peername_save)
-
- NEW_TLS_CONTEXT(TLScontext);
- TLScontext->log_level = var_smtpd_tls_loglevel;
- strncpy(TLScontext->peername_save, peername, PEERNAME_SIZE - 1);
- TLScontext->peername_save[PEERNAME_SIZE - 1] = 0;
- (void) lowercase(TLScontext->peername_save);
+ TLScontext = tls_alloc_context(var_smtpd_tls_loglevel, peername);
if ((TLScontext->con = (SSL *) SSL_new(server_ctx)) == NULL) {
msg_info("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
msg_info("Could not set application data for 'TLScontext->con'");
tls_print_errors();
- SSL_free(TLScontext->con);
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
@@ -493,7 +493,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
TLScontext->enforce_verify_errors = 1;
- SSL_set_verify(TLScontext->con, verify_flags, server_verify_callback);
+ SSL_set_verify(TLScontext->con, verify_flags,
+ tls_verify_certificate_callback);
} else {
TLScontext->enforce_verify_errors = 0;
}
@@ -511,8 +512,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
&TLScontext->network_bio, TLS_BIO_BUFSIZE)) {
msg_info("Could not obtain BIO_pair");
tls_print_errors();
- SSL_free(TLScontext->con);
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
@@ -559,9 +559,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
if (sts <= 0) {
msg_info("SSL_accept error from %s[%s]: %d", peername, peeraddr, sts);
tls_print_errors();
- SSL_free(TLScontext->con);
- BIO_free(TLScontext->network_bio); /* 200411 */
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
/* Only loglevel==4 dumps everything */
@@ -651,11 +649,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
if (requirecert) {
if (!tls_info->peer_verified || !tls_info->peer_CN) {
msg_info("Re-used session without peer certificate removed");
- session = SSL_get_session(TLScontext->con);
- SSL_CTX_remove_session(server_ctx, session);
- SSL_free(TLScontext->con);
- BIO_free(TLScontext->network_bio); /* 200411 */
- FREE_TLS_CONTEXT(TLScontext);
+ uncache_session(server_ctx, TLScontext);
+ tls_free_context(TLScontext);
return (0);
}
}
diff --git a/postfix/src/tls/tls_session.c b/postfix/src/tls/tls_session.c
index 8ffe56923..8caf341f0 100644
--- a/postfix/src/tls/tls_session.c
+++ b/postfix/src/tls/tls_session.c
@@ -101,22 +101,8 @@ void tls_session_stop(SSL_CTX *ctx, VSTREAM *stream, int timeout,
if (retval == 0)
tls_bio_shutdown(vstream_fileno(stream), timeout, TLScontext);
}
-
- /*
- * Free the SSL structure and the BIOs. Warning: the internal_bio is
- * connected to the SSL structure and is automatically freed with it. Do
- * not free it again (core dump)!! Only free the network_bio.
- *
- * XXX SSL_CTX_flush_sessions() searches memory for expired sessions and
- * removes them from memory and external cache.
- */
- SSL_free(TLScontext->con);
-
- BIO_free(TLScontext->network_bio);
- FREE_TLS_CONTEXT(TLScontext);
+ tls_free_context(TLScontext);
tls_stream_stop(stream);
- SSL_CTX_flush_sessions(ctx, time(NULL));
-
*tls_info = tls_info_zero;
}
diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c
index 2d4fb092e..508ba7f3c 100644
--- a/postfix/src/tls/tls_verify.c
+++ b/postfix/src/tls/tls_verify.c
@@ -7,10 +7,9 @@
/* #define TLS_INTERNAL
/* #include
/*
-/* int tls_verify_certificate_callback(ok, ctx, int flags)
+/* int tls_verify_certificate_callback(ok, ctx)
/* int ok;
/* X509_STORE_CTX *ctx;
-/* int flags;
/* DESCRIPTION
/* tls_verify_callback() is called several times (directly or
/* indirectly) from crypto/x509/x509_vfy.c. It is called as
@@ -73,13 +72,6 @@
/* .IP ctx
/* TLS client or server context. This also specifies the
/* TLScontext with enforcement options.
-/* .IP flags
-/* .RS
-/* .IP TLS_VERIFY_PEERNAME
-/* Verify the peer hostname against the names listed
-/* in the peer certificate. The peer hostname is specified
-/* via the ctx argument.
-/* .RE
/* LICENSE
/* .ad
/* .fi
@@ -121,21 +113,9 @@
#define TLS_INTERNAL
#include
-/* match_hostname - match hostname against pattern */
-
-static int match_hostname(const char *pattern, const char *hostname)
-{
- char *peername_left;
-
- return (strcasecmp(hostname, pattern) == 0
- || (pattern[0] == '*' && pattern[1] == '.' && pattern[2] != 0
- && (peername_left = strchr(hostname, '.')) != 0
- && strcasecmp(peername_left + 1, pattern + 2) == 0));
-}
-
/* tls_verify_certificate_callback - verify peer certificate info */
-int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx, int flags)
+int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
{
char buf[1024];
X509 *err_cert;
@@ -170,78 +150,15 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx, int flags)
}
if (!ok) {
msg_info("certificate verification failed for %s: num=%d:%s",
- TLScontext->peername_save, err,
+ TLScontext->peername, err,
X509_verify_cert_error_string(err));
}
/*
- * Match the peer hostname against the names listed in the peer
- * certificate.
+ * We delay peername verification until the SSL handshake completes. The
+ * peername verification previously done here is now called directly from
+ * tls_client_start(). This substantially simplifies the cache interface.
*/
- if (ok && (depth == 0) && (flags & TLS_VERIFY_PEERNAME)) {
- int i,
- r;
- int hostname_matched;
- int dNSName_found;
-
- STACK_OF(GENERAL_NAME) * gens;
-
- /*
- * Verify the name(s) in the peer certificate against the peer
- * hostname. Log peer hostname/certificate mis-matches. If a match is
- * required but fails, bail out with a verification error.
- */
- hostname_matched = dNSName_found = 0;
-
- gens = X509_get_ext_d2i(err_cert, NID_subject_alt_name, 0, 0);
- if (gens) {
- for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) {
- const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
-
- if (gn->type == GEN_DNS) {
- dNSName_found++;
- if ((hostname_matched =
- match_hostname((char *) gn->d.ia5->data,
- TLScontext->peername_save)))
- break;
- }
- }
- sk_GENERAL_NAME_free(gens);
- }
- if (dNSName_found) {
- if (!hostname_matched)
- msg_info("certificate peer name verification failed for %s: "
- "%d dNSNames in certificate found, but none matches",
- TLScontext->peername_save, dNSName_found);
- } else {
- buf[0] = '\0';
- if (!X509_NAME_get_text_by_NID(X509_get_subject_name(err_cert),
- NID_commonName, buf, sizeof(buf))) {
- msg_info("certificate peer name verification failed for %s:"
- "cannot parse subject CommonName",
- TLScontext->peername_save);
- tls_print_errors();
- } else {
- hostname_matched = match_hostname(buf,
- TLScontext->peername_save);
- if (!hostname_matched)
- msg_info("certificate peer name verification failed for %s:"
- " CommonName mis-match: %s",
- TLScontext->peername_save, buf);
- }
- }
-
- if (!hostname_matched) {
- if (TLScontext->enforce_verify_errors && TLScontext->enforce_CN) {
- err = X509_V_ERR_CERT_REJECTED;
- X509_STORE_CTX_set_error(ctx, err);
- msg_info("certificate peer name verification failed for %s:"
- " hostname mismatch", TLScontext->peername_save);
- ok = 0;
- }
- } else
- TLScontext->hostname_matched = 1;
- }
/*
* Other causes for verification failure.
@@ -252,19 +169,19 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx, int flags)
buf, sizeof(buf));
msg_info("certificate verification failed for %s:"
"issuer %s certificate unavailable",
- TLScontext->peername_save, buf);
+ TLScontext->peername, buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
msg_info("certificate verification failed for %s:"
"certificate not yet valid",
- TLScontext->peername_save);
+ TLScontext->peername);
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
msg_info("certificate verification failed for %s:"
"certificate has expired",
- TLScontext->peername_save);
+ TLScontext->peername);
break;
}
if (TLScontext->log_level >= 2)
diff --git a/postfix/src/tlsmgr/tlsmgr.c b/postfix/src/tlsmgr/tlsmgr.c
index cd2b1d927..565152397 100644
--- a/postfix/src/tlsmgr/tlsmgr.c
+++ b/postfix/src/tlsmgr/tlsmgr.c
@@ -521,8 +521,6 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
static VSTRING *buffer = 0;
int cache_type;
int len;
- long openssl_vsn;
- int flags;
static char wakeup[] = { /* master wakeup request */
TRIGGER_REQ_WAKEUP,
0,
@@ -560,20 +558,13 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
- ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, &openssl_vsn,
- ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, &flags,
- ATTR_TYPE_END) == 4) {
+ ATTR_TYPE_END) == 2) {
if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
msg_warn("bogus cache type \"%d\" in \"%s\" request",
cache_type, TLS_MGR_REQ_LOOKUP);
VSTRING_RESET(buffer);
} else {
- status =
- tls_scache_lookup(cache, STR(cache_id), openssl_vsn,
- flags,
- TLS_SCACHE_DONT_NEED_OPENSSL_VSN,
- TLS_SCACHE_DONT_NEED_FLAGS,
- buffer) ?
+ status = tls_scache_lookup(cache, STR(cache_id), buffer) ?
TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
}
}
@@ -591,17 +582,15 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
if (attr_scan(client_stream, ATTR_FLAG_STRICT,
ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
- ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, &openssl_vsn,
- ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, &flags,
ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer,
- ATTR_TYPE_END) == 5) {
+ ATTR_TYPE_END) == 3) {
if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
msg_warn("bogus cache type \"%d\" in \"%s\" request",
cache_type, TLS_MGR_REQ_UPDATE);
} else {
status =
- tls_scache_update(cache, STR(cache_id), openssl_vsn,
- flags, STR(buffer), LEN(buffer)) ?
+ tls_scache_update(cache, STR(cache_id),
+ STR(buffer), LEN(buffer)) ?
TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
}
}
diff --git a/postfix/src/virtual/mailbox.c b/postfix/src/virtual/mailbox.c
index 7f5b38a54..858cba066 100644
--- a/postfix/src/virtual/mailbox.c
+++ b/postfix/src/virtual/mailbox.c
@@ -61,6 +61,7 @@
#include
#include
#include
+#include
/* Application-specific. */
@@ -139,9 +140,10 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
deliver_status = DEL_STAT_DEFER;
} else if (mail_copy_status != 0) {
- deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+ deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+ defer_append : bounce_append)
(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr, why->dsn),
+ BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
"mailbox %s: %s", usr_attr.mailbox, vstring_str(why->vstring));
} else {
deliver_status = sent(BOUNCE_FLAGS(state.request),
diff --git a/postfix/src/virtual/maildir.c b/postfix/src/virtual/maildir.c
index 497c8b15e..9f9d6b432 100644
--- a/postfix/src/virtual/maildir.c
+++ b/postfix/src/virtual/maildir.c
@@ -61,6 +61,7 @@
#include
#include
#include
+#include
/* Application-specific. */
@@ -220,9 +221,10 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
deliver_status = DEL_STAT_DEFER;
} else if (mail_copy_status != 0) {
- deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+ deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+ defer_append : bounce_append)
(BOUNCE_FLAGS(state.request),
- BOUNCE_ATTR(state.msg_attr, why->dsn),
+ BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
"maildir delivery failed: %s", vstring_str(why->vstring));
if (errno == EACCES) {
msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
--
2.47.3