From: Wietse Venema Date: Sat, 12 Jul 2003 05:00:00 +0000 (-0500) Subject: postfix-2.0.14-20030712 X-Git-Tag: v2.1-RC1-20040331~50 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=96464630a1a614a057dd269389c277791a99c65d;p=thirdparty%2Fpostfix.git postfix-2.0.14-20030712 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 2388a60ed..f146bceba 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -1,7 +1,9 @@ -TABOUNCE -TALIAS_TOKEN -TARGV +-TATTR_CLNT -TATTR_TABLE +-TAUTO_CLNT -TBH_TABLE -TBINATTR -TBINATTR_INFO diff --git a/postfix/HISTORY b/postfix/HISTORY index 9d3745c08..075de2f22 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -8339,6 +8339,55 @@ Apologies for any names omitted. and header/body checks. This eliminates the need to configure multiple cleanup services in the master.cf file. +20030707 + + Feature: context dependent SASL security options (i.e. + different options when TLS is enabled/disabled). Lutz + Jaenicke. Files: */*sasl_glue.[hc]. + +20030708 + + Hardened the attr_scan routines for exposure to an untrusted + environment, in preparation of the tuple0 and tuple64 + protocols that will be used for SMTP policy delegation. + + Feature: address filter for RBL lookups, for use with + multi-valued RBL services. File: smtpd/smtpd_check.c. + + Feature: accept_socket option to pass the spawn(8) listen + socket to a persistent non-Postfix process. File: spawn/spawn.c. + +20030709 + + Cleanup: use off_t instead of int for VSTREAM file offsets. + This was needed for mailboxes > 2GB on 32-bit systems. + Files: util/vstream.c, global/mail_copy.c. + +20030710 + + Support for multiple A and TXT results in RBL lookups. + Victor Duchovni, Morgan Stanley. File: smtpd/smtpd_check.c. + + Support for attribute-based query-reply protocols. + Files: util/attr_clnt.[hc], util/auto_clnt.[hc]. + +20030711 + + Support for plain "name=value\n" attribute protocol. + Files: util/attr_{scan,print}_plain.c. + + Bugfix: the LMTP session caching code did not reset the + EHLO server feature list when it needed to reconnect. + Problem found by Tobias Erbsland. + +20030712 + + Feature: delegated SMTP policy server. As an example, see + the greylisting server in examples/smtpd-policy. Specify + "check_smtpd_policy_service" in smtpd_mumble_restrictions. + See SMTPD_POLICY_SERVICE_README for details. + + Open problems: Low: smtp-source may block when sending large test messages. diff --git a/postfix/README_FILES/ADDRESS_VERIFICATION_README b/postfix/README_FILES/ADDRESS_VERIFICATION_README index 01262944f..d2cc1129c 100644 --- a/postfix/README_FILES/ADDRESS_VERIFICATION_README +++ b/postfix/README_FILES/ADDRESS_VERIFICATION_README @@ -61,7 +61,7 @@ Turning on recipient address verification Recipient address verification may be useful to block mail for undeliverable recipients on mail relay hosts that do not have a copy of all the relayed recipient addresses. This prevents the mail -queue from filling up with undeliverable and bounced SPAM. +queue from filling up with undeliverable and bounced mail. Recipient address verification is relatively straightforward and there are no surprises. If a recipient probe fails, then Postfix diff --git a/postfix/README_FILES/FILTER_README b/postfix/README_FILES/FILTER_README index fb7469ff2..9c8e592d8 100644 --- a/postfix/README_FILES/FILTER_README +++ b/postfix/README_FILES/FILTER_README @@ -17,7 +17,7 @@ and does one of the following: content. 2 - Reject the mail (by sending a suitable status code back to - Postfix) so that it is returned to sender. + Postfix). Postfix will return the mail to the sender. 3 - Send the mail somewhere else. diff --git a/postfix/README_FILES/SMTPD_PROXY_README b/postfix/README_FILES/SMTPD_PROXY_README index 0b3a24455..613ad27e9 100644 --- a/postfix/README_FILES/SMTPD_PROXY_README +++ b/postfix/README_FILES/SMTPD_PROXY_README @@ -111,6 +111,11 @@ process. smtp inet n - n - - smtpd -o smtpd_proxy_filter=26 :26 inet n - n - - smtpd + -o smtpd_client_restrictions= + -o smtpd_helo_restrictions= + -o smtpd_sender_restrictions= + -o smtpd_recipient_restrictions=permit_mynetworks,reject + -o mynetworks=127.0.0.0/8 -o receive_override_options=no_unknown_recipient_checks Note: do not specify spaces around the "=" or "," characters. diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 3d3bc6706..c139cee41 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -559,7 +559,7 @@ unknown_local_recipient_reject_code = 450 # most delivery transports. For the local delivery agent the default is 2. #local_destination_concurrency_limit = 2 -#default_destination_concurrency_limit = 10 +#default_destination_concurrency_limit = 20 # DEBUGGING CONTROL # diff --git a/postfix/conf/sample-smtp.cf b/postfix/conf/sample-smtp.cf index 19b76893b..b9c521b2f 100644 --- a/postfix/conf/sample-smtp.cf +++ b/postfix/conf/sample-smtp.cf @@ -134,6 +134,18 @@ smtp_pix_workaround_delay_time = 10s # smtp_pix_workaround_threshold_time = 500s +# The smtp_quote_rfc821_envelope parameter controls whether the +# Postfix SMTP client quotes addresses in MAIL FROM and RCPT TO +# commands as required by RFC 821. This includes putting quotes around +# an address localpart that ends in ".". +# +# The default is to comply with RFC 821. If you have to send mail to +# a broken SMTP server, configure a special SMTP client in master.cf +# with "-o smtp_quote_rfc821_envelope=no" on the SMTP client command +# line. +# +smtp_quote_rfc821_envelope = yes + # # RATE CONTROLS # diff --git a/postfix/conf/tcp_table b/postfix/conf/tcp_table index a9d69a86c..bb58ce363 100644 --- a/postfix/conf/tcp_table +++ b/postfix/conf/tcp_table @@ -27,6 +27,8 @@ # terminated by the ASCII newline character. Request and # reply parameters (see below) are separated by whitespace. # +# Send and receive operations must complete in 100 seconds. +# # REQUEST FORMAT # Each request specifies a command, a lookup key, and possi- # bly a lookup result. @@ -38,40 +40,40 @@ # This request is currently not implemented. # # REPLY FORMAT -# Each reply specifies a status code and text. Replies must -# be no longer than 4096 characters including the newline +# Each reply specifies a status code and text. Replies must +# be no longer than 4096 characters including the newline # terminator. # # 500 SPACE text NEWLINE -# In case of a lookup request, the requested data -# does not exist. In case of an update request, the +# In case of a lookup request, the requested data +# does not exist. In case of an update request, the # request was rejected. The text describes the # nature of the problem. # # 400 SPACE text NEWLINE -# This indicates an error condition. The text -# describes the nature of the problem. The client +# This indicates an error condition. The text +# describes the nature of the problem. The client # should retry the request later. # # 200 SPACE text NEWLINE # The request was successful. In the case of a lookup -# request, the text contains an encoded version of +# request, the text contains an encoded version of # the requested data. # # ENCODING -# In request and reply parameters, the character %, each +# In request and reply parameters, the character %, each # non-printing character, and each whitespace character must -# be replaced by %XX, where XX is the corresponding ASCII -# hexadecimal character value. The hexadecimal codes can be +# be replaced by %XX, where XX is the corresponding ASCII +# hexadecimal character value. The hexadecimal codes can be # specified in any case (upper, lower, mixed). # -# The Postfix client always encodes a request. The server -# may omit the encoding as long as the reply is guaranteed +# The Postfix client always encodes a request. The server +# may omit the encoding as long as the reply is guaranteed # to not contain the % or NEWLINE character. # # SECURITY -# Do not use TCP lookup tables for security critical pur- -# poses. The client-server connection is not protected and +# Do not use TCP lookup tables for security critical pur- +# poses. The client-server connection is not protected and # the server is not authenticated. # # SEE ALSO @@ -82,11 +84,11 @@ # BUGS # Only the lookup method is currently implemented. # -# The client does not hang up when the connection is idle +# The client does not hang up when the connection is idle # for a long time. # # LICENSE -# The Secure Mailer license must be distributed with this +# The Secure Mailer license must be distributed with this # software. # # AUTHOR(S) diff --git a/postfix/examples/smtpd-policy/smtpd-policy.pl b/postfix/examples/smtpd-policy/smtpd-policy.pl new file mode 100755 index 000000000..aed1e72e4 --- /dev/null +++ b/postfix/examples/smtpd-policy/smtpd-policy.pl @@ -0,0 +1,183 @@ +#!/usr/bin/perl + +use DB_File; +use Fcntl; +use Sys::Syslog qw(:DEFAULT setlogsock); + +# +# Usage: smtpd-policy.pl [-v] +# +# Demo delegated Postfix SMTPD policy server. This server implements +# greylisting. State is kept in a Berkeley DB database. Logging is +# sent to syslogd. +# +# To run this from /etc/postfix/master.cf: +# +# policy unix - n n - - spawn +# user=nobody argv=/usr/bin/perl /usr/libexec/postfix/smtpd-policy.pl +# +# To use this from Postfix SMTPD, use in /etc/postfix/main.cf: +# +# smtpd_policy_service_endpoint = plain:unix:private/policy +# smtpd_recipient_restrictions = ... check_policy_service ... +# +# This script runs as one PERL process per SMTP server process. +# By default, a Postfix SMTP server process terminates after 100 +# seconds of idle time, or after serving 100 clients. +# + +# +# greylist status database and greylist time interval. DO NOT create the +# greylist status database in a world-writable directory such as /tmp +# or /var/tmp. DO NOT create the greylist database in a file system +# that can run out of space. +# +$database_name="/var/mta/smtpd-policy.db"; +$greylist_delay=3600; + +# +# Syslogging options for verbose mode and for fatal errors. Comment +# out the $syslog_socktype line if syslogging does not work. +# +$syslog_socktype = 'unix'; # inet, unix, stream, console +$syslog_facility="mail"; +$syslog_options="pid"; +$syslog_priority="info"; + +# +# Demo policy routine. The result is an action just like it would +# be specified on the right-hand side of a Postfix access table. +# Request attributes are passed in via the %attr hash. +# +sub policy { + local(*attr) = @_; + my($key, $time_stamp, $now); + + # Open the database on the fly. + open_database() unless $database_obj; + + # Lookup the time stamp for this client/sender/recipient. + $key = $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"}; + $time_stamp = read_database($key); + $now = time(); + + # If new request, add this client/sender/recipient to the database. + if ($time_stamp == 0) { + $time_stamp = $now; + update_database($key, $time_stamp); + } + + syslog $syslog_priority, "request age %d", $now - $time_stamp if $verbose; + if ($time_stamp + $greylist_delay < $now) { + return "ok"; + } else { + return "450 request is greylisted"; + } +} + +# +# You should not have to make changes below this point. +# +sub LOCK_SH { 1 }; # Shared lock (used for reading). +sub LOCK_EX { 2 }; # Exclusive lock (used for writing). +sub LOCK_NB { 4 }; # Don't block (for testing). +sub LOCK_UN { 8 }; # Release lock. + +# +# Log an error and abort. +# +sub fatal_exit { + syslog "err", @_; + exit 1; +} + +# +# Open hash database. +# +sub open_database { + my($database_fd); + + $database_obj = tie(%db_hash, 'DB_File', $database_name, + O_CREAT|O_RDWR, 0644) || + fatal_exit "Cannot open database %s: %m", $database_name; + $database_fd = $database_obj->fd; + open DATABASE_HANDLE, "+<&=$database_fd" || + fatal_exit "Cannot fdopen database %s: %m", $database_name; + syslog $syslog_priority, "open %s", $database_name if $verbose; +} + +# +# Read database. +# +sub read_database { + my($key) = @_; + my($value); + + flock DATABASE_HANDLE, LOCK_SH || + fatal_exit "Can't get shared lock on %s: %m", $database_name; + $value = $db_hash{$key}; + syslog $syslog_priority, "lookup %s: %s", $key, $value if $verbose; + flock DATABASE_HANDLE, LOCK_UN || + fatal_exit "Can't unlock %s: %m", $database_name; + return $value; +} + +# +# Update database. +# +sub update_database { + my($key, $value) = @_; + + syslog $syslog_priority, "store %s: %s", $key, $value if $verbose; + flock DATABASE_HANDLE, LOCK_EX || + fatal_exit "Can't exclusively lock %s: %m", $database_name; + $db_hash{$key} = $value; + $database_obj->sync() && + fatal_exit "Can't update %s: %m", $database_name; + flock DATABASE_HANDLE, LOCK_UN || + fatal_exit "Can't unlock %s: %m", $database_name; +} + +# +# This process runs as a daemon, so it can't log to a terminal. Use +# syslog so that people can actually see our messages. +# +setlogsock $syslog_socktype; +openlog $0, $syslog_options, $syslog_facility; + +# +# We don't need getopt() for now. +# +while ($option = shift(@ARGV)) { + if ($option eq "-v") { + $verbose = 1; + } else { + syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]", + $option, $0; + exit 1; + } +} + +# +# Unbuffer standard output. +# +select((select(STDOUT), $| = 1)[0]); + +# +# Receive a bunch of attributes, evaluate the policy, send the result. +# +while () { + if (/([^=]+)=(.*)\n/) { + $attr{$1} = $2; + } else { + if ($verbose) { + for (keys %attr) { + syslog $syslog_priority, "Attribute: %s=%s", $_, $attr{$_}; + } + } + $action = &policy(*attr); + syslog $syslog_priority, "Action: %s", $action if $verbose; + print STDOUT "action=$action\n\n"; + %attr = (); + } +} diff --git a/postfix/html/nqmgr.8.html b/postfix/html/nqmgr.8.html index aef34e563..90cf9d859 100644 --- a/postfix/html/nqmgr.8.html +++ b/postfix/html/nqmgr.8.html @@ -15,10 +15,10 @@ NQMGR(8) NQMGR(8) from the master(8) process manager. Mail addressed to the local double-bounce address is - silently discarded. This stops potential loops caused by - undeliverable bounce notifications. + logged and discarded. This stops potential loops caused + by undeliverable bounce notifications. -MAIL QUEUES +MAIL QUEUES The nqmgr daemon maintains the following queues: incoming @@ -43,7 +43,7 @@ NQMGR(8) NQMGR(8) hold Messages that are kept "on hold" are kept here until someone sets them free. -DELIVERY STATUS REPORTS +DELIVERY STATUS REPORTS The nqmgr daemon keeps an eye on per-message delivery sta- tus reports in the following directories. Each status report file has the same name as the corresponding message @@ -65,7 +65,7 @@ NQMGR(8) NQMGR(8) either opening queue files (input) or for message delivery (output). - leaky bucket + leaky bucket This strategy limits the number of messages in the active queue and prevents the queue manager from running out of memory under heavy load. @@ -76,28 +76,28 @@ NQMGR(8) NQMGR(8) from the deferred queue. This prevents a large mail backlog from blocking the delivery of new mail. - slow start + slow start This strategy eliminates "thundering herd" problems by slowly adjusting the number of parallel deliver- ies to the same destination. - round robin + round robin The queue manager sorts delivery requests by desti- nation. Round-robin selection prevents one desti- nation from dominating deliveries to other destina- tions. - exponential backoff + exponential backoff Mail that cannot be delivered upon the first attempt is deferred. The time interval between delivery attempts is doubled after each attempt. - destination status cache + destination status cache The queue manager avoids unnecessary delivery attempts by maintaining a short-term, in-memory list of unreachable destinations. - preemptive message scheduling + preemptive message scheduling The queue manager attempts to minimize the average per-recipient delay while still preserving the cor- rect per-message delays, using a sophisticated pre- @@ -111,25 +111,25 @@ NQMGR(8) NQMGR(8) actions (the message is followed by the symbolic constant used internally by the software): - D (QMGR_REQ_SCAN_DEFERRED) + D (QMGR_REQ_SCAN_DEFERRED) Start a deferred queue scan. If a deferred queue scan is already in progress, that scan will be restarted as soon as it finishes. - I (QMGR_REQ_SCAN_INCOMING) + I (QMGR_REQ_SCAN_INCOMING) Start an incoming queue scan. If an incoming queue scan is already in progress, that scan will be restarted as soon as it finishes. - A (QMGR_REQ_SCAN_ALL) + A (QMGR_REQ_SCAN_ALL) Ignore deferred queue file time stamps. The request affects the next deferred queue scan. - F (QMGR_REQ_FLUSH_DEAD) + F (QMGR_REQ_FLUSH_DEAD) Purge all information about dead transports and destinations. - W (TRIGGER_REQ_WAKEUP) + W (TRIGGER_REQ_WAKEUP) Wakeup call, This is used by the master server to instantiate servers that should not go away for- ever. The action is to start an incoming queue @@ -139,7 +139,7 @@ NQMGR(8) NQMGR(8) Multiple identical trigger requests are collapsed into one, and trigger requests are sorted so that A and F pre- cede D and I. Thus, in order to force a deferred queue - run, one would request A F D; in order to notify the queue + run, one would request A F D; in order to notify the queue manager of the arrival of new mail one would request I. STANDARDS @@ -169,10 +169,10 @@ NQMGR(8) NQMGR(8) sudden burst of inbound mail can negatively impact out- bound delivery rates. -CONFIGURATION PARAMETERS +CONFIGURATION PARAMETERS The following main.cf parameters are especially relevant to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + details and for default values. Use the postfix reload command after a configuration change. Miscellaneous @@ -183,7 +183,7 @@ NQMGR(8) NQMGR(8) queue_directory Top-level directory of the Postfix queue. -Active queue controls +Active queue controls In the text below, transport is the first field in a mas- ter.cf entry. @@ -222,7 +222,7 @@ NQMGR(8) NQMGR(8) all preempting messages delivered by the transport transport can have. -Timing controls +Timing controls minimal_backoff_time Minimal time in seconds between delivery attempts of a deferred message. @@ -247,7 +247,7 @@ NQMGR(8) NQMGR(8) Time in seconds between attempts to contact a bro- ken delivery transport. -Concurrency controls +Concurrency controls initial_destination_concurrency Initial per-destination concurrency level for par- allel delivery to the same destination. @@ -261,7 +261,7 @@ NQMGR(8) NQMGR(8) same destination, for delivery via the named mes- sage transport. -Recipient controls +Recipient controls default_destination_recipient_limit Default limit on the number of recipients per mes- sage transfer. @@ -270,7 +270,7 @@ NQMGR(8) NQMGR(8) Limit on the number of recipients per message transfer, for the named message transport. -Message scheduling +Message scheduling transport_delivery_slot_cost (valid range: 0,2,3...) This parameter basically controls how often a mes- sage delivered by transport can be preempted by @@ -316,7 +316,7 @@ NQMGR(8) NQMGR(8) Default values for the transport specific parame- ters described above. -SEE ALSO +SEE ALSO master(8), process manager syslogd(8) system logging trivial-rewrite(8), address routing diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html index 48c8b188f..8e741d266 100644 --- a/postfix/html/qmgr.8.html +++ b/postfix/html/qmgr.8.html @@ -15,10 +15,10 @@ QMGR(8) QMGR(8) from the master(8) process manager. Mail addressed to the local double-bounce address is - silently discarded. This stops potential loops caused by - undeliverable bounce notifications. + logged and discarded. This stops potential loops caused + by undeliverable bounce notifications. -MAIL QUEUES +MAIL QUEUES The qmgr daemon maintains the following queues: incoming @@ -43,7 +43,7 @@ QMGR(8) QMGR(8) hold Messages that are kept "on hold" are kept here until someone sets them free. -DELIVERY STATUS REPORTS +DELIVERY STATUS REPORTS The qmgr daemon keeps an eye on per-message delivery sta- tus reports in the following directories. Each status report file has the same name as the corresponding message @@ -65,7 +65,7 @@ QMGR(8) QMGR(8) either opening queue files (input) or for message delivery (output). - leaky bucket + leaky bucket This strategy limits the number of messages in the active queue and prevents the queue manager from running out of memory under heavy load. @@ -76,23 +76,23 @@ QMGR(8) QMGR(8) from the deferred queue. This prevents a large mail backlog from blocking the delivery of new mail. - slow start + slow start This strategy eliminates "thundering herd" problems by slowly adjusting the number of parallel deliver- ies to the same destination. - round robin + round robin The queue manager sorts delivery requests by desti- nation. Round-robin selection prevents one desti- nation from dominating deliveries to other destina- tions. - exponential backoff + exponential backoff Mail that cannot be delivered upon the first attempt is deferred. The time interval between delivery attempts is doubled after each attempt. - destination status cache + destination status cache The queue manager avoids unnecessary delivery attempts by maintaining a short-term, in-memory list of unreachable destinations. @@ -105,25 +105,25 @@ QMGR(8) QMGR(8) actions (the message is followed by the symbolic constant used internally by the software): - D (QMGR_REQ_SCAN_DEFERRED) + D (QMGR_REQ_SCAN_DEFERRED) Start a deferred queue scan. If a deferred queue scan is already in progress, that scan will be restarted as soon as it finishes. - I (QMGR_REQ_SCAN_INCOMING) + I (QMGR_REQ_SCAN_INCOMING) Start an incoming queue scan. If an incoming queue scan is already in progress, that scan will be restarted as soon as it finishes. - A (QMGR_REQ_SCAN_ALL) + A (QMGR_REQ_SCAN_ALL) Ignore deferred queue file time stamps. The request affects the next deferred queue scan. - F (QMGR_REQ_FLUSH_DEAD) + F (QMGR_REQ_FLUSH_DEAD) Purge all information about dead transports and destinations. - W (TRIGGER_REQ_WAKEUP) + W (TRIGGER_REQ_WAKEUP) Wakeup call, This is used by the master server to instantiate servers that should not go away for- ever. The action is to start an incoming queue @@ -133,7 +133,7 @@ QMGR(8) QMGR(8) Multiple identical trigger requests are collapsed into one, and trigger requests are sorted so that A and F pre- cede D and I. Thus, in order to force a deferred queue - run, one would request A F D; in order to notify the queue + run, one would request A F D; in order to notify the queue manager of the arrival of new mail one would request I. STANDARDS @@ -162,10 +162,10 @@ QMGR(8) QMGR(8) sudden burst of inbound mail can negatively impact out- bound delivery rates. -CONFIGURATION PARAMETERS +CONFIGURATION PARAMETERS The following main.cf parameters are especially relevant to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + details and for default values. Use the postfix reload command after a configuration change. Miscellaneous @@ -176,7 +176,7 @@ QMGR(8) QMGR(8) queue_directory Top-level directory of the Postfix queue. -Active queue controls +Active queue controls qmgr_clog_warn_time Minimal delay between warnings that a specific des- tination is clogging up the active queue. Specify 0 @@ -191,7 +191,7 @@ QMGR(8) QMGR(8) This parameter also limits the size of the short- term, in-memory destination cache. -Timing controls +Timing controls minimal_backoff_time Minimal time in seconds between delivery attempts of a deferred message. @@ -216,7 +216,7 @@ QMGR(8) QMGR(8) Time in seconds between attempts to contact a bro- ken delivery transport. -Concurrency controls +Concurrency controls In the text below, transport is the first field in a mas- ter.cf entry. @@ -249,7 +249,7 @@ QMGR(8) QMGR(8) same destination, for delivery via the named mes- sage transport. -Recipient controls +Recipient controls default_destination_recipient_limit Default limit on the number of recipients per mes- sage transfer. @@ -258,7 +258,7 @@ QMGR(8) QMGR(8) Limit on the number of recipients per message transfer, for the named message transport. -SEE ALSO +SEE ALSO master(8), process manager syslogd(8) system logging trivial-rewrite(8), address routing diff --git a/postfix/html/showq.8.html b/postfix/html/showq.8.html index 77b7c1528..d694e57ce 100644 --- a/postfix/html/showq.8.html +++ b/postfix/html/showq.8.html @@ -34,7 +34,7 @@ SHOWQ(8) SHOWQ(8) quently, it cannot extract information from queue files in the maildrop directory. -SEE ALSO +SEE ALSO cleanup(8) canonicalize and enqueue mail pickup(8) local mail pickup service qmgr(8) mail being delivered, delayed mail diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 167e4b343..a6ebcb31a 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -57,10 +57,10 @@ SMTP(8) SMTP(8) and of other trouble. BUGS -CONFIGURATION PARAMETERS +CONFIGURATION PARAMETERS The following main.cf parameters are especially relevant to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + details and for default values. Use the postfix reload command after a configuration change. Miscellaneous @@ -101,7 +101,7 @@ SMTP(8) SMTP(8) ignore_mx_lookup_error When a name server fails to respond to an MX query, - search for an A record instead deferring mail + search for an A record, instead of deferring mail delivery. inet_interfaces @@ -167,7 +167,7 @@ SMTP(8) SMTP(8) PIX firewall <CR><LF>.<CR><LF> bug workaround is turned on. -MIME Conversion +MIME Conversion disable_mime_output_conversion Disable the conversion of 8BITMIME format to 7BIT format when the remote system does not advertise @@ -186,7 +186,7 @@ SMTP(8) SMTP(8) nested deeper, when converting from 8BITMIME format to 7BIT format. -Authentication controls +Authentication controls smtp_sasl_auth_enable Enable per-session authentication as per RFC 2554 (SASL). By default, Postfix is built without SASL @@ -215,7 +215,7 @@ SMTP(8) SMTP(8) noanonymous Disallow anonymous logins. -Resource controls +Resource controls smtp_destination_concurrency_limit Limit the number of parallel deliveries to the same destination. The default limit is taken from the @@ -226,7 +226,7 @@ SMTP(8) SMTP(8) ery. The default limit is taken from the default_destination_recipient_limit parameter. -Timeout controls +Timeout controls The default time unit is seconds; an explicit time unit can be specified by appending a one-letter suffix to the value: s (seconds), m (minutes), h (hours), d (days) or w @@ -250,11 +250,11 @@ SMTP(8) SMTP(8) receiving the server response. smtp_mail_timeout - Timeout for sending the MAIL FROM command, and for + Timeout for sending the MAIL FROM command, and for receiving the server response. smtp_rcpt_timeout - Timeout for sending the RCPT TO command, and for + Timeout for sending the RCPT TO command, and for receiving the server response. smtp_data_init_timeout @@ -277,7 +277,7 @@ SMTP(8) SMTP(8) Timeout for sending the QUIT command, and for receiving the server response. -SEE ALSO +SEE ALSO bounce(8) non-delivery status reports master(8) process manager qmgr(8) queue manager diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 8eba72b2a..fd9e64219 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -73,6 +73,9 @@ SMTPD(8) SMTPD(8) overrides built-in command definitions. Content inspection controls + Optionally, Postfix can be configured to send new mail to + external content filter software AFTER the mail is queued. + content_filter The name of a mail delivery transport that filters mail and that either bounces mail or re-injects the @@ -103,10 +106,27 @@ SMTPD(8) SMTPD(8) cally specified with the SMTP server after an external content filter. +Pass-through proxy + Optionally, the Postfix SMTP server can be configured to + forward all mail to a proxy server, for example a real- + time content filter, BEFORE mail is queued. + + smtpd_proxy_filter + The host:port of the SMTP proxy server. The host or + host: portion is optional. + + smtpd_proxy_timeout + Timeout for connecting to, sending to and receiving + from the SMTP proxy server. + + smtpd_proxy_ehlo + The hostname to use when sending an EHLO command to + the SMTP proxy server. + Authentication controls enable_sasl_authentication - Enable per-session authentication as per RFC 2554 - (SASL). This functionality is available only when + Enable per-session authentication as per RFC 2554 + (SASL). This functionality is available only when explicitly selected at program build time and explicitly enabled at runtime. @@ -132,30 +152,11 @@ SMTPD(8) SMTPD(8) Disallow anonymous logins. smtpd_sender_login_maps - Maps that specify the SASL login name that owns a - MAIL FROM sender address. Used by the + Maps that specify the SASL login name that owns a + MAIL FROM sender address. Used by the reject_sender_login_mismatch sender anti-spoofing restriction. -Pass-through proxy - Optionally, the Postfix SMTP server can be configured to - forward all mail to a proxy server, for example a real- - time content filter. This proxy server should support the - same MAIL FROM and RCPT TO command syntax as Postfix, but - does not need to support ESMTP command pipelining. - - smtpd_proxy_filter - The host:port of the SMTP proxy server. The host or - host: portion is optional. - - smtpd_proxy_timeout - Timeout for connecting to, sending to and receiving - from the SMTP proxy server. - - smtpd_proxy_ehlo - The hostname to use when sending an EHLO command to - the SMTP proxy server. - Miscellaneous authorized_verp_clients Hostnames, domain names and/or addresses of clients @@ -293,6 +294,25 @@ SMTPD(8) SMTPD(8) SMTP session before it is penalized with tarpit delays. +Delegated policy + smtpd_policy_service_endpoint + The transport:endpoint of a server that speaks the + delegated SMTP policy protocol. transport is either + inet or unix. endpoint is either host:port, pri- + vate/servicename or public/servicename. + + smtpd_policy_service_timeout + Time limit for connecting to, writing to and + receiving from a delegated SMTP policy server. + + smtpd_policy_service_max_idle + Time after which an unused policy service connec- + tion is closed. + + smtpd_policy_service_timeout + Time after which an active policy service connec- + tion is closed. + UCE control restrictions parent_domain_matches_subdomains List of Postfix features that use domain.tld pat- diff --git a/postfix/html/spawn.8.html b/postfix/html/spawn.8.html index cf7d27797..498058fb1 100644 --- a/postfix/html/spawn.8.html +++ b/postfix/html/spawn.8.html @@ -20,7 +20,7 @@ SPAWN(8) SPAWN(8) This daemon expects to be run from the master(8) process manager. -COMMAND ATTRIBUTE SYNTAX +COMMAND ATTRIBUTE SYNTAX The external command attributes are given in the master.cf file at the end of a service definition. The syntax is as follows: @@ -60,10 +60,10 @@ SPAWN(8) SPAWN(8) talk to the external command and thus is not vulnerable to data-driven attacks. -CONFIGURATION PARAMETERS +CONFIGURATION PARAMETERS The following main.cf parameters are especially relevant to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + details and for default values. Use the postfix reload command after a configuration change. Miscellaneous @@ -75,7 +75,7 @@ SPAWN(8) SPAWN(8) The process privileges used while not running an external command. -Resource control +Resource control service_command_time_limit The amount of time the command is allowed to run before it is killed with force. The service name is @@ -83,7 +83,7 @@ SPAWN(8) SPAWN(8) default time limit is given by the global com- mand_time_limit configuration parameter. -SEE ALSO +SEE ALSO master(8) process manager syslogd(8) system logging diff --git a/postfix/html/tcp_table.5.html b/postfix/html/tcp_table.5.html index cd248906c..177f9ccf4 100644 --- a/postfix/html/tcp_table.5.html +++ b/postfix/html/tcp_table.5.html @@ -28,6 +28,8 @@ TCP_TABLE(5) TCP_TABLE(5) terminated by the ASCII newline character. Request and reply parameters (see below) are separated by whitespace. + Send and receive operations must complete in 100 seconds. + REQUEST FORMAT Each request specifies a command, a lookup key, and possi- bly a lookup result. @@ -39,40 +41,40 @@ TCP_TABLE(5) TCP_TABLE(5) This request is currently not implemented. REPLY FORMAT - Each reply specifies a status code and text. Replies must - be no longer than 4096 characters including the newline + Each reply specifies a status code and text. Replies must + be no longer than 4096 characters including the newline terminator. 500 SPACE text NEWLINE - In case of a lookup request, the requested data - does not exist. In case of an update request, the + In case of a lookup request, the requested data + does not exist. In case of an update request, the request was rejected. The text describes the nature of the problem. 400 SPACE text NEWLINE - This indicates an error condition. The text - describes the nature of the problem. The client + This indicates an error condition. The text + describes the nature of the problem. The client should retry the request later. 200 SPACE text NEWLINE The request was successful. In the case of a lookup - request, the text contains an encoded version of + request, the text contains an encoded version of the requested data. ENCODING - In request and reply parameters, the character %, each + In request and reply parameters, the character %, each non-printing character, and each whitespace character must - be replaced by %XX, where XX is the corresponding ASCII - hexadecimal character value. The hexadecimal codes can be + be replaced by %XX, where XX is the corresponding ASCII + hexadecimal character value. The hexadecimal codes can be specified in any case (upper, lower, mixed). - The Postfix client always encodes a request. The server - may omit the encoding as long as the reply is guaranteed + The Postfix client always encodes a request. The server + may omit the encoding as long as the reply is guaranteed to not contain the % or NEWLINE character. SECURITY - Do not use TCP lookup tables for security critical pur- - poses. The client-server connection is not protected and + Do not use TCP lookup tables for security critical pur- + poses. The client-server connection is not protected and the server is not authenticated. SEE ALSO @@ -83,11 +85,11 @@ TCP_TABLE(5) TCP_TABLE(5) BUGS Only the lookup method is currently implemented. - The client does not hang up when the connection is idle + The client does not hang up when the connection is idle for a long time. LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/man/man5/tcp_table.5 b/postfix/man/man5/tcp_table.5 index 2ae2ea6af..9630cb80e 100644 --- a/postfix/man/man5/tcp_table.5 +++ b/postfix/man/man5/tcp_table.5 @@ -34,6 +34,8 @@ sends a request, and the server sends one reply. Requests and replies are sent as one line of ASCII text, terminated by the ASCII newline character. Request and reply parameters (see below) are separated by whitespace. + +Send and receive operations must complete in 100 seconds. .SH REQUEST FORMAT .na .nf diff --git a/postfix/man/man8/nqmgr.8 b/postfix/man/man8/nqmgr.8 index c57d03413..1f3ffe198 100644 --- a/postfix/man/man8/nqmgr.8 +++ b/postfix/man/man8/nqmgr.8 @@ -19,9 +19,9 @@ The actual mail routing strategy is delegated to the This program expects to be run from the \fBmaster\fR(8) process manager. -Mail addressed to the local \fBdouble-bounce\fR address is silently -discarded. This stops potential loops caused by undeliverable -bounce notifications. +Mail addressed to the local \fBdouble-bounce\fR address is +logged and discarded. This stops potential loops caused by +undeliverable bounce notifications. .SH MAIL QUEUES .na .nf diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index 039112b61..529fb3989 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -19,9 +19,9 @@ The actual mail routing strategy is delegated to the This program expects to be run from the \fBmaster\fR(8) process manager. -Mail addressed to the local \fBdouble-bounce\fR address is silently -discarded. This stops potential loops caused by undeliverable -bounce notifications. +Mail addressed to the local \fBdouble-bounce\fR address is +logged and discarded. This stops potential loops caused by +undeliverable bounce notifications. .SH MAIL QUEUES .na .nf diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index 10484ffac..4f751864b 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -103,7 +103,7 @@ Hosts to hand off mail to if a message destination is not found or if a destination is unreachable. .IP \fBignore_mx_lookup_error\fR When a name server fails to respond to an MX query, search for an -A record instead deferring mail delivery. +A record, instead of deferring mail delivery. .IP \fBinet_interfaces\fR The network interface addresses that this mail system receives mail on. When any of those addresses appears in the list of mail diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index b40558130..f5a939851 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -81,6 +81,8 @@ List of commands that are treated as NOOP (no operation) commands, without any parameter syntax checking and without any state change. This list overrides built-in command definitions. .SH "Content inspection controls" +Optionally, Postfix can be configured to send new mail to +external content filter software AFTER the mail is queued. .IP \fBcontent_filter\fR The name of a mail delivery transport that filters mail and that either bounces mail or re-injects the result back into Postfix. @@ -103,6 +105,22 @@ content filter. Disable header/body_checks. This is typically specified with the SMTP server \fBafter\fR an external content filter. .RE +.SH "Pass-through proxy" +.ad +.fi +.ad +Optionally, the Postfix SMTP server can be configured to +forward all mail to a proxy server, for example a real-time +content filter, BEFORE mail is queued. +.IP \fBsmtpd_proxy_filter\fR +The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR +or \fIhost:\fR portion is optional. +.IP \fBsmtpd_proxy_timeout\fR +Timeout for connecting to, sending to and receiving from +the SMTP proxy server. +.IP \fBsmtpd_proxy_ehlo\fR +The hostname to use when sending an EHLO command to the +SMTP proxy server. .SH "Authentication controls" .IP \fBenable_sasl_authentication\fR Enable per-session authentication as per RFC 2554 (SASL). @@ -128,24 +146,6 @@ Disallow anonymous logins. Maps that specify the SASL login name that owns a MAIL FROM sender address. Used by the \fBreject_sender_login_mismatch\fR sender anti-spoofing restriction. -.SH "Pass-through proxy" -.ad -.fi -.ad -Optionally, the Postfix SMTP server can be configured to -forward all mail to a proxy server, for example a real-time -content filter. This proxy server should support the same -MAIL FROM and RCPT TO command syntax as Postfix, but does not -need to support ESMTP command pipelining. -.IP \fBsmtpd_proxy_filter\fR -The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR -or \fIhost:\fR portion is optional. -.IP \fBsmtpd_proxy_timeout\fR -Timeout for connecting to, sending to and receiving from -the SMTP proxy server. -.IP \fBsmtpd_proxy_ehlo\fR -The hostname to use when sending an EHLO command to the -SMTP proxy server. .SH Miscellaneous .ad .fi @@ -247,6 +247,22 @@ Disconnect after a client has made this number of errors. Limit the number of times a client can issue a junk command such as NOOP, VRFY, ETRN or RSET in one SMTP session before it is penalized with tarpit delays. +.SH "Delegated policy" +.ad +.fi +.IP \fBsmtpd_policy_service_endpoint\fR +The \fItransport\fR:\fIendpoint\fR of a server that speaks +the delegated SMTP policy protocol. \fItransport\fR is +either \fBinet\fR or \fBunix\fR. \fIendpoint\fR is either +\fIhost:port\fR, \fBprivate/\fIservicename\fR or +\fBpublic/\fIservicename\fR. +.IP \fBsmtpd_policy_service_timeout\fR +Time limit for connecting to, writing to and receiving from +a delegated SMTP policy server. +.IP \fBsmtpd_policy_service_max_idle\fR +Time after which an unused policy service connection is closed. +.IP \fBsmtpd_policy_service_timeout\fR +Time after which an active policy service connection is closed. .SH "UCE control restrictions" .ad .fi diff --git a/postfix/proto/tcp_table b/postfix/proto/tcp_table index 54406626a..f009c54b4 100644 --- a/postfix/proto/tcp_table +++ b/postfix/proto/tcp_table @@ -26,6 +26,8 @@ # replies are sent as one line of ASCII text, terminated by the # ASCII newline character. Request and reply parameters (see below) # are separated by whitespace. +# +# Send and receive operations must complete in 100 seconds. # REQUEST FORMAT # .ad # .fi diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 4ff4fe2bd..5281296d9 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -546,8 +546,12 @@ int dns_lookup(const char *name, unsigned type, unsigned flags, vstring_sprintf(why, "Name service error for name=%s type=%s: " "Malformed name server reply", name, dns_strtype(type)); - case DNS_NOTFOUND: case DNS_OK: + /* Don't return DNS_OK when all results are censored away. */ + if (rrlist && *rrlist == 0) + msg_panic("dns_lookup: name=%s type=%s: success but no data", + name, dns_strtype(type)); + case DNS_NOTFOUND: return (status); case DNS_RECURSE: if (msg_verbose) diff --git a/postfix/src/global/mail_copy.c b/postfix/src/global/mail_copy.c index 496625764..463b5a363 100644 --- a/postfix/src/global/mail_copy.c +++ b/postfix/src/global/mail_copy.c @@ -126,7 +126,7 @@ int mail_copy(const char *sender, char *myname = "mail_copy"; VSTRING *buf; char *bp; - long orig_length; + off_t orig_length; int read_error; int write_error; int corrupt_error = 0; @@ -139,7 +139,7 @@ int mail_copy(const char *sender, */ #ifndef NO_TRUNCATE if ((flags & MAIL_COPY_TOFILE) != 0) - if ((orig_length = vstream_fseek(dst, 0L, SEEK_END)) < 0) + if ((orig_length = vstream_fseek(dst, (off_t) 0, SEEK_END)) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(dst)); #endif buf = vstring_alloc(100); @@ -249,7 +249,7 @@ int mail_copy(const char *sender, #ifndef NO_TRUNCATE if ((flags & MAIL_COPY_TOFILE) != 0) if (corrupt_error || read_error || write_error) - ftruncate(vstream_fileno(dst), (off_t) orig_length); + ftruncate(vstream_fileno(dst), orig_length); #endif write_error |= vstream_fclose(dst); if (why && read_error) diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index c36fe78a8..bd96bcd96 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1850,6 +1850,27 @@ extern int var_smtpd_proxy_tmout; #define DEF_INPUT_TRANSP "" extern char *var_smtpd_input_transp; + /* + * SMTP server policy delegation. + */ +#define VAR_SMTPD_POLICY_SRV "smtpd_policy_service_endpoint" +#define DEF_SMTPD_POLICY_SRV "" +extern char *var_smtpd_policy_srv; + +#define VAR_SMTPD_POLICY_TMOUT "smtpd_policy_service_timeout" +#define DEF_SMTPD_POLICY_TMOUT "100s" +extern int var_smtpd_policy_tmout; + +#define VAR_SMTPD_POLICY_IDLE "smtpd_policy_service_max_idle" +#define DEF_SMTPD_POLICY_IDLE "300s" +extern int var_smtpd_policy_idle; + +#define VAR_SMTPD_POLICY_TTL "smtpd_policy_service_max_ttl" +#define DEF_SMTPD_POLICY_TTL "1000s" +extern int var_smtpd_policy_ttl; + +#define CHECK_POLICY_SERVICE "check_smtpd_policy_service" + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 5c0b972a8..f9b390b0b 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,10 +20,10 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20030706" +#define MAIL_RELEASE_DATE "20030712" #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "2.0.13-" MAIL_RELEASE_DATE +#define DEF_MAIL_VERSION "2.0.14-" MAIL_RELEASE_DATE extern char *var_mail_version; /* diff --git a/postfix/src/lmtp/lmtp_proto.c b/postfix/src/lmtp/lmtp_proto.c index e17d7b0aa..808811bae 100644 --- a/postfix/src/lmtp/lmtp_proto.c +++ b/postfix/src/lmtp/lmtp_proto.c @@ -226,6 +226,7 @@ int lmtp_lhlo(LMTP_STATE *state) * LMTP server. Otherwise, we might do the wrong thing when the server * advertises a really huge message size limit. */ + state->features = 0; lines = resp->str; (void) mystrtok(&lines, "\n"); while ((words = mystrtok(&lines, "\n")) != 0) { diff --git a/postfix/src/lmtp/lmtp_sasl.h b/postfix/src/lmtp/lmtp_sasl.h index 1054d688c..c1e7a5bd8 100644 --- a/postfix/src/lmtp/lmtp_sasl.h +++ b/postfix/src/lmtp/lmtp_sasl.h @@ -14,7 +14,7 @@ extern void lmtp_sasl_initialize(void); extern void lmtp_sasl_connect(LMTP_STATE *); extern int lmtp_sasl_passwd_lookup(LMTP_STATE *); -extern void lmtp_sasl_start(LMTP_STATE *); +extern void lmtp_sasl_start(LMTP_STATE *, const char *, const char *); extern int lmtp_sasl_authenticate(LMTP_STATE *, VSTRING *); extern void lmtp_sasl_cleanup(LMTP_STATE *); diff --git a/postfix/src/lmtp/lmtp_sasl_glue.c b/postfix/src/lmtp/lmtp_sasl_glue.c index 60e622fca..ca11019ca 100644 --- a/postfix/src/lmtp/lmtp_sasl_glue.c +++ b/postfix/src/lmtp/lmtp_sasl_glue.c @@ -11,7 +11,7 @@ /* void lmtp_sasl_connect(state) /* LMTP_STATE *state; /* -/* void lmtp_sasl_start(state) +/* void lmtp_sasl_start(state, sasl_opts_name, sasl_opts_var) /* LMTP_STATE *state; /* /* int lmtp_sasl_passwd_lookup(state) @@ -33,7 +33,9 @@ /* /* lmtp_sasl_start() performs per-session initialization. This /* routine must be called once per session before doing any SASL -/* authentication. +/* authentication. The sasl_opts_name and sasl_opts_var parameters are +/* the postfix configuration parameters setting the security +/* policy of the SASL authentication. /* /* lmtp_sasl_passwd_lookup() looks up the username/password /* for the current SMTP server. The result is zero in case @@ -122,8 +124,6 @@ static NAME_MASK lmtp_sasl_sec_mask[] = { 0, }; -static int lmtp_sasl_sec_opts; - /* * Silly little macros. */ @@ -319,11 +319,6 @@ void lmtp_sasl_initialize(void) if (sasl_client_init(callbacks) != SASL_OK) msg_fatal("SASL library initialization"); - /* - * Configuration parameters. - */ - lmtp_sasl_sec_opts = name_mask(VAR_LMTP_SASL_OPTS, lmtp_sasl_sec_mask, - var_lmtp_sasl_opts); } /* lmtp_sasl_connect - per-session client initialization */ @@ -341,7 +336,8 @@ void lmtp_sasl_connect(LMTP_STATE *state) /* lmtp_sasl_start - per-session SASL initialization */ -void lmtp_sasl_start(LMTP_STATE *state) +void lmtp_sasl_start(LMTP_STATE *state, const char *sasl_opts_name, + const char *sasl_opts_var) { static sasl_callback_t callbacks[] = { {SASL_CB_USER, &lmtp_sasl_get_user, 0}, @@ -383,7 +379,8 @@ void lmtp_sasl_start(LMTP_STATE *state) sec_props.min_ssf = 0; sec_props.max_ssf = 1; /* don't allow real SASL * security layer */ - sec_props.security_flags = lmtp_sasl_sec_opts; + sec_props.security_flags = name_mask(sasl_opts_name, lmtp_sasl_sec_mask, + sasl_opts_var); sec_props.maxbufsize = 0; sec_props.property_names = 0; sec_props.property_values = 0; diff --git a/postfix/src/lmtp/lmtp_sasl_proto.c b/postfix/src/lmtp/lmtp_sasl_proto.c index b8c280c01..e9fc09a2c 100644 --- a/postfix/src/lmtp/lmtp_sasl_proto.c +++ b/postfix/src/lmtp/lmtp_sasl_proto.c @@ -114,7 +114,7 @@ int lmtp_sasl_helo_login(LMTP_STATE *state) * required, and assume that an authentication error is recoverable. */ if (lmtp_sasl_passwd_lookup(state) != 0) { - lmtp_sasl_start(state); + lmtp_sasl_start(state, VAR_LMTP_SASL_OPTS, var_lmtp_sasl_opts); if (lmtp_sasl_authenticate(state, why) <= 0) ret = lmtp_site_fail(state, 450, "Authentication failed: %s", vstring_str(why)); diff --git a/postfix/src/nqmgr/qmgr.c b/postfix/src/nqmgr/qmgr.c index 1c37cdef4..36ebef017 100644 --- a/postfix/src/nqmgr/qmgr.c +++ b/postfix/src/nqmgr/qmgr.c @@ -13,9 +13,9 @@ /* This program expects to be run from the \fBmaster\fR(8) process /* manager. /* -/* Mail addressed to the local \fBdouble-bounce\fR address is silently -/* discarded. This stops potential loops caused by undeliverable -/* bounce notifications. +/* Mail addressed to the local \fBdouble-bounce\fR address is +/* logged and discarded. This stops potential loops caused by +/* undeliverable bounce notifications. /* MAIL QUEUES /* .ad /* .fi diff --git a/postfix/src/qmgr/qmgr.c b/postfix/src/qmgr/qmgr.c index 4ff798eb7..7e899d4c9 100644 --- a/postfix/src/qmgr/qmgr.c +++ b/postfix/src/qmgr/qmgr.c @@ -13,9 +13,9 @@ /* This program expects to be run from the \fBmaster\fR(8) process /* manager. /* -/* Mail addressed to the local \fBdouble-bounce\fR address is silently -/* discarded. This stops potential loops caused by undeliverable -/* bounce notifications. +/* Mail addressed to the local \fBdouble-bounce\fR address is +/* logged and discarded. This stops potential loops caused by +/* undeliverable bounce notifications. /* MAIL QUEUES /* .ad /* .fi diff --git a/postfix/src/showq/showq.c b/postfix/src/showq/showq.c index 4c561a1af..e514f396f 100644 --- a/postfix/src/showq/showq.c +++ b/postfix/src/showq/showq.c @@ -254,8 +254,8 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv) static struct queue_info queue_info[] = { MAIL_QUEUE_MAILDROP, scan_dir_next, - MAIL_QUEUE_INCOMING, mail_scan_dir_next, MAIL_QUEUE_ACTIVE, mail_scan_dir_next, + MAIL_QUEUE_INCOMING, mail_scan_dir_next, MAIL_QUEUE_DEFERRED, mail_scan_dir_next, MAIL_QUEUE_HOLD, mail_scan_dir_next, 0, diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 531878753..cd48b8bf7 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -87,7 +87,7 @@ /* or if a destination is unreachable. /* .IP \fBignore_mx_lookup_error\fR /* When a name server fails to respond to an MX query, search for an -/* A record instead deferring mail delivery. +/* A record, instead of deferring mail delivery. /* .IP \fBinet_interfaces\fR /* The network interface addresses that this mail system receives /* mail on. When any of those addresses appears in the list of mail diff --git a/postfix/src/smtp/smtp_sasl.h b/postfix/src/smtp/smtp_sasl.h index 43c721006..35f9e1c4d 100644 --- a/postfix/src/smtp/smtp_sasl.h +++ b/postfix/src/smtp/smtp_sasl.h @@ -14,7 +14,7 @@ extern void smtp_sasl_initialize(void); extern void smtp_sasl_connect(SMTP_STATE *); extern int smtp_sasl_passwd_lookup(SMTP_STATE *); -extern void smtp_sasl_start(SMTP_STATE *); +extern void smtp_sasl_start(SMTP_STATE *, const char *, const char *); extern int smtp_sasl_authenticate(SMTP_STATE *, VSTRING *); extern void smtp_sasl_cleanup(SMTP_STATE *); diff --git a/postfix/src/smtp/smtp_sasl_glue.c b/postfix/src/smtp/smtp_sasl_glue.c index a1a7c9952..d357d85b0 100644 --- a/postfix/src/smtp/smtp_sasl_glue.c +++ b/postfix/src/smtp/smtp_sasl_glue.c @@ -11,7 +11,7 @@ /* void smtp_sasl_connect(state) /* SMTP_STATE *state; /* -/* void smtp_sasl_start(state) +/* void smtp_sasl_start(state, sasl_opts_name, sasl_opts_var) /* SMTP_STATE *state; /* /* int smtp_sasl_passwd_lookup(state) @@ -33,7 +33,9 @@ /* /* smtp_sasl_start() performs per-session initialization. This /* routine must be called once per session before doing any SASL -/* authentication. +/* authentication. The sasl_opts_name and sasl_opts_var parameters are +/* the postfix configuration parameters setting the security +/* policy of the SASL authentication. /* /* smtp_sasl_passwd_lookup() looks up the username/password /* for the current SMTP server. The result is zero in case @@ -122,8 +124,6 @@ static NAME_MASK smtp_sasl_sec_mask[] = { 0, }; -static int smtp_sasl_sec_opts; - /* * Silly little macros. */ @@ -319,11 +319,6 @@ void smtp_sasl_initialize(void) if (sasl_client_init(callbacks) != SASL_OK) msg_fatal("SASL library initialization"); - /* - * Configuration parameters. - */ - smtp_sasl_sec_opts = name_mask(VAR_SMTP_SASL_OPTS, smtp_sasl_sec_mask, - var_smtp_sasl_opts); } /* smtp_sasl_connect - per-session client initialization */ @@ -341,7 +336,8 @@ void smtp_sasl_connect(SMTP_STATE *state) /* smtp_sasl_start - per-session SASL initialization */ -void smtp_sasl_start(SMTP_STATE *state) +void smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name, + const char *sasl_opts_var) { static sasl_callback_t callbacks[] = { {SASL_CB_USER, &smtp_sasl_get_user, 0}, @@ -383,7 +379,8 @@ void smtp_sasl_start(SMTP_STATE *state) sec_props.min_ssf = 0; sec_props.max_ssf = 1; /* don't allow real SASL * security layer */ - sec_props.security_flags = smtp_sasl_sec_opts; + sec_props.security_flags = name_mask(sasl_opts_name, smtp_sasl_sec_mask, + sasl_opts_var); sec_props.maxbufsize = 0; sec_props.property_names = 0; sec_props.property_values = 0; diff --git a/postfix/src/smtp/smtp_sasl_proto.c b/postfix/src/smtp/smtp_sasl_proto.c index 97c2592c0..30cf0ab58 100644 --- a/postfix/src/smtp/smtp_sasl_proto.c +++ b/postfix/src/smtp/smtp_sasl_proto.c @@ -114,7 +114,7 @@ int smtp_sasl_helo_login(SMTP_STATE *state) * required, and assume that an authentication error is recoverable. */ if (smtp_sasl_passwd_lookup(state) != 0) { - smtp_sasl_start(state); + smtp_sasl_start(state, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts); if (smtp_sasl_authenticate(state, why) <= 0) ret = smtp_site_fail(state, 450, "Authentication failed: %s", vstring_str(why)); diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index ec72a986e..e6b6a31b8 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -67,6 +67,8 @@ /* without any parameter syntax checking and without any state change. /* This list overrides built-in command definitions. /* .SH "Content inspection controls" +/* Optionally, Postfix can be configured to send new mail to +/* external content filter software AFTER the mail is queued. /* .IP \fBcontent_filter\fR /* The name of a mail delivery transport that filters mail and that /* either bounces mail or re-injects the result back into Postfix. @@ -89,6 +91,22 @@ /* Disable header/body_checks. This is typically specified with the /* SMTP server \fBafter\fR an external content filter. /* .RE +/* .SH "Pass-through proxy" +/* .ad +/* .fi +/* .ad +/* Optionally, the Postfix SMTP server can be configured to +/* forward all mail to a proxy server, for example a real-time +/* content filter, BEFORE mail is queued. +/* .IP \fBsmtpd_proxy_filter\fR +/* The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR +/* or \fIhost:\fR portion is optional. +/* .IP \fBsmtpd_proxy_timeout\fR +/* Timeout for connecting to, sending to and receiving from +/* the SMTP proxy server. +/* .IP \fBsmtpd_proxy_ehlo\fR +/* The hostname to use when sending an EHLO command to the +/* SMTP proxy server. /* .SH "Authentication controls" /* .IP \fBenable_sasl_authentication\fR /* Enable per-session authentication as per RFC 2554 (SASL). @@ -114,24 +132,6 @@ /* Maps that specify the SASL login name that owns a MAIL FROM sender /* address. Used by the \fBreject_sender_login_mismatch\fR sender /* anti-spoofing restriction. -/* .SH "Pass-through proxy" -/* .ad -/* .fi -/* .ad -/* Optionally, the Postfix SMTP server can be configured to -/* forward all mail to a proxy server, for example a real-time -/* content filter. This proxy server should support the same -/* MAIL FROM and RCPT TO command syntax as Postfix, but does not -/* need to support ESMTP command pipelining. -/* .IP \fBsmtpd_proxy_filter\fR -/* The \fIhost:port\fR of the SMTP proxy server. The \fIhost\fR -/* or \fIhost:\fR portion is optional. -/* .IP \fBsmtpd_proxy_timeout\fR -/* Timeout for connecting to, sending to and receiving from -/* the SMTP proxy server. -/* .IP \fBsmtpd_proxy_ehlo\fR -/* The hostname to use when sending an EHLO command to the -/* SMTP proxy server. /* .SH Miscellaneous /* .ad /* .fi @@ -233,6 +233,22 @@ /* Limit the number of times a client can issue a junk command /* such as NOOP, VRFY, ETRN or RSET in one SMTP session before /* it is penalized with tarpit delays. +/* .SH "Delegated policy" +/* .ad +/* .fi +/* .IP \fBsmtpd_policy_service_endpoint\fR +/* The \fItransport\fR:\fIendpoint\fR of a server that speaks +/* the delegated SMTP policy protocol. \fItransport\fR is +/* either \fBinet\fR or \fBunix\fR. \fIendpoint\fR is either +/* \fIhost:port\fR, \fBprivate/\fIservicename\fR or +/* \fBpublic/\fIservicename\fR. +/* .IP \fBsmtpd_policy_service_timeout\fR +/* Time limit for connecting to, writing to and receiving from +/* a delegated SMTP policy server. +/* .IP \fBsmtpd_policy_service_max_idle\fR +/* Time after which an unused policy service connection is closed. +/* .IP \fBsmtpd_policy_service_timeout\fR +/* Time after which an active policy service connection is closed. /* .SH "UCE control restrictions" /* .ad /* .fi @@ -506,6 +522,10 @@ char *var_smtpd_proxy_filt; int var_smtpd_proxy_tmout; char *var_smtpd_proxy_ehlo; char *var_input_transp; +char *var_smtpd_policy_srv; +int var_smtpd_policy_tmout; +int var_smtpd_policy_idle; +int var_smtpd_policy_ttl; /* * Silly little macros. @@ -1860,6 +1880,9 @@ int main(int argc, char **argv) VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0, VAR_SMTPD_PROXY_TMOUT, DEF_SMTPD_PROXY_TMOUT, &var_smtpd_proxy_tmout, 1, 0, VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay, 1, 0, + VAR_SMTPD_POLICY_TMOUT, DEF_SMTPD_POLICY_TMOUT, &var_smtpd_policy_tmout, 1, 0, + VAR_SMTPD_POLICY_IDLE, DEF_SMTPD_POLICY_IDLE, &var_smtpd_policy_idle, 1, 0, + VAR_SMTPD_POLICY_TTL, DEF_SMTPD_POLICY_TTL, &var_smtpd_policy_ttl, 1, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { @@ -1905,6 +1928,7 @@ int main(int argc, char **argv) VAR_SMTPD_PROXY_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0, VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0, VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0, + VAR_SMTPD_POLICY_SRV, DEF_SMTPD_POLICY_SRV, &var_smtpd_policy_srv, 0, 0, 0, }; static CONFIG_RAW_TABLE raw_table[] = { diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 888055c9f..8cc4e5ab5 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -119,14 +119,14 @@ extern void smtpd_state_reset(SMTPD_STATE *); /* * SMPTD peer information lookup. */ -void smtpd_peer_init(SMTPD_STATE *state); -void smtpd_peer_reset(SMTPD_STATE *state); +extern void smtpd_peer_init(SMTPD_STATE *state); +extern void smtpd_peer_reset(SMTPD_STATE *state); /* * Transparency: before mail is queued, do we check for unknown recipients, * do we allow address mapping, automatic bcc, header/body checks? */ -int smtpd_input_transp_mask; +extern int smtpd_input_transp_mask; /* LICENSE /* .ad diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 0eaa39c0c..7d87b06e8 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -57,6 +57,9 @@ /* Reject, defer or permit the request unconditionally. This is to be used /* at the end of a restriction list in order to make the default /* action explicit. +/* .IP check_policy_service +/* query an external policy service with client, helo, sender, recipient +/* and queue ID attributes. /* .IP reject_unknown_client /* Reject the request when the client hostname could not be found. /* The \fIunknown_client_reject_code\fR configuration parameter @@ -224,6 +227,9 @@ /* The message would use up more than half the available queue file /* system space. This is a temporary error. /* .PP +/* smtpd_check_data() enforces generic restrictions after the +/* client has sent the DATA command. +/* /* Arguments: /* .IP name /* The client hostname, or \fIunknown\fR. @@ -291,6 +297,8 @@ #include #include #include +#include +#include /* DNS library. */ @@ -415,6 +423,11 @@ static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, con */ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient); + /* + * Delegated policy. + */ +static ATTR_CLNT *policy_clnt; + /* * Reject context. */ @@ -478,7 +491,8 @@ static void PRINTFLIKE(3, 4) defer_if(SMTPD_DEFER *, int, const char *,...); * Cached RBL lookup state. */ typedef struct { - char *txt; /* TXT record or "" */ + ARGV *txt; /* TXT records or NULL */ + ARGV *a; /* A records */ } SMTPD_RBL_STATE; static void *rbl_pagein(const char *, void *); @@ -489,10 +503,10 @@ static void rbl_pageout(void *, void *); */ typedef struct { SMTPD_STATE *state; /* general state */ - SMTPD_RBL_STATE *rbl_state; /* cached RBL state */ const char *domain; /* query domain */ const char *what; /* rejected value */ const char *class; /* name of rejected value */ + const char *txt; /* randomly selected trimmed TXT rr */ } SMTPD_RBL_EXPAND_CONTEXT; /* resolve_pagein - page in an address resolver result */ @@ -750,6 +764,15 @@ void smtpd_check_init(void) */ expand_filter = vstring_alloc(10); unescape(expand_filter, var_smtpd_exp_filter); + + /* + * Delegated policy. + */ + if (*var_smtpd_policy_srv) + policy_clnt = attr_clnt_create(var_smtpd_policy_srv, + var_smtpd_policy_tmout, + var_smtpd_policy_idle, + var_smtpd_policy_ttl); } /* log_whatsup - log as much context as we have */ @@ -2357,6 +2380,9 @@ static void *rbl_pagein(const char *query, void *unused_context) VSTRING *why; int dns_status; SMTPD_RBL_STATE *rbl; + DNS_RR *addr_list; + struct in_addr addr; + DNS_RR *rr; /* * Do the query. If the DNS lookup produces no definitive reply, give the @@ -2364,8 +2390,7 @@ static void *rbl_pagein(const char *query, void *unused_context) * because an RBL server is unavailable. */ why = vstring_alloc(10); - dns_status = dns_lookup(query, T_A, 0, (DNS_RR **) 0, - (VSTRING *) 0, why); + dns_status = dns_lookup(query, T_A, 0, &addr_list, (VSTRING *) 0, why); if (dns_status != DNS_OK && dns_status != DNS_NOTFOUND) msg_warn("%s: RBL lookup error: %s", query, STR(why)); vstring_free(why); @@ -2376,13 +2401,24 @@ static void *rbl_pagein(const char *query, void *unused_context) * Save the result. Yes, we cache negative results as well as positive * results. */ +#define RBL_TXT_LIMIT 256 + rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl)); if (dns_lookup(query, T_TXT, 0, &txt_list, (VSTRING *) 0, (VSTRING *) 0) == DNS_OK) { - rbl->txt = mystrndup(txt_list->data, 512); + rbl->txt = argv_alloc(1); + for (rr = txt_list; rr != 0; rr = rr->next) + argv_addn(rbl->txt, rr->data, rr->data_len > RBL_TXT_LIMIT ? + RBL_TXT_LIMIT : rr->data_len, ARGV_END); dns_rr_free(txt_list); } else - rbl->txt = mystrdup(""); + rbl->txt = 0; + rbl->a = argv_alloc(1); + for (rr = addr_list; rr != 0; rr = rr->next) { + memcpy((char *) &addr.s_addr, addr_list->data, sizeof(addr.s_addr)); + argv_add(rbl->a, inet_ntoa(addr), ARGV_END); + } + dns_rr_free(addr_list); return ((void *) rbl); } @@ -2394,7 +2430,9 @@ static void rbl_pageout(void *data, void *unused_context) if (rbl != 0) { if (rbl->txt) - myfree(rbl->txt); + argv_free(rbl->txt); + if (rbl->a) + argv_free(rbl->a); myfree((char *) rbl); } } @@ -2405,7 +2443,6 @@ static const char *rbl_expand_lookup(const char *name, int mode, char *context) { SMTPD_RBL_EXPAND_CONTEXT *rbl_exp = (SMTPD_RBL_EXPAND_CONTEXT *) context; - SMTPD_RBL_STATE *rbl = rbl_exp->rbl_state; SMTPD_STATE *state = rbl_exp->state; if (state->expand_buf == 0) @@ -2423,9 +2460,9 @@ static const char *rbl_expand_lookup(const char *name, int mode, } else if (STREQ(name, MAIL_ATTR_RBL_DOMAIN)) { return (rbl_exp->domain); } else if (STREQ(name, MAIL_ATTR_RBL_REASON)) { - return (rbl->txt); + return (rbl_exp->txt); } else if (STREQ(name, MAIL_ATTR_RBL_TXT)) {/* LaMont compat */ - return (rbl->txt); + return (rbl_exp->txt); } else if (STREQ(name, MAIL_ATTR_RBL_WHAT)) { return (rbl_exp->what); } else if (STREQ(name, MAIL_ATTR_RBL_CLASS)) { @@ -2459,10 +2496,20 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl, } why = vstring_alloc(100); rbl_exp.state = state; - rbl_exp.rbl_state = rbl; rbl_exp.domain = rbl_domain; rbl_exp.what = what; rbl_exp.class = reply_class; + + /* + * XXX When presented with multiple txt records pick a random one! There + * is no way to pair up the A records with associated TXT records. If a + * client connects multiple times, it will learn the right reject reason + * at least some of the time. + */ + rbl_exp.txt = mystrdup(rbl->txt == 0 ? "" : + rbl->txt->argc == 1 ? rbl->txt->argv[0] : + rbl->txt->argv[myrand() % rbl->txt->argc]); + for (;;) { if (template == 0) template = var_def_rbl_reply; @@ -2483,10 +2530,23 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl, * Clean up. */ vstring_free(why); + myfree((char *) rbl_exp.txt); return (result); } +/* rbl_match_addr - match address list */ + +static int rbl_match_addr(SMTPD_RBL_STATE *rbl, const char *addr) +{ + char **cpp; + + for (cpp = rbl->a->argv; *cpp; cpp++) + if (strcmp(*cpp, addr) == 0) + return (1); + return (0); +} + /* reject_rbl_addr - reject if address in real-time blackhole list */ static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain, @@ -2497,6 +2557,7 @@ static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain, VSTRING *query; int i; SMTPD_RBL_STATE *rbl; + const char *reply_addr; if (msg_verbose) msg_info("%s: %s %s", myname, reply_class, addr); @@ -2521,15 +2582,17 @@ static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain, } argv_free(octets); vstring_strcat(query, rbl_domain); + reply_addr = split_at(STR(query), '='); rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query)); - vstring_free(query); /* * If the record exists, the address is blacklisted. */ - if (rbl == 0) { + if (rbl == 0 || (reply_addr != 0 && !rbl_match_addr(rbl, reply_addr))) { + vstring_free(query); return (SMTPD_CHECK_DUNNO); } else { + vstring_free(query); return (rbl_reject_reply(state, rbl, rbl_domain, addr, reply_class)); } } @@ -2543,6 +2606,7 @@ static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain, VSTRING *query; SMTPD_RBL_STATE *rbl; const char *domain; + const char *reply_addr; if (msg_verbose) msg_info("%s: %s %s", myname, reply_class, what); @@ -2562,15 +2626,17 @@ static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain, query = vstring_alloc(100); vstring_sprintf(query, "%s.%s", domain, rbl_domain); + reply_addr = split_at(STR(query), '='); rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query)); - vstring_free(query); /* * If the record exists, the domain is blacklisted. */ - if (rbl == 0) { + if (rbl == 0 || (reply_addr != 0 && !rbl_match_addr(rbl, reply_addr))) { + vstring_free(query); return (SMTPD_CHECK_DUNNO); } else { + vstring_free(query); return (rbl_reject_reply(state, rbl, rbl_domain, what, reply_class)); } } @@ -2646,6 +2712,59 @@ static int reject_sender_login_mismatch(SMTPD_STATE *state, const char *sender) return (SMTPD_CHECK_DUNNO); } +/* check_policy_service - check delegated policy service */ + +static int check_policy_service(SMTPD_STATE *state, const char *reply_name, + const char *reply_class, const char *def_acl) +{ + static VSTRING *action = 0; + + /* + * Sanity check. + */ + if (policy_clnt == 0) { + msg_warn("%s is used in %s restrictions, but no service is defined", + CHECK_POLICY_SERVICE, reply_class); + return (SMTPD_CHECK_DUNNO); + } + + /* + * Initialize. + */ + if (action == 0) + action = vstring_alloc(10); + + if (attr_clnt_request(policy_clnt, + ATTR_FLAG_NONE, /* Query attributes. */ + ATTR_TYPE_STR, MAIL_ATTR_CLIENT, state->namaddr, + ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, state->addr, + ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, state->name, + ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, + state->helo_name ? state->helo_name : "", + ATTR_TYPE_STR, MAIL_ATTR_SENDER, + state->sender ? state->sender : "", + ATTR_TYPE_STR, MAIL_ATTR_RECIP, + state->recipient ? state->recipient : "", + ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, + state->queue_id ? state->queue_id : "", + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply attributes. */ + ATTR_TYPE_STR, MAIL_ATTR_ACTION, action, + ATTR_TYPE_END) != 1) { + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "450 Server configuration problem")); + } else { + + /* + * XXX This produces bogus error messages when the reply is + * malformed. + */ + return (check_table_result(state, var_smtpd_policy_srv, STR(action), + "policy query", reply_name, + reply_class, def_acl)); + } +} + /* is_map_command - restriction has form: check_xxx_access type:name */ static int is_map_command(SMTPD_STATE *state, const char *name, @@ -2747,6 +2866,9 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, cpp[1], REJECT_ALL); } else if (strcasecmp(name, REJECT_UNAUTH_PIPE) == 0) { status = reject_unauth_pipelining(state, reply_name, reply_class); + } else if (strcasecmp(name, CHECK_POLICY_SERVICE) == 0) { + status = check_policy_service(state, reply_name, + reply_class, def_acl); } else if (strcasecmp(name, DEFER_IF_PERMIT) == 0) { DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY, "450 <%s>: %s rejected: defer_if_permit requested", @@ -3524,6 +3646,8 @@ char *smtpd_check_data(SMTPD_STATE *state) #include +int smtpd_input_transp_mask; + /* * Dummies. These are never set. */ @@ -3564,6 +3688,8 @@ char *var_smtpd_exp_filter; char *var_def_rbl_reply; char *var_relay_rcpt_maps; char *var_verify_sender; +char *var_smtpd_sasl_opts; +char *var_smtpd_policy_srv; typedef struct { char *name; @@ -3603,6 +3729,8 @@ static STRING_TABLE string_table[] = { VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name, + VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, + VAR_SMTPD_POLICY_SRV, DEF_SMTPD_POLICY_SRV, &var_smtpd_policy_srv, 0, 0, 0, }; @@ -3664,6 +3792,9 @@ int var_virt_alias_code; int var_show_unk_rcpt_table; int var_verify_poll_count; int var_verify_poll_delay; +int var_smtpd_policy_tmout; +int var_smtpd_policy_idle; +int var_smtpd_policy_ttl; static INT_TABLE int_table[] = { "msg_verbose", 0, &msg_verbose, @@ -3688,7 +3819,6 @@ static INT_TABLE int_table[] = { VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table, VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count, - VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay, 0, }; @@ -3795,7 +3925,8 @@ bool var_smtpd_sasl_enable = 0; /* smtpd_sasl_connect - stub */ -void smtpd_sasl_connect(SMTPD_STATE *state) +void smtpd_sasl_connect(SMTPD_STATE *state, const char *opts_name, + const char *opts_var) { msg_panic("smtpd_sasl_connect was called"); } diff --git a/postfix/src/smtpd/smtpd_check.h b/postfix/src/smtpd/smtpd_check.h index 302e26903..9f2927c92 100644 --- a/postfix/src/smtpd/smtpd_check.h +++ b/postfix/src/smtpd/smtpd_check.h @@ -20,6 +20,7 @@ extern char *smtpd_check_size(SMTPD_STATE *, off_t); extern char *smtpd_check_rcpt(SMTPD_STATE *, char *); extern char *smtpd_check_etrn(SMTPD_STATE *, char *); extern char *smtpd_check_data(SMTPD_STATE *); +extern char *smtpd_check_policy(SMTPD_STATE *, char *); /* LICENSE /* .ad diff --git a/postfix/src/smtpd/smtpd_exp.in b/postfix/src/smtpd/smtpd_exp.in index ce2a514d9..50aa0e18f 100644 --- a/postfix/src/smtpd/smtpd_exp.in +++ b/postfix/src/smtpd/smtpd_exp.in @@ -24,6 +24,11 @@ client spike.porcupine.org 168.100.189.2 rcpt rname@rdomain client foo 127.0.0.2 rcpt rname@rdomain +recipient_restrictions reject_rbl_client,relays.mail-abuse.org=127.0.0.2 +client foo 127.0.0.2 +rcpt rname@rdomain +client foo 127.0.0.1 +rcpt rname@rdomain # # RHSBL sender domain name # diff --git a/postfix/src/smtpd/smtpd_exp.ref b/postfix/src/smtpd/smtpd_exp.ref index 36fc6b161..36e2cf519 100644 --- a/postfix/src/smtpd/smtpd_exp.ref +++ b/postfix/src/smtpd/smtpd_exp.ref @@ -44,6 +44,17 @@ OK >>> rcpt rname@rdomain ./smtpd_check: : reject: RCPT from foo[127.0.0.2]: 554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org; from= to= proto=SMTP helo= 554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org +>>> recipient_restrictions reject_rbl_client,relays.mail-abuse.org=127.0.0.2 +OK +>>> client foo 127.0.0.2 +OK +>>> rcpt rname@rdomain +./smtpd_check: : reject: RCPT from foo[127.0.0.2]: 554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org=127.0.0.2; from= to= proto=SMTP helo= +554 Service unavailable; Client host [127.0.0.2] blocked using relays.mail-abuse.org=127.0.0.2 +>>> client foo 127.0.0.1 +OK +>>> rcpt rname@rdomain +OK >>> # >>> # RHSBL sender domain name >>> # diff --git a/postfix/src/smtpd/smtpd_proxy.c b/postfix/src/smtpd/smtpd_proxy.c index b34a7ea09..6ba94a55c 100644 --- a/postfix/src/smtpd/smtpd_proxy.c +++ b/postfix/src/smtpd/smtpd_proxy.c @@ -15,7 +15,7 @@ /* /* other fields... */ /* .in -4 /* } SMTPD_STATE; -/* SMTP-LEVEL ROUTINES +/* /* int smtpd_proxy_open(state, service, timeout, ehlo_name, mail_from) /* SMTPD_STATE *state; /* const char *service; diff --git a/postfix/src/smtpd/smtpd_sasl_glue.c b/postfix/src/smtpd/smtpd_sasl_glue.c index 936f16736..490a2633c 100644 --- a/postfix/src/smtpd/smtpd_sasl_glue.c +++ b/postfix/src/smtpd/smtpd_sasl_glue.c @@ -8,7 +8,7 @@ /* /* void smtpd_sasl_initialize() /* -/* void smtpd_sasl_connect(state) +/* void smtpd_sasl_connect(state, sasl_opts_name, sasl_opts_var) /* SMTPD_STATE *state; /* /* char *smtpd_sasl_authenticate(state, sasl_method, init_response) @@ -32,7 +32,9 @@ /* /* smtpd_sasl_connect() performs per-connection initialization. /* This routine should be called once at the start of every -/* connection. +/* connection. The sasl_opts_name and sasl_opts_var parameters +/* are the postfix configuration parameters setting the security +/* policy of the SASL authentication. /* /* smtpd_sasl_authenticate() implements the authentication dialog. /* The result is a null pointer in case of success, an SMTP reply @@ -201,8 +203,6 @@ static NAME_MASK smtpd_sasl_mask[] = { 0, }; -static int smtpd_sasl_opts; - /* smtpd_sasl_initialize - per-process initialization */ void smtpd_sasl_initialize(void) @@ -214,16 +214,12 @@ void smtpd_sasl_initialize(void) if (sasl_server_init(callbacks, "smtpd") != SASL_OK) msg_fatal("SASL per-process initialization failed"); - /* - * Configuration parameters. - */ - smtpd_sasl_opts = name_mask(VAR_SMTPD_SASL_OPTS, smtpd_sasl_mask, - var_smtpd_sasl_opts); } /* smtpd_sasl_connect - per-connection initialization */ -void smtpd_sasl_connect(SMTPD_STATE *state) +void smtpd_sasl_connect(SMTPD_STATE *state, const char *sasl_opts_name, + const char *sasl_opts_var) { #if SASL_VERSION_MAJOR < 2 unsigned sasl_mechanism_count; @@ -291,7 +287,8 @@ void smtpd_sasl_connect(SMTPD_STATE *state) sec_props.min_ssf = 0; sec_props.max_ssf = 1; /* don't allow real SASL * security layer */ - sec_props.security_flags = smtpd_sasl_opts; + sec_props.security_flags = name_mask(sasl_opts_name, smtpd_sasl_mask, + sasl_opts_var); sec_props.maxbufsize = 0; sec_props.property_names = 0; sec_props.property_values = 0; diff --git a/postfix/src/smtpd/smtpd_sasl_glue.h b/postfix/src/smtpd/smtpd_sasl_glue.h index 9bd793690..2466f9a57 100644 --- a/postfix/src/smtpd/smtpd_sasl_glue.h +++ b/postfix/src/smtpd/smtpd_sasl_glue.h @@ -12,7 +12,7 @@ * SASL protocol interface */ extern void smtpd_sasl_initialize(void); -extern void smtpd_sasl_connect(SMTPD_STATE *); +extern void smtpd_sasl_connect(SMTPD_STATE *, const char *, const char *); extern void smtpd_sasl_disconnect(SMTPD_STATE *); extern char *smtpd_sasl_authenticate(SMTPD_STATE *, const char *, const char *); extern void smtpd_sasl_logout(SMTPD_STATE *); diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index f0b148d55..53e805794 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -106,7 +106,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream) if (SMTPD_STAND_ALONE(state)) var_smtpd_sasl_enable = 0; if (var_smtpd_sasl_enable) - smtpd_sasl_connect(state); + smtpd_sasl_connect(state, VAR_SMTPD_SASL_OPTS, var_smtpd_sasl_opts); #endif /* diff --git a/postfix/src/spawn/spawn.c b/postfix/src/spawn/spawn.c index d33824a8d..3e0ab78de 100644 --- a/postfix/src/spawn/spawn.c +++ b/postfix/src/spawn/spawn.c @@ -174,11 +174,11 @@ static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv) if (*group == 0) group = 0; if ((pwd = getpwnam(user)) == 0) - msg_fatal("%s: unknown username: %s", myname, user); + msg_fatal("unknown user name: %s", user); attr->uid = pwd->pw_uid; if (group != 0) { if ((grp = getgrnam(group)) == 0) - msg_fatal("%s: unknown group: %s", myname, group); + msg_fatal("unknown group name: %s", group); attr->gid = grp->gr_gid; } else { attr->gid = pwd->pw_gid; diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 74b538783..e1012ea06 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -26,7 +26,8 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \ unescape.c unix_connect.c unix_listen.c unix_trigger.c unsafe.c \ username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \ vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \ - write_buf.c write_wait.c + write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \ + attr_print_plain.c OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \ attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \ chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \ @@ -54,7 +55,8 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \ unescape.o unix_connect.o unix_listen.o unix_trigger.o unsafe.o \ username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \ vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \ - write_buf.o write_wait.o $(STRCASE) + write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \ + attr_print_plain.o $(STRCASE) HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ dict_cidr.h dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \ @@ -72,7 +74,8 @@ HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ scan_dir.h set_eugid.h set_ugid.h sigdelay.h spawn_command.h \ split_at.h stat_as.h stringops.h sys_defs.h timed_connect.h \ timed_wait.h trigger.h username.h valid_hostname.h vbuf.h \ - vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h + vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h \ + auto_clnt.h attr_clnt.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ stream_test.c dup2_pass_on_exec.c WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ @@ -324,6 +327,16 @@ host_port: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o +attr_scan_plain: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +attr_print_plain: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + depend: $(MAKES) (sed '1,/^# do not edit/!d' Makefile.in; \ set -e; for i in [a-z][a-z0-9]*.c; do \ @@ -338,7 +351,7 @@ stream_test: stream_test.c $(LIB) tests: valid_hostname_test mac_expand_test dict_test unescape_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \ attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \ - dict_cidr_test + dict_cidr_test attr_scan_plain_test valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref ./valid_hostname valid_hostname.tmp @@ -414,6 +427,11 @@ host_port_test: host_port host_port.in host_port.ref diff host_port.ref host_port.tmp rm -f host_port.tmp +attr_scan_plain_test: attr_print_plain attr_scan_plain attr_scan_plain.ref + (./attr_print_plain 2>&3 | (sleep 1; ./attr_scan_plain)) >attr_scan_plain.tmp 2>&1 3>&1 + diff attr_scan_plain.ref attr_scan_plain.tmp + rm -f attr_scan_plain.tmp + # do not edit below this line - it is generated by 'make depend' alldig.o: alldig.c alldig.o: sys_defs.h @@ -423,6 +441,7 @@ alldig.o: vbuf.h argv.o: argv.c argv.o: sys_defs.h argv.o: mymalloc.h +argv.o: msg.h argv.o: argv.h argv_split.o: argv_split.c argv_split.o: sys_defs.h @@ -431,6 +450,18 @@ argv_split.o: stringops.h argv_split.o: vstring.h argv_split.o: vbuf.h argv_split.o: argv.h +attr_clnt.o: attr_clnt.c +attr_clnt.o: sys_defs.h +attr_clnt.o: msg.h +attr_clnt.o: mymalloc.h +attr_clnt.o: split_at.h +attr_clnt.o: vstream.h +attr_clnt.o: vbuf.h +attr_clnt.o: connect.h +attr_clnt.o: iostuff.h +attr_clnt.o: attr.h +attr_clnt.o: auto_clnt.h +attr_clnt.o: attr_clnt.h attr_print0.o: attr_print0.c attr_print0.o: sys_defs.h attr_print0.o: msg.h @@ -449,6 +480,16 @@ attr_print64.o: htable.h attr_print64.o: base64_code.h attr_print64.o: vstring.h attr_print64.o: attr.h +attr_print_plain.o: attr_print_plain.c +attr_print_plain.o: sys_defs.h +attr_print_plain.o: msg.h +attr_print_plain.o: mymalloc.h +attr_print_plain.o: vstream.h +attr_print_plain.o: vbuf.h +attr_print_plain.o: htable.h +attr_print_plain.o: base64_code.h +attr_print_plain.o: vstring.h +attr_print_plain.o: attr.h attr_scan0.o: attr_scan0.c attr_scan0.o: sys_defs.h attr_scan0.o: msg.h @@ -469,6 +510,24 @@ attr_scan64.o: vstring.h attr_scan64.o: htable.h attr_scan64.o: base64_code.h attr_scan64.o: attr.h +attr_scan_plain.o: attr_scan_plain.c +attr_scan_plain.o: sys_defs.h +attr_scan_plain.o: msg.h +attr_scan_plain.o: mymalloc.h +attr_scan_plain.o: vstream.h +attr_scan_plain.o: vbuf.h +attr_scan_plain.o: vstring.h +attr_scan_plain.o: htable.h +attr_scan_plain.o: attr.h +auto_clnt.o: auto_clnt.c +auto_clnt.o: sys_defs.h +auto_clnt.o: msg.h +auto_clnt.o: mymalloc.h +auto_clnt.o: vstream.h +auto_clnt.o: vbuf.h +auto_clnt.o: events.h +auto_clnt.o: iostuff.h +auto_clnt.o: auto_clnt.h base64_code.o: base64_code.c base64_code.o: sys_defs.h base64_code.o: msg.h diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index 88721096e..ff94f0328 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -16,6 +16,11 @@ /* ARGV *argvp; /* char *arg; /* +/* void argv_addn(argvp, arg, arg_len, ..., ARGV_END) +/* ARGV *argvp; +/* char *arg; +/* int arg_len; +/* /* void argv_terminate(argvp); /* ARGV *argvp; /* DESCRIPTION @@ -37,6 +42,9 @@ /* Terminate the argument list with a null pointer. The manifest /* constant ARGV_END provides a convenient notation for this. /* +/* argv_addn() is like argv_add(), but each string is followed +/* by a string length argument. +/* /* argv_free() releases storage for a string array, and conveniently /* returns a null pointer. /* @@ -66,6 +74,7 @@ /* Application-specific. */ #include "mymalloc.h" +#include "msg.h" #include "argv.h" /* argv_free - destroy string array */ @@ -98,6 +107,16 @@ ARGV *argv_alloc(int len) return (argvp); } +/* argv_extend - extend array */ + +static void argv_extend(ARGV *argvp) +{ + argvp->len *= 2; + argvp->argv = (char **) + myrealloc((char *) argvp->argv, + (argvp->len + 1) * sizeof(char *)); +} + /* argv_add - add string to vector */ void argv_add(ARGV *argvp,...) @@ -108,20 +127,41 @@ void argv_add(ARGV *argvp,...) /* * Make sure that always argvp->argc < argvp->len. */ +#define ARGV_SPACE_LEFT(a) ((a)->len - (a)->argc - 1) + va_start(ap, argvp); while ((arg = va_arg(ap, char *)) != 0) { - if (argvp->argc + 1 >= argvp->len) { - argvp->len *= 2; - argvp->argv = (char **) - myrealloc((char *) argvp->argv, - (argvp->len + 1) * sizeof(char *)); - } + if (ARGV_SPACE_LEFT(argvp) <= 0) + argv_extend(argvp); argvp->argv[argvp->argc++] = mystrdup(arg); } va_end(ap); argvp->argv[argvp->argc] = 0; } +/* argv_addn - add string to vector */ + +void argv_addn(ARGV *argvp,...) +{ + char *arg; + int len; + va_list ap; + + /* + * Make sure that always argvp->argc < argvp->len. + */ + va_start(ap, argvp); + while ((arg = va_arg(ap, char *)) != 0) { + if ((len = va_arg(ap, int)) < 0) + msg_panic("argv_addn: bad string length %d", len); + if (ARGV_SPACE_LEFT(argvp) <= 0) + argv_extend(argvp); + argvp->argv[argvp->argc++] = mystrndup(arg, len); + } + va_end(ap); + argvp->argv[argvp->argc] = 0; +} + /* argv_terminate - terminate string array */ void argv_terminate(ARGV *argvp) diff --git a/postfix/src/util/argv.h b/postfix/src/util/argv.h index 0d5562310..d603dac1c 100644 --- a/postfix/src/util/argv.h +++ b/postfix/src/util/argv.h @@ -22,6 +22,7 @@ typedef struct ARGV { extern ARGV *argv_alloc(int); extern void argv_add(ARGV *,...); +extern void argv_addn(ARGV *,...); extern void argv_terminate(ARGV *); extern ARGV *argv_free(ARGV *); diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h index c656dadf8..f0626f304 100644 --- a/postfix/src/util/attr.h +++ b/postfix/src/util/attr.h @@ -28,8 +28,11 @@ #define ATTR_TYPE_NUM 1 /* Unsigned integer */ #define ATTR_TYPE_STR 2 /* Character string */ #define ATTR_TYPE_HASH 3 /* Hash table */ +#define ATTR_TYPE_NV 3 /* Name-value table */ #define ATTR_TYPE_LONG 4 /* Unsigned long */ +#define ATTR_HASH_LIMIT 1024 /* Size of hash table */ + /* * Flags that control processing. See attr_scan(3) for documentation. */ @@ -70,6 +73,19 @@ extern int attr_vprint0(VSTREAM *, int, va_list); extern int attr_scan0(VSTREAM *, int,...); extern int attr_vscan0(VSTREAM *, int, va_list); + /* + * attr_scan_plain.c. + */ +extern int attr_print_plain(VSTREAM *, int,...); +extern int attr_vprint_plain(VSTREAM *, int, va_list); + + /* + * attr_print_plain.c. + */ +extern int attr_scan_plain(VSTREAM *, int,...); +extern int attr_vscan_plain(VSTREAM *, int, va_list); + + /* * Attribute names for testing the compatibility of the read and write * routines. diff --git a/postfix/src/util/attr_clnt.c b/postfix/src/util/attr_clnt.c new file mode 100644 index 000000000..a0edae793 --- /dev/null +++ b/postfix/src/util/attr_clnt.c @@ -0,0 +1,230 @@ +/*++ +/* NAME +/* attr_clnt 3 +/* SUMMARY +/* attribute query-reply client +/* SYNOPSIS +/* #include +/* +/* ATTR_CLNT *attr_clnt_create(server, timeout, max_idle, max_ttl) +/* const char *server; +/* int timeout; +/* int max_idle; +/* int max_ttl; +/* +/* int attr_clnt_request(client, +/* send_flags, send_type, send_name, ..., ATTR_TYPE_END, +/* recv_flags, recv_type, recv_name, ..., ATTR_TYPE_END) +/* ATTR_CLNT *client; +/* int send_flags; +/* int send_type; +/* const char *send_name; +/* int recv_flags; +/* int recv_type; +/* const char *recv_name; +/* +/* void attr_clnt_free(client) +/* ATTR_CLNT *client; +/* DESCRIPTION +/* This module implements a client for a simple attribute-based +/* protocol as described in attr_scan0(3) and attr_scan64(3). +/* +/* attr_clnt_create() creates a client handle. The server +/* argument specifies "transport:servername" where transport is +/* currently limited to "inet" or "unix", and servername has the +/* form "host:port", "private/servicename" or "public/servicename". +/* The timeout parameter limits the time for sending or receiving +/* a reply, and the ttl parameter controls how long an unused +/* connection is kept open. +/* +/* attr_clnt_request() sends the specified request attributes and +/* receives a reply. The reply argument specifies a name-value table. +/* The other arguments are as described in attr_print0(3) and in +/* attr_print64(3). The result is the number of attributes received +/* or -1 in case of trouble. +/* +/* attr_clnt_free() destroys a client handle and closes its connection. +/* DIAGNOSTICS +/* Warnings: communication failure. +/* SEE ALSO +/* auto_clnt(3), client endpoint management +/* attr_scan_plain(3), attribute protocol +/* attr_print_plain(3), attribute protocol +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +struct ATTR_CLNT { + AUTO_CLNT *auto_clnt; + int (*connect) (const char *, int, int); + char *endpoint; + int timeout; + int (*print) (VSTREAM *, int, va_list); + int (*scan) (VSTREAM *, int, va_list); +}; + +/* attr_clnt_connect - connect to server */ + +static VSTREAM *attr_clnt_connect(void *context) +{ + const char *myname = "attr_clnt_connect"; + ATTR_CLNT *client = (ATTR_CLNT *) context; + VSTREAM *fp; + int fd; + + fd = client->connect(client->endpoint, NON_BLOCKING, client->timeout); + if (fd < 0) { + msg_warn("connect to %s: %m", client->endpoint); + return (0); + } else { + if (msg_verbose) + msg_info("%s: connected to %s", myname, client->endpoint); + fp = vstream_fdopen(fd, O_RDWR); + vstream_control(fp, VSTREAM_CTL_PATH, client->endpoint, + VSTREAM_CTL_TIMEOUT, client->timeout, + VSTREAM_CTL_END); + return (fp); + } +} + +/* attr_clnt_free - destroy attribute client */ + +void attr_clnt_free(ATTR_CLNT *client) +{ + myfree(client->endpoint); + auto_clnt_free(client->auto_clnt); + myfree((char *) client); +} + +/* attr_clnt_create - create attribute client */ + +ATTR_CLNT *attr_clnt_create(const char *service, int timeout, + int max_idle, int max_ttl) +{ + const char *myname = "attr_clnt_create"; + char *transport = mystrdup(service); + char *endpoint; + ATTR_CLNT *client; + + if ((endpoint = split_at(transport, ':')) == 0 || *endpoint == 0) + msg_fatal("missing attribute server endpoint: %s", service); + if (msg_verbose) + msg_info("%s: transport=%s endpoint=%s", myname, transport, endpoint); + + client = (ATTR_CLNT *) mymalloc(sizeof(*client)); + client->scan = attr_vscan_plain; + client->print = attr_vprint_plain; + client->endpoint = mystrdup(endpoint); + client->timeout = timeout; + if (strcmp(transport, "inet") == 0) { + client->connect = inet_connect; + } else if (strcmp(transport, "unix") == 0) { + client->connect = unix_connect; + } else { + msg_fatal("invalid attribute transport name: %s", service); + } + client->auto_clnt = auto_clnt_create(max_idle, max_ttl, + attr_clnt_connect, (void *) client); + myfree(transport); + return (client); +} + +/* attr_clnt_request - send query, receive reply */ + +int attr_clnt_request(ATTR_CLNT *client, int send_flags,...) +{ + const char *myname = "attr_clnt_request"; + VSTREAM *stream; + int count = 0; + va_list ap; + int type; + int recv_flags; + int err; + int ret; + + /* + * XXX If the stream is readable before we send anything, then assume the + * remote end disconnected. + * + * XXX For some reason we can't simply call the scan routine after the print + * routine, that messes up the argument list. + */ +#define SKIP_ARG(ap, type) { \ + (void) va_arg(ap, char *); \ + (void) va_arg(ap, type); \ + } + + for (;;) { + if ((stream = auto_clnt_access(client->auto_clnt)) != 0 + && readable(vstream_fileno(stream)) == 0) { + errno = 0; + va_start(ap, send_flags); + err = (client->print(stream, send_flags, ap) != 0 + || vstream_fflush(stream) != 0); + va_end(ap); + if (err == 0) { + va_start(ap, send_flags); + while ((type = va_arg(ap, int)) != ATTR_TYPE_END) { + switch (type) { + case ATTR_TYPE_STR: + SKIP_ARG(ap, char *); + break; + case ATTR_TYPE_NUM: + SKIP_ARG(ap, int); + break; + case ATTR_TYPE_LONG: + SKIP_ARG(ap, long); + break; + case ATTR_TYPE_HASH: + SKIP_ARG(ap, HTABLE *); + break; + default: + msg_panic("%s: unexpected attribute type %d", + myname, type); + } + } + recv_flags = va_arg(ap, int); + ret = client->scan(stream, recv_flags, ap); + va_end(ap); + if (ret > 0) + return (ret); + } + } + if (++count >= 2 + || msg_verbose + || (errno != EPIPE && errno != ENOENT && errno != ECONNRESET)) + msg_warn("problem talking to server %s: %m", client->endpoint); + if (count >= 3) + return (-1); + sleep(1); /* XXX make configurable */ + auto_clnt_recover(client->auto_clnt); + } +} diff --git a/postfix/src/util/attr_clnt.h b/postfix/src/util/attr_clnt.h new file mode 100644 index 000000000..9edb796fd --- /dev/null +++ b/postfix/src/util/attr_clnt.h @@ -0,0 +1,39 @@ +#ifndef _ATTR_CLNT_H_INCLUDED_ +#define _ATTR_CLNT_H_INCLUDED_ + +/*++ +/* NAME +/* attr_clnt 3h +/* SUMMARY +/* attribute query-reply client +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +typedef struct ATTR_CLNT ATTR_CLNT; + +extern ATTR_CLNT *attr_clnt_create(const char *, int, int, int); +extern int attr_clnt_request(ATTR_CLNT *, int,...); +extern void attr_clnt_free(ATTR_CLNT *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/util/attr_print0.c b/postfix/src/util/attr_print0.c index c39b72104..5bfa31df1 100644 --- a/postfix/src/util/attr_print0.c +++ b/postfix/src/util/attr_print0.c @@ -6,7 +6,7 @@ /* SYNOPSIS /* #include /* -/* int attr_print0(fp, flags, type, name, ...) +/* int attr_print0(fp, flags, type, name, ..., ATTR_TYPE_END) /* VSTREAM fp; /* int flags; /* int type; @@ -18,9 +18,8 @@ /* va_list ap; /* DESCRIPTION /* attr_print0() takes zero or more (name, value) simple attributes -/* or (name, count, value) list attributes, and converts its input -/* to a byte stream that can be recovered with attr_scan0(). The stream -/* is not flushed. +/* and converts its input to a byte stream that can be recovered with +/* attr_scan0(). The stream is not flushed. /* /* attr_vprint0() provides an alternate interface that is convenient /* for calling from within variadoc functions. @@ -53,8 +52,9 @@ /* This argument is followed by an attribute name and a null-terminated /* string. /* .IP "ATTR_TYPE_HASH (HTABLE *)" -/* The content of the hash table is sent as a sequence of string-valued -/* attributes with names equal to the hash table lookup key. +/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" +/* The content of the table is sent as a sequence of string-valued +/* attributes with names equal to the table lookup keys. /* .IP ATTR_TYPE_END /* This terminates the attribute list. /* .RE diff --git a/postfix/src/util/attr_print64.c b/postfix/src/util/attr_print64.c index 1fc37d7b3..d43d5db78 100644 --- a/postfix/src/util/attr_print64.c +++ b/postfix/src/util/attr_print64.c @@ -6,7 +6,7 @@ /* SYNOPSIS /* #include /* -/* int attr_print64(fp, flags, type, name, ...) +/* int attr_print64(fp, flags, type, name, ..., ATTR_TYPE_END) /* VSTREAM fp; /* int flags; /* int type; @@ -18,9 +18,8 @@ /* va_list ap; /* DESCRIPTION /* attr_print64() takes zero or more (name, value) simple attributes -/* or (name, count, value) list attributes, and converts its input -/* to a byte stream that can be recovered with attr_scan64(). The stream -/* is not flushed. +/* and converts its input to a byte stream that can be recovered with +/* attr_scan64(). The stream is not flushed. /* /* attr_vprint64() provides an alternate interface that is convenient /* for calling from within variadoc functions. @@ -53,8 +52,9 @@ /* This argument is followed by an attribute name and a null-terminated /* string. /* .IP "ATTR_TYPE_HASH (HTABLE *)" -/* The content of the hash table is sent as a sequence of string-valued -/* attributes with names equal to the hash table lookup key. +/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" +/* The content of the table is sent as a sequence of string-valued +/* attributes with names equal to the table lookup keys. /* .IP ATTR_TYPE_END /* This terminates the attribute list. /* .RE @@ -160,6 +160,7 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) int_val = va_arg(ap, int); VSTREAM_PUTC(':', fp); attr_print64_num(fp, (unsigned) int_val); + VSTREAM_PUTC('\n', fp); if (msg_verbose) msg_info("send attr %s = %u", attr_name, int_val); break; @@ -169,6 +170,7 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) long_val = va_arg(ap, long); VSTREAM_PUTC(':', fp); attr_print64_long_num(fp, (unsigned long) long_val); + VSTREAM_PUTC('\n', fp); if (msg_verbose) msg_info("send attr %s = %lu", attr_name, long_val); break; @@ -178,6 +180,7 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) str_val = va_arg(ap, char *); VSTREAM_PUTC(':', fp); attr_print64_str(fp, str_val, strlen(str_val)); + VSTREAM_PUTC('\n', fp); if (msg_verbose) msg_info("send attr %s = %s", attr_name, str_val); break; @@ -187,18 +190,16 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) attr_print64_str(fp, ht[0]->key, strlen(ht[0]->key)); VSTREAM_PUTC(':', fp); attr_print64_str(fp, ht[0]->value, strlen(ht[0]->value)); + VSTREAM_PUTC('\n', fp); if (msg_verbose) msg_info("send attr name %s value %s", ht[0]->key, ht[0]->value); - if (ht[1] != 0) - VSTREAM_PUTC('\n', fp); } myfree((char *) ht_info_list); break; default: msg_panic("%s: unknown type code: %d", myname, attr_type); } - VSTREAM_PUTC('\n', fp); } if ((flags & ATTR_FLAG_MORE) == 0) VSTREAM_PUTC('\n', fp); diff --git a/postfix/src/util/attr_print_plain b/postfix/src/util/attr_print_plain new file mode 100755 index 000000000..b94f004b1 Binary files /dev/null and b/postfix/src/util/attr_print_plain differ diff --git a/postfix/src/util/attr_print_plain.c b/postfix/src/util/attr_print_plain.c new file mode 100644 index 000000000..89054b248 --- /dev/null +++ b/postfix/src/util/attr_print_plain.c @@ -0,0 +1,207 @@ +/*++ +/* NAME +/* attr_print_plain 3 +/* SUMMARY +/* send attributes over byte stream +/* SYNOPSIS +/* #include +/* +/* int attr_print_plain(fp, flags, type, name, ..., ATTR_TYPE_END) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vprint_plain(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_print_plain() takes zero or more (name, value) simple attributes +/* and converts its input to a byte stream that can be recovered with +/* attr_scan_plain(). The stream is not flushed. +/* +/* attr_vprint_plain() provides an alternate interface that is convenient +/* for calling from within variadoc functions. +/* +/* Attributes are sent in the requested order as specified with the +/* attr_print_plain() argument list. This routine satisfies the formatting +/* rules as outlined in attr_scan_plain(3). +/* +/* Arguments: +/* .IP fp +/* Stream to write the result to. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MORE +/* After sending the requested attributes, leave the output stream in +/* a state that is usable for more attribute sending operations on +/* the same output attribute list. +/* By default, attr_print_plain() automatically appends an attribute list +/* terminator when it has sent the last requested attribute. +/* .RE +/* .IP type +/* The type determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int)" +/* This argument is followed by an attribute name and an integer. +/* .IP "ATTR_TYPE_NUM (char *, long)" +/* This argument is followed by an attribute name and a long integer. +/* .IP "ATTR_TYPE_STR (char *, char *)" +/* This argument is followed by an attribute name and a null-terminated +/* string. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" +/* The content of the table is sent as a sequence of string-valued +/* attributes with names equal to the table lookup keys. +/* .IP ATTR_TYPE_END +/* This terminates the attribute list. +/* .RE +/* DIAGNOSTICS +/* The result value is 0 in case of success, VSTREAM_EOF in case +/* of trouble. +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_scan_plain(3) recover attributes from byte stream +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_vprint_plain - send attribute list to stream */ + +int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_print_plain"; + int attr_type; + char *attr_name; + unsigned int_val; + unsigned long long_val; + char *str_val; + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Iterate over all (type, name, value) triples, and produce output on + * the fly. + */ + while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) { + switch (attr_type) { + case ATTR_TYPE_NUM: + attr_name = va_arg(ap, char *); + int_val = va_arg(ap, int); + vstream_fprintf(fp, "%s=%u\n", attr_name, (unsigned) int_val); + if (msg_verbose) + msg_info("send attr %s = %u", attr_name, (unsigned) int_val); + break; + case ATTR_TYPE_LONG: + attr_name = va_arg(ap, char *); + long_val = va_arg(ap, long); + vstream_fprintf(fp, "%s=%lu\n", attr_name, long_val); + if (msg_verbose) + msg_info("send attr %s = %lu", attr_name, long_val); + break; + case ATTR_TYPE_STR: + attr_name = va_arg(ap, char *); + str_val = va_arg(ap, char *); + vstream_fprintf(fp, "%s=%s\n", attr_name, str_val); + if (msg_verbose) + msg_info("send attr %s = %s", attr_name, str_val); + break; + case ATTR_TYPE_HASH: + ht_info_list = htable_list(va_arg(ap, HTABLE *)); + for (ht = ht_info_list; *ht; ht++) { + vstream_fprintf(fp, "%s=%s\n", ht[0]->key, ht[0]->value); + if (msg_verbose) + msg_info("send attr name %s value %s", + ht[0]->key, ht[0]->value); + } + myfree((char *) ht_info_list); + break; + default: + msg_panic("%s: unknown type code: %d", myname, attr_type); + } + } + if ((flags & ATTR_FLAG_MORE) == 0) + VSTREAM_PUTC('\n', fp); + return (vstream_ferror(fp)); +} + +int attr_print_plain(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vprint_plain(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan_plain test + * program. + */ +#include + +int main(int unused_argc, char **argv) +{ + HTABLE *table = htable_create(1); + + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_verbose = 1; + htable_enter(table, "foo-name", mystrdup("foo-value")); + htable_enter(table, "bar-name", mystrdup("bar-value")); + attr_print_plain(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_HASH, table, + ATTR_TYPE_END); + attr_print_plain(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_END); + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + htable_free(table, myfree); + return (0); +} + +#endif diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c index 528c6147a..c95342601 100644 --- a/postfix/src/util/attr_scan0.c +++ b/postfix/src/util/attr_scan0.c @@ -6,7 +6,7 @@ /* SYNOPSIS /* #include /* -/* int attr_scan0(fp, flags, type, name, ...) +/* int attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END) /* VSTREAM fp; /* int flags; /* int type; @@ -41,8 +41,9 @@ /* .in /* /* All attribute names and attribute values are sent as null terminated -/* strings. Each string must be no longer than 2*var_line_limit -/* characters. The formatting rules favor implementations in C. +/* strings. Each string must be no longer than 4*var_line_limit +/* characters including the terminator. +/* These formatting rules favor implementations in C. /* /* Normally, attributes must be received in the sequence as specified with /* the attr_scan0() argument list. The input stream may contain additional @@ -91,10 +92,12 @@ /* .IP "ATTR_TYPE_STR (char *, VSTRING *)" /* This argument is followed by an attribute name and a VSTRING pointer. /* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" /* All further input attributes are processed as string attributes. /* No specific attribute sequence is enforced. /* All attributes up to the attribute list terminator are read, /* but only the first instance of each attribute is stored. +/* There can be no more than 1024 attributes in a hash table. /* .sp /* The attribute string values are stored in the hash table under /* keys equal to the attribute name (obtained from the input stream). @@ -106,9 +109,10 @@ /* This argument terminates the requested attribute list. /* .RE /* BUGS -/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly -/* untrusted sources. This is unsafe, unless the resulting table is -/* queried only with known to be good attribute names. +/* ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary +/* names from possibly untrusted sources. +/* This is unsafe, unless the resulting table is queried only with +/* known to be good attribute names. /* DIAGNOSTICS /* attr_scan0() and attr_vscan0() return -1 when malformed input is /* detected (string too long, incomplete line, missing end marker). @@ -157,10 +161,10 @@ static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) { extern int var_line_limit; /* XXX */ - int limit = var_line_limit * 2; + int limit = var_line_limit * 4; int ch; - if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) { + if ((ch = vstring_get_null_bound(plain_buf, fp, limit)) == VSTREAM_EOF) { msg_warn("%s on %s while reading %s", vstream_ftimeout(fp) ? "timeout" : "premature end-of-input", VSTREAM_PATH(fp), context); @@ -351,6 +355,10 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) STR(name_buf), VSTREAM_PATH(fp)); return (conversions); } + } else if (hash_table->used >= ATTR_HASH_LIMIT) { + msg_warn("attribute count exceeds limit %d in input from %s", + ATTR_HASH_LIMIT, VSTREAM_PATH(fp)); + return (conversions); } else { htable_enter(hash_table, STR(name_buf), mystrdup(STR(str_buf))); diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c index 26e7d04d2..4da29aedd 100644 --- a/postfix/src/util/attr_scan64.c +++ b/postfix/src/util/attr_scan64.c @@ -6,7 +6,7 @@ /* SYNOPSIS /* #include /* -/* int attr_scan64(fp, flags, type, name, ...) +/* int attr_scan64(fp, flags, type, name, ..., ATTR_TYPE_END) /* VSTREAM fp; /* int flags; /* int type; @@ -43,7 +43,7 @@ /* .in /* /* All attribute names and attribute values are sent as base64-encoded -/* strings. Each base64 encoding must be no longer than 2*var_line_limit +/* strings. Each base64 encoding must be no longer than 4*var_line_limit /* characters. The formatting rules aim to make implementations in PERL /* and other languages easy. /* @@ -94,10 +94,12 @@ /* .IP "ATTR_TYPE_STR (char *, VSTRING *)" /* This argument is followed by an attribute name and a VSTRING pointer. /* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" /* All further input attributes are processed as string attributes. /* No specific attribute sequence is enforced. /* All attributes up to the attribute list terminator are read, /* but only the first instance of each attribute is stored. +/* There can be no more than 1024 attributes in a hash table. /* .sp /* The attribute string values are stored in the hash table under /* keys equal to the attribute name (obtained from the input stream). @@ -109,9 +111,10 @@ /* This argument terminates the requested attribute list. /* .RE /* BUGS -/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly -/* untrusted sources. This is unsafe, unless the resulting table is -/* queried only with known to be good attribute names. +/* ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary +/* names from possibly untrusted sources. +/* This is unsafe, unless the resulting table is queried only with +/* known to be good attribute names. /* DIAGNOSTICS /* attr_scan64() and attr_vscan64() return -1 when malformed input is /* detected (string too long, incomplete line, missing end marker). @@ -161,7 +164,7 @@ static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *conte { static VSTRING *base64_buf = 0; extern int var_line_limit; /* XXX */ - int limit = var_line_limit * 2; + int limit = var_line_limit * 4; int ch; if (base64_buf == 0) @@ -176,13 +179,11 @@ static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *conte return (-1); } VSTRING_ADDCH(base64_buf, ch); -#if 0 if (LEN(base64_buf) > limit) { msg_warn("string length > %d characters from %s while reading %s", limit, VSTREAM_PATH(fp), context); return (-1); } -#endif } VSTRING_TERMINATE(base64_buf); if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) { @@ -335,7 +336,7 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) /* * Skip over this attribute. The caller does not ask for it. */ - while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') + while (ch != '\n' && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) /* void */ ; } @@ -416,6 +417,10 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) STR(name_buf), VSTREAM_PATH(fp)); return (conversions); } + } else if (hash_table->used >= ATTR_HASH_LIMIT) { + msg_warn("attribute count exceeds limit %d in input from %s", + ATTR_HASH_LIMIT, VSTREAM_PATH(fp)); + return (conversions); } else { htable_enter(hash_table, STR(name_buf), mystrdup(STR(str_buf))); diff --git a/postfix/src/util/attr_scan_plain b/postfix/src/util/attr_scan_plain new file mode 100755 index 000000000..8293c4c27 Binary files /dev/null and b/postfix/src/util/attr_scan_plain differ diff --git a/postfix/src/util/attr_scan_plain.c b/postfix/src/util/attr_scan_plain.c new file mode 100644 index 000000000..de3c155c6 --- /dev/null +++ b/postfix/src/util/attr_scan_plain.c @@ -0,0 +1,484 @@ +/*++ +/* NAME +/* attr_scan_plain 3 +/* SUMMARY +/* recover attributes from byte stream +/* SYNOPSIS +/* #include +/* +/* int attr_scan_plain(fp, flags, type, name, ..., ATTR_TYPE_END) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vscan_plain(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_scan_plain() takes zero or more (name, value) request attributes +/* and recovers the attribute values from the byte stream that was +/* possibly generated by attr_print_plain(). +/* +/* attr_vscan_plain() provides an alternative interface that is convenient +/* for calling from within a variadic function. +/* +/* The input stream is formatted as follows, where (item)* stands +/* for zero or more instances of the specified item, and where +/* (item1 | item2) stands for choice: +/* +/* .in +5 +/* attr-list :== simple-attr* newline +/* .br +/* simple-attr :== attr-name "=" attr-value newline +/* .br +/* attr-name :== any string without null or "=" or newline. +/* .br +/* attr-value :== any string without null or newline. +/* .br +/* newline :== the ASCII newline character +/* .in +/* +/* All attribute names and attribute values are sent as plain +/* strings. Each string must be no longer than 4*var_line_limit +/* characters. The formatting rules aim to make implementations in PERL +/* and other languages easy. +/* +/* Normally, attributes must be received in the sequence as specified +/* with the attr_scan_plain() argument list. The input stream may +/* contain additional attributes at any point in the input stream, +/* including additional instances of requested attributes. +/* +/* Additional input attributes or input attribute instances are silently +/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified +/* (see below). This allows for some flexibility in the evolution of +/* protocols while still providing the option of being strict where +/* this is desirable. +/* +/* Arguments: +/* .IP fp +/* Stream to recover the input attributes from. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MISSING +/* Log a warning when the input attribute list terminates before all +/* requested attributes are recovered. It is always an error when the +/* input stream ends without the newline attribute list terminator. +/* .IP ATTR_FLAG_EXTRA +/* Log a warning and stop attribute recovery when the input stream +/* contains an attribute that was not requested. This includes the +/* case of additional instances of a requested attribute. +/* .IP ATTR_FLAG_MORE +/* After recovering the requested attributes, leave the input stream +/* in a state that is usable for more attr_scan_plain() operations +/* from the same input attribute list. +/* By default, attr_scan_plain() skips forward past the input attribute +/* list terminator. +/* .IP ATTR_FLAG_STRICT +/* For convenience, this value combines both ATTR_FLAG_MISSING and +/* ATTR_FLAG_EXTRA. +/* .IP ATTR_FLAG_NONE +/* For convenience, this value requests none of the above. +/* .RE +/* .IP type +/* The type argument determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int *)" +/* This argument is followed by an attribute name and an integer pointer. +/* .IP "ATTR_TYPE_LONG (char *, long *)" +/* This argument is followed by an attribute name and a long pointer. +/* .IP "ATTR_TYPE_STR (char *, VSTRING *)" +/* This argument is followed by an attribute name and a VSTRING pointer. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" +/* All further input attributes are processed as string attributes. +/* No specific attribute sequence is enforced. +/* All attributes up to the attribute list terminator are read, +/* but only the first instance of each attribute is stored. +/* There can be no more than 1024 attributes in a hash table. +/* .sp +/* The attribute string values are stored in the hash table under +/* keys equal to the attribute name (obtained from the input stream). +/* Values from the input stream are added to the hash table. Existing +/* hash table entries are not replaced. +/* .sp +/* N.B. This construct must be followed by an ATTR_TYPE_END argument. +/* .IP ATTR_TYPE_END +/* This argument terminates the requested attribute list. +/* .RE +/* BUGS +/* ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary +/* names from possibly untrusted sources. +/* This is unsafe, unless the resulting table is queried only with +/* known to be good attribute names. +/* DIAGNOSTICS +/* attr_scan_plain() and attr_vscan_plain() return -1 when malformed input +/* is detected (string too long, incomplete line, missing end marker). +/* Otherwise, the result value is the number of attributes that were +/* successfully recovered from the input stream (a hash table counts +/* as the number of entries stored into the table). +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_print_plain(3) send attributes over byte stream. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Application specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_scan_plain_string - pull a string from the input stream */ + +static int attr_scan_plain_string(VSTREAM *fp, VSTRING *plain_buf, + int terminator, const char *context) +{ + extern int var_line_limit; /* XXX */ + int limit = var_line_limit * 4; + int ch; + + VSTRING_RESET(plain_buf); + while ((ch = VSTREAM_GETC(fp)) != '\n' + && (terminator == 0 || ch != terminator)) { + if (ch == VSTREAM_EOF) { + msg_warn("%s on %s while reading %s", + vstream_ftimeout(fp) ? "timeout" : "premature end-of-input", + VSTREAM_PATH(fp), context); + return (-1); + } + VSTRING_ADDCH(plain_buf, ch); + if (LEN(plain_buf) > limit) { + msg_warn("string length > %d characters from %s while reading %s", + limit, VSTREAM_PATH(fp), context); + return (-1); + } + } + VSTRING_TERMINATE(plain_buf); + + if (msg_verbose) + msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); + return (ch); +} + +/* attr_scan_plain_number - pull a number from the input stream */ + +static int attr_scan_plain_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf, + int terminator, const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan_plain_string(fp, str_buf, terminator, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_scan_plain_long_number - pull a number from the input stream */ + +static int attr_scan_plain_long_number(VSTREAM *fp, unsigned long *ptr, + VSTRING *str_buf, + int terminator, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan_plain_string(fp, str_buf, terminator, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_vscan_plain - receive attribute list from stream */ + +int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_scan_plain"; + static VSTRING *str_buf = 0; + static VSTRING *name_buf = 0; + int wanted_type = -1; + char *wanted_name; + unsigned int *number; + unsigned long *long_number; + VSTRING *string; + HTABLE *hash_table; + int ch; + int conversions; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Initialize. + */ + if (str_buf == 0) { + str_buf = vstring_alloc(10); + name_buf = vstring_alloc(10); + } + + /* + * Iterate over all (type, name, value) triples. + */ + for (conversions = 0; /* void */ ; conversions++) { + + /* + * Determine the next attribute type and attribute name on the + * caller's wish list. + * + * If we're reading into a hash table, we already know that the + * attribute value is string-valued, and we get the attribute name + * from the input stream instead. This is secure only when the + * resulting table is queried with known to be good attribute names. + */ + if (wanted_type != ATTR_TYPE_HASH) { + wanted_type = va_arg(ap, int); + if (wanted_type == ATTR_TYPE_END) { + if ((flags & ATTR_FLAG_MORE) != 0) + return (conversions); + wanted_name = "(list terminator)"; + } else if (wanted_type == ATTR_TYPE_HASH) { + wanted_name = "(any attribute name or list terminator)"; + hash_table = va_arg(ap, HTABLE *); + if (va_arg(ap, int) !=ATTR_TYPE_END) + msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", + myname); + } else { + wanted_name = va_arg(ap, char *); + } + } + + /* + * Locate the next attribute of interest in the input stream. + */ + for (;;) { + + /* + * Get the name of the next attribute. Hitting EOF is always bad. + * Hitting the end-of-input early is OK if the caller is prepared + * to deal with missing inputs. + */ + if (msg_verbose) + msg_info("%s: wanted attribute: %s", + VSTREAM_PATH(fp), wanted_name); + if ((ch = attr_scan_plain_string(fp, name_buf, '=', + "input attribute name")) == VSTREAM_EOF) + return (-1); + if (ch == '\n' && LEN(name_buf) == 0) { + if (wanted_type == ATTR_TYPE_END + || wanted_type == ATTR_TYPE_HASH) + return (conversions); + if ((flags & ATTR_FLAG_MISSING) != 0) + msg_warn("missing attribute %s in input from %s", + wanted_name, VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * See if the caller asks for this attribute. + */ + if (wanted_type == ATTR_TYPE_HASH + || (wanted_type != ATTR_TYPE_END + && strcmp(wanted_name, STR(name_buf)) == 0)) + break; + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("unexpected attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * Skip over this attribute. The caller does not ask for it. + */ + while (ch != '\n' && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) + /* void */ ; + } + + /* + * Do the requested conversion. If the target attribute is a + * non-array type, disallow sending a multi-valued attribute, and + * disallow sending no value. If the target attribute is an array + * type, allow the sender to send a zero-element array (i.e. no value + * at all). XXX Need to impose a bound on the number of array + * elements. + */ + switch (wanted_type) { + case ATTR_TYPE_NUM: + if (ch != '=') { + msg_warn("missing value for number attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + number = va_arg(ap, unsigned int *); + if ((ch = attr_scan_plain_number(fp, number, str_buf, 0, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_LONG: + if (ch != '=') { + msg_warn("missing value for number attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + long_number = va_arg(ap, unsigned long *); + if ((ch = attr_scan_plain_long_number(fp, long_number, str_buf, + 0, "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_STR: + if (ch != '=') { + msg_warn("missing value for string attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + string = va_arg(ap, VSTRING *); + if ((ch = attr_scan_plain_string(fp, string, 0, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_HASH: + if (ch != '=') { + msg_warn("missing value for string attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + if ((ch = attr_scan_plain_string(fp, str_buf, 0, + "input attribute value")) < 0) + return (-1); + if (htable_locate(hash_table, STR(name_buf)) != 0) { + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("duplicate attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + } else if (hash_table->used >= ATTR_HASH_LIMIT) { + msg_warn("attribute count exceeds limit %d in input from %s", + ATTR_HASH_LIMIT, VSTREAM_PATH(fp)); + return (conversions); + } else { + htable_enter(hash_table, STR(name_buf), + mystrdup(STR(str_buf))); + } + break; + default: + msg_panic("%s: unknown type code: %d", myname, wanted_type); + } + } +} + +/* attr_scan_plain - read attribute list from stream */ + +int attr_scan_plain(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vscan_plain(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan_plain test + * program. + */ +#include + +int var_line_limit = 2048; + +int main(int unused_argc, char **used_argv) +{ + VSTRING *str_val = vstring_alloc(1); + HTABLE *table = htable_create(1); + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + int int_val; + long long_val; + int ret; + + msg_verbose = 1; + msg_vstream_init(used_argv[0], VSTREAM_ERR); + if ((ret = attr_scan_plain(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_HASH, table, + ATTR_TYPE_END)) > 3) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if ((ret = attr_scan_plain(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_END)) == 3) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + vstring_free(str_val); + htable_free(table, myfree); + + return (0); +} + +#endif diff --git a/postfix/src/util/attr_scan_plain.ref b/postfix/src/util/attr_scan_plain.ref new file mode 100644 index 000000000..68aa7d17f --- /dev/null +++ b/postfix/src/util/attr_scan_plain.ref @@ -0,0 +1,46 @@ +./attr_print_plain: send attr number = 4711 +./attr_print_plain: send attr long_number = 1234 +./attr_print_plain: send attr string = whoopee +./attr_print_plain: send attr name foo-name value foo-value +./attr_print_plain: send attr name bar-name value bar-value +./attr_print_plain: send attr number = 4711 +./attr_print_plain: send attr long_number = 1234 +./attr_print_plain: send attr string = whoopee +./attr_scan_plain: unknown_stream: wanted attribute: number +./attr_scan_plain: input attribute name: number +./attr_scan_plain: input attribute value: 4711 +./attr_scan_plain: unknown_stream: wanted attribute: long_number +./attr_scan_plain: input attribute name: long_number +./attr_scan_plain: input attribute value: 1234 +./attr_scan_plain: unknown_stream: wanted attribute: string +./attr_scan_plain: input attribute name: string +./attr_scan_plain: input attribute value: whoopee +./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan_plain: input attribute name: foo-name +./attr_scan_plain: input attribute value: foo-value +./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan_plain: input attribute name: bar-name +./attr_scan_plain: input attribute value: bar-value +./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan_plain: input attribute name: (end) +./attr_scan_plain: unknown_stream: wanted attribute: number +./attr_scan_plain: input attribute name: number +./attr_scan_plain: input attribute value: 4711 +./attr_scan_plain: unknown_stream: wanted attribute: long_number +./attr_scan_plain: input attribute name: long_number +./attr_scan_plain: input attribute value: 1234 +./attr_scan_plain: unknown_stream: wanted attribute: string +./attr_scan_plain: input attribute name: string +./attr_scan_plain: input attribute value: whoopee +./attr_scan_plain: unknown_stream: wanted attribute: (list terminator) +./attr_scan_plain: input attribute name: (end) +number 4711 +long_number 1234 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value +number 4711 +long_number 1234 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value diff --git a/postfix/src/util/auto_clnt.c b/postfix/src/util/auto_clnt.c new file mode 100644 index 000000000..1a487aad1 --- /dev/null +++ b/postfix/src/util/auto_clnt.c @@ -0,0 +1,246 @@ +/*++ +/* NAME +/* auto_clnt 3 +/* SUMMARY +/* client endpoint maintenance +/* SYNOPSIS +/* #include +/* +/* AUTO_CLNT *auto_clnt_create(max_idle, max_ttl, open_action, context) +/* int max_idle; +/* int max_ttl; +/* VSTREAM *(open_action)(void *context) +/* void *context; +/* +/* VSTREAM *auto_clnt_access(auto_clnt) +/* AUTO_CLNT *auto_clnt; +/* +/* void auto_clnt_recover(auto_clnt) +/* AUTO_CLNT *auto_clnt; +/* +/* void auto_clnt_free(auto_clnt) +/* AUTO_CLNT *auto_clnt; +/* DESCRIPTION +/* This module maintains local IPC client endpoints that automatically +/* disconnect after a being idle for a configurable amount of time, +/* that disconnect after a configurable time to live, +/* and that transparently handle most server-initiated disconnects. +/* Server disconnect is detected by read-selecting the client endpoint. +/* The code assumes that the server has disconnected when the endpoint +/* becomes readable. +/* +/* auto_clnt_create() instantiates a client endpoint. +/* +/* auto_clnt_access() returns an open stream to the service specified +/* to auto_clnt_create(). The stream instance may change between calls. +/* The result is a null pointer in case of failure. +/* +/* auto_clnt_recover() recovers from a server-initiated disconnect +/* that happened in the middle of an I/O operation. +/* +/* auto_clnt_free() destroys of the specified client endpoint. +/* +/* Arguments: +/* .IP max_idle +/* Idle time after which the client disconnects. +/* .IP max_ttl +/* Upper bound on the time that a connection is allowed to persist. +/* .IP open_action +/* Application call-back routine that opens a stream or returns a +/* null pointer upon failure. In case of success, the call-back routine +/* is expected to set the stream pathname to the server endpoint name. +/* .IP context +/* Application context that is passed to the open_action routine. +/* DIAGNOSTICS +/* Warnings: communication failure. Fatal error: out of memory. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + + /* + * AUTO_CLNT is an opaque structure. None of the access methods can easily + * be implemented as a macro, and access is not performance critical anyway. + */ +struct AUTO_CLNT { + VSTREAM *vstream; /* buffered I/O */ + int max_idle; /* time before client disconnect */ + int max_ttl; /* time before client disconnect */ + VSTREAM *(*open_action) (void *); /* callback */ + void *context; /* callback context */ +}; + +static void auto_clnt_close(AUTO_CLNT *); + +/* auto_clnt_event - server-initiated disconnect or client-side max_idle */ + +static void auto_clnt_event(int unused_event, char *context) +{ + AUTO_CLNT *auto_clnt = (AUTO_CLNT *) context; + + /* + * Sanity check. This routine causes the stream to be closed, so it + * cannot be called when the stream is already closed. + */ + if (auto_clnt->vstream == 0) + msg_panic("auto_clnt_event: stream is closed"); + + auto_clnt_close(auto_clnt); +} + +/* auto_clnt_ttl_event - client-side expiration */ + +static void auto_clnt_ttl_event(int event, char *context) +{ + + /* + * XXX This function is needed only because event_request_timer() cannot + * distinguish between requests that specify the same call-back routine + * and call-back context. The fix is obvious: specify a request ID along + * with the call-back routine, but there is too much code that would have + * to be changed. + * + * XXX Should we be concerned that an overly agressive optimizer will + * eliminate this function and replace calls to auto_clnt_ttl_event() by + * direct calls to auto_clnt_event()? It should not, because there exists + * code that takes the address of both functions. + */ + auto_clnt_event(event, context); +} + +/* auto_clnt_open - connect to service */ + +static void auto_clnt_open(AUTO_CLNT *auto_clnt) +{ + + /* + * Sanity check. + */ + if (auto_clnt->vstream) + msg_panic("auto_clnt_open: stream is open"); + + /* + * Schedule a read event so that we can clean up when the remote side + * disconnects, and schedule a timer event so that we can cleanup an idle + * connection. Note that both events are handled by the same routine. + * + * Finally, schedule an event to force disconnection even when the + * connection is not idle. This is to prevent one client from clinging on + * to a server forever. + */ + auto_clnt->vstream = + auto_clnt->open_action(auto_clnt->context); + + if (auto_clnt->vstream != 0) { + close_on_exec(vstream_fileno(auto_clnt->vstream), CLOSE_ON_EXEC); + event_enable_read(vstream_fileno(auto_clnt->vstream), auto_clnt_event, + (char *) auto_clnt); + event_request_timer(auto_clnt_event, (char *) auto_clnt, + auto_clnt->max_idle); + event_request_timer(auto_clnt_ttl_event, (char *) auto_clnt, + auto_clnt->max_ttl); + } +} + +/* auto_clnt_close - disconnect from service */ + +static void auto_clnt_close(AUTO_CLNT *auto_clnt) +{ + + /* + * Sanity check. + */ + if (auto_clnt->vstream == 0) + msg_panic("auto_clnt_close: stream is closed"); + + /* + * Be sure to disable read and timer events. + */ + if (msg_verbose) + msg_info("%s stream disconnect", VSTREAM_PATH(auto_clnt->vstream)); + event_disable_readwrite(vstream_fileno(auto_clnt->vstream)); + event_cancel_timer(auto_clnt_event, (char *) auto_clnt); + event_cancel_timer(auto_clnt_ttl_event, (char *) auto_clnt); + (void) vstream_fclose(auto_clnt->vstream); + auto_clnt->vstream = 0; +} + +/* auto_clnt_recover - recover from server-initiated disconnect */ + +void auto_clnt_recover(AUTO_CLNT *auto_clnt) +{ + + /* + * Clean up. Don't re-connect until the caller needs it. + */ + if (auto_clnt->vstream) + auto_clnt_close(auto_clnt); +} + +/* auto_clnt_access - access a client stream */ + +VSTREAM *auto_clnt_access(AUTO_CLNT *auto_clnt) +{ + + /* + * Open a stream or restart the idle timer. + * + * Important! Do not restart the TTL timer! + */ + if (auto_clnt->vstream == 0) { + auto_clnt_open(auto_clnt); + } else { + event_request_timer(auto_clnt_event, (char *) auto_clnt, + auto_clnt->max_idle); + } + return (auto_clnt->vstream); +} + +/* auto_clnt_create - create client stream connection */ + +AUTO_CLNT *auto_clnt_create(int max_idle, int max_ttl, + VSTREAM *(*open_action) (void *), void *context) +{ + AUTO_CLNT *auto_clnt; + + /* + * Don't open the stream until the caller needs it. + */ + auto_clnt = (AUTO_CLNT *) mymalloc(sizeof(*auto_clnt)); + auto_clnt->vstream = 0; + auto_clnt->max_idle = max_idle; + auto_clnt->max_ttl = max_ttl; + auto_clnt->open_action = open_action; + auto_clnt->context = context; + return (auto_clnt); +} + +/* auto_clnt_free - destroy client stream instance */ + +void auto_clnt_free(AUTO_CLNT *auto_clnt) +{ + if (auto_clnt->vstream) + auto_clnt_close(auto_clnt); + myfree((char *) auto_clnt); +} diff --git a/postfix/src/util/auto_clnt.h b/postfix/src/util/auto_clnt.h new file mode 100644 index 000000000..a4aaec21d --- /dev/null +++ b/postfix/src/util/auto_clnt.h @@ -0,0 +1,40 @@ +#ifndef _AUTO_CLNT_H_INCLUDED_ +#define _AUTO_CLNT_H_INCLUDED_ + +/*++ +/* NAME +/* auto_clnt 3h +/* SUMMARY +/* client endpoint maintenance +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +typedef struct AUTO_CLNT AUTO_CLNT; + +extern AUTO_CLNT *auto_clnt_create(int, int, VSTREAM *(*) (void *), void *); +extern VSTREAM *auto_clnt_access(AUTO_CLNT *); +extern void auto_clnt_recover(AUTO_CLNT *); +extern void auto_clnt_free(AUTO_CLNT *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 9366915c2..fc502d18c 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -44,12 +44,12 @@ /* char *str; /* VSTREAM *stream; /* -/* long vstream_ftell(stream) +/* off_t vstream_ftell(stream) /* VSTREAM *stream; /* -/* long vstream_fseek(stream, offset, whence) +/* off_t vstream_fseek(stream, offset, whence) /* VSTREAM *stream; -/* long offset; +/* off_t offset; /* int whence; /* /* int vstream_fflush(stream) @@ -795,7 +795,7 @@ static int vstream_buf_space(VBUF *bp, int want) /* vstream_fseek - change I/O position */ -long vstream_fseek(VSTREAM *stream, long offset, int whence) +off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence) { char *myname = "vstream_fseek"; VBUF *bp = &stream->buf; @@ -857,7 +857,7 @@ long vstream_fseek(VSTREAM *stream, long offset, int whence) /* vstream_ftell - return file offset */ -long vstream_ftell(VSTREAM *stream) +off_t vstream_ftell(VSTREAM *stream) { VBUF *bp = &stream->buf; @@ -874,7 +874,7 @@ long vstream_ftell(VSTREAM *stream) * the last read, write or seek operation. */ if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) { - if ((stream->offset = lseek(stream->fd, 0L, SEEK_CUR)) < 0) { + if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) { bp->flags |= VSTREAM_FLAG_NSEEK; return (-1); } diff --git a/postfix/src/util/vstream.h b/postfix/src/util/vstream.h index 3ec638295..322cd188e 100644 --- a/postfix/src/util/vstream.h +++ b/postfix/src/util/vstream.h @@ -37,7 +37,7 @@ typedef struct VSTREAM { VSTREAM_FN read_fn; /* buffer fill action */ VSTREAM_FN write_fn; /* buffer fill action */ void *context; /* application context */ - long offset; /* cached seek info */ + off_t offset; /* cached seek info */ char *path; /* give it at least try */ int read_fd; /* read channel (double-buffered) */ int write_fd; /* write channel (double-buffered) */ @@ -72,8 +72,8 @@ extern VSTREAM vstream_fstd[]; /* pre-defined streams */ extern VSTREAM *vstream_fopen(const char *, int, int); extern int vstream_fclose(VSTREAM *); -extern long vstream_fseek(VSTREAM *, long, int); -extern long vstream_ftell(VSTREAM *); +extern off_t vstream_fseek(VSTREAM *, off_t, int); +extern off_t vstream_ftell(VSTREAM *); extern int vstream_fflush(VSTREAM *); extern int vstream_fputs(const char *, VSTREAM *); extern VSTREAM *vstream_fdopen(int, int); diff --git a/postfix/src/util/vstring.c b/postfix/src/util/vstring.c index 0f3e33b5e..43ffae1e2 100644 --- a/postfix/src/util/vstring.c +++ b/postfix/src/util/vstring.c @@ -111,7 +111,7 @@ /* of at least "len" bytes. The minimal length is 1. The result /* is a null-terminated string of length zero. /* -/* vstring_ctl() gives additional control over vstring behavior. +/* vstring_ctl() gives additional control over VSTRING behavior. /* The function takes a VSTRING pointer and a list of zero /* or more (name,value) pairs. The expected value type /* depends on the specified name. The value name codes are: