From: Wietse Venema
The [[:<:]] matches the beginning of a word, -and the [[:>:]] matches the end.
+The "[[:<:]]" and "[[:>:]]" match +the beginning and end of a word, respectively. On some systems you +should specify "\<" and "\>" instead. For +details see your system documentation.
The "\." matches "." literally. Without the "\", the "." would match any character.
@@ -261,7 +263,8 @@ above techniques to recognize forgeries. because there is a lot of variation in report formats. The following is only a small example of message header patterns. For a large collection of header and body patterns that recognize virus -notification email, see http://www.dkuug.dk/keld/virus/. +notification email, see http://www.dkuug.dk/keld/virus/ +or http://www.t29.dk/antiantivirus.txt.diff --git a/postfix/html/INSTALL.html b/postfix/html/INSTALL.html index a45f8745b..0c08aa00a 100644 --- a/postfix/html/INSTALL.html +++ b/postfix/html/INSTALL.html @@ -952,9 +952,23 @@ systems. so that it listens on a socket inside the Postfix queue directory. Examples for specific systems: -FreeBSD: syslogd -l /var/spool/postfix/var/run/log
+-
Linux, OpenBSD: syslogd -a /var/spool/postfix/dev/log
+- FreeBSD:
+ +- + +
+# mkdir -p /var/spool/postfix/var/run +# syslogd -l /var/spool/postfix/var/run/log +- Linux, OpenBSD:
+ +- + +
+# mkdir -p /var/spool/postfix/dev +# syslogd -a /var/spool/postfix/dev/log +12 - Care and feeding of the Postfix system
diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index 6fdb57334..0ca93411d 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -107,9 +107,9 @@ LOCAL(8) LOCAL(8) Mailbox delivery can be delegated to an external command specified with the mailbox_command configuration parame- ter. The command executes with the privileges of the - recipient user (exception: in case of delivery as root, - the command executes with the privileges of - default_privs). + recipient user (exceptions: secondary groups are not + enabled; in case of delivery as root, the command executes + with the privileges of default_privs). Mailbox delivery can be delegated to alternative message transports specified in the master.cf file. The mail- @@ -144,6 +144,24 @@ LOCAL(8) LOCAL(8) ting (alias, forward) forbids command destinations in :include: files. + Optionally, the process working directory is changed to + the path specified with command_execution_directory (Post- + fix 2.2 and later). Failure to change directory causes + mail to be deferred. + + The command_execution_directory parameter value is subject + to interpolation of $user (recipient username), $home + (recipient home directory), $shell (recipient shell), + $recipient (complete recipient address), $extension + (recipient address extension), $domain (recipient domain), + local (entire recipient address localpart) and $recipi- + ent_delimiter. The forms ${name?value} and ${name:value} + expand conditionally to value when $name is (is not) + defined. Characters that may have special meaning to the + shell or file system are replaced by underscores. The + list of acceptable characters is specified with the execu- + tion_directory_expansion_filter configuration parameter. + The command is executed directly where possible. Assis- tance by the shell (/bin/sh on UNIX systems) is used only when the command contains shell magic characters, or when @@ -349,21 +367,27 @@ LOCAL(8) LOCAL(8) Optional catch-all destination for unknown local(8) recipients. + Available in Postfix version 2.2 and later: + + command_execution_directory (empty) + The local(8) delivery agent working directory for + delivery to external command. + MAILBOX LOCKING CONTROLS deliver_lock_attempts (20) The maximal number of attempts to acquire an exclu- sive lock on a mailbox file or bounce(8) logfile. deliver_lock_delay (1s) - The time between attempts to acquire an exclusive + The time between attempts to acquire an exclusive lock on a mailbox file or bounce(8) logfile. stale_lock_time (500s) - The time after which a stale exclusive mailbox + The time after which a stale exclusive mailbox lockfile is removed. mailbox_delivery_lock (see 'postconf -d' output) - How to lock a UNIX-style local(8) mailbox before + How to lock a UNIX-style local(8) mailbox before attempting delivery. RESOURCE AND RATE CONTROLS @@ -371,17 +395,17 @@ LOCAL(8) LOCAL(8) Time limit for delivery to external commands. duplicate_filter_limit (1000) - The maximal number of addresses remembered by the - address duplicate filter for aliases(5) or vir- + The maximal number of addresses remembered by the + address duplicate filter for aliases(5) or vir- tual(5) alias expansion, or for showq(8) queue dis- plays. local_destination_concurrency_limit (2) - The maximal number of parallel deliveries via the + The maximal number of parallel deliveries via the local mail delivery transport to the same recipient - (when "local_destination_recipient_limit = 1") or - the maximal number of parallel deliveries to the - same local domain (when "local_destination_recipi- + (when "local_destination_recipient_limit = 1") or + the maximal number of parallel deliveries to the + same local domain (when "local_destination_recipi- ent_limit > 1"). local_destination_recipient_limit (1) @@ -394,37 +418,45 @@ LOCAL(8) LOCAL(8) SECURITY CONTROLS allow_mail_to_commands (alias, forward) - Restrict local(8) mail delivery to external com- + Restrict local(8) mail delivery to external com- mands. allow_mail_to_files (alias, forward) - Restrict local(8) mail delivery to external files. + Restrict local(8) mail delivery to external files. command_expansion_filter (see 'postconf -d' output) - Restrict the characters that the local(8) delivery - agent allows in $name expansions of $mailbox_com- + Restrict the characters that the local(8) delivery + agent allows in $name expansions of $mailbox_com- mand. default_privs (nobody) - The default rights used by the local(8) delivery + The default rights used by the local(8) delivery agent for delivery to external file or command. forward_expansion_filter (see 'postconf -d' output) - Restrict the characters that the local(8) delivery - agent allows in $name expansions of $forward_path. + Restrict the characters that the local(8) delivery + agent allows in $name expansions of $forward_path. + + Available in Postfix version 2.2 and later: + + execution_directory_expansion_filter (see 'postconf -d' + output) + Restrict the characters that the local(8) delivery + agent allows in $name expansions of $command_execu- + tion_directory. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to - handle a request before it is terminated by a + How much time a Postfix daemon process may take to + handle a request before it is terminated by a built-in watchdog timer. export_environment (see 'postconf -d' output) - The list of environment variables that a Postfix + The list of environment variables that a Postfix process will export to non-Postfix processes. ipc_timeout (3600s) @@ -432,37 +464,37 @@ LOCAL(8) LOCAL(8) over an internal communication channel. local_command_shell (empty) - Optional shell program for local(8) delivery to + Optional shell program for local(8) delivery to non-Postfix command. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for the next service request + The maximum amount of time that an idle Postfix + daemon process waits for the next service request before exiting. max_use (100) - The maximal number of connection requests before a + The maximal number of connection requests before a Postfix daemon process terminates. prepend_delivered_header (command, file, forward) - The message delivery contexts where the Postfix - local(8) delivery agent prepends a Delivered-To: + The message delivery contexts where the Postfix + local(8) delivery agent prepends a Delivered-To: message header. process_id (read-only) - The process ID of a Postfix command or daemon pro- + The process ID of a Postfix command or daemon pro- cess. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. propagate_unmatched_extensions (canonical, virtual) - What address lookup tables copy an address exten- + What address lookup tables copy an address exten- sion from the lookup key to the lookup result. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. recipient_delimiter (empty) @@ -470,14 +502,14 @@ LOCAL(8) LOCAL(8) sions (user+foo). require_home_directory (no) - Whether or not a local(8) recipient's home direc- - tory must exist before mail delivery is attempted. + Whether or not a local(8) recipient's home direc- + tory must exist before mail delivery is attempted. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - The mail system name that is prepended to the pro- + The mail system name that is prepended to the pro- cess name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". @@ -497,14 +529,14 @@ LOCAL(8) LOCAL(8) syslogd(8), system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY The Delivered-To: message header appears in the qmail sys- tem by Daniel Bernstein. - The maildir structure appears in the qmail system by + The maildir structure appears in the qmail system by Daniel Bernstein. AUTHOR(S) diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index c40584f70..6bf9ef687 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -48,70 +48,80 @@ PIPE(8) PIPE(8) file at the end of a service definition. The syntax is as follows: + eol=string (optional, default: \n) + The output record delimiter. Typically one would + use either \r\n or \n. The usual C-style backslash + escape sequences are recognized: \a \b \f \n \r \t + \v \octal and \\. + flags=BDFORhqu.> (optional) - Optional message processing flags. By default, a + Optional message processing flags. By default, a message is copied unchanged. - B Append a blank line at the end of each mes- - sage. This is required by some mail user - agents that recognize "From " lines only + B Append a blank line at the end of each mes- + sage. This is required by some mail user + agents that recognize "From " lines only when preceded by a blank line. - D Prepend a "Delivered-To: recipient" message - header with the envelope recipient address. + D Prepend a "Delivered-To: recipient" message + header with the envelope recipient address. Note: for this to work, the transport_desti- nation_recipient_limit must be 1. This feature is available as of Postfix 2.0. - F Prepend a "From sender time_stamp" envelope - header to the message content. This is + F Prepend a "From sender time_stamp" envelope + header to the message content. This is expected by, for example, UUCP software. - O Prepend an "X-Original-To: recipient" mes- - sage header with the recipient address as - given to Postfix. Note: for this to work, + O Prepend an "X-Original-To: recipient" mes- + sage header with the recipient address as + given to Postfix. Note: for this to work, the transport_destination_recipient_limit must be 1. This feature is available as of Postfix 2.0. - R Prepend a Return-Path: message header with + R Prepend a Return-Path: message header with the envelope sender address. h Fold the command-line $recipient domain name - and $nexthop host name to lower case. This + and $nexthop host name to lower case. This is recommended for delivery via UUCP. - q Quote white space and other special charac- + q Quote white space and other special charac- ters in the command-line $sender and $recip- ient address localparts (text to the left of the right-most @ character), according to an - 8-bit transparent version of RFC 822. This - is recommended for delivery via UUCP or + 8-bit transparent version of RFC 822. This + is recommended for delivery via UUCP or BSMTP. - The result is compatible with the address - parsing of command-line recipients by the + The result is compatible with the address + parsing of command-line recipients by the Postfix sendmail mail submission command. - The q flag affects only entire addresses, + The q flag affects only entire addresses, not the partial address information from the - $user, $extension or $mailbox command-line + $user, $extension or $mailbox command-line macros. u Fold the command-line $recipient address - localpart (text to the left of the right- - most @ character) to lower case. This is + localpart (text to the left of the right- + most @ character) to lower case. This is recommended for delivery via UUCP. - . Prepend . to lines starting with ".". This + . Prepend . to lines starting with ".". This is needed by, for example, BSMTP software. - > Prepend > to lines starting with "From ". + > Prepend > to lines starting with "From ". This is expected by, for example, UUCP soft- ware. + size=size_limit (optional) + Messages greater in size than this limit (in bytes) + will be bounced back to the sender. + user=username (required) user=username:groupname @@ -122,16 +132,6 @@ PIPE(8) PIPE(8) is specified, the corresponding group ID is used instead of the group ID of username. - eol=string (optional, default: \n) - The output record delimiter. Typically one would - use either \r\n or \n. The usual C-style backslash - escape sequences are recognized: \a \b \f \n \r \t - \v \octal and \\. - - size=size_limit (optional) - Messages greater in size than this limit (in bytes) - will be bounced back to the sender. - argv=command... (required) The command to be executed. This must be specified as the last command attribute. The command is exe- @@ -216,6 +216,13 @@ PIPE(8) PIPE(8) $(name) are also recognized. Specify $$ where a single $ is wanted. + Available in Postfix 2.2 and later: + + directory=pathname (optional) + Change to the specified directory before executing + the command. Failure causes mail delivery to be + deferred. + DIAGNOSTICS Command exit status codes are expected to follow the con- ventions defined in <sysexits.h>. diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index ec6ac71ed..e91f74924 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -997,6 +997,72 @@ The location of all postfix administrative commands. + + +command_execution_directory +(default: empty) + + The local(8) delivery agent working directory for delivery to +external command. Failure to change directory causes the delivery +to be deferred.
+ +The following $name expansions are done on command_execution_directory +before the directory is changed. Expansion happens in the context +of the delivery request. The result of $name expansion is filtered +with the character set that is specified with the +execution_directory_expansion_filter parameter.
+ ++ +
+ +- $user
+ +- The recipient's username.
+ +- $shell
+ +- The recipient's login shell pathname.
+ +- $home
+ +- The recipient's home directory.
+ +- $recipient
+ +- The full recipient address.
+ +- $extension
+ +- The optional recipient address extension.
+ +- $domain
+ +- The recipient domain.
+ +- $local
+ +- The entire recipient localpart.
+ +- $recipient_delimiter
+ +- The system-wide recipient address extension delimiter.
+ +- ${name?value}
+ +- Expands to value when $name is non-empty.
+ +- ${name:value}
+ +- Expands to value when $name is empty.
+ ++Instead of $name you can also specify ${name} or $(name). +
+ +This feature is available in Postfix 2.2 and later.
+ +command_expansion_filter @@ -1817,6 +1883,18 @@ This feature is available in Postfix 2.0 and later. + + + execution_directory_expansion_filter +(default: see "postconf -d" output) + + Restrict the characters that the local(8) delivery agent allows +in $name expansions of $command_execution_directory. Characters +outside the allowed set are replaced by underscores.
+ +This feature is available in Postfix 2.2 and later.
+ +expand_owner_alias @@ -2026,10 +2104,6 @@ $name expansions of $forward_path. C allowed set are replaced by underscores. - -Characters outside the allowed set are replaced by underscores. -
- @@ -2040,10 +2114,10 @@ Characters outside the allowed set are replaced by underscores. file with user-specified delivery methods. The first file that is found is used. --The following expansions are done on forward_path before -the search actually happens: -
+The following $name expansions are done on forward_path before +the search actually happens. The result of $name expansion is +filtered with the character set that is specified with the +forward_expansion_filter parameter.
diff --git a/postfix/html/regexp_table.5.html b/postfix/html/regexp_table.5.html index 024f2add1..9dbf93cdb 100644 --- a/postfix/html/regexp_table.5.html +++ b/postfix/html/regexp_table.5.html @@ -68,7 +68,7 @@ REGEXP_TABLE(5) REGEXP_TABLE(5) Each pattern is a POSIX regular expression enclosed by a pair of delimiters. The regular expression syntax is docu- - mented in re_format(7) with 4.4BSD, in regcomp(3C) with + mented in re_format(7) with 4.4BSD, in regex(5) with Solaris, and in regex(7) with Linux. Other systems may use other document names. diff --git a/postfix/html/transport.5.html b/postfix/html/transport.5.html index a6609d7b8..ea57c1e11 100644 --- a/postfix/html/transport.5.html +++ b/postfix/html/transport.5.html @@ -117,73 +117,76 @@ TRANSPORT(5) TRANSPORT(5) $empty_address_recipient@$myhostname (default: mailer-dae- mon@hostname). + Note 3: user@domain or user+extension@domain lookup is + available in Postfix 2.0 and later. + RESULT FORMAT - The lookup result is of the form transport:nexthop. The - transport field specifies a mail delivery transport such - as smtp or local. The nexthop field specifies where and + The lookup result is of the form transport:nexthop. The + transport field specifies a mail delivery transport such + as smtp or local. The nexthop field specifies where and how to deliver mail. - The transport field specifies the name of a mail delivery + The transport field specifies the name of a mail delivery transport (the first name of a mail delivery service entry in the Postfix master.cf file). - The interpretation of the nexthop field is transport - dependent. In the case of SMTP, specify a service on a - non-default port as host:service, and disable MX (mail - exchanger) DNS lookups with [host] or [host]:port. The [] + The interpretation of the nexthop field is transport + dependent. In the case of SMTP, specify a service on a + non-default port as host:service, and disable MX (mail + exchanger) DNS lookups with [host] or [host]:port. The [] form is required when you specify an IP address instead of a hostname. - A null transport and null nexthop result means "do not - change": use the delivery transport and nexthop informa- - tion that would be used when the entire transport table + A null transport and null nexthop result means "do not + change": use the delivery transport and nexthop informa- + tion that would be used when the entire transport table did not exist. - A non-null transport field with a null nexthop field + A non-null transport field with a null nexthop field resets the nexthop information to the recipient domain. - A null transport field with non-null nexthop field does + A null transport field with non-null nexthop field does not modify the transport information. EXAMPLES - In order to deliver internal mail directly, while using a - mail relay for all other mail, specify a null entry for - internal destinations (do not change the delivery trans- - port or the nexthop information) and specify a wildcard + In order to deliver internal mail directly, while using a + mail relay for all other mail, specify a null entry for + internal destinations (do not change the delivery trans- + port or the nexthop information) and specify a wildcard for all other destinations. my.domain : .my.domain : * smtp:outbound-relay.my.domain - In order to send mail for example.com and its subdomains + In order to send mail for example.com and its subdomains via the uucp transport to the UUCP host named example: example.com uucp:example .example.com uucp:example - When no nexthop host name is specified, the destination - domain name is used instead. For example, the following - directs mail for user@example.com via the slow transport - to a mail exchanger for example.com. The slow transport + When no nexthop host name is specified, the destination + domain name is used instead. For example, the following + directs mail for user@example.com via the slow transport + to a mail exchanger for example.com. The slow transport could be configured to run at most one delivery process at a time: example.com slow: When no transport is specified, Postfix uses the transport - that matches the address domain class (see DESCRIPTION - above). The following sends all mail for example.com and + that matches the address domain class (see DESCRIPTION + above). The following sends all mail for example.com and its subdomains to host gateway.example.com: example.com :[gateway.example.com] .example.com :[gateway.example.com] - In the above example, the [] suppress MX lookups. This - prevents mail routing loops when your machine is primary + In the above example, the [] suppress MX lookups. This + prevents mail routing loops when your machine is primary MX host for example.com. - In the case of delivery via SMTP, one may specify host- + In the case of delivery via SMTP, one may specify host- name:service instead of just a host: example.com smtp:bar.example:2025 @@ -195,57 +198,57 @@ TRANSPORT(5) TRANSPORT(5) The error mailer can be used to bounce mail: - .example.com error:mail for *.example.com is not + .example.com error:mail for *.example.com is not deliverable - This causes all mail for user@anything.example.com to be + This causes all mail for user@anything.example.com to be bounced. REGULAR EXPRESSION TABLES - This section describes how the table lookups change when + This section describes how the table lookups change when the table is given in the form of regular expressions. For - a description of regular expression lookup table syntax, + a description of regular expression lookup table syntax, see regexp_table(5) or pcre_table(5). - Each pattern is a regular expression that is applied to - the entire address being looked up. Thus, - some.domain.hierarchy is not looked up via its parent - domains, nor is user+foo@domain looked up as user@domain. + Each pattern is a regular expression that is applied to + the entire address being looked up. Thus, + some.domain.hierarchy is not looked up via its parent + domains, nor is user+foo@domain looked up as user@domain. - Patterns are applied in the order as specified in the - table, until a pattern is found that matches the search + Patterns are applied in the order as specified in the + table, until a pattern is found that matches the search string. - Results are the same as with indexed file lookups, with - the additional feature that parenthesized substrings from + Results are the same as with indexed file lookups, with + the additional feature that parenthesized substrings from the pattern can be interpolated as $1, $2 and so on. TCP-BASED TABLES - This section describes how the table lookups change when + This section describes how the table lookups change when lookups are directed to a TCP-based server. For a descrip- - tion of the TCP client/server lookup protocol, see - tcp_table(5). This feature is not available in Postfix + tion of the TCP client/server lookup protocol, see + tcp_table(5). This feature is not available in Postfix version 2.1. - Each lookup operation uses the entire recipient address - once. Thus, some.domain.hierarchy is not looked up via - its parent domains, nor is user+foo@domain looked up as + Each lookup operation uses the entire recipient address + once. Thus, some.domain.hierarchy is not looked up via + its parent domains, nor is user+foo@domain looked up as user@domain. Results are the same as with indexed file lookups. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant. - The text below provides only a parameter summary. See + The following main.cf parameters are especially relevant. + The text below provides only a parameter summary. See postconf(5) for more details including examples. empty_address_recipient - The address that is looked up instead of the null + The address that is looked up instead of the null sender address. parent_domain_matches_subdomains - List of Postfix features that use domain.tld pat- - terns to match sub.domain.tld (as opposed to + List of Postfix features that use domain.tld pat- + terns to match sub.domain.tld (as opposed to requiring .domain.tld patterns). transport_maps @@ -261,7 +264,7 @@ TRANSPORT(5) TRANSPORT(5) FILTER_README, external content filter 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/postconf.5 b/postfix/man/man5/postconf.5 index 3c0ebc243..a60c2b9b1 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -507,6 +507,40 @@ and virtual(5) aliasing. This feature is available in Postfix 2.0 and later. .SH command_directory (default: see "postconf -d" output) The location of all postfix administrative commands. +.SH command_execution_directory (default: empty) +The local(8) delivery agent working directory for delivery to +external command. Failure to change directory causes the delivery +to be deferred. +.PP +The following $name expansions are done on command_execution_directory +before the directory is changed. Expansion happens in the context +of the delivery request. The result of $name expansion is filtered +with the character set that is specified with the +execution_directory_expansion_filter parameter. +.IP "\fB$user\fR" +The recipient's username. +.IP "\fB$shell\fR" +The recipient's login shell pathname. +.IP "\fB$home\fR" +The recipient's home directory. +.IP "\fB$recipient\fR" +The full recipient address. +.IP "\fB$extension\fR" +The optional recipient address extension. +.IP "\fB$domain\fR" +The recipient domain. +.IP "\fB$local\fR" +The entire recipient localpart. +.IP "\fB$recipient_delimiter\fR" +The system-wide recipient address extension delimiter. +.IP "\fB${name?value}\fR" +Expands to \fIvalue\fR when \fI$name\fR is non-empty. +.IP "\fB${name:value}\fR" +Expands to \fIvalue\fR when \fI$name\fR is empty. +.PP +Instead of $name you can also specify ${name} or $(name). +.PP +This feature is available in Postfix 2.2 and later. .SH command_expansion_filter (default: see "postconf -d" output) Restrict the characters that the local(8) delivery agent allows in $name expansions of $mailbox_command. Characters outside the @@ -920,6 +954,12 @@ The name of the error(8) pseudo delivery agent. This service always returns mail as undeliverable. .PP This feature is available in Postfix 2.0 and later. +.SH execution_directory_expansion_filter (default: see "postconf -d" output) +Restrict the characters that the local(8) delivery agent allows +in $name expansions of $command_execution_directory. Characters +outside the allowed set are replaced by underscores. +.PP +This feature is available in Postfix 2.2 and later. .SH expand_owner_alias (default: no) When delivering to an alias "aliasname" that has an "owner-aliasname" companion alias, set the envelope sender address to the expansion @@ -1015,15 +1055,15 @@ Time units: s (seconds), m (minutes), h (hours), d (days), w Restrict the characters that the local(8) delivery agent allows in $name expansions of $forward_path. Characters outside the allowed set are replaced by underscores. -.PP -Characters outside the allowed set are replaced by underscores. .SH forward_path (default: see "postconf -d" output) The local(8) delivery agent search list for finding a .forward file with user-specified delivery methods. The first file that is found is used. .PP -The following expansions are done on forward_path before -the search actually happens: +The following $name expansions are done on forward_path before +the search actually happens. The result of $name expansion is +filtered with the character set that is specified with the +forward_expansion_filter parameter. .IP "\fB$user\fR" The recipient's username. .IP "\fB$shell\fR" diff --git a/postfix/man/man5/regexp_table.5 b/postfix/man/man5/regexp_table.5 index 4f7907d9d..cb43347e7 100644 --- a/postfix/man/man5/regexp_table.5 +++ b/postfix/man/man5/regexp_table.5 @@ -62,7 +62,7 @@ starts with whitespace continues a logical line. .PP Each pattern is a POSIX regular expression enclosed by a pair of delimiters. The regular expression syntax is documented in -re_format(7) with 4.4BSD, in regcomp(3C) with Solaris, and in +re_format(7) with 4.4BSD, in regex(5) with Solaris, and in regex(7) with Linux. Other systems may use other document names. The expression delimiter can be any character, except whitespace diff --git a/postfix/man/man5/transport.5 b/postfix/man/man5/transport.5 index 094a75bed..9a6ba14dd 100644 --- a/postfix/man/man5/transport.5 +++ b/postfix/man/man5/transport.5 @@ -104,6 +104,9 @@ functions as the wild-card pattern). Note 2: the null recipient address is looked up as \fB$empty_address_recipient\fR@\fB$myhostname\fR (default: mailer-daemon@hostname). + +Note 3: \fIuser@domain\fR or \fIuser+extension@domain\fR +lookup is available in Postfix 2.0 and later. .SH "RESULT FORMAT" .na .nf diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index a01ccce70..a7c20e364 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -114,9 +114,9 @@ ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR delivery. Mailbox delivery can be delegated to an external command specified with the \fBmailbox_command\fR configuration parameter. The command -executes with the privileges of the recipient user (exception: in -case of delivery as root, the command executes with the privileges -of \fBdefault_privs\fR). +executes with the privileges of the recipient user (exceptions: +secondary groups are not enabled; in case of delivery as root, +the command executes with the privileges of \fBdefault_privs\fR). Mailbox delivery can be delegated to alternative message transports specified in the \fBmaster.cf\fR file. @@ -156,6 +156,25 @@ The \fBallow_mail_to_commands\fR configuration parameter restricts delivery to external commands. The default setting (\fBalias, forward\fR) forbids command destinations in \fB:include:\fR files. +Optionally, the process working directory is changed to the path +specified with \fBcommand_execution_directory\fR (Postfix 2.2 and +later). Failure to change directory causes mail to be deferred. + +The \fBcommand_execution_directory\fR parameter value is subject +to interpolation of \fB$user\fR (recipient username), +\fB$home\fR (recipient home directory), \fB$shell\fR +(recipient shell), \fB$recipient\fR (complete recipient +address), \fB$extension\fR (recipient address extension), +\fB$domain\fR (recipient domain), \fBlocal\fR (entire +recipient address localpart) and \fB$recipient_delimiter.\fR +The forms \fI${name?value}\fR and \fI${name:value}\fR expand +conditionally to \fIvalue\fR when \fI$name\fR is (is not) +defined. Characters that may have special meaning to the +shell or file system are replaced by underscores. The list +of acceptable characters is specified with the +\fBexecution_directory_expansion_filter\fR configuration +parameter. + The command is executed directly where possible. Assistance by the shell (\fB/bin/sh\fR on UNIX systems) is used only when the command contains shell magic characters, or when the command invokes a shell @@ -361,6 +380,11 @@ agent should use for names that are not found in the aliases(5) database or in the UNIX passwd database. .IP "\fBluser_relay (empty)\fR" Optional catch-all destination for unknown local(8) recipients. +.PP +Available in Postfix version 2.2 and later: +.IP "\fBcommand_execution_directory (empty)\fR" +The local(8) delivery agent working directory for delivery to +external command. .SH "MAILBOX LOCKING CONTROLS" .na .nf @@ -417,6 +441,11 @@ to external file or command. .IP "\fBforward_expansion_filter (see 'postconf -d' output)\fR" Restrict the characters that the local(8) delivery agent allows in $name expansions of $forward_path. +.PP +Available in Postfix version 2.2 and later: +.IP "\fBexecution_directory_expansion_filter (see 'postconf -d' output)\fR" +Restrict the characters that the local(8) delivery agent allows +in $name expansions of $command_execution_directory. .SH "MISCELLANEOUS CONTROLS" .na .nf diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8 index ebb14d046..8dc1369df 100644 --- a/postfix/man/man8/pipe.8 +++ b/postfix/man/man8/pipe.8 @@ -51,6 +51,11 @@ entry for the pipe-based delivery transport. .fi The external command attributes are given in the \fBmaster.cf\fR file at the end of a service definition. The syntax is as follows: +.IP "\fBeol=string\fR (optional, default: \fB\en\fR)" +The output record delimiter. Typically one would use either +\fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape +sequences are recognized: \fB\ea \eb \ef \en \er \et \ev +\e\fIoctal\fR and \fB\e\e\fR. .IP "\fBflags=BDFORhqu.>\fR (optional)" Optional message processing flags. By default, a message is copied unchanged. @@ -106,6 +111,9 @@ by, for example, \fBBSMTP\fR software. Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected by, for example, \fBUUCP\fR software. .RE +.IP "\fBsize\fR=\fIsize_limit\fR (optional)" +Messages greater in size than this limit (in bytes) will be bounced +back to the sender. .IP "\fBuser\fR=\fIusername\fR (required)" .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR" The external command is executed with the rights of the @@ -114,14 +122,6 @@ commands with root privileges, or with the privileges of the mail system owner. If \fIgroupname\fR is specified, the corresponding group ID is used instead of the group ID of \fIusername\fR. -.IP "\fBeol=string\fR (optional, default: \fB\en\fR)" -The output record delimiter. Typically one would use either -\fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape -sequences are recognized: \fB\ea \eb \ef \en \er \et \ev -\e\fIoctal\fR and \fB\e\e\fR. -.IP "\fBsize\fR=\fIsize_limit\fR (optional)" -Messages greater in size than this limit (in bytes) will be bounced -back to the sender. .IP "\fBargv\fR=\fIcommand\fR... (required)" The command to be executed. This must be specified as the last command attribute. @@ -183,6 +183,11 @@ This information is modified by the \fBu\fR flag for case folding. In addition to the form ${\fIname\fR}, the forms $\fIname\fR and $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single \fB$\fR is wanted. +.PP +Available in Postfix 2.2 and later: +.IP "\fBdirectory=\fIpathname\fR (optional)" +Change to the specified directory before executing the command. +Failure causes mail delivery to be deferred. .SH DIAGNOSTICS .ad .fi diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 785c0e2b8..bcbb1f05b 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -106,6 +106,8 @@ while (<>) { s;\bbroken_sasl_auth_clients\b;$&;g; s;\bcanonical_maps\b;$&;g; s;\bcleanup_service_name\b;$&;g; + s;\bcommand_execu[-]*\n* *[
]*tion_direc[- ]*\n* *[]*tory\b;$&;g; + s;\bexecu[- ]*\n* *[]*tion_directory_expansion_filter\b;$&;g; s;\banvil_status_update_time\b;$&;g; s;\bcommand_directory\b;$&;g; s;\bcommand_expan[- ]*\n* *[]*sion_filter\b;$&;g; diff --git a/postfix/mantools/xpostdef b/postfix/mantools/xpostdef index 77fd202bf..053610734 100755 --- a/postfix/mantools/xpostdef +++ b/postfix/mantools/xpostdef @@ -36,6 +36,7 @@ config_directory daemon_directory default_database_type default_rbl_reply +execution_directory_expansion_filter export_environment forward_expansion_filter forward_path diff --git a/postfix/proto/BACKSCATTER_README.html b/postfix/proto/BACKSCATTER_README.html index 15f7dadfb..069bf60e1 100644 --- a/postfix/proto/BACKSCATTER_README.html +++ b/postfix/proto/BACKSCATTER_README.html @@ -229,8 +229,10 @@ and is very easy to stop. reality, my patterns list multiple email addresses as "(user1@domain1\.tld|user2@domain2\.tld)". - The [[:<:]] matches the beginning of a word, -and the [[:>:]] matches the end.
+The "[[:<:]]" and "[[:>:]]" match +the beginning and end of a word, respectively. On some systems you +should specify "\<" and "\>" instead. For +details see your system documentation.
The "\." matches "." literally. Without the "\", the "." would match any character.
@@ -261,7 +263,8 @@ above techniques to recognize forgeries. because there is a lot of variation in report formats. The following is only a small example of message header patterns. For a large collection of header and body patterns that recognize virus -notification email, see http://www.dkuug.dk/keld/virus/. +notification email, see http://www.dkuug.dk/keld/virus/ +or http://www.t29.dk/antiantivirus.txt.diff --git a/postfix/proto/INSTALL.html b/postfix/proto/INSTALL.html index da821a27b..40fb4bf56 100644 --- a/postfix/proto/INSTALL.html +++ b/postfix/proto/INSTALL.html @@ -952,9 +952,23 @@ systems. so that it listens on a socket inside the Postfix queue directory. Examples for specific systems: -FreeBSD: syslogd -l /var/spool/postfix/var/run/log
+-
Linux, OpenBSD: syslogd -a /var/spool/postfix/dev/log
+- FreeBSD:
+ +- + +
+# mkdir -p /var/spool/postfix/var/run +# syslogd -l /var/spool/postfix/var/run/log +- Linux, OpenBSD:
+ +- + +
+# mkdir -p /var/spool/postfix/dev +# syslogd -a /var/spool/postfix/dev/log +12 - Care and feeding of the Postfix system
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index fa132d941..80dfd2a39 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -1189,16 +1189,86 @@ d=days, w=weeks. The default time unit is hours.Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks). The default time unit is s (seconds).
+%PARAM execution_directory_expansion_filter see "postconf -d" output + +Restrict the characters that the local(8) delivery agent allows +in $name expansions of $command_execution_directory. Characters +outside the allowed set are replaced by underscores.
+ +This feature is available in Postfix 2.2 and later.
+ +%PARAM command_execution_directory + +The local(8) delivery agent working directory for delivery to +external command. Failure to change directory causes the delivery +to be deferred.
+ +The following $name expansions are done on command_execution_directory +before the directory is changed. Expansion happens in the context +of the delivery request. The result of $name expansion is filtered +with the character set that is specified with the +execution_directory_expansion_filter parameter.
+ ++ +
+ +- $user
+ +- The recipient's username.
+ +- $shell
+ +- The recipient's login shell pathname.
+ +- $home
+ +- The recipient's home directory.
+ +- $recipient
+ +- The full recipient address.
+ +- $extension
+ +- The optional recipient address extension.
+ +- $domain
+ +- The recipient domain.
+ +- $local
+ +- The entire recipient localpart.
+ +- $recipient_delimiter
+ +- The system-wide recipient address extension delimiter.
+ +- ${name?value}
+ +- Expands to value when $name is non-empty.
+ +- ${name:value}
+ +- Expands to value when $name is empty.
+ ++Instead of $name you can also specify ${name} or $(name). +
+ +This feature is available in Postfix 2.2 and later.
+ %PARAM forward_path see "postconf -d" outputThe local(8) delivery agent search list for finding a .forward file with user-specified delivery methods. The first file that is found is used.
--The following expansions are done on forward_path before -the search actually happens: -
+The following $name expansions are done on forward_path before +the search actually happens. The result of $name expansion is +filtered with the character set that is specified with the +forward_expansion_filter parameter.
@@ -5648,10 +5718,6 @@ $name expansions of $forward_path. Characters outside the allowed set are replaced by underscores. -
-Characters outside the allowed set are replaced by underscores. -
- %PARAM header_address_token_limit 10240diff --git a/postfix/proto/regexp_table b/postfix/proto/regexp_table index 92c8d7820..3dcd43b26 100644 --- a/postfix/proto/regexp_table +++ b/postfix/proto/regexp_table @@ -54,7 +54,7 @@ # .PP # Each pattern is a POSIX regular expression enclosed by a pair of # delimiters. The regular expression syntax is documented in -# re_format(7) with 4.4BSD, in regcomp(3C) with Solaris, and in +# re_format(7) with 4.4BSD, in regex(5) with Solaris, and in # regex(7) with Linux. Other systems may use other document names. # # The expression delimiter can be any character, except whitespace diff --git a/postfix/proto/transport b/postfix/proto/transport index 23c956421..c16d0a878 100644 --- a/postfix/proto/transport +++ b/postfix/proto/transport @@ -94,6 +94,9 @@ # Note 2: the null recipient address is looked up as # \fB$empty_address_recipient\fR@\fB$myhostname\fR (default: # mailer-daemon@hostname). +# +# Note 3: \fIuser@domain\fR or \fIuser+extension@domain\fR +# lookup is available in Postfix 2.0 and later. # RESULT FORMAT # .ad # .fi diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index afcfac163..b3f91f51d 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -448,6 +448,19 @@ extern char *var_fallback_transport; #define DEF_FORWARD_PATH "$home/.forward${recipient_delimiter}${extension}, $home/.forward" extern char *var_forward_path; + /* + * Local delivery: external command execution directory. + */ +#define VAR_EXEC_DIRECTORY "command_execution_directory" +#define DEF_EXEC_DIRECTORY "" +extern char *var_exec_directory; + +#define VAR_EXEC_EXP_FILTER "execution_directory_expansion_filter" +#define DEF_EXEC_EXP_FILTER "1234567890!@%-_=+:,./\ +abcdefghijklmnopqrstuvwxyz\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ" +extern char *var_exec_exp_filter; + /* * Mailbox locking. DEF_MAILBOX_LOCK is defined in sys_defs.h. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 891c2fca5..23b742184 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only. */ -#define MAIL_RELEASE_DATE "20040616" +#define MAIL_RELEASE_DATE "20040621" #define MAIL_VERSION_NUMBER "2.2" #define VAR_MAIL_VERSION "mail_version" diff --git a/postfix/src/global/pipe_command.c b/postfix/src/global/pipe_command.c index 7cbd9795c..e091b6e77 100644 --- a/postfix/src/global/pipe_command.c +++ b/postfix/src/global/pipe_command.c @@ -38,6 +38,10 @@ /* The command is specified as an argument vector. This vector is /* passed without further inspection to the \fIexecvp\fR() routine. /* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified. +/* .IP "PIPE_CMD_CWD (char *)" +/* Working directory for command execution. A null pointer means +/* don't change directory anyway. Failure to change directory +/* causes mail delivery to be deferred. /* .IP "PIPE_CMD_ENV (char **)" /* Additional environment information, in the form of a null-terminated /* list of name, value, name, value, ... elements. By default only the @@ -159,6 +163,7 @@ struct pipe_args { char **env; /* extra environment */ char **export; /* exportable environment */ char *shell; /* command shell */ + char *cwd; /* preferred working directory */ }; static int pipe_command_timeout; /* command has timed out */ @@ -186,6 +191,7 @@ static void get_pipe_args(struct pipe_args * args, va_list ap) args->env = 0; args->export = 0; args->shell = 0; + args->cwd = 0; pipe_command_maxtime = var_command_maxtime; @@ -237,6 +243,9 @@ static void get_pipe_args(struct pipe_args * args, va_list ap) case PIPE_CMD_SHELL: args->shell = va_arg(ap, char *); break; + case PIPE_CMD_CWD: + args->cwd = va_arg(ap, char *); + break; default: msg_panic("%s: unknown key: %d", myname, key); } @@ -428,6 +437,16 @@ int pipe_command(VSTREAM *src, VSTRING *why,...) close(cmd_in_pipe[0]); close(cmd_out_pipe[1]); + /* + * Working directory plumbing. + */ + if (args.cwd && chdir(args.cwd) < 0) { + msg_warn("cannot change directory to \"%s\" for uid=%lu gid=%lu: %m", + args.cwd, (unsigned long) args.uid, + (unsigned long) args.gid); + exit(EX_TEMPFAIL); + } + /* * Environment plumbing. Always reset the command search path. XXX * That should probably be done by clean_env(). diff --git a/postfix/src/global/pipe_command.h b/postfix/src/global/pipe_command.h index 9a36832cb..a500e8440 100644 --- a/postfix/src/global/pipe_command.h +++ b/postfix/src/global/pipe_command.h @@ -39,6 +39,7 @@ #define PIPE_CMD_EOL 11 /* record delimiter */ #define PIPE_CMD_EXPORT 12 /* exportable environment */ #define PIPE_CMD_ORIG_RCPT 13 /* mail_copy() original recipient */ +#define PIPE_CMD_CWD 14 /* working directory */ /* * Command completion status. diff --git a/postfix/src/local/command.c b/postfix/src/local/command.c index 50f8f9d85..6bb68db8b 100644 --- a/postfix/src/local/command.c +++ b/postfix/src/local/command.c @@ -62,6 +62,7 @@ #include
#include #include +#include /* Global library. */ @@ -90,6 +91,8 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma char **cpp; char *cp; ARGV *export_env; + VSTRING *exec_dir; + int expand_status; /* * Make verbose logging easier to understand. @@ -168,22 +171,38 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma for (cp = cpp[1]; *(cp += strspn(cp, var_cmd_exp_filter)) != 0;) *cp++ = '_'; + /* + * Evaluate the command execution directory. Defer delivery if expansion + * fails. + */ export_env = argv_split(var_export_environ, ", \t\r\n"); - - cmd_status = pipe_command(state.msg_attr.fp, why, - PIPE_CMD_UID, usr_attr.uid, - PIPE_CMD_GID, usr_attr.gid, - PIPE_CMD_COMMAND, command, - PIPE_CMD_COPY_FLAGS, copy_flags, - PIPE_CMD_SENDER, state.msg_attr.sender, - PIPE_CMD_ORIG_RCPT, state.msg_attr.orig_rcpt, - PIPE_CMD_DELIVERED, state.msg_attr.delivered, - PIPE_CMD_TIME_LIMIT, var_command_maxtime, - PIPE_CMD_ENV, env->argv, - PIPE_CMD_EXPORT, export_env->argv, - PIPE_CMD_SHELL, var_local_cmd_shell, - PIPE_CMD_END); - + exec_dir = vstring_alloc(10); + expand_status = local_expand(exec_dir, var_exec_directory, + &state, &usr_attr, var_exec_exp_filter); + + if (expand_status & MAC_PARSE_ERROR) { + cmd_status = PIPE_STAT_DEFER; + vstring_strcpy(why, "Server configuration error"); + msg_warn("bad parameter value syntax for %s: %s", + VAR_EXEC_DIRECTORY, var_exec_directory); + } else { + cmd_status = pipe_command(state.msg_attr.fp, why, + PIPE_CMD_UID, usr_attr.uid, + PIPE_CMD_GID, usr_attr.gid, + PIPE_CMD_COMMAND, command, + PIPE_CMD_COPY_FLAGS, copy_flags, + PIPE_CMD_SENDER, state.msg_attr.sender, + PIPE_CMD_ORIG_RCPT, state.msg_attr.orig_rcpt, + PIPE_CMD_DELIVERED, state.msg_attr.delivered, + PIPE_CMD_TIME_LIMIT, var_command_maxtime, + PIPE_CMD_ENV, env->argv, + PIPE_CMD_EXPORT, export_env->argv, + PIPE_CMD_SHELL, var_local_cmd_shell, + PIPE_CMD_CWD, *vstring_str(exec_dir) ? + vstring_str(exec_dir) : (char *) 0, + PIPE_CMD_END); + } + vstring_free(exec_dir); argv_free(export_env); argv_free(env); diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c index 69eb905a8..4fb88a99d 100644 --- a/postfix/src/local/local.c +++ b/postfix/src/local/local.c @@ -102,9 +102,9 @@ /* /* Mailbox delivery can be delegated to an external command specified /* with the \fBmailbox_command\fR configuration parameter. The command -/* executes with the privileges of the recipient user (exception: in -/* case of delivery as root, the command executes with the privileges -/* of \fBdefault_privs\fR). +/* executes with the privileges of the recipient user (exceptions: +/* secondary groups are not enabled; in case of delivery as root, +/* the command executes with the privileges of \fBdefault_privs\fR). /* /* Mailbox delivery can be delegated to alternative message transports /* specified in the \fBmaster.cf\fR file. @@ -142,6 +142,25 @@ /* delivery to external commands. The default setting (\fBalias, /* forward\fR) forbids command destinations in \fB:include:\fR files. /* +/* Optionally, the process working directory is changed to the path +/* specified with \fBcommand_execution_directory\fR (Postfix 2.2 and +/* later). Failure to change directory causes mail to be deferred. +/* +/* The \fBcommand_execution_directory\fR parameter value is subject +/* to interpolation of \fB$user\fR (recipient username), +/* \fB$home\fR (recipient home directory), \fB$shell\fR +/* (recipient shell), \fB$recipient\fR (complete recipient +/* address), \fB$extension\fR (recipient address extension), +/* \fB$domain\fR (recipient domain), \fBlocal\fR (entire +/* recipient address localpart) and \fB$recipient_delimiter.\fR +/* The forms \fI${name?value}\fR and \fI${name:value}\fR expand +/* conditionally to \fIvalue\fR when \fI$name\fR is (is not) +/* defined. Characters that may have special meaning to the +/* shell or file system are replaced by underscores. The list +/* of acceptable characters is specified with the +/* \fBexecution_directory_expansion_filter\fR configuration +/* parameter. +/* /* The command is executed directly where possible. Assistance by the /* shell (\fB/bin/sh\fR on UNIX systems) is used only when the command /* contains shell magic characters, or when the command invokes a shell @@ -329,6 +348,11 @@ /* database or in the UNIX passwd database. /* .IP "\fBluser_relay (empty)\fR" /* Optional catch-all destination for unknown local(8) recipients. +/* .PP +/* Available in Postfix version 2.2 and later: +/* .IP "\fBcommand_execution_directory (empty)\fR" +/* The local(8) delivery agent working directory for delivery to +/* external command. /* MAILBOX LOCKING CONTROLS /* .ad /* .fi @@ -379,6 +403,11 @@ /* .IP "\fBforward_expansion_filter (see 'postconf -d' output)\fR" /* Restrict the characters that the local(8) delivery agent allows in /* $name expansions of $forward_path. +/* .PP +/* Available in Postfix version 2.2 and later: +/* .IP "\fBexecution_directory_expansion_filter (see 'postconf -d' output)\fR" +/* Restrict the characters that the local(8) delivery agent allows +/* in $name expansions of $command_execution_directory. /* MISCELLANEOUS CONTROLS /* .ad /* .fi @@ -519,6 +548,8 @@ int var_biff; char *var_mail_spool_dir; char *var_mailbox_transport; char *var_fallback_transport; +char *var_exec_directory; +char *var_exec_exp_filter; char *var_forward_path; char *var_cmd_exp_filter; char *var_fwd_exp_filter; @@ -755,6 +786,7 @@ int main(int argc, char **argv) VAR_FALLBACK_TRANSP, DEF_FALLBACK_TRANSP, &var_fallback_transport, 0, 0, VAR_CMD_EXP_FILTER, DEF_CMD_EXP_FILTER, &var_cmd_exp_filter, 1, 0, VAR_FWD_EXP_FILTER, DEF_FWD_EXP_FILTER, &var_fwd_exp_filter, 1, 0, + VAR_EXEC_EXP_FILTER, DEF_EXEC_EXP_FILTER, &var_exec_exp_filter, 1, 0, VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0, VAR_DELIVER_HDR, DEF_DELIVER_HDR, &var_deliver_hdr, 0, 0, VAR_MAILBOX_LOCK, DEF_MAILBOX_LOCK, &var_mailbox_lock, 1, 0, @@ -770,6 +802,7 @@ int main(int argc, char **argv) /* Suppress $name expansion upon loading. */ static CONFIG_RAW_TABLE raw_table[] = { + VAR_EXEC_DIRECTORY, DEF_EXEC_DIRECTORY, &var_exec_directory, 0, 0, VAR_FORWARD_PATH, DEF_FORWARD_PATH, &var_forward_path, 0, 0, VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0, VAR_MAILBOX_CMD_MAPS, DEF_MAILBOX_CMD_MAPS, &var_mailbox_cmd_maps, 0, 0, diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index 20491d377..dcb52da65 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -41,6 +41,11 @@ /* .fi /* The external command attributes are given in the \fBmaster.cf\fR /* file at the end of a service definition. The syntax is as follows: +/* .IP "\fBeol=string\fR (optional, default: \fB\en\fR)" +/* The output record delimiter. Typically one would use either +/* \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape +/* sequences are recognized: \fB\ea \eb \ef \en \er \et \ev +/* \e\fIoctal\fR and \fB\e\e\fR. /* .IP "\fBflags=BDFORhqu.>\fR (optional)" /* Optional message processing flags. By default, a message is /* copied unchanged. @@ -96,6 +101,9 @@ /* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected /* by, for example, \fBUUCP\fR software. /* .RE +/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)" +/* Messages greater in size than this limit (in bytes) will be bounced +/* back to the sender. /* .IP "\fBuser\fR=\fIusername\fR (required)" /* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR" /* The external command is executed with the rights of the @@ -104,14 +112,6 @@ /* mail system owner. If \fIgroupname\fR is specified, the /* corresponding group ID is used instead of the group ID of /* \fIusername\fR. -/* .IP "\fBeol=string\fR (optional, default: \fB\en\fR)" -/* The output record delimiter. Typically one would use either -/* \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape -/* sequences are recognized: \fB\ea \eb \ef \en \er \et \ev -/* \e\fIoctal\fR and \fB\e\e\fR. -/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)" -/* Messages greater in size than this limit (in bytes) will be bounced -/* back to the sender. /* .IP "\fBargv\fR=\fIcommand\fR... (required)" /* The command to be executed. This must be specified as the /* last command attribute. @@ -173,6 +173,11 @@ /* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and /* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single /* \fB$\fR is wanted. +/* .PP +/* Available in Postfix 2.2 and later: +/* .IP "\fBdirectory=\fIpathname\fR (optional)" +/* Change to the specified directory before executing the command. +/* Failure causes mail delivery to be deferred. /* DIAGNOSTICS /* Command exit status codes are expected to /* follow the conventions defined in <\fBsysexits.h\fR>. @@ -382,6 +387,7 @@ typedef struct { uid_t uid; /* command privileges */ gid_t gid; /* command privileges */ int flags; /* mail_copy() flags */ + char *exec_dir; /* working directory */ VSTRING *eol; /* output record delimiter */ off_t size_limit; /* max size in bytes we will accept */ } PIPE_ATTR; @@ -626,6 +632,7 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv) group = 0; attr->command = 0; attr->flags = 0; + attr->exec_dir = 0; attr->eol = vstring_strcpy(vstring_alloc(1), "\n"); attr->size_limit = 0; @@ -697,6 +704,13 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv) } } + /* + * directory=string + */ + else if (strncasecmp("directory=", *argv, sizeof("directory=") - 1) == 0) { + attr->exec_dir = mystrdup(*argv + sizeof("directory=") - 1); + } + /* * eol=string */ @@ -977,6 +991,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv) PIPE_CMD_TIME_LIMIT, conf.time_limit, PIPE_CMD_EOL, STR(attr.eol), PIPE_CMD_EXPORT, export_env->argv, + PIPE_CMD_CWD, attr.exec_dir, PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr, PIPE_CMD_DELIVERED, rcpt_list->info[0].address, PIPE_CMD_END); diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 72be1d833..dbcb0c1d2 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -382,7 +382,6 @@ static int deliver_message(DELIVER_REQUEST *request) /* * Clean up. */ - smtp_chat_reset(state); smtp_state_free(state); return (result); diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 813309803..c965c25e7 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -36,25 +36,8 @@ typedef struct SMTP_STATE { VSTREAM *src; /* queue file stream */ DELIVER_REQUEST *request; /* envelope info, offsets */ struct SMTP_SESSION *session; /* network connection */ - VSTRING *buffer; /* I/O buffer */ - VSTRING *scratch; /* scratch buffer */ - VSTRING *scratch2; /* scratch buffer */ int status; /* delivery status */ - int features; /* server features */ - ARGV *history; /* transaction log */ - int error_mask; /* error classes */ -#ifdef USE_SASL_AUTH - char *sasl_mechanism_list; /* server mechanism list */ - char *sasl_username; /* client username */ - char *sasl_passwd; /* client password */ - sasl_conn_t *sasl_conn; /* SASL internal state */ - VSTRING *sasl_encoded; /* encoding buffer */ - VSTRING *sasl_decoded; /* decoding buffer */ - sasl_callback_t *sasl_callbacks; /* stateful callbacks */ -#endif - off_t size_limit; /* server limit or unknown */ int space_left; /* output length control */ - struct MIME_STATE *mime_state; /* mime state machine */ /* * Flags and counters to control the handling of mail delivery errors. @@ -81,6 +64,7 @@ typedef struct SMTP_STATE { #define SMTP_FEATURE_XFORWARD_ADDR (1<<8) #define SMTP_FEATURE_XFORWARD_PROTO (1<<9) #define SMTP_FEATURE_XFORWARD_HELO (1<<10) +#define SMTP_FEATURE_CACHE_SESSION (1<<11) /* * Misc flags. @@ -109,13 +93,37 @@ extern int smtp_host_lookup_mask; /* host lookup methods to use */ */ typedef struct SMTP_SESSION { VSTREAM *stream; /* network connection */ + char *dest; /* nexthop[:port] or fallback relay */ char *host; /* mail exchanger */ char *addr; /* mail exchanger */ char *namaddr; /* mail exchanger */ int best; /* most preferred host */ + + VSTRING *buffer; /* I/O buffer */ + VSTRING *scratch; /* scratch buffer */ + VSTRING *scratch2; /* scratch buffer */ + + int features; /* server features */ + off_t size_limit; /* server limit or unknown */ + + ARGV *history; /* transaction log */ + int error_mask; /* error classes */ + struct MIME_STATE *mime_state; /* mime state machine */ + +#ifdef USE_SASL_AUTH + char *sasl_mechanism_list; /* server mechanism list */ + char *sasl_username; /* client username */ + char *sasl_passwd; /* client password */ + sasl_conn_t *sasl_conn; /* SASL internal state */ + VSTRING *sasl_encoded; /* encoding buffer */ + VSTRING *sasl_decoded; /* decoding buffer */ + sasl_callback_t *sasl_callbacks; /* stateful callbacks */ +#endif + } SMTP_SESSION; -extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *); +extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *, char *); +extern void smtp_session_reuse(SMTP_SESSION *); extern void smtp_session_free(SMTP_SESSION *); /* @@ -128,7 +136,6 @@ extern int smtp_connect(SMTP_STATE *); */ extern int smtp_helo(SMTP_STATE *, int); extern int smtp_xfer(SMTP_STATE *); -extern void smtp_quit(SMTP_STATE *); /* * smtp_chat.c @@ -139,10 +146,11 @@ typedef struct SMTP_RESP { /* server response */ VSTRING *buf; /* origin of text */ } SMTP_RESP; -extern void PRINTFLIKE(2, 3) smtp_chat_cmd(SMTP_STATE *, char *,...); -extern SMTP_RESP *smtp_chat_resp(SMTP_STATE *); -extern void smtp_chat_reset(SMTP_STATE *); -extern void smtp_chat_notify(SMTP_STATE *); +extern void PRINTFLIKE(2, 3) smtp_chat_cmd(SMTP_SESSION *, char *,...); +extern SMTP_RESP *smtp_chat_resp(SMTP_SESSION *); +extern void smtp_chat_init(SMTP_SESSION *); +extern void smtp_chat_reset(SMTP_SESSION *); +extern void smtp_chat_notify(SMTP_SESSION *); /* * These operations implement a redundant mark-and-sweep algorithm that diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c index e3d036765..06a21273c 100644 --- a/postfix/src/smtp/smtp_chat.c +++ b/postfix/src/smtp/smtp_chat.c @@ -14,18 +14,21 @@ /* .in -4 /* } SMTP_RESP; /* -/* void smtp_chat_cmd(state, format, ...) -/* SMTP_STATE *state; +/* void smtp_chat_cmd(session, format, ...) +/* SMTP_SESSION *session; /* char *format; /* -/* SMTP_RESP *smtp_chat_resp(state) -/* SMTP_STATE *state; +/* SMTP_RESP *smtp_chat_resp(session) +/* SMTP_SESSION *session; /* -/* void smtp_chat_notify(state) -/* SMTP_STATE *state; +/* void smtp_chat_notify(session) +/* SMTP_SESSION *session; /* -/* void smtp_chat_reset(state) -/* SMTP_STATE *state; +/* void smtp_chat_init(session) +/* SMTP_SESSION *session; +/* +/* void smtp_chat_reset(session) +/* SMTP_SESSION *session; /* DESCRIPTION /* This module implements SMTP client support for request/reply /* conversations, and maintains a limited SMTP transaction log. @@ -43,6 +46,9 @@ /* when delivery is possible immediately. It is an error to call /* smtp_chat_notify() when no SMTP transaction log exists. /* +/* smtp_chat_init() initializes the per-session transaction log. +/* This must be done at the beginning of a new SMTP session. +/* /* smtp_chat_reset() resets the transaction log. This is /* typically done at the beginning or end of an SMTP session, /* or within a session to discard non-error information. @@ -100,55 +106,62 @@ #define STR(x) ((char *) vstring_str(x)) #define LEN VSTRING_LEN +/* smtp_chat_init - initialize SMTP transaction log */ + +void smtp_chat_init(SMTP_SESSION *session) +{ + session->history = 0; +} + /* smtp_chat_reset - reset SMTP transaction log */ -void smtp_chat_reset(SMTP_STATE *state) +void smtp_chat_reset(SMTP_SESSION *session) { - if (state->history) { - argv_free(state->history); - state->history = 0; + + if (session->history) { + argv_free(session->history); + session->history = 0; } } /* smtp_chat_append - append record to SMTP transaction log */ -static void smtp_chat_append(SMTP_STATE *state, char *direction, char *data) +static void smtp_chat_append(SMTP_SESSION *session, char *direction, char *data) { char *line; - if (state->history == 0) - state->history = argv_alloc(10); + if (session->history == 0) + session->history = argv_alloc(10); line = concatenate(direction, data, (char *) 0); - argv_add(state->history, line, (char *) 0); + argv_add(session->history, line, (char *) 0); myfree(line); } /* smtp_chat_cmd - send an SMTP command */ -void smtp_chat_cmd(SMTP_STATE *state, char *fmt,...) +void smtp_chat_cmd(SMTP_SESSION *session, char *fmt,...) { - SMTP_SESSION *session = state->session; va_list ap; /* * Format the command, and update the transaction log. */ va_start(ap, fmt); - vstring_vsprintf(state->buffer, fmt, ap); + vstring_vsprintf(session->buffer, fmt, ap); va_end(ap); - smtp_chat_append(state, "Out: ", STR(state->buffer)); + smtp_chat_append(session, "Out: ", STR(session->buffer)); /* * Optionally log the command first, so we can see in the log what the * program is trying to do. */ if (msg_verbose) - msg_info("> %s: %s", session->namaddr, STR(state->buffer)); + msg_info("> %s: %s", session->namaddr, STR(session->buffer)); /* * Send the command to the SMTP server. */ - smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream); + smtp_fputs(STR(session->buffer), LEN(session->buffer), session->stream); /* * Flush unsent data to avoid timeouts after slow DNS lookups. @@ -167,9 +180,8 @@ void smtp_chat_cmd(SMTP_STATE *state, char *fmt,...) /* smtp_chat_resp - read and process SMTP server response */ -SMTP_RESP *smtp_chat_resp(SMTP_STATE *state) +SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session) { - SMTP_SESSION *session = state->session; static SMTP_RESP rdata; char *cp; int last_char; @@ -187,13 +199,13 @@ SMTP_RESP *smtp_chat_resp(SMTP_STATE *state) */ VSTRING_RESET(rdata.buf); for (;;) { - last_char = smtp_get(state->buffer, session->stream, var_line_limit); - printable(STR(state->buffer), '?'); + last_char = smtp_get(session->buffer, session->stream, var_line_limit); + printable(STR(session->buffer), '?'); if (last_char != '\n') msg_warn("%s: response longer than %d: %.30s...", - session->namaddr, var_line_limit, STR(state->buffer)); + session->namaddr, var_line_limit, STR(session->buffer)); if (msg_verbose) - msg_info("< %s: %.100s", session->namaddr, STR(state->buffer)); + msg_info("< %s: %.100s", session->namaddr, STR(session->buffer)); /* * Defend against a denial of service attack by limiting the amount @@ -202,8 +214,8 @@ SMTP_RESP *smtp_chat_resp(SMTP_STATE *state) if (LEN(rdata.buf) < var_line_limit) { if (VSTRING_LEN(rdata.buf)) VSTRING_ADDCH(rdata.buf, '\n'); - vstring_strcat(rdata.buf, STR(state->buffer)); - smtp_chat_append(state, "In: ", STR(state->buffer)); + vstring_strcat(rdata.buf, STR(session->buffer)); + smtp_chat_append(session, "In: ", STR(session->buffer)); } /* @@ -211,17 +223,17 @@ SMTP_RESP *smtp_chat_resp(SMTP_STATE *state) * that any character except space (or end of line) will have the * same effect as the '-' line continuation character. */ - for (cp = STR(state->buffer); *cp && ISDIGIT(*cp); cp++) + for (cp = STR(session->buffer); *cp && ISDIGIT(*cp); cp++) /* void */ ; - if (cp - STR(state->buffer) == 3) { + if (cp - STR(session->buffer) == 3) { if (*cp == '-') continue; if (*cp == ' ' || *cp == 0) break; } - state->error_mask |= MAIL_ERROR_PROTOCOL; + session->error_mask |= MAIL_ERROR_PROTOCOL; } - rdata.code = atoi(STR(state->buffer)); + rdata.code = atoi(STR(session->buffer)); VSTRING_TERMINATE(rdata.buf); rdata.str = STR(rdata.buf); return (&rdata); @@ -238,17 +250,16 @@ static void print_line(const char *str, int len, int indent, char *context) /* smtp_chat_notify - notify postmaster */ -void smtp_chat_notify(SMTP_STATE *state) +void smtp_chat_notify(SMTP_SESSION *session) { char *myname = "smtp_chat_notify"; - SMTP_SESSION *session = state->session; VSTREAM *notice; char **cpp; /* * Sanity checks. */ - if (state->history == 0) + if (session->history == 0) msg_panic("%s: no conversation history", myname); if (msg_verbose) msg_info("%s: notify postmaster", myname); @@ -282,8 +293,8 @@ void smtp_chat_notify(SMTP_STATE *state) post_mail_fputs(notice, ""); post_mail_fputs(notice, "Transcript of session follows."); post_mail_fputs(notice, ""); - argv_terminate(state->history); - for (cpp = state->history->argv; *cpp; cpp++) + argv_terminate(session->history); + for (cpp = session->history->argv; *cpp; cpp++) line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line, (char *) notice); (void) post_mail_fclose(notice); diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 604c90c64..c81120adf 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -98,7 +98,7 @@ /* smtp_connect_addr - connect to explicit address */ -static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, +static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port, VSTRING *why) { char *myname = "smtp_connect_addr"; @@ -212,7 +212,8 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, return (0); } vstream_ungetc(stream, ch); - return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr))); + return (smtp_session_alloc(stream, dest, addr->name, + inet_ntoa(sin.sin_addr))); } /* smtp_parse_destination - parse destination */ @@ -269,6 +270,7 @@ int smtp_connect(SMTP_STATE *state) int addr_count; int sess_count; int misc_flags = SMTP_MISC_FLAG_DEFAULT; + SMTP_SESSION *session; /* * First try to deliver to the indicated destination, then try to deliver @@ -348,27 +350,23 @@ int smtp_connect(SMTP_STATE *state) next = addr->next; if (++addr_count == var_smtp_mxaddr_limit) next = 0; - if ((state->session = smtp_connect_addr(addr, port, why)) != 0) { - state->features = 0; /* XXX should be SESSION info */ + session = state->session = smtp_connect_addr(dest, addr, port, why); + if (session != 0) { if (++sess_count == var_smtp_mxsess_limit) next = 0; state->final_server = (cpp[1] == 0 && next == 0); - state->session->best = (addr->pref == addr_list->pref); - debug_peer_check(state->session->host, state->session->addr); + session->best = (addr->pref == addr_list->pref); + debug_peer_check(session->host, session->addr); if (smtp_helo(state, misc_flags) == 0) smtp_xfer(state); - if (state->history != 0) { - if (state->error_mask & name_mask(VAR_NOTIFY_CLASSES, + if (session->history != 0) { + if (session->error_mask & name_mask(VAR_NOTIFY_CLASSES, mail_error_masks, var_notify_classes)) - smtp_chat_notify(state); - smtp_chat_reset(state); + smtp_chat_notify(session); } /* XXX smtp_xfer() may abort in the middle of DATA. */ - smtp_session_free(state->session); + smtp_session_free(session); state->session = 0; -#ifdef USE_SASL_AUTH - smtp_sasl_cleanup(state); -#endif debug_peer_restore(); smtp_rcpt_cleanup(state); } else { diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 71369a19d..18fcb3ea1 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -196,7 +196,7 @@ int smtp_helo(SMTP_STATE *state, int misc_flags) /* * Read and parse the server's SMTP greeting banner. */ - switch ((resp = smtp_chat_resp(state))->code / 100) { + switch ((resp = smtp_chat_resp(session))->code / 100) { case 2: break; case 5: @@ -215,7 +215,7 @@ int smtp_helo(SMTP_STATE *state, int misc_flags) * on by default. */ if (resp->str[strspn(resp->str, "20 *\t\n")] == 0) - state->features |= SMTP_FEATURE_MAYBEPIX; + session->features |= SMTP_FEATURE_MAYBEPIX; /* * See if we are talking to ourself. This should not be possible with the @@ -231,25 +231,27 @@ int smtp_helo(SMTP_STATE *state, int misc_flags) msg_warn("host %s greeted me with my own hostname %s", session->namaddr, var_myhostname); } else if (strcasecmp(word, "ESMTP") == 0) - state->features |= SMTP_FEATURE_ESMTP; + session->features |= SMTP_FEATURE_ESMTP; } - if (var_smtp_always_ehlo && (state->features & SMTP_FEATURE_MAYBEPIX) == 0) - state->features |= SMTP_FEATURE_ESMTP; - if (var_smtp_never_ehlo || (state->features & SMTP_FEATURE_MAYBEPIX) != 0) - state->features &= ~SMTP_FEATURE_ESMTP; + if (var_smtp_always_ehlo + && (session->features & SMTP_FEATURE_MAYBEPIX) == 0) + session->features |= SMTP_FEATURE_ESMTP; + if (var_smtp_never_ehlo + || (session->features & SMTP_FEATURE_MAYBEPIX) != 0) + session->features &= ~SMTP_FEATURE_ESMTP; /* * Return the compliment. Fall back to SMTP if our ESMTP recognition * heuristic failed. */ - if (state->features & SMTP_FEATURE_ESMTP) { - smtp_chat_cmd(state, "EHLO %s", var_smtp_helo_name); - if ((resp = smtp_chat_resp(state))->code / 100 != 2) - state->features &= ~SMTP_FEATURE_ESMTP; + if (session->features & SMTP_FEATURE_ESMTP) { + smtp_chat_cmd(session, "EHLO %s", var_smtp_helo_name); + if ((resp = smtp_chat_resp(session))->code / 100 != 2) + session->features &= ~SMTP_FEATURE_ESMTP; } - if ((state->features & SMTP_FEATURE_ESMTP) == 0) { - smtp_chat_cmd(state, "HELO %s", var_smtp_helo_name); - if ((resp = smtp_chat_resp(state))->code / 100 != 2) + if ((session->features & SMTP_FEATURE_ESMTP) == 0) { + smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name); + if ((resp = smtp_chat_resp(session))->code / 100 != 2) return (smtp_site_fail(state, resp->code, "host %s refused to talk to me: %s", session->namaddr, @@ -271,26 +273,26 @@ int smtp_helo(SMTP_STATE *state, int misc_flags) while ((words = mystrtok(&lines, "\n")) != 0) { if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) { if (strcasecmp(word, "8BITMIME") == 0) - state->features |= SMTP_FEATURE_8BITMIME; + session->features |= SMTP_FEATURE_8BITMIME; else if (strcasecmp(word, "PIPELINING") == 0) - state->features |= SMTP_FEATURE_PIPELINING; + session->features |= SMTP_FEATURE_PIPELINING; else if (strcasecmp(word, "XFORWARD") == 0) while ((word = mystrtok(&words, " \t")) != 0) - state->features |= name_code(xforward_features, - NAME_CODE_FLAG_NONE, word); + session->features |= name_code(xforward_features, + NAME_CODE_FLAG_NONE, word); else if (strcasecmp(word, "SIZE") == 0) { - state->features |= SMTP_FEATURE_SIZE; + session->features |= SMTP_FEATURE_SIZE; if ((word = mystrtok(&words, " \t")) != 0) { if (!alldig(word)) msg_warn("bad size limit \"%s\" in EHLO reply from %s", word, session->namaddr); else - state->size_limit = off_cvt_string(word); + session->size_limit = off_cvt_string(word); } } #ifdef USE_SASL_AUTH else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) - smtp_sasl_helo_auth(state, words); + smtp_sasl_helo_auth(session, words); #endif else if (strcasecmp(word, var_myhostname) == 0) { if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) { @@ -305,10 +307,10 @@ int smtp_helo(SMTP_STATE *state, int misc_flags) } if (msg_verbose) msg_info("server features: 0x%x size %.0f", - state->features, (double) state->size_limit); + session->features, (double) session->size_limit); #ifdef USE_SASL_AUTH - if (var_smtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH)) + if (var_smtp_sasl_enable && (session->features & SMTP_FEATURE_AUTH)) return (smtp_sasl_helo_login(state)); #endif @@ -429,8 +431,8 @@ int smtp_xfer(SMTP_STATE *state) #define RETURN(x) do { \ vstring_free(next_command); \ - if (state->mime_state) \ - state->mime_state = mime_state_free(state->mime_state); \ + if (session->mime_state) \ + session->mime_state = mime_state_free(session->mime_state); \ return (x); \ } while (0) @@ -458,10 +460,10 @@ int smtp_xfer(SMTP_STATE *state) * here rather than in the EHLO processing code, because of future SMTP * connection caching. */ - if (state->size_limit > 0 && state->size_limit < request->data_size) { + if (session->size_limit > 0 && session->size_limit < request->data_size) { smtp_mesg_fail(state, 552, "message size %lu exceeds size limit %.0f of server %s", - request->data_size, (double) state->size_limit, + request->data_size, (double) session->size_limit, session->namaddr); RETURN(0); } @@ -479,15 +481,15 @@ int smtp_xfer(SMTP_STATE *state) * to be aware of application-level buffering by the vstream module, * which is limited to a couple kbytes. */ - if (state->features & SMTP_FEATURE_PIPELINING) { - if (getsockopt(vstream_fileno(state->session->stream), SOL_SOCKET, + if (session->features & SMTP_FEATURE_PIPELINING) { + if (getsockopt(vstream_fileno(session->stream), SOL_SOCKET, SO_SNDBUF, (char *) &sndbufsize, &optlen) < 0) msg_fatal("%s: getsockopt: %m", myname); if (sndbufsize > VSTREAM_BUFSIZE) sndbufsize = VSTREAM_BUFSIZE; if (sndbufsize == 0) { sndbufsize = VSTREAM_BUFSIZE; - if (setsockopt(vstream_fileno(state->session->stream), SOL_SOCKET, + if (setsockopt(vstream_fileno(session->stream), SOL_SOCKET, SO_SNDBUF, (char *) &sndbufsize, optlen) < 0) msg_fatal("%s: setsockopt: %m", myname); } @@ -521,15 +523,15 @@ int smtp_xfer(SMTP_STATE *state) nrcpt = 0; send_name_addr = var_smtp_send_xforward - && (((state->features & SMTP_FEATURE_XFORWARD_NAME) + && (((session->features & SMTP_FEATURE_XFORWARD_NAME) && DEL_REQ_ATTR_AVAIL(request->client_name)) - || ((state->features & SMTP_FEATURE_XFORWARD_ADDR) + || ((session->features & SMTP_FEATURE_XFORWARD_ADDR) && DEL_REQ_ATTR_AVAIL(request->client_addr))); send_proto_helo = var_smtp_send_xforward - && (((state->features & SMTP_FEATURE_XFORWARD_PROTO) + && (((session->features & SMTP_FEATURE_XFORWARD_PROTO) && DEL_REQ_ATTR_AVAIL(request->client_proto)) - || ((state->features & SMTP_FEATURE_XFORWARD_HELO) + || ((session->features & SMTP_FEATURE_XFORWARD_HELO) && DEL_REQ_ATTR_AVAIL(request->client_helo))); if (send_name_addr) recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR; @@ -560,11 +562,11 @@ int smtp_xfer(SMTP_STATE *state) */ case SMTP_STATE_XFORWARD_NAME_ADDR: vstring_strcpy(next_command, XFORWARD_CMD); - if (state->features & SMTP_FEATURE_XFORWARD_NAME) + if (session->features & SMTP_FEATURE_XFORWARD_NAME) vstring_sprintf_append(next_command, " %s=%s", XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ? request->client_name : XFORWARD_UNAVAILABLE); - if (state->features & SMTP_FEATURE_XFORWARD_ADDR) + if (session->features & SMTP_FEATURE_XFORWARD_ADDR) vstring_sprintf_append(next_command, " %s=%s", XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ? request->client_addr : XFORWARD_UNAVAILABLE); @@ -576,11 +578,11 @@ int smtp_xfer(SMTP_STATE *state) case SMTP_STATE_XFORWARD_PROTO_HELO: vstring_strcpy(next_command, XFORWARD_CMD); - if (state->features & SMTP_FEATURE_XFORWARD_PROTO) + if (session->features & SMTP_FEATURE_XFORWARD_PROTO) vstring_sprintf_append(next_command, " %s=%s", XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ? request->client_proto : XFORWARD_UNAVAILABLE); - if (state->features & SMTP_FEATURE_XFORWARD_HELO) + if (session->features & SMTP_FEATURE_XFORWARD_HELO) vstring_sprintf_append(next_command, " %s=%s", XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ? request->client_helo : XFORWARD_UNAVAILABLE); @@ -591,13 +593,13 @@ int smtp_xfer(SMTP_STATE *state) * Build the MAIL FROM command. */ case SMTP_STATE_MAIL: - QUOTE_ADDRESS(state->scratch, request->sender); + QUOTE_ADDRESS(session->scratch, request->sender); vstring_sprintf(next_command, "MAIL FROM:<%s>", - vstring_str(state->scratch)); - if (state->features & SMTP_FEATURE_SIZE) /* RFC 1870 */ + vstring_str(session->scratch)); + if (session->features & SMTP_FEATURE_SIZE) /* RFC 1870 */ vstring_sprintf_append(next_command, " SIZE=%lu", request->data_size); - if (state->features & SMTP_FEATURE_8BITMIME) { /* RFC 1652 */ + if (session->features & SMTP_FEATURE_8BITMIME) { /* RFC 1652 */ if (strcmp(request->encoding, MAIL_ATTR_ENC_8BIT) == 0) vstring_strcat(next_command, " BODY=8BITMIME"); else if (strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) == 0) @@ -612,8 +614,7 @@ int smtp_xfer(SMTP_STATE *state) */ #ifdef USE_SASL_AUTH if (var_smtp_sasl_enable - && (state->features & SMTP_FEATURE_AUTH) - && state->sasl_passwd) + && (session->features & SMTP_FEATURE_AUTH)) vstring_strcat(next_command, " AUTH=<>"); #endif next_state = SMTP_STATE_RCPT; @@ -625,9 +626,9 @@ int smtp_xfer(SMTP_STATE *state) */ case SMTP_STATE_RCPT: rcpt = request->rcpt_list.info + send_rcpt; - QUOTE_ADDRESS(state->scratch, rcpt->address); + QUOTE_ADDRESS(session->scratch, rcpt->address); vstring_sprintf(next_command, "RCPT TO:<%s>", - vstring_str(state->scratch)); + vstring_str(session->scratch)); if ((next_rcpt = send_rcpt + 1) == SMTP_RCPT_LEFT(state)) next_state = DEL_REQ_TRACE_ONLY(request->flags) ? SMTP_STATE_ABORT : SMTP_STATE_DATA; @@ -705,12 +706,12 @@ int smtp_xfer(SMTP_STATE *state) * Receive the next server response. Use the proper timeout, * and log the proper client state in case of trouble. */ - smtp_timeout_setup(state->session->stream, + smtp_timeout_setup(session->stream, *xfer_timeouts[recv_state]); - if ((except = vstream_setjmp(state->session->stream)) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) RETURN(SENDING_MAIL ? smtp_stream_except(state, except, xfer_states[recv_state]) : -1); - resp = smtp_chat_resp(state); + resp = smtp_chat_resp(session); /* * Process the response. @@ -902,10 +903,10 @@ int smtp_xfer(SMTP_STATE *state) if (send_state == SMTP_STATE_DOT && nrcpt > 0) { downgrading = (var_disable_mime_oconv == 0 - && (state->features & SMTP_FEATURE_8BITMIME) == 0 + && (session->features & SMTP_FEATURE_8BITMIME) == 0 && strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) != 0); if (downgrading) - state->mime_state = mime_state_alloc(MIME_OPT_DOWNGRADE + session->mime_state = mime_state_alloc(MIME_OPT_DOWNGRADE | MIME_OPT_REPORT_NESTING, smtp_header_out, (MIME_STATE_ANY_END) 0, @@ -914,28 +915,28 @@ int smtp_xfer(SMTP_STATE *state) (MIME_STATE_ERR_PRINT) 0, (void *) state); state->space_left = var_smtp_line_limit; - smtp_timeout_setup(state->session->stream, + smtp_timeout_setup(session->stream, var_smtp_data1_tmout); - if ((except = vstream_setjmp(state->session->stream)) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) RETURN(smtp_stream_except(state, except, "sending message body")); if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0) msg_fatal("seek queue file: %m"); - while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) { + while ((rec_type = rec_get(state->src, session->scratch, 0)) > 0) { if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) break; if (downgrading == 0) { smtp_text_out((void *) state, rec_type, - vstring_str(state->scratch), - VSTRING_LEN(state->scratch), + vstring_str(session->scratch), + VSTRING_LEN(session->scratch), (off_t) 0); } else { mime_errs = - mime_state_update(state->mime_state, rec_type, - vstring_str(state->scratch), - VSTRING_LEN(state->scratch)); + mime_state_update(session->mime_state, rec_type, + vstring_str(session->scratch), + VSTRING_LEN(session->scratch)); if (mime_errs) { smtp_mesg_fail(state, 554, "MIME 7-bit conversion failed: %s", @@ -946,7 +947,7 @@ int smtp_xfer(SMTP_STATE *state) prev_type = rec_type; } - if (state->mime_state) { + if (session->mime_state) { /* * The cleanup server normally ends MIME content with a @@ -957,7 +958,7 @@ int smtp_xfer(SMTP_STATE *state) * is requested upon delivery. */ mime_errs = - mime_state_update(state->mime_state, rec_type, "", 0); + mime_state_update(session->mime_state, rec_type, "", 0); if (mime_errs) { smtp_mesg_fail(state, 554, "MIME 7-bit conversion failed: %s", @@ -966,7 +967,7 @@ int smtp_xfer(SMTP_STATE *state) } } else if (prev_type == REC_TYPE_CONT) /* missing newline */ smtp_fputs("", 0, session->stream); - if ((state->features & SMTP_FEATURE_MAYBEPIX) != 0 + if ((session->features & SMTP_FEATURE_MAYBEPIX) != 0 && request->arrival_time < vstream_ftime(session->stream) - var_smtp_pix_thresh) { msg_info("%s: enabling PIX . workaround for %s", @@ -985,7 +986,7 @@ int smtp_xfer(SMTP_STATE *state) */ if (sndbuffree > 0) sndbuffree -= VSTRING_LEN(next_command) + 2; - smtp_chat_cmd(state, "%s", vstring_str(next_command)); + smtp_chat_cmd(session, "%s", vstring_str(next_command)); send_state = next_state; send_rcpt = next_rcpt; } diff --git a/postfix/src/smtp/smtp_sasl.h b/postfix/src/smtp/smtp_sasl.h index 35f9e1c4d..c7550aae2 100644 --- a/postfix/src/smtp/smtp_sasl.h +++ b/postfix/src/smtp/smtp_sasl.h @@ -12,13 +12,13 @@ * SASL protocol functions */ 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 *, const char *, const char *); -extern int smtp_sasl_authenticate(SMTP_STATE *, VSTRING *); -extern void smtp_sasl_cleanup(SMTP_STATE *); +extern void smtp_sasl_connect(SMTP_SESSION *); +extern int smtp_sasl_passwd_lookup(SMTP_SESSION *); +extern void smtp_sasl_start(SMTP_SESSION *, const char *, const char *); +extern int smtp_sasl_authenticate(SMTP_SESSION *, VSTRING *); +extern void smtp_sasl_cleanup(SMTP_SESSION *); -extern void smtp_sasl_helo_auth(SMTP_STATE *, const char *); +extern void smtp_sasl_helo_auth(SMTP_SESSION *, const char *); extern int smtp_sasl_helo_login(SMTP_STATE *); /* LICENSE diff --git a/postfix/src/smtp/smtp_sasl_glue.c b/postfix/src/smtp/smtp_sasl_glue.c index 211a30a4e..30251c8d3 100644 --- a/postfix/src/smtp/smtp_sasl_glue.c +++ b/postfix/src/smtp/smtp_sasl_glue.c @@ -8,21 +8,21 @@ /* /* void smtp_sasl_initialize() /* -/* void smtp_sasl_connect(state) -/* SMTP_STATE *state; +/* void smtp_sasl_connect(session) +/* SMTP_SESSION *session; /* -/* void smtp_sasl_start(state, sasl_opts_name, sasl_opts_val) -/* SMTP_STATE *state; +/* void smtp_sasl_start(session, sasl_opts_name, sasl_opts_val) +/* SMTP_SESSION *session; /* -/* int smtp_sasl_passwd_lookup(state) -/* SMTP_STATE *state; +/* int smtp_sasl_passwd_lookup(session) +/* SMTP_SESSION *session; /* -/* int smtp_sasl_authenticate(state, why) -/* SMTP_STATE *state; +/* int smtp_sasl_authenticate(session, why) +/* SMTP_SESSION *session; /* VSTRING *why; /* -/* void smtp_sasl_cleanup(state) -/* SMTP_STATE *state; +/* void smtp_sasl_cleanup(session) +/* SMTP_SESSION *session; /* DESCRIPTION /* smtp_sasl_initialize() initializes the SASL library. This /* routine must be called once at process startup, before any @@ -53,7 +53,7 @@ /* This routine is a noop for non-SASL sessions. /* /* Arguments: -/* .IP state +/* .IP session /* Session context. /* .IP mech_list /* String of SASL mechanisms (separated by blanks) @@ -203,20 +203,20 @@ static int smtp_sasl_get_user(void *context, int unused_id, const char **result, unsigned *len) { char *myname = "smtp_sasl_get_user"; - SMTP_STATE *state = (SMTP_STATE *) context; + SMTP_SESSION *session = (SMTP_SESSION *) context; if (msg_verbose) - msg_info("%s: %s", myname, state->sasl_username); + msg_info("%s: %s", myname, session->sasl_username); /* * Sanity check. */ - if (state->sasl_passwd == 0) + if (session->sasl_passwd == 0) msg_panic("%s: no username looked up", myname); - *result = state->sasl_username; + *result = session->sasl_username; if (len) - *len = strlen(state->sasl_username); + *len = strlen(session->sasl_username); return (SASL_OK); } @@ -226,35 +226,35 @@ static int smtp_sasl_get_passwd(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret) { char *myname = "smtp_sasl_get_passwd"; - SMTP_STATE *state = (SMTP_STATE *) context; + SMTP_SESSION *session = (SMTP_SESSION *) context; int len; if (msg_verbose) - msg_info("%s: %s", myname, state->sasl_passwd); + msg_info("%s: %s", myname, session->sasl_passwd); /* * Sanity check. */ if (!conn || !psecret || id != SASL_CB_PASS) return (SASL_BADPARAM); - if (state->sasl_passwd == 0) + if (session->sasl_passwd == 0) msg_panic("%s: no password looked up", myname); /* * Convert the password into a counted string. */ - len = strlen(state->sasl_passwd); + len = strlen(session->sasl_passwd); if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0) return (SASL_NOMEM); (*psecret)->len = len; - memcpy((*psecret)->data, state->sasl_passwd, len + 1); + memcpy((*psecret)->data, session->sasl_passwd, len + 1); return (SASL_OK); } /* smtp_sasl_passwd_lookup - password lookup routine */ -int smtp_sasl_passwd_lookup(SMTP_STATE *state) +int smtp_sasl_passwd_lookup(SMTP_SESSION *session) { char *myname = "smtp_sasl_passwd_lookup"; const char *value; @@ -269,21 +269,24 @@ int smtp_sasl_passwd_lookup(SMTP_STATE *state) /* * Look up the per-server password information. Try the hostname first, * then try the destination. + * + * XXX Instead of using nexthop (the intended destination) we use dest + * (either the intended destination, or a fall-back destination). */ - if ((value = maps_find(smtp_sasl_passwd_map, state->session->host, 0)) != 0 - || (value = maps_find(smtp_sasl_passwd_map, state->request->nexthop, 0)) != 0) { - state->sasl_username = mystrdup(value); - passwd = split_at(state->sasl_username, ':'); - state->sasl_passwd = mystrdup(passwd ? passwd : ""); + if ((value = maps_find(smtp_sasl_passwd_map, session->host, 0)) != 0 + || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) { + session->sasl_username = mystrdup(value); + passwd = split_at(session->sasl_username, ':'); + session->sasl_passwd = mystrdup(passwd ? passwd : ""); if (msg_verbose) msg_info("%s: host `%s' user `%s' pass `%s'", - myname, state->session->host, - state->sasl_username, state->sasl_passwd); + myname, session->host, + session->sasl_username, session->sasl_passwd); return (1); } else { if (msg_verbose) msg_info("%s: host `%s' no auth info found", - myname, state->session->host); + myname, session->host); return (0); } } @@ -323,20 +326,20 @@ void smtp_sasl_initialize(void) /* smtp_sasl_connect - per-session client initialization */ -void smtp_sasl_connect(SMTP_STATE *state) +void smtp_sasl_connect(SMTP_SESSION *session) { - state->sasl_mechanism_list = 0; - state->sasl_username = 0; - state->sasl_passwd = 0; - state->sasl_conn = 0; - state->sasl_encoded = 0; - state->sasl_decoded = 0; - state->sasl_callbacks = 0; + session->sasl_mechanism_list = 0; + session->sasl_username = 0; + session->sasl_passwd = 0; + session->sasl_conn = 0; + session->sasl_encoded = 0; + session->sasl_decoded = 0; + session->sasl_callbacks = 0; } /* smtp_sasl_start - per-session SASL initialization */ -void smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name, +void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name, const char *sasl_opts_val) { static sasl_callback_t callbacks[] = { @@ -357,18 +360,18 @@ void smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name, */ #define NULL_SECFLAGS 0 - state->sasl_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks)); - memcpy((char *) state->sasl_callbacks, callbacks, sizeof(callbacks)); - for (cp = state->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++) - cp->context = (void *) state; + session->sasl_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks)); + memcpy((char *) session->sasl_callbacks, callbacks, sizeof(callbacks)); + for (cp = session->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++) + cp->context = (void *) session; #define NULL_SERVER_ADDR ((char *) 0) #define NULL_CLIENT_ADDR ((char *) 0) - if (SASL_CLIENT_NEW("smtp", state->session->host, + if (SASL_CLIENT_NEW("smtp", session->host, NULL_CLIENT_ADDR, NULL_SERVER_ADDR, - state->sasl_callbacks, NULL_SECFLAGS, - (sasl_conn_t **) &state->sasl_conn) != SASL_OK) + session->sasl_callbacks, NULL_SECFLAGS, + (sasl_conn_t **) &session->sasl_conn) != SASL_OK) msg_fatal("per-session SASL client initialization"); /* @@ -384,7 +387,7 @@ void smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name, sec_props.maxbufsize = 0; sec_props.property_names = 0; sec_props.property_values = 0; - if (sasl_setprop(state->sasl_conn, SASL_SEC_PROPS, + if (sasl_setprop(session->sasl_conn, SASL_SEC_PROPS, &sec_props) != SASL_OK) msg_fatal("set per-session SASL security properties"); @@ -393,13 +396,13 @@ void smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name, * order to avoid memory leaks in case of read/write timeout or I/O * error. */ - state->sasl_encoded = vstring_alloc(10); - state->sasl_decoded = vstring_alloc(10); + session->sasl_encoded = vstring_alloc(10); + session->sasl_decoded = vstring_alloc(10); } /* smtp_sasl_authenticate - run authentication protocol */ -int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) +int smtp_sasl_authenticate(SMTP_SESSION *session, VSTRING *why) { char *myname = "smtp_sasl_authenticate"; unsigned enc_length; @@ -426,18 +429,18 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) if (msg_verbose) msg_info("%s: %s: SASL mechanisms %s", - myname, state->session->namaddr, state->sasl_mechanism_list); + myname, session->namaddr, session->sasl_mechanism_list); /* * Start the client side authentication protocol. */ - result = SASL_CLIENT_START((sasl_conn_t *) state->sasl_conn, - state->sasl_mechanism_list, + result = SASL_CLIENT_START((sasl_conn_t *) session->sasl_conn, + session->sasl_mechanism_list, NO_SASL_SECRET, NO_SASL_INTERACTION, &clientout, &clientoutlen, &mechanism); if (result != SASL_OK && result != SASL_CONTINUE) { vstring_sprintf(why, "cannot SASL authenticate to server %s: %s", - state->session->namaddr, + session->namaddr, sasl_errstring(result, NO_SASL_LANGLIST, NO_SASL_OUTLANG)); return (-1); @@ -453,28 +456,28 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) if (clientoutlen > 0) { if (msg_verbose) msg_info("%s: %s: uncoded initial reply: %.*s", - myname, state->session->namaddr, - (int) clientoutlen, clientout); + myname, session->namaddr, (int) clientoutlen, clientout); enc_length = ENCODE64_LENGTH(clientoutlen) + 1; - VSTRING_SPACE(state->sasl_encoded, enc_length); + VSTRING_SPACE(session->sasl_encoded, enc_length); if (sasl_encode64(clientout, clientoutlen, - STR(state->sasl_encoded), enc_length, + STR(session->sasl_encoded), enc_length, &enc_length_out) != SASL_OK) msg_panic("%s: sasl_encode64 botch", myname); #if SASL_VERSION_MAJOR < 2 /* SASL version 1 doesn't free memory that it allocates. */ free(clientout); #endif - smtp_chat_cmd(state, "AUTH %s %s", mechanism, STR(state->sasl_encoded)); + smtp_chat_cmd(session, "AUTH %s %s", mechanism, + STR(session->sasl_encoded)); } else { - smtp_chat_cmd(state, "AUTH %s", mechanism); + smtp_chat_cmd(session, "AUTH %s", mechanism); } /* * Step through the authentication protocol until the server tells us * that we are done. */ - while ((resp = smtp_chat_resp(state))->code / 100 == 3) { + while ((resp = smtp_chat_resp(session))->code / 100 == 3) { /* * Process a server challenge. @@ -482,25 +485,24 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) line = resp->str; (void) mystrtok(&line, "- \t\n"); /* skip over result code */ serverinlen = strlen(line); - VSTRING_SPACE(state->sasl_decoded, serverinlen); - if (SASL_DECODE64(line, serverinlen, STR(state->sasl_decoded), + VSTRING_SPACE(session->sasl_decoded, serverinlen); + if (SASL_DECODE64(line, serverinlen, STR(session->sasl_decoded), serverinlen, &enc_length) != SASL_OK) { vstring_sprintf(why, "malformed SASL challenge from server %s", - state->session->namaddr); + session->namaddr); return (-1); } if (msg_verbose) msg_info("%s: %s: decoded challenge: %.*s", - myname, state->session->namaddr, - (int) enc_length, STR(state->sasl_decoded)); - result = sasl_client_step((sasl_conn_t *) state->sasl_conn, - STR(state->sasl_decoded), enc_length, + myname, session->namaddr, (int) enc_length, + STR(session->sasl_decoded)); + result = sasl_client_step((sasl_conn_t *) session->sasl_conn, + STR(session->sasl_decoded), enc_length, NO_SASL_INTERACTION, &clientout, &clientoutlen); if (result != SASL_OK && result != SASL_CONTINUE) msg_warn("SASL authentication failed to server %s: %s", - state->session->namaddr, - sasl_errstring(result, NO_SASL_LANGLIST, - NO_SASL_OUTLANG)); + session->namaddr, sasl_errstring(result, NO_SASL_LANGLIST, + NO_SASL_OUTLANG)); /* * Send a client response. @@ -508,12 +510,12 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) if (clientoutlen > 0) { if (msg_verbose) msg_info("%s: %s: uncoded client response %.*s", - myname, state->session->namaddr, + myname, session->namaddr, (int) clientoutlen, clientout); enc_length = ENCODE64_LENGTH(clientoutlen) + 1; - VSTRING_SPACE(state->sasl_encoded, enc_length); + VSTRING_SPACE(session->sasl_encoded, enc_length); if (sasl_encode64(clientout, clientoutlen, - STR(state->sasl_encoded), enc_length, + STR(session->sasl_encoded), enc_length, &enc_length_out) != SASL_OK) msg_panic("%s: sasl_encode64 botch", myname); #if SASL_VERSION_MAJOR < 2 @@ -521,9 +523,9 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) free(clientout); #endif } else { - vstring_strcat(state->sasl_encoded, ""); + vstring_strcat(session->sasl_encoded, ""); } - smtp_chat_cmd(state, "%s", STR(state->sasl_encoded)); + smtp_chat_cmd(session, "%s", STR(session->sasl_encoded)); } /* @@ -531,7 +533,7 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) */ if (resp->code / 100 != 2) { vstring_sprintf(why, "SASL authentication failed; server %s said: %s", - state->session->namaddr, resp->str); + session->namaddr, resp->str); return (0); } return (1); @@ -539,37 +541,37 @@ int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why) /* smtp_sasl_cleanup - per-session cleanup */ -void smtp_sasl_cleanup(SMTP_STATE *state) +void smtp_sasl_cleanup(SMTP_SESSION *session) { - if (state->sasl_username) { - myfree(state->sasl_username); - state->sasl_username = 0; + if (session->sasl_username) { + myfree(session->sasl_username); + session->sasl_username = 0; } - if (state->sasl_passwd) { - myfree(state->sasl_passwd); - state->sasl_passwd = 0; + if (session->sasl_passwd) { + myfree(session->sasl_passwd); + session->sasl_passwd = 0; } - if (state->sasl_mechanism_list) { + if (session->sasl_mechanism_list) { /* allocated in smtp_sasl_helo_auth */ - myfree(state->sasl_mechanism_list); - state->sasl_mechanism_list = 0; + myfree(session->sasl_mechanism_list); + session->sasl_mechanism_list = 0; } - if (state->sasl_conn) { + if (session->sasl_conn) { if (msg_verbose) msg_info("disposing SASL state information"); - sasl_dispose(&state->sasl_conn); + sasl_dispose(&session->sasl_conn); } - if (state->sasl_callbacks) { - myfree((char *) state->sasl_callbacks); - state->sasl_callbacks = 0; + if (session->sasl_callbacks) { + myfree((char *) session->sasl_callbacks); + session->sasl_callbacks = 0; } - if (state->sasl_encoded) { - vstring_free(state->sasl_encoded); - state->sasl_encoded = 0; + if (session->sasl_encoded) { + vstring_free(session->sasl_encoded); + session->sasl_encoded = 0; } - if (state->sasl_decoded) { - vstring_free(state->sasl_decoded); - state->sasl_decoded = 0; + if (session->sasl_decoded) { + vstring_free(session->sasl_decoded); + session->sasl_decoded = 0; } } diff --git a/postfix/src/smtp/smtp_sasl_proto.c b/postfix/src/smtp/smtp_sasl_proto.c index 30cf0ab58..bcd87cb46 100644 --- a/postfix/src/smtp/smtp_sasl_proto.c +++ b/postfix/src/smtp/smtp_sasl_proto.c @@ -75,28 +75,26 @@ /* smtp_sasl_helo_auth - handle AUTH option in EHLO reply */ -void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words) +void smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words) { /* * XXX If the server offers a null list of authentication mechanisms, * then pretend that the server doesn't support SASL authentication. */ - if (state->sasl_mechanism_list) { - if (strcasecmp(state->sasl_mechanism_list, words) == 0) + if (session->sasl_mechanism_list) { + if (strcasecmp(session->sasl_mechanism_list, words) == 0) return; - myfree(state->sasl_mechanism_list); - msg_warn("%s offered AUTH option multiple times", - state->session->namaddr); - state->sasl_mechanism_list = 0; - state->features &= ~SMTP_FEATURE_AUTH; + myfree(session->sasl_mechanism_list); + msg_warn("%s offered AUTH option multiple times", session->namaddr); + session->sasl_mechanism_list = 0; + session->features &= ~SMTP_FEATURE_AUTH; } if (strlen(words) > 0) { - state->sasl_mechanism_list = mystrdup(words); - state->features |= SMTP_FEATURE_AUTH; + session->sasl_mechanism_list = mystrdup(words); + session->features |= SMTP_FEATURE_AUTH; } else { - msg_warn("%s offered null AUTH mechanism list", - state->session->namaddr); + msg_warn("%s offered null AUTH mechanism list", session->namaddr); } } @@ -104,20 +102,33 @@ void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words) int smtp_sasl_helo_login(SMTP_STATE *state) { - VSTRING *why = vstring_alloc(10); - int ret = 0; + SMTP_SESSION *session = state->session; + VSTRING *why; + int ret; /* * Skip authentication when no authentication info exists for this - * server, so that we talk to each other like strangers. Otherwise, if - * authentication information exists, assume that authentication is - * required, and assume that an authentication error is recoverable. + * server, so that we talk to each other like strangers. */ - if (smtp_sasl_passwd_lookup(state) != 0) { - 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)); + if (smtp_sasl_passwd_lookup(session) == 0) { + session->features &= ~SMTP_FEATURE_AUTH; + return 0; + } + + /* + * Otherwise, if authentication information exists, assume that + * authentication is required, and assume that an authentication error is + * recoverable from the message delivery point of view. An authentication + * error is unrecoverable from a session point of view - the session will + * not be reused. + */ + why = vstring_alloc(10); + ret = 0; + smtp_sasl_start(session, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts); + if (smtp_sasl_authenticate(session, why) <= 0) { + ret = smtp_site_fail(state, 450, "Authentication failed: %s", + vstring_str(why)); + /* Session reuse is disabled. */ } vstring_free(why); return (ret); diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c index 7f913d2df..8ac687a2d 100644 --- a/postfix/src/smtp/smtp_session.c +++ b/postfix/src/smtp/smtp_session.c @@ -6,8 +6,9 @@ /* SYNOPSIS /* #include "smtp.h" /* -/* SMTP_SESSION *smtp_session_alloc(stream, host, addr) +/* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr) /* VSTREAM *stream; +/* char *dest; /* char *host; /* char *addr; /* @@ -15,9 +16,10 @@ /* SMTP_SESSION *session; /* DESCRIPTION /* smtp_session_alloc() allocates memory for an SMTP_SESSION structure -/* and initializes it with the given stream and host name and address -/* information. The host name and address strings are copied. The code -/* assumes that the stream is connected to the "best" alternative. +/* and initializes it with the given stream and destination, host name +/* and address information. The host name and address strings are +/* copied. The code assumes that the stream is connected to the "best" +/* alternative. /* /* smtp_session_free() destroys an SMTP_SESSION structure and its /* members, making memory available for reuse. @@ -42,22 +44,42 @@ #include #include +/* Global library. */ + +#include + /* Application-specific. */ #include "smtp.h" /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */ -SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *host, char *addr) +SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *dest, char *host, + char *addr) { SMTP_SESSION *session; session = (SMTP_SESSION *) mymalloc(sizeof(*session)); session->stream = stream; + session->dest = mystrdup(dest); session->host = mystrdup(host); session->addr = mystrdup(addr); session->namaddr = concatenate(host, "[", addr, "]", (char *) 0); session->best = 1; + + session->size_limit = 0; + session->error_mask = 0; + session->buffer = vstring_alloc(100); + session->scratch = vstring_alloc(100); + session->scratch2 = vstring_alloc(100); + smtp_chat_init(session); + session->features = SMTP_FEATURE_CACHE_SESSION; + session->mime_state = 0; + +#ifdef USE_SASL_AUTH + smtp_sasl_connect(session); +#endif + return (session); } @@ -65,9 +87,25 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *host, char *addr) void smtp_session_free(SMTP_SESSION *session) { - vstream_fclose(session->stream); + if (session->stream) + vstream_fclose(session->stream); + myfree(session->dest); myfree(session->host); myfree(session->addr); myfree(session->namaddr); + + vstring_free(session->buffer); + vstring_free(session->scratch); + vstring_free(session->scratch2); + + if (session->history) + smtp_chat_reset(session); + if (session->mime_state) + mime_state_free(session->mime_state); + +#ifdef USE_SASL_AUTH + smtp_sasl_cleanup(session); +#endif + myfree((char *) session); } diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index 8b14f0c1d..f871a391a 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -59,19 +59,8 @@ SMTP_STATE *smtp_state_alloc(void) state->src = 0; state->request = 0; state->session = 0; - state->buffer = vstring_alloc(100); - state->scratch = vstring_alloc(100); - state->scratch2 = vstring_alloc(100); state->status = 0; - state->features = 0; - state->history = 0; - state->error_mask = 0; -#ifdef USE_SASL_AUTH - smtp_sasl_connect(state); -#endif - state->size_limit = 0; state->space_left = 0; - state->mime_state = 0; return (state); } @@ -79,13 +68,5 @@ SMTP_STATE *smtp_state_alloc(void) void smtp_state_free(SMTP_STATE *state) { - vstring_free(state->buffer); - vstring_free(state->scratch); - vstring_free(state->scratch2); -#ifdef USE_SASL_AUTH - smtp_sasl_cleanup(state); -#endif - if (state->mime_state) - mime_state_free(state->mime_state); myfree((char *) state); } diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index ea98fb54f..f323dfb92 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -60,6 +60,7 @@ /* record why the host is being skipped; soft error, final server: /* defer delivery of all remaining recipients and mark the destination /* as problematic; hard error: bounce all remaining recipients. +/* The session is marked as "do not cache". /* The result is non-zero. /* /* smtp_mesg_fail() handles the case where the smtp server @@ -86,6 +87,7 @@ /* The policy is: non-final server: log an informational record /* with the reason why the host is being skipped; final server: /* defer delivery of all remaining recipients. +/* The session is marked as "do not cache". /* The result is non-zero. /* DIAGNOSTICS /* Panic: unknown exception code. @@ -136,7 +138,7 @@ /* smtp_check_code - check response code */ -static void smtp_check_code(SMTP_STATE *state, int code) +static void smtp_check_code(SMTP_SESSION *session, int code) { /* @@ -152,7 +154,7 @@ static void smtp_check_code(SMTP_STATE *state, int code) if ((!SMTP_SOFT(code) && !SMTP_HARD(code)) || code == 555 /* RFC 1869, section 6.1. */ || (code >= 500 && code < 510)) - state->error_mask |= MAIL_ERROR_PROTOCOL; + session->error_mask |= MAIL_ERROR_PROTOCOL; } /* smtp_site_fail - skip site, defer or bounce all recipients */ @@ -214,7 +216,14 @@ int smtp_site_fail(SMTP_STATE *state, int code, char *format,...) if (soft_error && request->hop_status == 0) request->hop_status = mystrdup(vstring_str(why)); } - smtp_check_code(state, code); + if (session) + smtp_check_code(session, code); + + /* + * Don't cache this session. We can't talk to this server. + */ + if (session) + session->features &= ~SMTP_FEATURE_CACHE_SESSION; /* * Cleanup. @@ -271,15 +280,16 @@ int smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...) status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, rcpt->orig_addr, rcpt->address, rcpt->offset, - session->namaddr, request->arrival_time, - "%s", vstring_str(why)); + session ? session->namaddr : "none", + request->arrival_time, "%s", vstring_str(why)); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); state->status |= status; } } - smtp_check_code(state, code); + if (session) + smtp_check_code(session, code); /* * Cleanup. @@ -334,14 +344,16 @@ void smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt, status = (soft_error ? vdefer_append : vbounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, rcpt->orig_addr, rcpt->address, rcpt->offset, - session->namaddr, request->arrival_time, format, ap); + session ? session->namaddr : "none", + request->arrival_time, format, ap); va_end(ap); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); state->status |= status; } - smtp_check_code(state, code); + if (session) + smtp_check_code(session, code); } /* smtp_stream_except - defer domain after I/O problem */ @@ -354,6 +366,12 @@ int smtp_stream_except(SMTP_STATE *state, int code, char *description) int nrcpt; VSTRING *why = vstring_alloc(100); + /* + * Sanity check. + */ + if (session == 0) + msg_panic("smtp_stream_except: no session"); + /* * Initialize. */ @@ -404,6 +422,11 @@ int smtp_stream_except(SMTP_STATE *state, int code, char *description) } } + /* + * Don't attempt to cache this session. + */ + session->features &= ~SMTP_FEATURE_CACHE_SESSION; + /* * Cleanup. */