From: Wietse Venema Date: Fri, 31 Dec 1999 05:00:00 +0000 (-0500) Subject: postfix-19991231 X-Git-Tag: v20010228~74 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=09f6aad7bce7947e3537870c644921286e7c7928;p=thirdparty%2Fpostfix.git postfix-19991231 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/HISTORY b/postfix/HISTORY index 31cc0327f..147acda81 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -3497,3 +3497,21 @@ Apologies for any names omitted. with @ in the recipient localpart no longer bounces with "user unknown" but instead is rejected with "relay access denied" or "source-routed relay access denied". + +19991227 + + Workaround: the BSD/OS "mkdir -p" and "cmp -s" commands + misbehave on boundary cases: directory exists or file does + not exist. Those who re-invent... + +19991229 + + Added the no source routing info requirement to addresses + accepted by the permit_mx_backup UCE restriction. + +19991230 + + Added a spawn daemon (not compiled and installed by default) + to enable LMTP delivery over UNIX-domain sockets. The goal + is to simplify the experimental LMTP delivery agent by + ripping out the privileged code that forks the LMTP server. diff --git a/postfix/INSTALL.sh b/postfix/INSTALL.sh index 4fceb90d3..d6a82f21f 100644 --- a/postfix/INSTALL.sh +++ b/postfix/INSTALL.sh @@ -233,7 +233,7 @@ test -d $COMMAND_DIRECTORY || mkdir -p $COMMAND_DIRECTORY || exit 1 test -d $QUEUE_DIRECTORY || mkdir -p $QUEUE_DIRECTORY || exit 1 for path in $SENDMAIL_PATH $NEWALIASES_PATH $MAILQ_PATH do - dir=`echo $path|sed 's/[^/]*[/]*$//'` + dir=`echo $path|sed -e 's/[/][/]*[^/]*$//' -e 's/^$/\//'` test -d $dir || mkdir -p $dir || exit 1 done @@ -324,7 +324,7 @@ no) ;; done for file in man?/* do - cmp -s $file $MANPAGES/$file || { + (test -f $MANPAGES/$file && cmp -s $file $MANPAGES/$file) || { rm -f $MANPAGES/$file cp $file $MANPAGES/$file || exit 1 chmod 644 $MANPAGES/$file || exit 1 diff --git a/postfix/Makefile.in b/postfix/Makefile.in index 9733deb7e..fd29c9d46 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -4,7 +4,7 @@ OPTS = "CC=$(CC)" DIRS = util global dns master postfix smtpstone sendmail error \ pickup cleanup smtpd local trivial-rewrite qmgr smtp bounce pipe \ showq postalias postcat postconf postdrop postkick postlock postlog \ - postmap postsuper # man html + postmap postsuper # spawn man html default: update diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index ebdd3f783..02eb24fa9 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,14 +1,19 @@ -Incompatible changes with snapshot 19991227 +Incompatible changes with postfix-19991231: =========================================== - The SMTP server no longer forwards mail from untrusted clients -with sender-specified routing (stuff[@%!]stuff[@%!]stuff) to +with sender-specified routing (stuff[@%!]stuff[@%!]stuff) through destinations that are authorized by the relay_domains parameter. This closes a loophole that exploits trust relationships between hosts. Example: a trusted backup MX host forwards junk mail to a primary MX host which forwards the junk to the Internet. Specify "allow_untrusted_routing = yes" to restore the old behavior. +- The SMTP server no longer forwards mail with sender-specified +routing (stuff[@%!]stuff[@%!]stuff) through destinations that are +authorized by the permit_mx_backup feature. This change is under +control by the allow_untrusted_routing parameter discussed above. + - In order to support the above, the data structure and protocol of the trivial-rewrite service was changed. This means you must re-compile and re-link existing software that uses the Postfix @@ -18,32 +23,33 @@ resolve_clnt interface. with @ in the localpart (user@remote@here) no longer bounces with "user unknown" but instead is rejected with "relay access denied". -- The experimental permit_recipient_map and local_transports features -are gone. They were never part of an official release. Both are -replaced by a "local_recipient_map" parameter that allows the SMTP -server to reject mail for unknown local users (see below). +- Incompatible SMTPD access map changes: + + An all-numeric right-hand side now means OK. This is for better + cooperation with out-of-band authentication mechanisms such as + POP before SMTP etc. -- In an SMTPD access map, an all-numeric right-hand side now means -OK. This is for better cooperation with out-of-band authentication -mechanisms such as POP before SMTP etc. + An empty right-hand sides still mean OK, but Postfix will log a + warning in order to discourage such usage. -- You can no longer use an empty right-hand side in SMTPD access -maps. + You can no longer use virtual, canonical or aliases tables as + SMTPD access maps. Use the local_recipient_maps feature instead. - Recipient addresses may no longer begin with `-'. In order to -reinstate the old behavior, specify "allow_min_user = yes" in -main.cf. +get the old behavior, specify "allow_min_user = yes" in main.cf. + +- Incompatible transport map changes: -- You can no longer use virtual, canonical or aliases tables as -SMTPD access control tables. Use the local_recipient_maps feature -instead. + Transport map entries override mydestination. If you use transport + maps, it is better to always have explicit entries for all domain + names you have in $mydestination. See the html/faq.html sections + for firewalls and intranets. -- transport_maps entries override mydestination. If you use -transport maps, it is better to always have explicit entries for -all domain names you have in $mydestination. See the html/faq.html -sections for firewalls and intranets. + The nexthop information given to a local delivery agent may have + changed. This information was never intended to be used as a + next-hop destination. -Major changes with snapshot 19991227 +Major changes with postfix-19991231: ==================================== - It is now much more difficult to configure Postfix as an open @@ -52,14 +58,6 @@ contains at least one restriction that by default refuses mail (as is the default). There were too many accidents with changes to the UCE restrictions. -- The SMTP server no longer forwards mail from untrusted clients -with sender-specified routing (stuff[@%!]stuff[@%!]stuff) to -destinations that are authorized by the relay_domains parameter. -This closes a loophole that exploits trust relationships between -hosts. Example: a trusted backup MX host forwards junk mail to -a primary MX host which forwards the junk to the Internet. Specify -"allow_untrusted_routing = yes" to restore the old behavior. - - The relay_domains parameter no longer needs to contain $virtual_maps. - Overhauled FAQ (html/faq.html) with many more examples. diff --git a/postfix/bounce/.indent.pro b/postfix/bounce/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/bounce/.indent.pro +++ b/postfix/bounce/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/cleanup/.indent.pro b/postfix/cleanup/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/cleanup/.indent.pro +++ b/postfix/cleanup/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index e33946f03..c3a9979fb 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -49,6 +49,9 @@ # In order to use the "cyrus" message transport below, configure it # in main.cf as the mailbox_transport. # +# SPECIFY ONLY PROGRAMS THAT ARE WRITTEN TO RUN AS POSTFIX DAEMONS. +# ALL DAEMONS SPECIFIED HERE MUST SPEAK A POSTFIX-INTERNAL PROTOCOL. +# # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (50) @@ -72,4 +75,3 @@ ifmail unix - n n - - pipe flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) bsmtp unix - n n - - pipe flags=F. user=foo argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient - diff --git a/postfix/conf/transport b/postfix/conf/transport index cfcb2659b..258a7b37d 100644 --- a/postfix/conf/transport +++ b/postfix/conf/transport @@ -24,6 +24,17 @@ # .IP "\fI.domain transport\fR:\fInexthop\fR" # Mail for any subdomain of \fIdomain\fR is delivered through # \fItransport\fR to \fInexthop\fR. +# .PP +# Note: transport map entries take precedence over domains +# specified in the \fBmydestination\fR parameter. If you use +# the optional transport map, it may be safer to specify explicit +# entries for all domains specified in \fBmydestination\fR, +# for example: +# +# .ti +5 +# \fBhostname.my.domain local:\fR +# .ti +5 +# \fBlocalhost.my.domain local:\fR # # The interpretation of the \fInexthop\fR field is transport # dependent. In the case of SMTP, specify \fIhost\fR:\fIservice\fR for a diff --git a/postfix/dns/.indent.pro b/postfix/dns/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/dns/.indent.pro +++ b/postfix/dns/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/error/.indent.pro b/postfix/error/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/error/.indent.pro +++ b/postfix/error/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/fsstone/.indent.pro b/postfix/fsstone/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/fsstone/.indent.pro +++ b/postfix/fsstone/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/global/.indent.pro b/postfix/global/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/global/.indent.pro +++ b/postfix/global/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index 82bec8f2d..9bce34df4 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-19991227" +#define DEF_MAIL_VERSION "Postfix-19991231" extern char *var_mail_version; /* LICENSE diff --git a/postfix/html/faq.html b/postfix/html/faq.html index d5f5d8e65..fd4cce913 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -76,7 +76,7 @@
  • Root's mail is delivered to nobody -
  • Postfix accepts mail for non-existing users +
  • Postfix accepts mail for non-existing local users
  • Postfix sends duplicate mail @@ -118,7 +118,7 @@ virtual domains
  • Root's mail is delivered to nobody -
  • Postfix accepts mail for non-existing users +
  • Postfix accepts mail for non-existing local users
  • Delivering some users locally while sending mail as user@domain @@ -569,6 +569,30 @@ Put the following command into your PPP or SLIP dialup scripts: The exact location of the sendmail command is system-specific. With some UNIX versions, use /usr/lib/sendmail. +

    + +In order to find out if the mail queue is flushed, use something +like: + +

    +

    +    #!/bin/sh
    +
    +    # Start deliveries.
    +    /usr/sbin/sendmail -q
    +
    +    # Allow deliveries to start.
    +    sleep 10
    +
    +    # Loop until all messages have been tried at least once.
    +    while mailq | grep '^[^ ]*\*' >/dev/null
    +    do  
    +        sleep 10
    +    done
    +
    + +

    + If you have disabled spontaneous SMTP mail delivery, you also need to run the above command every now and then while the dialup link is up, so that newly-posted mail @@ -704,18 +728,106 @@ mail for arbitrary non-local destinations: <<< 250 Ok: queued as A958F5A15 -

    Don't Panic! Upgrade to a Postfix version of 19991227 or later.

    -Older Postfix versions would either bounce the mail because -"test@some.other.site" is not a known local username (which is -good), or they would forward the mail to a primary MX host -for "some.site" which would then spam it into the Internet (which -is bad). +With earlier Postfix versions, + +

      + +
    1. Good but confusing: a Postfix primary MX host for some.site +accepts test@some.other.site@some.site then bounces it because +test@some.other.site is not a known local username. + +
    2. Good: a Postfix primary MX host for some.site rejects +other source-routed addresses such as test%some.other.site@some.site +or some.other.site!test@some.site. + +
    3. Loophole: a Postfix backup MX host for some.site forwards +source-routed addresses such as test@some.other.site@some.site +or test%some.other.site@some.site to a primary MX host for +some.site. Depending on the primary MX host's mailer +configuration, the primary MX host could then spam the mail into +the Internet. + +
    + +

    + +With newer Postfix versions, + +

      + +
    1. A Postfix primary MX host for some.site host rejects +test@some.other.site@some.site just like it rejects +test%some.other.site@some.site. This ends the confusion +mentioned in 1 above. + +
    2. A Postfix backup MX host for some.site host rejects +source-routed addresses including test@some.other.site@some.site. +This closes the loophole mentioned in 3 above. + +
    + +

    + +To be precise, Postfix UCE restrictions refuse to forward source-routed +addresses under the following conditions: + +

    + +

    + +

    + +However, a Postfix primary MX host for still forwards source-routed +addresses if received from a trusted client, just like it +did before. + +

    + +In order to have guaranteed protection against source-routed relaying +through trusted SMTP clients, specify a regular expression restriction +ahead of the other SMTPD recipient restrictions: + +

    + +

    +    /etc/postfix/main.cf:
    +        smtpd_recipient_restrictions = 
    +	    regexp:/etc/postfix/regexp_access
    +	    ...other restrictions...
    +
    +    /etc/postfix/regexp_access:
    +        /[%!@].*[%!@]/ 550 Sender specified routing is not supported here.
    +
    + +

    + +This would be installed on all MX hosts.


    @@ -1008,10 +1120,11 @@ To find out the location for your system, execute the command
    -

    Postfix accepts mail for non-existing users

    +

    Postfix accepts mail for non-existing local users

    The information in this section applies to Postfix versions 19991216 -and later. +and later. See elsewhere for
    unknown +virtual users.

    @@ -1024,7 +1137,7 @@ types of user databases. Of course mail for a non-existent local user will eventually bounce as undeliverable, but why accept such mail in the first place? You -can tell the Postfix SMTP server how find out if a user exists by +can tell the Postfix SMTP server how to find out if a user exists by listing all tables with local addresses in the local_recipient_maps parameter: @@ -1686,8 +1799,17 @@ host.

    @@ -656,7 +656,7 @@ or a subdomain thereof,
  • the resolved destination address matches $relay_domains or a subdomain thereof, and the address contains no sender-specified routing -(user@there@here), +(user@elsewhere@domain),
  • Postfix is the final destination: any destination that matches $mydestination,
    permit_auth_destination
    Ignore the client hostname. -Permit the request when: +Permit the request when one of the following is true:
    • the resolved destination address matches $relay_domains or a subdomain thereof, and the address contains no sender-specified routing -(user@there@here), +(user@elsewhere@domain),
    • Postfix is the final destination: any destination that matches $mydestination,
      reject_unauth_destination
      Ignore the client -hostname. Reject the request unless: +hostname. Reject the request unless one of the following is true: diff --git a/postfix/local/.indent.pro b/postfix/local/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/local/.indent.pro +++ b/postfix/local/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/man/man5/transport.5 b/postfix/man/man5/transport.5 index d6293343f..78983c8f0 100644 --- a/postfix/man/man5/transport.5 +++ b/postfix/man/man5/transport.5 @@ -30,6 +30,17 @@ Mail for \fIdomain\fR is delivered through \fItransport\fR to .IP "\fI.domain transport\fR:\fInexthop\fR" Mail for any subdomain of \fIdomain\fR is delivered through \fItransport\fR to \fInexthop\fR. +.PP +Note: transport map entries take precedence over domains +specified in the \fBmydestination\fR parameter. If you use +the optional transport map, it may be safer to specify explicit +entries for all domains specified in \fBmydestination\fR, +for example: + +.ti +5 +\fBhostname.my.domain local:\fR +.ti +5 +\fBlocalhost.my.domain local:\fR The interpretation of the \fInexthop\fR field is transport dependent. In the case of SMTP, specify \fIhost\fR:\fIservice\fR for a diff --git a/postfix/master/.indent.pro b/postfix/master/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/master/.indent.pro +++ b/postfix/master/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/pickup/.indent.pro b/postfix/pickup/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/pickup/.indent.pro +++ b/postfix/pickup/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/pipe/.indent.pro b/postfix/pipe/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/pipe/.indent.pro +++ b/postfix/pipe/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postalias/.indent.pro b/postfix/postalias/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postalias/.indent.pro +++ b/postfix/postalias/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postcat/.indent.pro b/postfix/postcat/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postcat/.indent.pro +++ b/postfix/postcat/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postconf/.indent.pro b/postfix/postconf/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postconf/.indent.pro +++ b/postfix/postconf/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postdrop/.indent.pro b/postfix/postdrop/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postdrop/.indent.pro +++ b/postfix/postdrop/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postfix/.indent.pro b/postfix/postfix/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postfix/.indent.pro +++ b/postfix/postfix/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postkick/.indent.pro b/postfix/postkick/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postkick/.indent.pro +++ b/postfix/postkick/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postlock/.indent.pro b/postfix/postlock/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postlock/.indent.pro +++ b/postfix/postlock/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postlog/.indent.pro b/postfix/postlog/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postlog/.indent.pro +++ b/postfix/postlog/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postmap/.indent.pro b/postfix/postmap/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postmap/.indent.pro +++ b/postfix/postmap/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/postsuper/.indent.pro b/postfix/postsuper/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/postsuper/.indent.pro +++ b/postfix/postsuper/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/qmgr/.indent.pro b/postfix/qmgr/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/qmgr/.indent.pro +++ b/postfix/qmgr/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/sendmail/.indent.pro b/postfix/sendmail/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/sendmail/.indent.pro +++ b/postfix/sendmail/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/showq/.indent.pro b/postfix/showq/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/showq/.indent.pro +++ b/postfix/showq/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/smtp/.indent.pro b/postfix/smtp/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/smtp/.indent.pro +++ b/postfix/smtp/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/smtpd/.indent.pro b/postfix/smtpd/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/smtpd/.indent.pro +++ b/postfix/smtpd/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c index 3be739290..0171c1e6a 100644 --- a/postfix/smtpd/smtpd_check.c +++ b/postfix/smtpd/smtpd_check.c @@ -962,6 +962,12 @@ static int permit_mx_backup(SMTPD_STATE *unused_state, const char *recipient) if (msg_verbose) msg_info("%s: not local: %s", myname, recipient); + /* + * Skip source-routed mail (uncertain destination). + */ + if (var_allow_untrust_route == 0 && (reply.flags & RESOLVE_FLAG_ROUTED)) + return (SMTPD_CHECK_DUNNO); + /* * Skip numerical forms that didn't match the local system. */ @@ -1184,11 +1190,19 @@ static int check_table_result(SMTPD_STATE *state, char *table, } /* - * Recursively evaluate the restrictions given in the right-hand side. + * Recursively evaluate the restrictions given in the right-hand side. In + * the dark ages, an empty right-hand side meant OK. Make some + * discouraging comments. */ restrictions = argv_split(value, " \t\r\n,"); - status = generic_checks(state, restrictions, reply_name, - reply_class, def_acl); + if (restrictions->argc == 0) { + msg_warn("SMTPD access map %s entry %s has empty value", + table, value); + status = SMTPD_CHECK_OK; + } else { + status = generic_checks(state, restrictions, reply_name, + reply_class, def_acl); + } argv_free(restrictions); return (status); } diff --git a/postfix/smtpstone/.indent.pro b/postfix/smtpstone/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/smtpstone/.indent.pro +++ b/postfix/smtpstone/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/spawn/.indent.pro b/postfix/spawn/.indent.pro new file mode 100644 index 000000000..4a836704c --- /dev/null +++ b/postfix/spawn/.indent.pro @@ -0,0 +1,111 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-TCLNT_STREAM +-TCONFIG_BOOL_FN_TABLE +-TCONFIG_BOOL_TABLE +-TCONFIG_INT_FN_TABLE +-TCONFIG_INT_TABLE +-TCONFIG_STR_FN_TABLE +-TCONFIG_STR_TABLE +-TDELIVER_ATTR +-TDELIVER_REQUEST +-TDICT +-TDICT_DB +-TDICT_DBM +-TDICT_ENV +-TDICT_HT +-TDICT_LDAP +-TDICT_MYSQL +-TDICT_NI +-TDICT_NIS +-TDICT_NISPLUS +-TDICT_NODE +-TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_REGEXP +-TDICT_REGEXP_RULE +-TDICT_UNIX +-TDNS_FIXED +-TDNS_REPLY +-TDNS_RR +-TDOMAIN_LIST +-TEXPAND_ATTR +-TFILE +-TFORWARD_INFO +-THEADER_OPTS +-THOST +-THTABLE +-THTABLE_INFO +-TINET_ADDR_LIST +-TINT_TABLE +-TLOCAL_EXP +-TLOCAL_STATE +-TMAC_EXP +-TMAC_HEAD +-TMAC_PARSE +-TMAIL_PRINT +-TMAIL_SCAN +-TMAPS +-TMASTER_PROC +-TMASTER_SERV +-TMASTER_STATUS +-TMBLOCK +-TMKMAP +-TMKMAP_OPEN_INFO +-TMULTI_SERVER +-TMVECT +-TMYSQL_NAME +-TNAMADR_LIST +-TNAME_MASK +-TPEER_NAME +-TPICKUP_INFO +-TPIPE_ATTR +-TPIPE_PARAMS +-TPLMYSQL +-TQMGR_ENTRY +-TQMGR_MESSAGE +-TQMGR_QUEUE +-TQMGR_RCPT_LIST +-TQMGR_RECIPIENT +-TQMGR_SCAN +-TQMGR_TRANSPORT +-TRECIPIENT +-TRECIPIENT_LIST +-TREC_TYPE_NAME +-TRESOLVE_REPLY +-TRESPONSE +-TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION +-TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE +-TSMTPD_CMD +-TSMTPD_STATE +-TSMTPD_TOKEN +-TSMTP_ADDR +-TSMTP_CMD +-TSMTP_RESP +-TSMTP_SESSION +-TSMTP_STATE +-TSOCKADDR_SIZE +-TSPAWN_ATTR +-TSTRING_TABLE +-TSYS_EXITS_TABLE +-TTOK822 +-TTRIGGER_SERVER +-TUSER_ATTR +-TVBUF +-TVSTREAM +-TVSTREAM_POPEN_ARGS +-TVSTRING +-TWAIT_STATUS_T +-TWATCHDOG +-TWATCH_FD diff --git a/postfix/spawn/.printfck b/postfix/spawn/.printfck new file mode 100644 index 000000000..65eb6bfa6 --- /dev/null +++ b/postfix/spawn/.printfck @@ -0,0 +1,25 @@ +been_here_xt 2 0 +bounce_append 5 0 +cleanup_out_format 1 0 +defer_append 5 0 +mail_command 1 0 +mail_print 1 0 +msg_error 0 0 +msg_fatal 0 0 +msg_info 0 0 +msg_panic 0 0 +msg_warn 0 0 +opened 3 0 +post_mail_fprintf 1 0 +qmgr_message_bounce 2 0 +rec_fprintf 2 0 +sent 4 0 +smtp_cmd 1 0 +smtp_mesg_fail 2 0 +smtp_printf 1 0 +smtp_rcpt_fail 3 0 +smtp_site_fail 2 0 +udp_syslog 1 0 +vstream_fprintf 1 0 +vstream_printf 0 0 +vstring_sprintf 1 0 diff --git a/postfix/spawn/Makefile.in b/postfix/spawn/Makefile.in new file mode 100644 index 000000000..b04e71303 --- /dev/null +++ b/postfix/spawn/Makefile.in @@ -0,0 +1,70 @@ +SHELL = /bin/sh +SRCS = spawn.c +OBJS = spawn.o +HDRS = +TESTSRC = +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +TESTPROG= +PROG = spawn +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../lib/libglobal.a ../lib/libutil.a + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../makedefs; cat $?) >$@ + +test: $(TESTPROG) + +update: ../libspawn/$(PROG) + +../libspawn/$(PROG): $(PROG) + cp $(PROG) ../libspawn + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../include" `cd ..; ls *.o` + +lint: + lint $(DEFS) $(SRCS) $(LINTFIX) + +clean: + rm -f *.o *core $(PROG) $(TESTPROG) junk + rm -rf printfck + +tidy: clean + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \ + done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @make -f Makefile.in Makefile + +# do not edit below this line - it is generated by 'make depend' +spawn.o: spawn.c +spawn.o: ../include/sys_defs.h +spawn.o: ../include/msg.h +spawn.o: ../include/argv.h +spawn.o: ../include/dict.h +spawn.o: ../include/vstream.h +spawn.o: ../include/vbuf.h +spawn.o: ../include/mymalloc.h +spawn.o: ../include/spawn_command.h +spawn.o: ../include/split_at.h +spawn.o: ../include/timed_wait.h +spawn.o: ../include/set_eugid.h +spawn.o: ../include/mail_params.h +spawn.o: ../include/mail_server.h +spawn.o: ../include/mail_conf.h diff --git a/postfix/spawn/spawn.c b/postfix/spawn/spawn.c new file mode 100644 index 000000000..06e094bd5 --- /dev/null +++ b/postfix/spawn/spawn.c @@ -0,0 +1,304 @@ +/*++ +/* NAME +/* spawn 8 +/* SUMMARY +/* Postfix external command spawner +/* SYNOPSIS +/* \fBspawn\fR [generic Postfix daemon options] command_attributes... +/* DESCRIPTION +/* The \fBspawn\fR daemon provides the Postfix equivalent of \fBinetd\fR. +/* It listens on a port as specified in the Postfix \fBmaster.cf\fR file +/* and spawns an external command whenever a connection is established. +/* The connection can be made over local IPC (such as UNIX-domain +/* sockets) or over non-local IPC (such as TCP sockets). +/* The command\'s standard input, output and error streams are connected +/* directly to the communication endpoint. +/* +/* This daemon expects to be run from the \fBmaster\fR(8) process +/* manager. +/* COMMAND ATTRIBUTE SYNTAX +/* .ad +/* .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 "\fBuser\fR=\fIusername\fR (required)" +/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR" +/* The external command is executed with the rights of the +/* specified \fIusername\fR. The software refuses to execute +/* 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 +/* of \fIusername\fR. +/* .IP "\fBargv\fR=\fIcommand\fR... (required)" +/* The command to be executed. This must be specified as the +/* last command attribute. +/* The command is executed directly, i.e. without interpretation of +/* shell meta characters by a shell command interpreter. +/* BUGS +/* In order to enforce standard Postfix process resource controls, +/* the \fBspawn\fR daemon runs only one external command at a time. +/* As such, it presents a noticeable overhead by wasting precious +/* process resources. The \fBspawn\fR daemon is expected to be +/* replaced by a more structural solution. +/* DIAGNOSTICS +/* The \fBspawn\fR daemon reports abnormal child exits. +/* Problems are logged to \fBsyslogd\fR(8). +/* SECURITY +/* .fi +/* .ad +/* This program needs root privilege in order to execute external +/* commands as the specified user. It is therefore security sensitive. +/* However the \fBspawn\fR daemon does not talk to the external command +/* and thus is not vulnerable to data-driven attacks. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBmail_owner\fR +/* The process privileges used while not running an external command. +/* .SH Resource control +/* .ad +/* .fi +/* .IP \fIservice\fB_command_time_limit\fR +/* The amount of time the command is allowed to run before it is +/* killed with force. The \fIservice\fR name is the name of the entry +/* in the \fBmastr.cf\fR file. The default time limit is given by the +/* global \fBcommand_time_limit\fR configuration parameter. +/* SEE ALSO +/* master(8) process manager +/* syslogd(8) system logging +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Single server skeleton. */ + +#include +#include +#include + +/* Application-specific. */ + + /* + * Tunable parameters. Values are taken from the config file, after + * prepending the service name to _name, and so on. + */ +int var_command_maxtime; /* system-wide */ + + /* + * For convenience. Instead of passing around lists of parameters, bundle + * them up in convenient structures. + */ +typedef struct { + char **argv; /* argument vector */ + uid_t uid; /* command privileges */ + gid_t gid; /* command privileges */ + int time_limit; /* per-service time limit */ +} SPAWN_ATTR; + +/* get_service_attr - get service attributes */ + +static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv) +{ + char *myname = "get_service_attr"; + struct passwd *pwd; + struct group *grp; + char *user; /* user name */ + char *group; /* group name */ + + /* + * Initialize. + */ + user = 0; + group = 0; + attr->argv = 0; + + /* + * Figure out the command time limit for this transport. + */ + attr->time_limit = + get_mail_conf_int2(service, "_time_limit", var_command_maxtime, 1, 0); + + /* + * Iterate over the command-line attribute list. + */ + for ( /* void */ ; *argv != 0; argv++) { + + /* + * user=username[:groupname] + */ + if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) { + user = *argv + sizeof("user=") - 1; + if ((group = split_at(user, ':')) != 0) /* XXX clobbers argv */ + if (*group == 0) + group = 0; + if ((pwd = getpwnam(user)) == 0) + msg_fatal("%s: unknown username: %s", myname, user); + attr->uid = pwd->pw_uid; + if (group != 0) { + if ((grp = getgrnam(group)) == 0) + msg_fatal("%s: unknown group: %s", myname, group); + attr->gid = grp->gr_gid; + } else { + attr->gid = pwd->pw_gid; + } + } + + /* + * argv=command... + */ + else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) { + *argv += sizeof("argv=") - 1; /* XXX clobbers argv */ + attr->argv = argv; + break; + } + + /* + * Bad. + */ + else + msg_fatal("unknown attribute name: %s", *argv); + } + + /* + * Sanity checks. Verify that every member has an acceptable value. + */ + if (user == 0) + msg_fatal("missing user= attribute"); + if (attr->argv == 0) + msg_fatal("missing argv= attribute"); + if (attr->uid == 0) + msg_fatal("request to deliver as root"); + if (attr->uid == var_owner_uid) + msg_fatal("request to deliver as mail system owner"); + if (attr->gid == 0) + msg_fatal("request to use privileged group id %d", attr->gid); + if (attr->gid == var_owner_gid) + msg_fatal("request to use mail system owner group id %d", attr->gid); + + /* + * Give the poor tester a clue of what is going on. + */ + if (msg_verbose) + msg_info("%s: uid %d, gid %d; time %d", + myname, attr->uid, attr->gid, attr->time_limit); +} + +/* spawn_service - perform service for client */ + +static void spawn_service(VSTREAM *client_stream, char *service, char **argv) +{ + char *myname = "spawn_service"; + static SPAWN_ATTR attr; + WAIT_STATUS_T status; + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * dedicated to running an external command. + */ + if (msg_verbose) + msg_info("%s: service=%s, command=%s...", myname, service, argv[0]); + + /* + * Look up service attributes and config information only once. This is + * safe since the information comes from a trusted source. + */ + if (attr.argv == 0) { + get_service_attr(&attr, service, argv); + } + + /* + * Execute the command. + */ + status = spawn_command(SPAWN_CMD_STDIN, vstream_fileno(client_stream), + SPAWN_CMD_STDOUT, vstream_fileno(client_stream), + SPAWN_CMD_STDERR, vstream_fileno(client_stream), + SPAWN_CMD_UID, attr.uid, + SPAWN_CMD_GID, attr.gid, + SPAWN_CMD_ARGV, attr.argv, + SPAWN_CMD_TIME_LIMIT, attr.time_limit, + SPAWN_CMD_END); + + /* + * Warn about unsuccessful completion. + */ + if (!NORMAL_EXIT_STATUS(status)) { + if (WIFEXITED(status)) + msg_warn("command %s exit status %d", + attr.argv[0], WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + msg_warn("command %s killed by signal %d", + attr.argv[0], WTERMSIG(status)); + } +} + +/* pre_accept - see if tables have changed */ + +static void pre_accept(char *unused_name, char **unused_argv) +{ + if (dict_changed()) { + msg_info("table has changed -- exiting"); + exit(0); + } +} + +/* drop_privileges - drop privileges most of the time */ + +static void drop_privileges(char *unused_name, char **unused_argv) +{ + set_eugid(var_owner_uid, var_owner_gid); +} + +/* main - pass control to the single-threaded skeleton */ + +int main(int argc, char **argv) +{ + static CONFIG_INT_TABLE int_table[] = { + VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0, + 0, + }; + + single_server_main(argc, argv, spawn_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_POST_INIT, drop_privileges, + MAIL_SERVER_PRE_ACCEPT, pre_accept, + 0); +} diff --git a/postfix/trivial-rewrite/.indent.pro b/postfix/trivial-rewrite/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/trivial-rewrite/.indent.pro +++ b/postfix/trivial-rewrite/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/util/.indent.pro b/postfix/util/.indent.pro index dec6edcbf..4a836704c 100644 --- a/postfix/util/.indent.pro +++ b/postfix/util/.indent.pro @@ -96,6 +96,7 @@ -TSMTP_SESSION -TSMTP_STATE -TSOCKADDR_SIZE +-TSPAWN_ATTR -TSTRING_TABLE -TSYS_EXITS_TABLE -TTOK822 diff --git a/postfix/util/Makefile.in b/postfix/util/Makefile.in index 6d7c51246..b13d41aa2 100644 --- a/postfix/util/Makefile.in +++ b/postfix/util/Makefile.in @@ -20,7 +20,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ vstream.c vstream_popen.c vstring.c vstring_vstream.c writable.c \ write_buf.c write_wait.c dict_unix.c dict_pcre.c stream_listen.c \ stream_connect.c stream_trigger.c dict_regexp.c mac_expand.c \ - clean_env.c watchdog.c + clean_env.c watchdog.c spawn_command.c OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \ dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ @@ -42,7 +42,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ vstream.o vstream_popen.o vstring.o vstring_vstream.o writable.o \ write_buf.o write_wait.o dict_unix.o dict_pcre.o stream_listen.o \ stream_connect.o stream_trigger.o dict_regexp.o mac_expand.o \ - clean_env.o watchdog.o + clean_env.o watchdog.o spawn_command.o HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \ dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \ @@ -57,7 +57,7 @@ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ timed_connect.h timed_wait.h trigger.h username.h valid_hostname.h \ vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \ dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \ - watchdog.h + watchdog.h spawn_command.h TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ stream_test.c dup2_pass_on_exec.c WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ @@ -746,6 +746,15 @@ sigdelay.o: sigdelay.h skipblanks.o: skipblanks.c skipblanks.o: sys_defs.h skipblanks.o: stringops.h +spawn_command.o: spawn_command.c +spawn_command.o: sys_defs.h +spawn_command.o: msg.h +spawn_command.o: timed_wait.h +spawn_command.o: set_ugid.h +spawn_command.o: argv.h +spawn_command.o: spawn_command.h +spawn_command.o: exec_command.h +spawn_command.o: clean_env.h split_at.o: split_at.c split_at.o: sys_defs.h split_at.o: split_at.h diff --git a/postfix/util/spawn_command.c b/postfix/util/spawn_command.c new file mode 100644 index 000000000..3f009d36e --- /dev/null +++ b/postfix/util/spawn_command.c @@ -0,0 +1,298 @@ +/*++ +/* NAME +/* spawn_command 3 +/* SUMMARY +/* run external command +/* SYNOPSIS +/* #include +/* +/* WAIT_STATUS_T spawn_command(key, value, ...) +/* int key; +/* DESCRIPTION +/* spawn_command() runs a command in a child process and returns +/* the command exit status. +/* +/* Arguments: +/* .IP key +/* Specifies what value will follow. spawn_command() takes a list +/* of (key, value) arguments, terminated by SPAWN_CMD_END. The +/* following is a listing of key codes together with the expected +/* value type. +/* .RS +/* .IP "SPAWN_CMD_COMMAND (char *)" +/* Specifies the command to execute as a string. The string is +/* passed to the shell when it contains shell meta characters +/* or when it appears to be a shell built-in command, otherwise +/* the command is executed without invoking a shell. +/* One of SPAWN_CMD_COMMAND or SPAWN_CMD_ARGV must be specified. +/* See also the SPAWN_CMD_SHELL attribute below. +/* .IP "SPAWN_CMD_ARGV (char **)" +/* The command is specified as an argument vector. This vector is +/* passed without further inspection to the \fIexecvp\fR() routine. +/* One of SPAWN_CMD_COMMAND or SPAWN_CMD_ARGV must be specified. +/* .IP "SPAWN_CMD_ENV (char **)" +/* Additional environment information, in the form of a null-terminated +/* list of name, value, name, value, ... elements. By default only the +/* command search path is initialized to _PATH_DEFPATH. +/* .IP "SPAWN_CMD_STDIN (int)" +/* .IP "SPAWN_CMD_STDOUT (int)" +/* .IP "SPAWN_CMD_STDERR (int)" +/* Each of these specifies I/O redirection of one of the standard file +/* descriptors for the command. +/* .IP "SPAWN_CMD_UID (int)" +/* The user ID to execute the command as. +/* .IP "SPAWN_CMD_GID (int)" +/* The group ID to execute the command as. +/* .IP "SPAWN_CMD_TIME_LIMIT (int)" +/* The amount of time in seconds the command is allowed to run before +/* it is terminated with SIGKILL. The default is no time limit. +/* .IP "SPAWN_CMD_SHELL (char *)" +/* The shell to use when executing the command specified with +/* SPAWN_CMD_COMMAND. This shell is invoked regardless of the +/* command content. +/* .RE +/* DIAGNOSTICS +/* Panic: interface violations (for example, a missing command). +/* +/* Fatal error: fork() failure, other system call failures. +/* +/* spawn_command() returns the exit status as defined by wait(2). +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* SEE ALSO +/* exec_command(3) execute command +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_PATHS_H +#include +#endif +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +struct spawn_args { + char **argv; /* argument vector */ + char *command; /* or a plain string */ + int stdin_fd; /* read stdin here */ + int stdout_fd; /* write stdout here */ + int stderr_fd; /* write stderr here */ + uid_t uid; /* privileges */ + gid_t gid; /* privileges */ + char **env; /* extra environment */ + char *shell; /* command shell */ + int time_limit; /* command time limit */ +}; + +/* get_spawn_args - capture the variadic argument list */ + +static void get_spawn_args(struct spawn_args * args, int init_key, va_list ap) +{ + char *myname = "get_spawn_args"; + int key; + + /* + * First, set the default values. + */ + args->argv = 0; + args->command = 0; + args->stdin_fd = -1; + args->stdout_fd = -1; + args->stderr_fd = -1; + args->uid = (uid_t) - 1; + args->gid = (gid_t) - 1; + args->env = 0; + args->shell = 0; + args->time_limit = 0; + + /* + * Then, override the defaults with user-supplied inputs. + */ + for (key = init_key; key != SPAWN_CMD_END; key = va_arg(ap, int)) { + switch (key) { + case SPAWN_CMD_ARGV: + if (args->command) + msg_panic("%s: specify SPAWN_CMD_ARGV or SPAWN_CMD_COMMAND", + myname); + args->argv = va_arg(ap, char **); + break; + case SPAWN_CMD_COMMAND: + if (args->argv) + msg_panic("%s: specify SPAWN_CMD_ARGV or SPAWN_CMD_COMMAND", + myname); + args->command = va_arg(ap, char *); + break; + case SPAWN_CMD_STDIN: + args->stdin_fd = va_arg(ap, int); + break; + case SPAWN_CMD_STDOUT: + args->stdout_fd = va_arg(ap, int); + break; + case SPAWN_CMD_STDERR: + args->stderr_fd = va_arg(ap, int); + break; + case SPAWN_CMD_UID: + args->uid = va_arg(ap, int); /* in case uid_t is short */ + break; + case SPAWN_CMD_GID: + args->gid = va_arg(ap, int); /* in case gid_t is short */ + break; + case SPAWN_CMD_TIME_LIMIT: + args->time_limit = va_arg(ap, int); + break; + case SPAWN_CMD_ENV: + args->env = va_arg(ap, char **); + break; + case SPAWN_CMD_SHELL: + args->shell = va_arg(ap, char *); + break; + default: + msg_panic("%s: unknown key: %d", myname, key); + } + } + if (args->command == 0 && args->argv == 0) + msg_panic("%s: missing SPAWN_CMD_ARGV or SPAWN_CMD_COMMAND", myname); + if (args->command == 0 && args->shell != 0) + msg_panic("%s: SPAWN_CMD_ARGV cannot be used with SPAWN_CMD_SHELL", + myname); +} + +/* spawn_command - execute command with extreme prejudice */ + +WAIT_STATUS_T spawn_command(int key,...) +{ + char *myname = "spawn_comand"; + va_list ap; + pid_t pid; + WAIT_STATUS_T wait_status; + struct spawn_args args; + char **cpp; + ARGV *argv; + int err; + + /* + * Process the variadic argument list. This also does sanity checks on + * what data the caller is passing to us. + */ + va_start(ap, key); + get_spawn_args(&args, key, ap); + va_end(ap); + + /* + * For convenience... + */ + if (args.command == 0) + args.command = args.argv[0]; + + /* + * Spawn off a child process and irrevocably change privilege to the + * user. This includes revoking all rights on open files (via the close + * on exec flag). If we cannot run the command now, try again some time + * later. + */ + switch (pid = fork()) { + + /* + * Error. Instead of trying again right now, back off, give the + * system a chance to recover, and try again later. + */ + case -1: + msg_fatal("fork: %m"); + + /* + * Child. Run the child in a separate process group so that the + * parent can kill not just the child but also its offspring. + */ + case 0: + if (args.uid != (uid_t) - 1 || args.gid != (gid_t) - 1) + set_ugid(args.uid, args.gid); + setsid(); + + /* + * Pipe plumbing. + */ + if ((args.stdin_fd >= 0 && DUP2(args.stdin_fd, STDIN_FILENO) < 0) + || (args.stdout_fd >= 0 && DUP2(args.stdout_fd, STDOUT_FILENO) < 0) + || (args.stderr_fd >= 0 && DUP2(args.stderr_fd, STDERR_FILENO) < 0)) + msg_fatal("%s: dup2: %m", myname); + + /* + * Environment plumbing. Always reset the command search path. XXX + * That should probably be done by clean_env(). + */ + clean_env(); + if (setenv("PATH", _PATH_DEFPATH, 1)) + msg_fatal("%s: setenv: %m", myname); + if (args.env) + for (cpp = args.env; *cpp; cpp += 2) + if (setenv(cpp[0], cpp[1], 1)) + msg_fatal("setenv: %m"); + + /* + * Process plumbing. If possible, avoid running a shell. + */ + closelog(); + if (args.argv) { + execvp(args.argv[0], args.argv); + msg_fatal("%s: execvp %s: %m", myname, args.argv[0]); + } else if (args.shell && *args.shell) { + argv = argv_split(args.shell, " \t\r\n"); + argv_add(argv, args.command, (char *) 0); + argv_terminate(argv); + execvp(argv->argv[0], argv->argv); + msg_fatal("%s: execvp %s: %m", myname, argv->argv[0]); + } else { + exec_command(args.command); + } + /* NOTREACHED */ + + /* + * Parent. + */ + default: + + /* + * Be prepared for the situation that the child does not terminate. + * Make sure that the child terminates before the parent attempts to + * retrieve its exit status, otherwise the parent could become stuck, + * and the mail system would eventually run out of exec daemons. Do a + * thorough job, and kill not just the child process but also its + * offspring. + */ + if ((err = timed_waitpid(pid, &wait_status, 0, args.time_limit)) < 0 + && errno == ETIMEDOUT) { + msg_warn("%s: process id %d: command time limit exceeded", + args.command, pid); + kill(-pid, SIGKILL); + err = waitpid(pid, &wait_status, 0); + } + if (err < 0) + msg_fatal("wait: %m"); + return (wait_status); + } +} diff --git a/postfix/util/spawn_command.h b/postfix/util/spawn_command.h new file mode 100644 index 000000000..a6addf53f --- /dev/null +++ b/postfix/util/spawn_command.h @@ -0,0 +1,42 @@ +#ifndef _SPAWN_COMMAND_H_INCLUDED_ +#define _SPAWN_COMMAND_H_INCLUDED_ + +/*++ +/* NAME +/* spawn_command 3h +/* SUMMARY +/* run external command +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Request arguments. + */ +#define SPAWN_CMD_END 0 /* terminator */ +#define SPAWN_CMD_ARGV 1 /* command is array */ +#define SPAWN_CMD_COMMAND 2 /* command is string */ +#define SPAWN_CMD_STDIN 3 /* mail_copy() flags */ +#define SPAWN_CMD_STDOUT 4 /* mail_copy() sender */ +#define SPAWN_CMD_STDERR 5 /* mail_copy() recipient */ +#define SPAWN_CMD_UID 6 /* privileges */ +#define SPAWN_CMD_GID 7 /* privileges */ +#define SPAWN_CMD_TIME_LIMIT 8 /* time limit */ +#define SPAWN_CMD_ENV 9 /* extra environment */ +#define SPAWN_CMD_SHELL 10 /* alternative shell */ + +extern int spawn_command(int,...); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif