From 88a2e39fc79c09ccaf3a8d044d6de2bf98a78811 Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Mon, 10 Oct 2005 00:00:00 -0500 Subject: [PATCH] postfix-2.3-20051010 --- postfix/.indent.pro | 2 + postfix/HISTORY | 44 +- postfix/README_FILES/SMTPD_POLICY_README | 3 +- postfix/RELEASE_NOTES | 9 +- postfix/html/SMTPD_POLICY_README.html | 3 +- postfix/html/anvil.8.html | 182 ++++---- postfix/html/local.8.html | 7 - postfix/html/postconf.5.html | 69 ++- postfix/html/smtpd.8.html | 13 + postfix/man/man5/postconf.5 | 43 +- postfix/man/man8/anvil.8 | 154 ++++--- postfix/man/man8/local.8 | 5 - postfix/man/man8/smtpd.8 | 10 + postfix/mantools/postlink | 3 +- postfix/proto/SMTPD_POLICY_README.html | 3 +- postfix/proto/postconf.proto | 57 ++- postfix/src/anvil/anvil.c | 553 ++++++++++++----------- postfix/src/global/anvil_clnt.c | 137 ++++-- postfix/src/global/anvil_clnt.h | 7 +- postfix/src/global/deliver_request.c | 1 + postfix/src/global/mail_params.h | 12 +- postfix/src/global/mail_version.h | 2 +- postfix/src/global/smtp_stream.c | 3 + postfix/src/global/smtp_stream.h | 1 + postfix/src/lmtp/lmtp_chat.c | 14 + postfix/src/lmtp/lmtp_trouble.c | 6 + postfix/src/local/alias.c | 3 +- postfix/src/local/local.c | 7 - postfix/src/smtp/smtp_chat.c | 14 + postfix/src/smtp/smtp_trouble.c | 6 + postfix/src/smtpd/Makefile.in | 1 + postfix/src/smtpd/smtpd.c | 65 ++- postfix/src/smtpd/smtpd_peer.c | 16 +- postfix/src/tls/tls.h | 3 +- postfix/src/tls/tls_client.c | 6 + postfix/src/tls/tls_server.c | 8 + postfix/src/util/dict_alloc.c | 3 +- 37 files changed, 932 insertions(+), 543 deletions(-) diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 2a8c443b7..289be98f6 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -3,7 +3,9 @@ -TALIAS_TOKEN -TANVIL_CLNT -TANVIL_LOCAL +-TANVIL_MAX -TANVIL_REMOTE +-TANVIL_REQ_TABLE -TARGV -TATTR_CLNT -TATTR_TABLE diff --git a/postfix/HISTORY b/postfix/HISTORY index 7378cf24f..36ddd3356 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -11148,19 +11148,47 @@ Apologies for any names omitted. others. Files: local/local.c, local/aliases.c, local/dotforward.c, local/mailbox.c, local/maildir.c. - Cleanup: while expanding a local(8) alias that has an owner - alias, don't reset the owner-alias information when recursing - into an alias that has no owner alias. This produces better - error reports when delivering mail to nested aliases that - have an owner alias only at the top level. To get the old - behavior, specify "sticky_owner_alias = no". Problem - reported by Victor Duchovni. File: local/alias.c. - Logging: additional SASL debug logging by Andreas Winkelmann. Files: */*sasl_glue.c. +20050929 + + Paranoia: don't ignore garbage in SMTP or LMTP server replies + when ESMTP command pipelining is turned on. For example, + after sending ".QUIT", Postfix could recognize + the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after + garbage, causing mail to be lost. The SMTP and LMTP clients + now report a remote protocol error and defer delivery. + Files: smtp/smtp_chat.c, smtp/smtp_trouble.c, lmtp/lmtp_chat.c, + lmtp/lmtp_trouble.c. + + Performance: specify "smtpd_peername_lookup = no" to disable + client hostname lookups in the SMTP server. All clients are + treated as "unknown". This should be used only under extreme + conditions where DNS lookup latencies are critical. File: + smtpd/smtpd_peer.c. + +20051010 + + Feature: smtpd_client_new_tls_session_rate_limit parameter + to limit the number of new (i.e. uncached) TLS sessions + that a remote SMTP client may negotiate per unit time. This + feature, which is off by default, can limit the CPU load + due to expensive crypto operations. Files: global/anvil_clnt.c, + anvil/anvil.c, smtpd/smtpd.c. + + Cleanup: eliminated code duplicatiom in the anvil server + that resulted from adding similar features one at a time. + File: anvil/anvil.c. + Open problems: + Try to recognize that Resent- headers appear in blocks, + newest block first. But don't break on incorrect header + block organization. + + Hard limits on cache sizes (anvil, specifically). + Look for systems with XPG basename() declared in , and prepare for phasing out the Postfix-supplied one. Beware, however, that XPG basename() takes (char *), and diff --git a/postfix/README_FILES/SMTPD_POLICY_README b/postfix/README_FILES/SMTPD_POLICY_README index d690a47ff..b8a1436fb 100644 --- a/postfix/README_FILES/SMTPD_POLICY_README +++ b/postfix/README_FILES/SMTPD_POLICY_README @@ -17,7 +17,8 @@ Postfix source code, in the directory examples/smtpd-policy. Policy delegation is now the preferred method for adding policies to Postfix. It's much easier to develop a new feature in few lines of Perl, than trying to do the same in C code. The difference in performance will be unnoticeable -except in the most demanding environments. +except in the most demanding environments. On active systems a policy daemon +process is used multiple times, for up to 100 incoming SMTP connections. This document covers the following topics: diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index ce56ca140..1f24d92df 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -26,12 +26,9 @@ files. With deeply nested aliases or .forward files, this can greatly reduce the number of queue files and cleanup process instances. To get the earlier behavior, specify "frozen_delivered_to = no". -While expanding an alias that has an owner alias, the Postfix -local(8) delivery agent no longer resets the owner information when -it expands a subordinate alias that has no owner alias. This -produces better error reporting with nested aliases that have an -owner alias only at the top level. To get the earlier behavior, -specify "sticky_owner_alias = no". +The frozen_delivered_to feature also fixes an old problem with +duplicate deliveries to recipients that are listed in multiple +nested aliases. Incompatibility with snapshot 20050828 ====================================== diff --git a/postfix/html/SMTPD_POLICY_README.html b/postfix/html/SMTPD_POLICY_README.html index 325e7e8f7..c6c6fe8a6 100644 --- a/postfix/html/SMTPD_POLICY_README.html +++ b/postfix/html/SMTPD_POLICY_README.html @@ -36,7 +36,8 @@ the Postfix source code, in the directory examples/smtpd-policy. to Postfix. It's much easier to develop a new feature in few lines of Perl, than trying to do the same in C code. The difference in performance will be unnoticeable except in the most demanding -environments.

+environments. On active systems a policy daemon process is used +multiple times, for up to 100 incoming SMTP connections.

This document covers the following topics:

diff --git a/postfix/html/anvil.8.html b/postfix/html/anvil.8.html index e55868714..b10dd5705 100644 --- a/postfix/html/anvil.8.html +++ b/postfix/html/anvil.8.html @@ -13,151 +13,167 @@ ANVIL(8) ANVIL(8) anvil [generic Postfix daemon options] DESCRIPTION - The Postfix anvil(8) server maintains short-term statis- - tics to defend against clients that hammer a server with - either too many simultaneous sessions, or with too many - successive requests within a configurable time interval. - This server is designed to run under control by the Post- - fix master(8) server. + The Postfix anvil(8) server maintains statistics about + client connection counts or client request rates. This + information can be used to defend against clients that + hammer a server with either too many simultaneous ses- + sions, or with too many successive requests within a con- + figurable time interval. This server is designed to run + under control by the Postfix master(8) server. - The anvil(8) server maintains no persistent database. - Standard library utilities do not meet Postfix performance - and robustness requirements. + In the following text, ident specifies a (service, client) + combination. The exact syntax of that information is + application-dependent; the anvil(8) server does not care. -CONNECTION COUNT/RATE LIMITING - When a remote client connects, a connection count (or - rate) limited server should send the following request to +CONNECTION COUNT/RATE CONTROL + To register a new connection send the following request to the anvil(8) server: request=connect ident=string - This registers a new connection for the (service, client) - combination specified with ident. The anvil(8) server - answers with the number of simultaneous connections and - the number of connections per unit time for that (service, - client) combination: + The anvil(8) server answers with the number of simultane- + ous connections and the number of connections per unit + time for the (service, client) combination specified with + ident: status=0 count=number rate=number - The rate is computed as the number of connections that - were registered in the current "time unit" interval. It - is left up to the server to decide if the remote client - exceeds the connection count (or rate) limit. - - When a remote client disconnects, a connection count (or - rate) limited server should send the following request to - the anvil(8) server: + To register a disconnect event send the following request + to the anvil(8) server: request=disconnect ident=string - This registers a disconnect event for the (service, - client) combination specified with ident. The anvil(8) - server replies with: + The anvil(8) server replies with: status=0 -MESSAGE RATE LIMITING - When a remote client sends a message delivery request, a - message rate limited server should send the following +MESSAGE RATE CONTROL + To register a message delivery request send the following request to the anvil(8) server: request=message ident=string - This registers a message delivery request for the (ser- - vice, client) combination specified with ident. The - anvil(8) server answers with the number of message deliv- - ery requests per unit time for that (service, client) com- - bination: + The anvil(8) server answers with the number of message + delivery requests per unit time for the (service, client) + combination specified with ident: status=0 rate=number - In order to prevent the anvil(8) server from discarding - client request rates too early or too late, a message rate - limited service should also register connect/disconnect - events. - -RECIPIENT RATE LIMITING - When a remote client sends a recipient address, a recipi- - ent rate limited server should send the following request +RECIPIENT RATE CONTROL + To register a recipient request send the following request to the anvil(8) server: request=recipient ident=string - This registers a recipient request for the (service, - client) combination specified with ident. The anvil(8) - server answers with the number of recipient addresses per - unit time for that (service, client) combination: + The anvil(8) server answers with the number of recipient + addresses per unit time for the (service, client) combina- + tion specified with ident: status=0 rate=number - In order to prevent the anvil(8) server from discarding - client request rates too early or too late, a recipient - rate limited service should also register connect/discon- - nect events. +TLS SESSION NEGOTIATION RATE CONTROL + The features described in this section are available with + Postfix 2.3 and later. + + To register a request for a new (i.e. not cached) TLS ses- + sion send the following request to the anvil(8) server: + + request=newtls + ident=string + + The anvil(8) server answers with the number of new TLS + session requests per unit time for the (service, client) + combination specified with ident: + + status=0 + rate=number + + To retrieve new TLS session request rate information with- + out updating the counter information, use: + + request=newtls_report + ident=string + + The anvil(8) server answers with the number of new TLS + session requests per unit time for the (service, client) + combination specified with ident. + + status=0 + rate=number SECURITY The anvil(8) server does not talk to the network or to local users, and can run chrooted at fixed low privilege. The anvil(8) server maintains an in-memory table with - information about recent clients of a connection count (or - rate) limited service. Although state is kept only tempo- - rarily, this may require a lot of memory on systems that - handle connections from many remote clients. To reduce - memory usage, reduce the time unit over which state is - kept. + information about recent clients requests. No persistent + state is kept because standard system library routines are + not sufficiently robust for update-intensive applications. + + Although the in-memory state is kept only temporarily, + this may require a lot of memory on systems that handle + connections from many remote clients. To reduce memory + usage, reduce the time unit over which state is kept. DIAGNOSTICS Problems and transactions are logged to syslogd(8). Upon exit, and every anvil_status_update_time seconds, the - server logs the maximal count and rate values measured, - together with (service, client) information and the time - of day associated with those events. In order to avoid - unnecessary overhead, no measurements are done for activ- + server logs the maximal count and rate values measured, + together with (service, client) information and the time + of day associated with those events. In order to avoid + unnecessary overhead, no measurements are done for activ- ity that isn't concurrency limited or rate limited. BUGS - Systems behind network address translating routers or + Systems behind network address translating routers or proxies appear to have the same client address and can run into connection count and/or rate limits falsely. In this preliminary implementation, a count (or rate) lim- - ited server can have only one remote client at a time. If - a server reports multiple simultaneous clients, all but + ited server can have only one remote client at a time. If + a server reports multiple simultaneous clients, all but the last reported client are ignored. -CONFIGURATION PARAMETERS - Changes to main.cf are picked up automatically as anvil(8) - processes run for only a limited amount of time. Use the - command "postfix reload" to speed up a change. + The anvil(8) server automatically discards client request + information after it expires. To prevent the anvil(8) + server from discarding client request rate information too + early or too late, a rate limited service should always + register connect/disconnect events even when it does not + explicitly limit them. - The text below provides only a parameter summary. See +CONFIGURATION PARAMETERS + On low-traffic mail systems, changes to main.cf are picked + up automatically as anvil(8) processes run for only a lim- + ited amount of time. On other mail systems, use the com- + mand "postfix reload" to speed up a change. + + The text below provides only a parameter summary. See postconf(5) for more details including examples. anvil_rate_time_unit (60s) - The time unit over which client connection rates + The time unit over which client connection rates and other rates are calculated. anvil_status_update_time (600s) - How frequently the anvil(8) connection and rate + How frequently the anvil(8) connection and rate limiting server logs peak usage information. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to - handle a request before it is terminated by a + How much time a Postfix daemon process may take to + handle a request before it is terminated by a built-in watchdog timer. ipc_timeout (3600s) @@ -165,28 +181,28 @@ ANVIL(8) ANVIL(8) over an internal communication channel. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for the next service request + The maximum amount of time that an idle Postfix + daemon process waits for the next service request before exiting. max_use (100) - The maximal number of connection requests before a + The maximal number of connection requests before a Postfix daemon process terminates. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". SEE ALSO @@ -198,7 +214,7 @@ ANVIL(8) ANVIL(8) TUNING_README, performance tuning LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index 5bb9b37a7..344e4613b 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -386,13 +386,6 @@ LOCAL(8) LOCAL(8) attempt; do not update the Delivered-To: address while expanding aliases or .forward files. - sticky_owner_alias (yes) - When expanding a local(8) alias that has an owner - alias (see owner-name discussion in aliases(5)), - use the owner information even when the expansion - invokes a subordinate alias that has no owner - alias. - DELIVERY METHOD CONTROLS The precedence of local(8) delivery methods from high to low is: aliases, .forward files, mailbox_transport, mail- diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index bccd8ed65..7b018576f 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -7205,6 +7205,47 @@ Example: + + +
smtpd_client_new_tls_session_rate_limit +(default: 0)
+ +

+The maximal number of new (i.e., uncached) TLS sessions that a +remote SMTP client is allowed to negotiate with this service per +time unit. The time unit is specified with the anvil_rate_time_unit +configuration parameter. +

+ +

+By default, a remote SMTP client can negotiate as many new TLS +sessions per time unit as Postfix can accept. +

+ +

+To disable this feature, specify a limit of 0. Otherwise, specify +a limit that is at least the per-client concurrent session limit, +or else legitimate client sessions may be rejected. +

+ +

+WARNING: The purpose of this feature is to limit abuse. It must +not be used to regulate legitimate mail traffic. +

+ +

+This feature is available in Postfix 2.3 and later. +

+ +

+Example: +

+ +
+smtpd_client_new_tls_session_rate_limit = 100
+
+ +
smtpd_client_recipient_rate_limit @@ -7924,6 +7965,18 @@ null sender address.

+ + +
smtpd_peername_lookup +(default: yes)
+ +

Attempt to look up the SMTP client hostname, and verify that +the name matches the client IP address. A client name is set to +"unknown" when it cannot be looked up or verified, or when name +lookup is disabled. Turning off name lookup reduces delays due to +DNS lookup and increases the maximal inbound delivery rate.

+ +
smtpd_policy_service_max_idle @@ -9149,22 +9202,6 @@ The default time unit is s (seconds).

- - -
sticky_owner_alias -(default: yes)
- -

When expanding a local(8) alias that has an owner alias (see -owner-name discussion in aliases(5)), use the owner information -even when the expansion invokes a subordinate alias that has no -owner alias.

- -

This feature is available in Postfix 2.3 and later. With older -Postfix releases, the behavior is as if this parameter is set to -"no". The old setting provides poorer error reporting with nested -aliases that only have an owner alias at the top level.

- -
strict_7bit_headers diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 142aa8884..edd1fca3d 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -548,6 +548,12 @@ SMTPD(8) SMTPD(8) server command history before it is flushed upon receipt of EHLO, RSET, or end of DATA. + Available in Postfix version 2.3 and later: + + smtpd_peername_lookup (yes) + Attempt to look up the SMTP client hostname, and + verify that the name matches the client IP address. + The per SMTP client connection count and request rate lim- its are implemented in co-operation with the anvil(8) ser- vice, and are available in Postfix version 2.2 and later. @@ -577,6 +583,13 @@ SMTPD(8) SMTPD(8) Clients that are excluded from connection count, connection rate, or SMTP request rate restrictions. + Available in Postfix version 2.3 and later: + + smtpd_client_new_tls_session_rate_limit (0) + The maximal number of new (i.e., uncached) TLS ses- + sions that any client is allowed to negotiate with + this service per time unit. + TARPIT CONTROLS When a remote SMTP client makes errors, the Postfix SMTP server can insert delays before responding. This can help diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index ef44cd526..f4aa2e9fe 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -4070,6 +4070,33 @@ smtpd_client_message_rate_limit = 1000 .fi .ad .ft R +.SH smtpd_client_new_tls_session_rate_limit (default: 0) +The maximal number of new (i.e., uncached) TLS sessions that a +remote SMTP client is allowed to negotiate with this service per +time unit. The time unit is specified with the anvil_rate_time_unit +configuration parameter. +.PP +By default, a remote SMTP client can negotiate as many new TLS +sessions per time unit as Postfix can accept. +.PP +To disable this feature, specify a limit of 0. Otherwise, specify +a limit that is at least the per-client concurrent session limit, +or else legitimate client sessions may be rejected. +.PP +WARNING: The purpose of this feature is to limit abuse. It must +not be used to regulate legitimate mail traffic. +.PP +This feature is available in Postfix 2.3 and later. +.PP +Example: +.PP +.nf +.na +.ft C +smtpd_client_new_tls_session_rate_limit = 100 +.fi +.ad +.ft R .SH smtpd_client_recipient_rate_limit (default: 0) The maximal number of recipient addresses that any client is allowed to send to this service per time unit, regardless of whether or not @@ -4540,6 +4567,12 @@ This list overrides any commands built into the Postfix SMTP server. .SH smtpd_null_access_lookup_key (default: <>) The lookup key to be used in SMTP \fBaccess\fR(5) tables instead of the null sender address. +.SH smtpd_peername_lookup (default: yes) +Attempt to look up the SMTP client hostname, and verify that +the name matches the client IP address. A client name is set to +"unknown" when it cannot be looked up or verified, or when name +lookup is disabled. Turning off name lookup reduces delays due to +DNS lookup and increases the maximal inbound delivery rate. .SH smtpd_policy_service_max_idle (default: 300s) The time after which an idle SMTPD policy service connection is closed. @@ -5349,16 +5382,6 @@ This is used for delivery to file or mailbox. .PP Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks). The default time unit is s (seconds). -.SH sticky_owner_alias (default: yes) -When expanding a \fBlocal\fR(8) alias that has an owner alias (see -owner-\fIname\fR discussion in \fBaliases\fR(5)), use the owner information -even when the expansion invokes a subordinate alias that has no -owner alias. -.PP -This feature is available in Postfix 2.3 and later. With older -Postfix releases, the behavior is as if this parameter is set to -"no". The old setting provides poorer error reporting with nested -aliases that only have an owner alias at the top level. .SH strict_7bit_headers (default: no) Reject mail with 8-bit text in message headers. This blocks mail from poorly written applications. diff --git a/postfix/man/man8/anvil.8 b/postfix/man/man8/anvil.8 index c44b8d8b8..bcb2d76c5 100644 --- a/postfix/man/man8/anvil.8 +++ b/postfix/man/man8/anvil.8 @@ -12,23 +12,25 @@ Postfix session count and request rate control .SH DESCRIPTION .ad .fi -The Postfix \fBanvil\fR(8) server maintains short-term statistics -to defend against clients that hammer a server with either too -many simultaneous sessions, or with too many successive requests -within a configurable time interval. -This server is designed to run under control by the Postfix -\fBmaster\fR(8) server. +The Postfix \fBanvil\fR(8) server maintains statistics about +client connection counts or client request rates. This +information can be used to defend against clients that +hammer a server with either too many simultaneous sessions, +or with too many successive requests within a configurable +time interval. This server is designed to run under control +by the Postfix \fBmaster\fR(8) server. -The \fBanvil\fR(8) server maintains no persistent database. Standard -library utilities do not meet Postfix performance and robustness -requirements. -.SH "CONNECTION COUNT/RATE LIMITING" +In the following text, \fBident\fR specifies a (service, +client) combination. The exact syntax of that information +is application-dependent; the \fBanvil\fR(8) server does +not care. +.SH "CONNECTION COUNT/RATE CONTROL" .na .nf .ad .fi -When a remote client connects, a connection count (or rate) limited -server should send the following request to the \fBanvil\fR(8) server: +To register a new connection send the following request to +the \fBanvil\fR(8) server: .PP .in +4 \fBrequest=connect\fR @@ -36,11 +38,10 @@ server should send the following request to the \fBanvil\fR(8) server: \fBident=\fIstring\fR .in .PP -This registers a new connection for the (service, client) -combination specified with \fBident\fR. The \fBanvil\fR(8) server -answers with the number of simultaneous connections and the -number of connections per unit time for that (service, client) -combination: +The \fBanvil\fR(8) server answers with the number of +simultaneous connections and the number of connections per +unit time for the (service, client) combination specified +with \fBident\fR: .PP .in +4 \fBstatus=0\fR @@ -50,13 +51,8 @@ combination: \fBrate=\fInumber\fR .in .PP -The \fBrate\fR is computed as the number of connections -that were registered in the current "time unit" interval. -It is left up to the server to decide if the remote client -exceeds the connection count (or rate) limit. -.PP -When a remote client disconnects, a connection count (or rate) limited -server should send the following request to the \fBanvil\fR(8) server: +To register a disconnect event send the following request +to the \fBanvil\fR(8) server: .PP .in +4 \fBrequest=disconnect\fR @@ -64,19 +60,16 @@ server should send the following request to the \fBanvil\fR(8) server: \fBident=\fIstring\fR .in .PP -This registers a disconnect event for the (service, client) -combination specified with \fBident\fR. The \fBanvil\fR(8) -server replies with: +The \fBanvil\fR(8) server replies with: .PP .ti +4 \fBstatus=0\fR -.SH "MESSAGE RATE LIMITING" +.SH "MESSAGE RATE CONTROL" .na .nf .ad .fi -When a remote client sends a message delivery request, a -message rate limited server should send the following +To register a message delivery request send the following request to the \fBanvil\fR(8) server: .PP .in +4 @@ -85,28 +78,22 @@ request to the \fBanvil\fR(8) server: \fBident=\fIstring\fR .in .PP -This registers a message delivery request for the (service, client) -combination specified with \fBident\fR. The \fBanvil\fR(8) server -answers with the number of message delivery requests per unit time -for that (service, client) combination: +The \fBanvil\fR(8) server answers with the number of message +delivery requests per unit time for the (service, client) +combination specified with \fBident\fR: .PP .in +4 \fBstatus=0\fR .br \fBrate=\fInumber\fR .in -.PP -In order to prevent the \fBanvil\fR(8) server from discarding client -request rates too early or too late, a message rate limited -service should also register connect/disconnect events. -.SH "RECIPIENT RATE LIMITING" +.SH "RECIPIENT RATE CONTROL" .na .nf .ad .fi -When a remote client sends a recipient address, a recipient -rate limited server should send the following request to -the \fBanvil\fR(8) server: +To register a recipient request send the following request +to the \fBanvil\fR(8) server: .PP .in +4 \fBrequest=recipient\fR @@ -114,10 +101,35 @@ the \fBanvil\fR(8) server: \fBident=\fIstring\fR .in .PP -This registers a recipient request for the (service, client) -combination specified with \fBident\fR. The \fBanvil\fR(8) server -answers with the number of recipient addresses per unit time -for that (service, client) combination: +The \fBanvil\fR(8) server answers with the number of recipient +addresses per unit time for the (service, client) combination +specified with \fBident\fR: +.PP +.in +4 +\fBstatus=0\fR +.br +\fBrate=\fInumber\fR +.in +.SH "TLS SESSION NEGOTIATION RATE CONTROL" +.na +.nf +.ad +.fi +The features described in this section are available with +Postfix 2.3 and later. + +To register a request for a new (i.e. not cached) TLS session +send the following request to the \fBanvil\fR(8) server: +.PP +.in +4 +\fBrequest=newtls\fR +.br +\fBident=\fIstring\fR +.in +.PP +The \fBanvil\fR(8) server answers with the number of new +TLS session requests per unit time for the (service, client) +combination specified with \fBident\fR: .PP .in +4 \fBstatus=0\fR @@ -125,9 +137,24 @@ for that (service, client) combination: \fBrate=\fInumber\fR .in .PP -In order to prevent the \fBanvil\fR(8) server from discarding client -request rates too early or too late, a recipient rate limited -service should also register connect/disconnect events. +To retrieve new TLS session request rate information without +updating the counter information, use: +.PP +.in +4 +\fBrequest=newtls_report\fR +.br +\fBident=\fIstring\fR +.in +.PP +The \fBanvil\fR(8) server answers with the number of new +TLS session requests per unit time for the (service, client) +combination specified with \fBident\fR. +.PP +.in +4 +\fBstatus=0\fR +.br +\fBrate=\fInumber\fR +.in .SH "SECURITY" .na .nf @@ -136,12 +163,15 @@ service should also register connect/disconnect events. The \fBanvil\fR(8) server does not talk to the network or to local users, and can run chrooted at fixed low privilege. -The \fBanvil\fR(8) server maintains an in-memory table with information -about recent clients of a connection count (or rate) limited service. -Although state is kept only temporarily, this may require a lot of -memory on systems that handle connections from many remote clients. -To reduce memory usage, reduce the time unit over which state -is kept. +The \fBanvil\fR(8) server maintains an in-memory table with +information about recent clients requests. No persistent +state is kept because standard system library routines are +not sufficiently robust for update-intensive applications. + +Although the in-memory state is kept only temporarily, this +may require a lot of memory on systems that handle connections +from many remote clients. To reduce memory usage, reduce +the time unit over which state is kept. .SH DIAGNOSTICS .ad .fi @@ -165,14 +195,22 @@ In this preliminary implementation, a count (or rate) limited server can have only one remote client at a time. If a server reports multiple simultaneous clients, all but the last reported client are ignored. + +The \fBanvil\fR(8) server automatically discards client +request information after it expires. To prevent the +\fBanvil\fR(8) server from discarding client request rate +information too early or too late, a rate limited service +should always register connect/disconnect events even when +it does not explicitly limit them. .SH "CONFIGURATION PARAMETERS" .na .nf .ad .fi -Changes to \fBmain.cf\fR are picked up automatically as \fBanvil\fR(8) -processes run for only a limited amount of time. Use the command -"\fBpostfix reload\fR" to speed up a change. +On low-traffic mail systems, changes to \fBmain.cf\fR are +picked up automatically as \fBanvil\fR(8) processes run for +only a limited amount of time. On other mail systems, use +the command "\fBpostfix reload\fR" to speed up a change. The text below provides only a parameter summary. See \fBpostconf\fR(5) for more details including examples. diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index 409f7f5d3..c5fb993bd 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -399,11 +399,6 @@ Update the \fBlocal\fR(8) delivery agent's idea of the Delivered-To: address (see prepend_delivered_header) only once, at the start of a delivery attempt; do not update the Delivered-To: address while expanding aliases or .forward files. -.IP "\fBsticky_owner_alias (yes)\fR" -When expanding a \fBlocal\fR(8) alias that has an owner alias (see -owner-\fIname\fR discussion in \fBaliases\fR(5)), use the owner information -even when the expansion invokes a subordinate alias that has no -owner alias. .SH "DELIVERY METHOD CONTROLS" .na .nf diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index f4ea87057..7ecb1c2e4 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -453,6 +453,11 @@ receiving a remote SMTP client request. The maximal number of lines in the Postfix SMTP server command history before it is flushed upon receipt of EHLO, RSET, or end of DATA. .PP +Available in Postfix version 2.3 and later: +.IP "\fBsmtpd_peername_lookup (yes)\fR" +Attempt to look up the SMTP client hostname, and verify that +the name matches the client IP address. +.PP The per SMTP client connection count and request rate limits are implemented in co-operation with the \fBanvil\fR(8) service, and are available in Postfix version 2.2 and later. @@ -473,6 +478,11 @@ Postfix actually accepts those recipients. .IP "\fBsmtpd_client_event_limit_exceptions ($mynetworks)\fR" Clients that are excluded from connection count, connection rate, or SMTP request rate restrictions. +.PP +Available in Postfix version 2.3 and later: +.IP "\fBsmtpd_client_new_tls_session_rate_limit (0)\fR" +The maximal number of new (i.e., uncached) TLS sessions that any +client is allowed to negotiate with this service per time unit. .SH "TARPIT CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 06a3a1f3a..65704bbbe 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -372,6 +372,7 @@ while (<>) { s;\bsmtpd_client_connection_rate_limit\b;$&;g; s;\bsmtpd_client_message_rate_limit\b;$&;g; s;\bsmtpd_client_recipient_rate_limit\b;$&;g; + s;\bsmtpd_client_new_tls_session_rate_limit\b;$&;g; s;\bsmtpd_client_restrictions\b;$&;g; s;\bsmtpd_data_restrictions\b;$&;g; s;\bsmtpd_delay_reject\b;$&;g; @@ -390,6 +391,7 @@ while (<>) { s;\bsmtpd_noop_commands\b;$&;g; s;\bsmtpd_null_access_lookup_key\b;$&;g; s;\bsmtpd_recipient_overshoot_limit\b;$&;g; + s;\bsmtpd_peername_lookup\b;$&;g; s;\bsmtpd_policy_service_max_idle\b;$&;g; s;\bsmtpd_policy_service_max_ttl\b;$&;g; s;\bsmtpd_policy_service_timeout\b;$&;g; @@ -502,7 +504,6 @@ while (<>) { s;\btls_ran[-]*\n* *[]*dom_source\b;$&;g; s;\bfrozen_delivered_to\b;$&;g; - s;\bsticky_owner_alias\b;$&;g; # Undo hyperlinks of manual pages with the same name as parameters. diff --git a/postfix/proto/SMTPD_POLICY_README.html b/postfix/proto/SMTPD_POLICY_README.html index 884d0de52..9e8c32917 100644 --- a/postfix/proto/SMTPD_POLICY_README.html +++ b/postfix/proto/SMTPD_POLICY_README.html @@ -36,7 +36,8 @@ the Postfix source code, in the directory examples/smtpd-policy. to Postfix. It's much easier to develop a new feature in few lines of Perl, than trying to do the same in C code. The difference in performance will be unnoticeable except in the most demanding -environments.

+environments. On active systems a policy daemon process is used +multiple times, for up to 100 incoming SMTP connections.

This document covers the following topics:

diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index b37eadef6..adcf4124d 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -4399,6 +4399,43 @@ Example: smtpd_client_recipient_rate_limit = 1000 +%PARAM smtpd_client_new_tls_session_rate_limit 0 + +

+The maximal number of new (i.e., uncached) TLS sessions that a +remote SMTP client is allowed to negotiate with this service per +time unit. The time unit is specified with the anvil_rate_time_unit +configuration parameter. +

+ +

+By default, a remote SMTP client can negotiate as many new TLS +sessions per time unit as Postfix can accept. +

+ +

+To disable this feature, specify a limit of 0. Otherwise, specify +a limit that is at least the per-client concurrent session limit, +or else legitimate client sessions may be rejected. +

+ +

+WARNING: The purpose of this feature is to limit abuse. It must +not be used to regulate legitimate mail traffic. +

+ +

+This feature is available in Postfix 2.3 and later. +

+ +

+Example: +

+ +
+smtpd_client_new_tls_session_rate_limit = 100
+
+ %PARAM smtpd_client_restrictions

@@ -8542,18 +8579,6 @@ message_strip_characters = \0

This feature is available in Postfix 2.3 and later.

-%PARAM sticky_owner_alias yes - -

When expanding a local(8) alias that has an owner alias (see -owner-name discussion in aliases(5)), use the owner information -even when the expansion invokes a subordinate alias that has no -owner alias.

- -

This feature is available in Postfix 2.3 and later. With older -Postfix releases, the behavior is as if this parameter is set to -"no". The old setting provides poorer error reporting with nested -aliases that only have an owner alias at the top level.

- %PARAM frozen_delivered_to yes

Update the local(8) delivery agent's idea of the Delivered-To: @@ -8567,3 +8592,11 @@ Postfix releases, the behavior is as if this parameter is set to or .forward files. When an alias or .forward file changes the Delivered-To: address, it ties up one queue file and one cleanup process instance while mail is being forwarded.

+ +%PARAM smtpd_peername_lookup yes + +

Attempt to look up the SMTP client hostname, and verify that +the name matches the client IP address. A client name is set to +"unknown" when it cannot be looked up or verified, or when name +lookup is disabled. Turning off name lookup reduces delays due to +DNS lookup and increases the maximal inbound delivery rate.

diff --git a/postfix/src/anvil/anvil.c b/postfix/src/anvil/anvil.c index 43f7e54d6..9f8a08439 100644 --- a/postfix/src/anvil/anvil.c +++ b/postfix/src/anvil/anvil.c @@ -6,21 +6,23 @@ /* SYNOPSIS /* \fBanvil\fR [generic Postfix daemon options] /* DESCRIPTION -/* The Postfix \fBanvil\fR(8) server maintains short-term statistics -/* to defend against clients that hammer a server with either too -/* many simultaneous sessions, or with too many successive requests -/* within a configurable time interval. -/* This server is designed to run under control by the Postfix -/* \fBmaster\fR(8) server. +/* The Postfix \fBanvil\fR(8) server maintains statistics about +/* client connection counts or client request rates. This +/* information can be used to defend against clients that +/* hammer a server with either too many simultaneous sessions, +/* or with too many successive requests within a configurable +/* time interval. This server is designed to run under control +/* by the Postfix \fBmaster\fR(8) server. /* -/* The \fBanvil\fR(8) server maintains no persistent database. Standard -/* library utilities do not meet Postfix performance and robustness -/* requirements. -/* CONNECTION COUNT/RATE LIMITING +/* In the following text, \fBident\fR specifies a (service, +/* client) combination. The exact syntax of that information +/* is application-dependent; the \fBanvil\fR(8) server does +/* not care. +/* CONNECTION COUNT/RATE CONTROL /* .ad /* .fi -/* When a remote client connects, a connection count (or rate) limited -/* server should send the following request to the \fBanvil\fR(8) server: +/* To register a new connection send the following request to +/* the \fBanvil\fR(8) server: /* .PP /* .in +4 /* \fBrequest=connect\fR @@ -28,11 +30,10 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a new connection for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) server -/* answers with the number of simultaneous connections and the -/* number of connections per unit time for that (service, client) -/* combination: +/* The \fBanvil\fR(8) server answers with the number of +/* simultaneous connections and the number of connections per +/* unit time for the (service, client) combination specified +/* with \fBident\fR: /* .PP /* .in +4 /* \fBstatus=0\fR @@ -42,13 +43,8 @@ /* \fBrate=\fInumber\fR /* .in /* .PP -/* The \fBrate\fR is computed as the number of connections -/* that were registered in the current "time unit" interval. -/* It is left up to the server to decide if the remote client -/* exceeds the connection count (or rate) limit. -/* .PP -/* When a remote client disconnects, a connection count (or rate) limited -/* server should send the following request to the \fBanvil\fR(8) server: +/* To register a disconnect event send the following request +/* to the \fBanvil\fR(8) server: /* .PP /* .in +4 /* \fBrequest=disconnect\fR @@ -56,17 +52,14 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a disconnect event for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) -/* server replies with: +/* The \fBanvil\fR(8) server replies with: /* .PP /* .ti +4 /* \fBstatus=0\fR -/* MESSAGE RATE LIMITING +/* MESSAGE RATE CONTROL /* .ad /* .fi -/* When a remote client sends a message delivery request, a -/* message rate limited server should send the following +/* To register a message delivery request send the following /* request to the \fBanvil\fR(8) server: /* .PP /* .in +4 @@ -75,26 +68,20 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a message delivery request for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) server -/* answers with the number of message delivery requests per unit time -/* for that (service, client) combination: +/* The \fBanvil\fR(8) server answers with the number of message +/* delivery requests per unit time for the (service, client) +/* combination specified with \fBident\fR: /* .PP /* .in +4 /* \fBstatus=0\fR /* .br /* \fBrate=\fInumber\fR /* .in -/* .PP -/* In order to prevent the \fBanvil\fR(8) server from discarding client -/* request rates too early or too late, a message rate limited -/* service should also register connect/disconnect events. -/* RECIPIENT RATE LIMITING +/* RECIPIENT RATE CONTROL /* .ad /* .fi -/* When a remote client sends a recipient address, a recipient -/* rate limited server should send the following request to -/* the \fBanvil\fR(8) server: +/* To register a recipient request send the following request +/* to the \fBanvil\fR(8) server: /* .PP /* .in +4 /* \fBrequest=recipient\fR @@ -102,10 +89,33 @@ /* \fBident=\fIstring\fR /* .in /* .PP -/* This registers a recipient request for the (service, client) -/* combination specified with \fBident\fR. The \fBanvil\fR(8) server -/* answers with the number of recipient addresses per unit time -/* for that (service, client) combination: +/* The \fBanvil\fR(8) server answers with the number of recipient +/* addresses per unit time for the (service, client) combination +/* specified with \fBident\fR: +/* .PP +/* .in +4 +/* \fBstatus=0\fR +/* .br +/* \fBrate=\fInumber\fR +/* .in +/* TLS SESSION NEGOTIATION RATE CONTROL +/* .ad +/* .fi +/* The features described in this section are available with +/* Postfix 2.3 and later. +/* +/* To register a request for a new (i.e. not cached) TLS session +/* send the following request to the \fBanvil\fR(8) server: +/* .PP +/* .in +4 +/* \fBrequest=newtls\fR +/* .br +/* \fBident=\fIstring\fR +/* .in +/* .PP +/* The \fBanvil\fR(8) server answers with the number of new +/* TLS session requests per unit time for the (service, client) +/* combination specified with \fBident\fR: /* .PP /* .in +4 /* \fBstatus=0\fR @@ -113,21 +123,39 @@ /* \fBrate=\fInumber\fR /* .in /* .PP -/* In order to prevent the \fBanvil\fR(8) server from discarding client -/* request rates too early or too late, a recipient rate limited -/* service should also register connect/disconnect events. +/* To retrieve new TLS session request rate information without +/* updating the counter information, use: +/* .PP +/* .in +4 +/* \fBrequest=newtls_report\fR +/* .br +/* \fBident=\fIstring\fR +/* .in +/* .PP +/* The \fBanvil\fR(8) server answers with the number of new +/* TLS session requests per unit time for the (service, client) +/* combination specified with \fBident\fR. +/* .PP +/* .in +4 +/* \fBstatus=0\fR +/* .br +/* \fBrate=\fInumber\fR +/* .in /* SECURITY /* .ad /* .fi /* The \fBanvil\fR(8) server does not talk to the network or to local /* users, and can run chrooted at fixed low privilege. /* -/* The \fBanvil\fR(8) server maintains an in-memory table with information -/* about recent clients of a connection count (or rate) limited service. -/* Although state is kept only temporarily, this may require a lot of -/* memory on systems that handle connections from many remote clients. -/* To reduce memory usage, reduce the time unit over which state -/* is kept. +/* The \fBanvil\fR(8) server maintains an in-memory table with +/* information about recent clients requests. No persistent +/* state is kept because standard system library routines are +/* not sufficiently robust for update-intensive applications. +/* +/* Although the in-memory state is kept only temporarily, this +/* may require a lot of memory on systems that handle connections +/* from many remote clients. To reduce memory usage, reduce +/* the time unit over which state is kept. /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8). /* @@ -147,12 +175,20 @@ /* can have only one remote client at a time. If a server reports /* multiple simultaneous clients, all but the last reported client /* are ignored. +/* +/* The \fBanvil\fR(8) server automatically discards client +/* request information after it expires. To prevent the +/* \fBanvil\fR(8) server from discarding client request rate +/* information too early or too late, a rate limited service +/* should always register connect/disconnect events even when +/* it does not explicitly limit them. /* CONFIGURATION PARAMETERS /* .ad /* .fi -/* Changes to \fBmain.cf\fR are picked up automatically as \fBanvil\fR(8) -/* processes run for only a limited amount of time. Use the command -/* "\fBpostfix reload\fR" to speed up a change. +/* On low-traffic mail systems, changes to \fBmain.cf\fR are +/* picked up automatically as \fBanvil\fR(8) processes run for +/* only a limited amount of time. On other mail systems, use +/* the command "\fBpostfix reload\fR" to speed up a change. /* /* The text below provides only a parameter summary. See /* \fBpostconf\fR(5) for more details including examples. @@ -252,28 +288,53 @@ static HTABLE *anvil_remote_map; /* indexed by service+ remote client */ * Absent a real-time query interface, these are logged at process exit time * and at regular intervals. */ -static int max_count; -static char *max_count_user; -static time_t max_count_time; +typedef struct { + int value; /* peak value */ + char *ident; /* lookup key */ + time_t when; /* time of peak value */ +} ANVIL_MAX; -static int max_rate; -static char *max_rate_user; -static time_t max_rate_time; +static ANVIL_MAX max_conn_count; /* peak connection count */ +static ANVIL_MAX max_conn_rate; /* peak connection rate */ +static ANVIL_MAX max_mail_rate; /* peak message rate */ +static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */ +static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */ -static int max_mail; -static char *max_mail_user; -static time_t max_mail_time; +static int max_cache_size; /* peak cache size */ +static time_t max_cache_time; /* time of peak size */ -static int max_rcpt; -static char *max_rcpt_user; -static time_t max_rcpt_time; +/* Update/report peak usage. */ -static int max_newtls; -static char *max_newtls_user; -static time_t max_newtls_time; +#define ANVIL_MAX_UPDATE(_max, _value, _ident) \ + do { \ + _max.value = _value; \ + if (_max.ident == 0) { \ + _max.ident = mystrdup(_ident); \ + } else if (!STREQ(_max.ident, _ident)) { \ + myfree(_max.ident); \ + _max.ident = mystrdup(_ident); \ + } \ + _max.when = event_time(); \ + } while (0) -static int max_cache; -static time_t max_cache_time; +#define ANVIL_MAX_RATE_REPORT(_max, _name) \ + do { \ + if (_max.value > 0) { \ + msg_info("statistics: max " _name " rate %d/%ds for (%s) at %.15s", \ + _max.value, var_anvil_time_unit, \ + _max.ident, ctime(&_max.when) + 4); \ + _max.value = 0; \ + } \ + } while (0); + +#define ANVIL_MAX_COUNT_REPORT(_max, _name) \ + do { \ + if (_max.value > 0) { \ + msg_info("statistics: max " _name " count %d for (%s) at %.15s", \ + _max.value, _max.ident, ctime(&_max.when) + 4); \ + _max.value = 0; \ + } \ + } while (0); /* * Remote connection state, one instance for each (service, client) pair. @@ -284,7 +345,7 @@ typedef struct { int rate; /* connection rate */ int mail; /* message rate */ int rcpt; /* recipient rate */ - int newtls; /* newtls rate */ + int ntls; /* new TLS session rate */ time_t start; /* time of first rate sample */ } ANVIL_REMOTE; @@ -296,12 +357,6 @@ typedef struct { ANVIL_REMOTE *anvil_remote; /* XXX should be list */ } ANVIL_LOCAL; - /* - * Silly little macros. - */ -#define STR(x) vstring_str(x) -#define STREQ(x,y) (strcmp((x), (y)) == 0) - /* * The following operations are implemented as macros with recognizable * names so that we don't lose sight of what the code is trying to do. @@ -312,14 +367,14 @@ typedef struct { /* Create new (service, client) state. */ -#define ANVIL_REMOTE_FIRST(remote, id) \ +#define ANVIL_REMOTE_FIRST_CONN(remote, id) \ do { \ (remote)->ident = mystrdup(id); \ (remote)->count = 1; \ (remote)->rate = 1; \ (remote)->mail = 0; \ (remote)->rcpt = 0; \ - (remote)->newtls = 0; \ + (remote)->ntls = 0; \ (remote)->start = event_time(); \ } while(0) @@ -331,66 +386,41 @@ typedef struct { myfree((char *) (remote)); \ } while(0) +/* Reset event rate counters and start of data collection interval. */ + +#define ANVIL_REMOTE_RSET_RATE(remote, _start) \ + do { \ + (remote)->rate = 0; \ + (remote)->mail = 0; \ + (remote)->rcpt = 0; \ + (remote)->ntls = 0; \ + (remote)->start = _start; \ + } while(0) + /* Add connection to (service, client) state. */ -#define ANVIL_REMOTE_NEXT(remote) \ +#define ANVIL_REMOTE_INCR_RATE(remote, _what) \ do { \ time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 1; \ - (remote)->mail = 0; \ - (remote)->rcpt = 0; \ - (remote)->newtls = 0; \ - (remote)->start = _now; \ - } else if ((remote)->rate < INT_MAX) { \ - (remote)->rate += 1; \ - } \ + if ((remote)->start + var_anvil_time_unit < _now) \ + ANVIL_REMOTE_RSET_RATE((remote), _now); \ + if ((remote)->_what < INT_MAX) \ + (remote)->_what += 1; \ + } while(0) + +#define ANVIL_REMOTE_NEXT_CONN(remote) \ + do { \ + ANVIL_REMOTE_INCR_RATE((remote), rate); \ if ((remote)->count == 0) \ event_cancel_timer(anvil_remote_expire, (char *) remote); \ (remote)->count++; \ } while(0) -#define ANVIL_ADD_MAIL(remote) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 0; \ - (remote)->mail = 1; \ - (remote)->rcpt = 0; \ - (remote)->newtls = 0; \ - (remote)->start = _now; \ - } else if ((remote)->mail < INT_MAX) { \ - (remote)->mail += 1; \ - } \ - } while(0) +#define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail) -#define ANVIL_ADD_RCPT(remote) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 0; \ - (remote)->mail = 0; \ - (remote)->rcpt = 1; \ - (remote)->newtls = 0; \ - (remote)->start = _now; \ - } else if ((remote)->rcpt < INT_MAX) { \ - (remote)->rcpt += 1; \ - } \ - } while(0) +#define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt) -#define ANVIL_ADD_STARTTLS(remote) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) { \ - (remote)->rate = 0; \ - (remote)->mail = 0; \ - (remote)->rcpt = 0; \ - (remote)->newtls = 1; \ - (remote)->start = _now; \ - } else if ((remote)->newtls < INT_MAX) { \ - (remote)->newtls += 1; \ - } \ - } while(0) +#define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls) /* Drop connection from (service, client) state. */ @@ -443,6 +473,20 @@ typedef struct { anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \ } while (0) + /* + * Lookup table to map request names to action routines. + */ +typedef struct { + const char *name; + void (*action) (VSTREAM *, const char *); +} ANVIL_REQ_TABLE; + + /* + * Silly little macros. + */ +#define STR(x) vstring_str(x) +#define STREQ(x,y) (strcmp((x), (y)) == 0) + /* anvil_remote_expire - purge expired connection state */ static void anvil_remote_expire(int unused_event, char *context) @@ -472,8 +516,6 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident) { ANVIL_REMOTE *anvil_remote; char *myname = "anvil_remote_lookup"; - HTABLE_INFO **ht_info; - HTABLE_INFO **ht; if (msg_verbose) msg_info("%s fd=%d stream=0x%lx ident=%s", @@ -483,39 +525,31 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident) /* * Look up remote client information. */ - if (STREQ(ident, "*")) { - attr_print_plain(client_stream, ATTR_FLAG_MORE, - ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, - ATTR_TYPE_END); - ht_info = htable_list(anvil_remote_map); - for (ht = ht_info; *ht; ht++) { - anvil_remote = (ANVIL_REMOTE *) ht[0]->value; - attr_print_plain(client_stream, ATTR_FLAG_MORE, - ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ht[0]->key, - ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, anvil_remote->count, - ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rate, - ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, anvil_remote->mail, - ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, anvil_remote->rcpt, - ATTR_TYPE_END); - } - attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_END); - myfree((char *) ht_info); - } else if ((anvil_remote = - (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { + if ((anvil_remote = + (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { attr_print_plain(client_stream, ATTR_FLAG_NONE, - ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL, + ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, 0, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, 0, ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, 0, ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, 0, + ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, 0, ATTR_TYPE_END); } else { + + /* + * Do not report stale information. + */ + if (anvil_remote->start != 0 + && anvil_remote->start + var_anvil_time_unit < event_time()) + ANVIL_REMOTE_RSET_RATE(anvil_remote, 0); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, anvil_remote->count, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rate, ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, anvil_remote->mail, ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, anvil_remote->rcpt, + ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, anvil_remote->ntls, ATTR_TYPE_END); } } @@ -542,14 +576,14 @@ static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char if ((anvil_remote = (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { anvil_remote = (ANVIL_REMOTE *) mymalloc(sizeof(*anvil_remote)); - ANVIL_REMOTE_FIRST(anvil_remote, ident); + ANVIL_REMOTE_FIRST_CONN(anvil_remote, ident); htable_enter(anvil_remote_map, ident, (char *) anvil_remote); - if (max_cache < anvil_remote_map->used) { - max_cache = anvil_remote_map->used; + if (max_cache_size < anvil_remote_map->used) { + max_cache_size = anvil_remote_map->used; max_cache_time = event_time(); } } else { - ANVIL_REMOTE_NEXT(anvil_remote); + ANVIL_REMOTE_NEXT_CONN(anvil_remote); } /* @@ -592,28 +626,12 @@ static void anvil_remote_connect(VSTREAM *client_stream, const char *ident) ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->rate > max_rate) { - max_rate = anvil_remote->rate; - if (max_rate_user == 0) { - max_rate_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_rate_user, anvil_remote->ident)) { - myfree(max_rate_user); - max_rate_user = mystrdup(anvil_remote->ident); - } - max_rate_time = event_time(); - } - if (anvil_remote->count > max_count) { - max_count = anvil_remote->count; - if (max_count_user == 0) { - max_count_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_count_user, anvil_remote->ident)) { - myfree(max_count_user); - max_count_user = mystrdup(anvil_remote->ident); - } - max_count_time = event_time(); - } + if (anvil_remote->rate > max_conn_rate.value) + ANVIL_MAX_UPDATE(max_conn_rate, anvil_remote->rate, anvil_remote->ident); + if (anvil_remote->count > max_conn_count.value) + ANVIL_MAX_UPDATE(max_conn_count, anvil_remote->count, anvil_remote->ident); } /* anvil_remote_mail - register message delivery request */ @@ -632,25 +650,17 @@ static void anvil_remote_mail(VSTREAM *client_stream, const char *ident) /* * Update message delivery request rate and respond to local client. */ - ANVIL_ADD_MAIL(anvil_remote); + ANVIL_REMOTE_INCR_MAIL(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->mail, ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->mail > max_mail) { - max_mail = anvil_remote->mail; - if (max_mail_user == 0) { - max_mail_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_mail_user, anvil_remote->ident)) { - myfree(max_mail_user); - max_mail_user = mystrdup(anvil_remote->ident); - } - max_mail_time = event_time(); - } + if (anvil_remote->mail > max_mail_rate.value) + ANVIL_MAX_UPDATE(max_mail_rate, anvil_remote->mail, anvil_remote->ident); } /* anvil_remote_rcpt - register recipient address event */ @@ -669,25 +679,17 @@ static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident) /* * Update recipient address rate and respond to local client. */ - ANVIL_ADD_RCPT(anvil_remote); + ANVIL_REMOTE_INCR_RCPT(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rcpt, ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->rcpt > max_rcpt) { - max_rcpt = anvil_remote->rcpt; - if (max_rcpt_user == 0) { - max_rcpt_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_rcpt_user, anvil_remote->ident)) { - myfree(max_rcpt_user); - max_rcpt_user = mystrdup(anvil_remote->ident); - } - max_rcpt_time = event_time(); - } + if (anvil_remote->rcpt > max_rcpt_rate.value) + ANVIL_MAX_UPDATE(max_rcpt_rate, anvil_remote->rcpt, anvil_remote->ident); } /* anvil_remote_newtls - register newtls event */ @@ -706,25 +708,51 @@ static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident) /* * Update newtls rate and respond to local client. */ - ANVIL_ADD_STARTTLS(anvil_remote); + ANVIL_REMOTE_INCR_NTLS(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, - ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->newtls, + ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->ntls, ATTR_TYPE_END); /* - * Update local statistics. + * Update peak statistics. */ - if (anvil_remote->newtls > max_newtls) { - max_newtls = anvil_remote->newtls; - if (max_newtls_user == 0) { - max_newtls_user = mystrdup(anvil_remote->ident); - } else if (!STREQ(max_newtls_user, anvil_remote->ident)) { - myfree(max_newtls_user); - max_newtls_user = mystrdup(anvil_remote->ident); - } - max_newtls_time = event_time(); + if (anvil_remote->ntls > max_ntls_rate.value) + ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident); +} + +/* anvil_remote_newtls_stat - report newtls stats */ + +static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident) +{ + ANVIL_REMOTE *anvil_remote; + int rate; + + /* + * Be prepared for "postfix reload" after "connect". + */ + if ((anvil_remote = + (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) { + rate = 0; } + + /* + * Do not report stale information. + */ + else { + if (anvil_remote->start != 0 + && anvil_remote->start + var_anvil_time_unit < event_time()) + ANVIL_REMOTE_RSET_RATE(anvil_remote, 0); + rate = anvil_remote->ntls; + } + + /* + * Respond to local client. + */ + attr_print_plain(client_stream, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, + ATTR_TYPE_NUM, ANVIL_ATTR_RATE, rate, + ATTR_TYPE_END); } /* anvil_remote_disconnect - report disconnect event */ @@ -796,8 +824,19 @@ static void anvil_service_done(VSTREAM *client_stream, char *unused_service, static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv) { - VSTRING *request = vstring_alloc(10); - VSTRING *ident = vstring_alloc(10); + static VSTRING *request; + static VSTRING *ident; + static ANVIL_REQ_TABLE request_table[] = { + ANVIL_REQ_CONN, anvil_remote_connect, + ANVIL_REQ_MAIL, anvil_remote_mail, + ANVIL_REQ_RCPT, anvil_remote_rcpt, + ANVIL_REQ_NTLS, anvil_remote_newtls, + ANVIL_REQ_DISC, anvil_remote_disconnect, + ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat, + ANVIL_REQ_LOOKUP, anvil_remote_lookup, + 0, 0, + }; + ANVIL_REQ_TABLE *rp; /* * Sanity check. This service takes no command-line arguments. @@ -805,6 +844,14 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); + /* + * Initialize. + */ + if (request == 0) { + request = vstring_alloc(10); + ident = vstring_alloc(10); + } + /* * This routine runs whenever a client connects to the socket dedicated * to the client connection rate management service. All @@ -818,23 +865,18 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a ATTR_TYPE_STR, ANVIL_ATTR_REQ, request, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, ATTR_TYPE_END) == 2) { - if (STREQ(STR(request), ANVIL_REQ_CONN)) { - anvil_remote_connect(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_MAIL)) { - anvil_remote_mail(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_RCPT)) { - anvil_remote_rcpt(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_NEWTLS)) { - anvil_remote_newtls(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_DISC)) { - anvil_remote_disconnect(client_stream, STR(ident)); - } else if (STREQ(STR(request), ANVIL_REQ_LOOKUP)) { - anvil_remote_lookup(client_stream, STR(ident)); - } else { - msg_warn("unrecognized request: \"%s\", ignored", STR(request)); - attr_print_plain(client_stream, ATTR_FLAG_NONE, + for (rp = request_table; /* see below */ ; rp++) { + if (rp->name == 0) { + msg_warn("unrecognized request: \"%s\", ignored", STR(request)); + attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL, - ATTR_TYPE_END); + ATTR_TYPE_END); + break; + } + if (STREQ(rp->name, STR(request))) { + rp->action(client_stream, STR(ident)); + break; + } } vstream_fflush(client_stream); } else { @@ -843,8 +885,6 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a } if (msg_verbose) msg_info("--- end request ---"); - vstring_free(ident); - vstring_free(request); } /* post_jail_init - post-jail initialization */ @@ -874,39 +914,16 @@ static void post_jail_init(char *unused_name, char **unused_argv) static void anvil_status_dump(char *unused_name, char **unused_argv) { - if (max_rate > 0) { - msg_info("statistics: max connection rate %d/%ds for (%s) at %.15s", - max_rate, var_anvil_time_unit, - max_rate_user, ctime(&max_rate_time) + 4); - max_rate = 0; - } - if (max_count > 0) { - msg_info("statistics: max connection count %d for (%s) at %.15s", - max_count, max_count_user, ctime(&max_count_time) + 4); - max_count = 0; - } - if (max_mail > 0) { - msg_info("statistics: max message rate %d/%ds for (%s) at %.15s", - max_mail, var_anvil_time_unit, - max_mail_user, ctime(&max_mail_time) + 4); - max_mail = 0; - } - if (max_rcpt > 0) { - msg_info("statistics: max recipient rate %d/%ds for (%s) at %.15s", - max_rcpt, var_anvil_time_unit, - max_rcpt_user, ctime(&max_rcpt_time) + 4); - max_rcpt = 0; - } - if (max_newtls > 0) { - msg_info("statistics: max newtls rate %d/%ds for (%s) at %.15s", - max_newtls, var_anvil_time_unit, - max_newtls_user, ctime(&max_newtls_time) + 4); - max_newtls = 0; - } - if (max_cache > 0) { + ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection"); + ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection"); + ANVIL_MAX_RATE_REPORT(max_mail_rate, "message"); + ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient"); + ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls"); + + if (max_cache_size > 0) { msg_info("statistics: max cache size %d at %.15s", - max_cache, ctime(&max_cache_time) + 4); - max_cache = 0; + max_cache_size, ctime(&max_cache_time) + 4); + max_cache_size = 0; } } diff --git a/postfix/src/global/anvil_clnt.c b/postfix/src/global/anvil_clnt.c index c54b9500b..a0117cdc7 100644 --- a/postfix/src/global/anvil_clnt.c +++ b/postfix/src/global/anvil_clnt.c @@ -37,6 +37,12 @@ /* const char *addr; /* int *newtls; /* +/* int anvil_clnt_newtls_stat(anvil_clnt, service, addr, newtls) +/* ANVIL_CLNT *anvil_clnt; +/* const char *service; +/* const char *addr; +/* int *newtls; +/* /* int anvil_clnt_disconnect(anvil_clnt, service, addr) /* ANVIL_CLNT *anvil_clnt; /* const char *service; @@ -52,29 +58,36 @@ /* int *msgs; /* int *rcpts; /* DESCRIPTION -/* anvil_clnt_create() instantiates an anvil service client endpoint. +/* anvil_clnt_create() instantiates a local anvil service +/* client endpoint. /* /* anvil_clnt_connect() informs the anvil server that a -/* client has connected, and returns the current connection -/* count and connection rate for that client. +/* remote client has connected, and returns the current +/* connection count and connection rate for that remote client. /* -/* anvil_clnt_mail() registers a MAIL FROM event and returns -/* the current MAIL FROM rate for the specified client. +/* anvil_clnt_mail() registers a MAIL FROM event and +/* returns the current MAIL FROM rate for the specified remote +/* client. /* -/* anvil_clnt_rcpt() registers a RCPT TO event and returns -/* the current RCPT TO rate for the specified client. +/* anvil_clnt_rcpt() registers a RCPT TO event and +/* returns the current RCPT TO rate for the specified remote +/* client. /* -/* anvil_clnt_newtls() registers a request to negotiate a new -/* (uncached) TLS session and returns the current request rate -/* for the specified client. +/* anvil_clnt_newtls() registers a remote client request +/* to negotiate a new (uncached) TLS session and returns the +/* current newtls request rate for the specified remote client. /* -/* anvil_clnt_disconnect() informs the anvil server that a +/* anvil_clnt_newtls_stat() returns the current newtls request +/* rate for the specified remote client. +/* +/* anvil_clnt_disconnect() informs the anvil server that a remote /* client has disconnected. /* -/* anvil_clnt_lookup() looks up the current connection -/* count and connection rate for that client. +/* anvil_clnt_lookup() returns the current count and rate +/* information for the specified client. /* -/* anvil_clnt_free() destroys an anvil service client endpoint. +/* anvil_clnt_free() destroys a local anvil service client +/* endpoint. /* /* Arguments: /* .IP anvil_clnt @@ -99,7 +112,7 @@ /* Pointer to storage for the current "new TLS session" rate /* for this remote client. /* DIAGNOSTICS -/* anvil_clnt_connect() and anvil_clnt_disconnect() return +/* The update and status query routines return /* ANVIL_STAT_OK in case of success, ANVIL_STAT_FAIL otherwise /* (either the communication with the server is broken or the /* server experienced a problem). @@ -168,7 +181,7 @@ void anvil_clnt_free(ANVIL_CLNT *anvil_clnt) int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *count, int *rate, - int *msgs, int *rcpts) + int *msgs, int *rcpts, int *newtls) { char *ident = ANVIL_IDENT(service, addr); int status; @@ -184,7 +197,8 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, ATTR_TYPE_NUM, ANVIL_ATTR_RATE, rate, ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, msgs, ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, rcpts, - ATTR_TYPE_END) != 5) + ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, newtls, + ATTR_TYPE_END) != 6) status = ANVIL_STAT_FAIL; else if (status != ANVIL_STAT_OK) status = ANVIL_STAT_FAIL; @@ -192,7 +206,7 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_connect - heads-up and policy query */ +/* anvil_clnt_connect - heads-up and status query */ int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *count, int *rate) @@ -217,7 +231,7 @@ int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_mail - heads-up and policy query */ +/* anvil_clnt_mail - heads-up and status query */ int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *msgs) @@ -241,7 +255,7 @@ int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_rcpt - heads-up and policy query */ +/* anvil_clnt_rcpt - heads-up and status query */ int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *rcpts) @@ -265,17 +279,41 @@ int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service, return (status); } -/* anvil_clnt_newtls - heads-up and policy query */ +/* anvil_clnt_newtls - heads-up and status query */ -int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, - const char *addr, int *newtls) +int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, + const char *addr, int *newtls) { char *ident = ANVIL_IDENT(service, addr); - int status; + int status; - if (attr_clnt_request((ATTR_CLNT *)anvil_clnt, + if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, ATTR_FLAG_NONE, /* Query attributes. */ - ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NEWTLS, + ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS, + ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply attributes. */ + ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, &status, + ATTR_TYPE_NUM, ANVIL_ATTR_RATE, newtls, + ATTR_TYPE_END) != 2) + status = ANVIL_STAT_FAIL; + else if (status != ANVIL_STAT_OK) + status = ANVIL_STAT_FAIL; + myfree(ident); + return (status); +} + +/* anvil_clnt_newtls_stat - status query */ + +int anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service, + const char *addr, int *newtls) +{ + char *ident = ANVIL_IDENT(service, addr); + int status; + + if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, + ATTR_FLAG_NONE, /* Query attributes. */ + ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply attributes. */ @@ -291,13 +329,13 @@ int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service, /* anvil_clnt_disconnect - heads-up only */ -int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, - const char *addr) +int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, + const char *addr) { char *ident = ANVIL_IDENT(service, addr); - int status; + int status; - if (attr_clnt_request((ATTR_CLNT *)anvil_clnt, + if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, ATTR_FLAG_NONE, /* Query attributes. */ ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_DISC, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, @@ -326,13 +364,17 @@ int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, static void usage(void) { - vstream_printf("usage: %s service addr | %s service addr |" - " %s service addr | %s service addr\n", - ANVIL_REQ_CONN, ANVIL_REQ_DISC, - ANVIL_REQ_MAIL, ANVIL_REQ_RCPT); + vstream_printf("usage: " + ANVIL_REQ_CONN " service addr | " + ANVIL_REQ_DISC " service addr | " + ANVIL_REQ_MAIL " service addr | " + ANVIL_REQ_RCPT " service addr | " + ANVIL_REQ_NTLS " service addr | " + ANVIL_REQ_NTLS_STAT " service addr | " + ANVIL_REQ_LOOKUP " service addr\n"); } -int main(int unused_argc, char **argv) +int main(int unused_argc, char **argv) { VSTRING *inbuf = vstring_alloc(1); char *bufp; @@ -340,10 +382,11 @@ int main(int unused_argc, char **argv) ssize_t cmd_len; char *service; char *addr; - int count; - int rate; - int msgs; - int rcpts; + int count; + int rate; + int msgs; + int rcpts; + int newtls; ANVIL_CLNT *anvil; msg_vstream_init(argv[0], VSTREAM_ERR); @@ -384,6 +427,16 @@ int main(int unused_argc, char **argv) msg_warn("error!"); else vstream_printf("rate=%d\n", rcpts); + } else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) { + if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK) + msg_warn("error!"); + else + vstream_printf("rate=%d\n", newtls); + } else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) { + if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK) + msg_warn("error!"); + else + vstream_printf("rate=%d\n", newtls); } else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) { if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK) msg_warn("error!"); @@ -391,11 +444,11 @@ int main(int unused_argc, char **argv) vstream_printf("OK\n"); } else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) { if (anvil_clnt_lookup(anvil, service, addr, &count, &rate, - &msgs, &rcpts) != ANVIL_STAT_OK) + &msgs, &rcpts, &newtls) != ANVIL_STAT_OK) msg_warn("error!"); else - vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d\n", - count, rate, msgs, rcpts); + vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d\n", + count, rate, msgs, rcpts, newtls); } else { vstream_printf("bad command: \"%s\"\n", cmd); usage(); diff --git a/postfix/src/global/anvil_clnt.h b/postfix/src/global/anvil_clnt.h index dbdce6313..0e48a42dd 100644 --- a/postfix/src/global/anvil_clnt.h +++ b/postfix/src/global/anvil_clnt.h @@ -32,13 +32,15 @@ #define ANVIL_REQ_DISC "disconnect" #define ANVIL_REQ_MAIL "message" #define ANVIL_REQ_RCPT "recipient" -#define ANVIL_REQ_NEWTLS "newtls" +#define ANVIL_REQ_NTLS "newtls" +#define ANVIL_REQ_NTLS_STAT "newtls_status" #define ANVIL_REQ_LOOKUP "lookup" #define ANVIL_ATTR_IDENT "ident" #define ANVIL_ATTR_COUNT "count" #define ANVIL_ATTR_RATE "rate" #define ANVIL_ATTR_MAIL "mail" #define ANVIL_ATTR_RCPT "rcpt" +#define ANVIL_ATTR_NTLS "newtls" #define ANVIL_ATTR_STATUS "status" #define ANVIL_STAT_OK 0 @@ -54,7 +56,8 @@ extern int anvil_clnt_connect(ANVIL_CLNT *, const char *, const char *, int *, i extern int anvil_clnt_mail(ANVIL_CLNT *, const char *, const char *, int *); extern int anvil_clnt_rcpt(ANVIL_CLNT *, const char *, const char *, int *); extern int anvil_clnt_newtls(ANVIL_CLNT *, const char *, const char *, int *); -extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *); +extern int anvil_clnt_newtls_stat(ANVIL_CLNT *, const char *, const char *, int *); +extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *, int *); extern int anvil_clnt_disconnect(ANVIL_CLNT *, const char *, const char *); extern void anvil_clnt_free(ANVIL_CLNT *); diff --git a/postfix/src/global/deliver_request.c b/postfix/src/global/deliver_request.c index 26b4cb013..14ddda513 100644 --- a/postfix/src/global/deliver_request.c +++ b/postfix/src/global/deliver_request.c @@ -355,6 +355,7 @@ static DELIVER_REQUEST *deliver_request_alloc(void) request->sasl_username = 0; request->sasl_sender = 0; request->rewrite_context = 0; + request->dsn_envid = 0; return (request); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 8a22a1c6d..0ca0f9a1b 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1728,6 +1728,10 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`\ abcdefghijklmnopqrstuvwxyz{|}~" extern char *var_smtpd_exp_filter; +#define VAR_SMTPD_PEERNAME_LOOKUP "smtpd_peername_lookup" +#define DEF_SMTPD_PEERNAME_LOOKUP 1 +extern bool var_smtpd_peername_lookup; + /* * Heuristic to reject unknown local recipients at the SMTP port. */ @@ -2301,6 +2305,10 @@ extern int var_smtpd_cmail_limit; #define DEF_SMTPD_CRCPT_LIMIT 0 extern int var_smtpd_crcpt_limit; +#define VAR_SMTPD_CNTLS_LIMIT "smtpd_client_new_tls_session_rate_limit" +#define DEF_SMTPD_CNTLS_LIMIT 0 +extern int var_smtpd_cntls_limit; + #define VAR_SMTPD_HOGGERS "smtpd_client_event_limit_exceptions" #define DEF_SMTPD_HOGGERS "${smtpd_client_connection_limit_exceptions:$" VAR_MYNETWORKS "}" extern char *var_smtpd_hoggers; @@ -2380,10 +2388,6 @@ extern char *var_msg_strip_chars; #define DEF_FROZEN_DELIVERED 1 extern bool var_frozen_delivered; -#define VAR_STICKY_OWNER_ALIAS "sticky_owner_alias" -#define DEF_STICKY_OWNER_ALIAS 1 -extern bool var_sticky_owner_alias; - /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 9e1b0d5af..ac5579892 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20050923" +#define MAIL_RELEASE_DATE "20051010" #define MAIL_VERSION_NUMBER "2.3" #ifdef SNAPSHOT diff --git a/postfix/src/global/smtp_stream.c b/postfix/src/global/smtp_stream.c index d836e6736..fc83695f5 100644 --- a/postfix/src/global/smtp_stream.c +++ b/postfix/src/global/smtp_stream.c @@ -95,6 +95,9 @@ /* An I/O error happened, or the peer has disconnected unexpectedly. /* .IP SMTP_ERR_TIME /* The time limit specified to smtp_timeout_setup() was exceeded. +/* .IP SMTP_ERR_PROTO +/* This error is never generated by the smtp_stream(3) module, but +/* is defined for application-specific use. /* BUGS /* The timeout deadline affects all I/O on the named stream, not /* just the I/O done on behalf of this module. diff --git a/postfix/src/global/smtp_stream.h b/postfix/src/global/smtp_stream.h index 2fc2f5f66..3ca7a0452 100644 --- a/postfix/src/global/smtp_stream.h +++ b/postfix/src/global/smtp_stream.h @@ -28,6 +28,7 @@ */ #define SMTP_ERR_EOF 1 /* unexpected client disconnect */ #define SMTP_ERR_TIME 2 /* time out */ +#define SMTP_ERR_PROTO 3 /* protocol (application) */ extern void smtp_timeout_setup(VSTREAM *, int); extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...); diff --git a/postfix/src/lmtp/lmtp_chat.c b/postfix/src/lmtp/lmtp_chat.c index 608b8a8f3..621d17d89 100644 --- a/postfix/src/lmtp/lmtp_chat.c +++ b/postfix/src/lmtp/lmtp_chat.c @@ -231,7 +231,21 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state) if (*cp == ' ' || *cp == 0) break; } + + /* + * XXX Do not ignore garbage when ESMTP command pipelining is turned + * on. After sending ".QUIT", Postfix might recognize + * the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after + * garbage, causing mail to be lost. Instead, make a long jump so + * that all recipients of multi-recipient mail get consistent + * treatment. + */ state->error_mask |= MAIL_ERROR_PROTOCOL; + if (state->features & LMTP_FEATURE_PIPELINING) { + msg_warn("non-SMTP response from %s: %s", + session->namaddr, STR(state->buffer)); + vstream_longjmp(session->stream, SMTP_ERR_PROTO); + } } /* diff --git a/postfix/src/lmtp/lmtp_trouble.c b/postfix/src/lmtp/lmtp_trouble.c index 15d5fc948..9aafe927f 100644 --- a/postfix/src/lmtp/lmtp_trouble.c +++ b/postfix/src/lmtp/lmtp_trouble.c @@ -378,6 +378,12 @@ int lmtp_stream_except(LMTP_STATE *state, int code, const char *description) "conversation with %s timed out while %s", session->namaddr, description); break; + case SMTP_ERR_PROTO: + lmtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA, + "4.5.0", "403 remote protocol error", + "protocol error in reply from %s while %s", + session->namaddr, description); + break; } return (lmtp_bulk_fail(state, &dsn, LMTP_THROTTLE)); } diff --git a/postfix/src/local/alias.c b/postfix/src/local/alias.c index 5149f3ef6..5ff6f4e47 100644 --- a/postfix/src/local/alias.c +++ b/postfix/src/local/alias.c @@ -263,8 +263,7 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level); } else { canon_owner = 0; - if (var_sticky_owner_alias == 0) - RESET_OWNER_ATTR(state.msg_attr, state.level); + RESET_OWNER_ATTR(state.msg_attr, state.level); } /* diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c index f849c1da5..d01207be7 100644 --- a/postfix/src/local/local.c +++ b/postfix/src/local/local.c @@ -367,11 +367,6 @@ /* address (see prepend_delivered_header) only once, at the start of /* a delivery attempt; do not update the Delivered-To: address while /* expanding aliases or .forward files. -/* .IP "\fBsticky_owner_alias (yes)\fR" -/* When expanding a \fBlocal\fR(8) alias that has an owner alias (see -/* owner-\fIname\fR discussion in \fBaliases\fR(5)), use the owner information -/* even when the expansion invokes a subordinate alias that has no -/* owner alias. /* DELIVERY METHOD CONTROLS /* .ad /* .fi @@ -620,7 +615,6 @@ int var_mailtool_compat; char *var_mailbox_lock; int var_mailbox_limit; bool var_frozen_delivered; -bool var_sticky_owner_alias; int local_cmd_deliver_mask; int local_file_deliver_mask; @@ -861,7 +855,6 @@ int main(int argc, char **argv) VAR_STAT_HOME_DIR, DEF_STAT_HOME_DIR, &var_stat_home_dir, VAR_MAILTOOL_COMPAT, DEF_MAILTOOL_COMPAT, &var_mailtool_compat, VAR_FROZEN_DELIVERED, DEF_FROZEN_DELIVERED, &var_frozen_delivered, - VAR_STICKY_OWNER_ALIAS, DEF_STICKY_OWNER_ALIAS, &var_sticky_owner_alias, 0, }; diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c index 7f27fc20f..00f4ccbb5 100644 --- a/postfix/src/smtp/smtp_chat.c +++ b/postfix/src/smtp/smtp_chat.c @@ -251,7 +251,21 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session) if (*cp == ' ' || *cp == 0) break; } + + /* + * XXX Do not ignore garbage when ESMTP command pipelining is turned + * on. After sending ".QUIT", Postfix might recognize + * the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after + * garbage, causing mail to be lost. Instead, make a long jump so + * that all recipients of multi-recipient mail get consistent + * treatment. + */ session->error_mask |= MAIL_ERROR_PROTOCOL; + if (session->features & SMTP_FEATURE_PIPELINING) { + msg_warn("non-SMTP response from %s: %s", + session->namaddr, STR(session->buffer)); + vstream_longjmp(session->stream, SMTP_ERR_PROTO); + } } /* diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index e3a40e6e7..2896f96ea 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -432,6 +432,12 @@ int smtp_stream_except(SMTP_STATE *state, int code, const char *description) "conversation with %s timed out while %s", session->namaddr, description); break; + case SMTP_ERR_PROTO: + smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA, + "4.5.0", "403 remote protocol error", + "protocol error in reply from %s while %s", + session->namaddr, description); + break; } return (smtp_bulk_fail(state, &dsn, SMTP_THROTTLE)); } diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index faf956ee0..5ce6bf606 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -275,6 +275,7 @@ smtpd_peer.o: ../../include/argv.h smtpd_peer.o: ../../include/attr.h smtpd_peer.o: ../../include/inet_proto.h smtpd_peer.o: ../../include/iostuff.h +smtpd_peer.o: ../../include/mail_params.h smtpd_peer.o: ../../include/mail_proto.h smtpd_peer.o: ../../include/mail_stream.h smtpd_peer.o: ../../include/msg.h diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index b9a4d1aab..107e80343 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -415,6 +415,11 @@ /* The maximal number of lines in the Postfix SMTP server command history /* before it is flushed upon receipt of EHLO, RSET, or end of DATA. /* .PP +/* Available in Postfix version 2.3 and later: +/* .IP "\fBsmtpd_peername_lookup (yes)\fR" +/* Attempt to look up the SMTP client hostname, and verify that +/* the name matches the client IP address. +/* .PP /* The per SMTP client connection count and request rate limits are /* implemented in co-operation with the \fBanvil\fR(8) service, and /* are available in Postfix version 2.2 and later. @@ -435,6 +440,11 @@ /* .IP "\fBsmtpd_client_event_limit_exceptions ($mynetworks)\fR" /* Clients that are excluded from connection count, connection rate, /* or SMTP request rate restrictions. +/* .PP +/* Available in Postfix version 2.3 and later: +/* .IP "\fBsmtpd_client_new_tls_session_rate_limit (0)\fR" +/* The maximal number of new (i.e., uncached) TLS sessions that any +/* client is allowed to negotiate with this service per time unit. /* TARPIT CONTROLS /* .ad /* .fi @@ -912,6 +922,7 @@ int var_smtpd_crate_limit; int var_smtpd_cconn_limit; int var_smtpd_cmail_limit; int var_smtpd_crcpt_limit; +int var_smtpd_cntls_limit; char *var_smtpd_hoggers; char *var_local_rwr_clients; char *var_smtpd_ehlo_dis_words; @@ -933,6 +944,8 @@ char *var_smtpd_sasl_tls_opts; #endif +bool var_smtpd_peername_lookup; + /* * Silly little macros. */ @@ -982,6 +995,7 @@ int smtpd_input_transp_mask; static void helo_reset(SMTPD_STATE *); static void mail_reset(SMTPD_STATE *); static void rcpt_reset(SMTPD_STATE *); +static void tls_reset(SMTPD_STATE *); static void chat_reset(SMTPD_STATE *, int); /* @@ -3025,6 +3039,25 @@ static void chat_reset(SMTPD_STATE *state, int threshold) static void smtpd_start_tls(SMTPD_STATE *state) { + int rate; + + /* + * XXX The client event count/rate control must be consistent in its use + * of client address information in connect and disconnect events. For + * now we exclude xclient authorized hosts from event count/rate control. + */ + if (SMTPD_STAND_ALONE(state) == 0 + && !xclient_allowed + && anvil_clnt + && var_smtpd_cntls_limit > 0 + && !namadr_list_match(hogger_list, state->name, state->addr) + && anvil_clnt_newtls_stat(anvil_clnt, state->service, state->addr, + &rate) == ANVIL_STAT_OK + && rate > var_smtpd_cntls_limit) { + msg_warn("Refusing STARTTLS request from %s for service %s", + state->namaddr, state->service); + vstream_longjmp(state->client, SMTP_ERR_EOF); + } /* * Wrapper mode uses a dedicated port and always requires TLS. @@ -3037,9 +3070,30 @@ static void smtpd_start_tls(SMTPD_STATE *state) * verification unless TLS is required. */ state->tls_context = - tls_server_start(smtpd_tls_ctx, state->client, - var_smtpd_starttls_tmout, state->name, state->addr, - (var_smtpd_tls_req_ccert && state->tls_enforce_tls)); + tls_server_start(smtpd_tls_ctx, state->client, + var_smtpd_starttls_tmout, state->name, state->addr, + (var_smtpd_tls_req_ccert && state->tls_enforce_tls)); + + /* + * XXX The client event count/rate control must be consistent in its use + * of client address information in connect and disconnect events. For + * now we exclude xclient authorized hosts from event count/rate control. + */ + if (state->tls_context + && state->tls_context->session_reused == 0 + && SMTPD_STAND_ALONE(state) == 0 + && !xclient_allowed + && anvil_clnt + && var_smtpd_cntls_limit > 0 + && !namadr_list_match(hogger_list, state->name, state->addr) + && anvil_clnt_newtls(anvil_clnt, state->service, state->addr, + &rate) == ANVIL_STAT_OK + && rate > var_smtpd_cntls_limit) { + msg_warn("Too many uncached TLS sessions: " + "%d from %s for service %s", + rate, state->namaddr, state->service); + tls_reset(state); + } /* * When the TLS handshake fails, the conversation is in an unknown state. @@ -3587,7 +3641,8 @@ static void post_jail_init(char *unused_name, char **unused_argv) * Connection rate management. */ if (var_smtpd_crate_limit || var_smtpd_cconn_limit - || var_smtpd_cmail_limit || var_smtpd_crcpt_limit) + || var_smtpd_cmail_limit || var_smtpd_crcpt_limit + || var_smtpd_cntls_limit) anvil_clnt = anvil_clnt_create(); } @@ -3625,6 +3680,7 @@ int main(int argc, char **argv) VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0, VAR_SMTPD_CMAIL_LIMIT, DEF_SMTPD_CMAIL_LIMIT, &var_smtpd_cmail_limit, 0, 0, VAR_SMTPD_CRCPT_LIMIT, DEF_SMTPD_CRCPT_LIMIT, &var_smtpd_crcpt_limit, 0, 0, + VAR_SMTPD_CNTLS_LIMIT, DEF_SMTPD_CNTLS_LIMIT, &var_smtpd_cntls_limit, 0, 0, #ifdef USE_TLS VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0, #endif @@ -3664,6 +3720,7 @@ int main(int argc, char **argv) VAR_SMTPD_TLS_RCERT, DEF_SMTPD_TLS_RCERT, &var_smtpd_tls_req_ccert, VAR_SMTPD_TLS_RECHEAD, DEF_SMTPD_TLS_RECHEAD, &var_smtpd_tls_received_header, #endif + VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup, 0, }; static CONFIG_STR_TABLE str_table[] = { diff --git a/postfix/src/smtpd/smtpd_peer.c b/postfix/src/smtpd/smtpd_peer.c index 0eba3b07f..b49eff3ea 100644 --- a/postfix/src/smtpd/smtpd_peer.c +++ b/postfix/src/smtpd/smtpd_peer.c @@ -118,6 +118,7 @@ #include #include +#include /* Application-specific. */ @@ -251,8 +252,19 @@ void smtpd_peer_init(SMTPD_STATE *state) state->name_status = code; \ } - if ((aierr = sockaddr_to_hostname(sa, sa_len, &client_name, - (MAI_SERVNAME_STR *) 0, 0)) != 0) { + if (var_smtpd_peername_lookup == 0) { + state->name = mystrdup(CLIENT_NAME_UNKNOWN); + state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); +#ifdef FORWARD_CLIENT_NAME + state->forward_name = mystrdup(CLIENT_NAME_UNKNOWN); +#endif + state->name_status = SMTPD_PEER_CODE_PERM; + state->reverse_name_status = SMTPD_PEER_CODE_PERM; +#ifdef FORWARD_CLIENT_NAME + state->forward_name_status = SMTPD_PEER_CODE_PERM; +#endif + } else if ((aierr = sockaddr_to_hostname(sa, sa_len, &client_name, + (MAI_SERVNAME_STR *) 0, 0)) != 0) { state->name = mystrdup(CLIENT_NAME_UNKNOWN); state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); #ifdef FORWARD_CLIENT_NAME diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 2fed1254b..134037071 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -56,7 +56,8 @@ typedef struct { const char *cipher_name; int cipher_usebits; int cipher_algbits; - int log_level; + int log_level; /* TLS library logging level */ + int session_reused; /* this session was reused */ } TLScontext_t; #define TLS_BIO_BUFSIZE 8192 diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 979f4e8e2..3e4a0dc3e 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -693,6 +693,12 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream, if (var_smtp_tls_loglevel < 4) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); + /* + * The caller may want to know if this session was reused or if a new + * session was negotiated. + */ + TLScontext->session_reused = SSL_session_reused(TLScontext->con); + /* * Do peername verification if requested and extract useful information * from the certificate for later use. diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 6bfb6cd84..91122a0a3 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -564,6 +564,12 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream, if (var_smtpd_tls_loglevel < 4) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); + /* + * The caller may want to know if this session was reused or if a new + * session was negotiated. + */ + TLScontext->session_reused = SSL_session_reused(TLScontext->con); + /* * Let's see whether a peer certificate is available and what is the * actual information. We want to save it for later use. @@ -618,6 +624,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream, */ if (requirecert) { if (!TLScontext->peer_verified || !TLScontext->peer_CN) { + if (TLScontext->session_reused == 0) + msg_panic("tls_server_start: peer was not verified"); msg_info("Re-used session without peer certificate removed"); uncache_session(server_ctx, TLScontext); tls_free_context(TLScontext); diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c index e2c812e3f..3ac5be42f 100644 --- a/postfix/src/util/dict_alloc.c +++ b/postfix/src/util/dict_alloc.c @@ -15,7 +15,8 @@ /* DICT *ptr; /* DESCRIPTION /* dict_alloc() allocates memory for a dictionary structure of -/* \fIsize\fR bytes, initializes all properties to default settings, +/* \fIsize\fR bytes, initializes all generic dictionary +/* properties to default settings, /* and installs default methods that do not support any operation. /* The caller is supposed to override the default methods with /* ones that it supports.