]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-19991231
authorWietse Venema <wietse@porcupine.org>
Fri, 31 Dec 1999 05:00:00 +0000 (00:00 -0500)
committerWietse Venema <wietse@porcupine.org>
Thu, 17 Jan 2013 23:10:04 +0000 (18:10 -0500)
48 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/INSTALL.sh
postfix/Makefile.in
postfix/RELEASE_NOTES
postfix/bounce/.indent.pro
postfix/cleanup/.indent.pro
postfix/conf/master.cf
postfix/conf/transport
postfix/dns/.indent.pro
postfix/error/.indent.pro
postfix/fsstone/.indent.pro
postfix/global/.indent.pro
postfix/global/mail_version.h
postfix/html/faq.html
postfix/html/transport.5.html
postfix/html/uce.html
postfix/local/.indent.pro
postfix/man/man5/transport.5
postfix/master/.indent.pro
postfix/pickup/.indent.pro
postfix/pipe/.indent.pro
postfix/postalias/.indent.pro
postfix/postcat/.indent.pro
postfix/postconf/.indent.pro
postfix/postdrop/.indent.pro
postfix/postfix/.indent.pro
postfix/postkick/.indent.pro
postfix/postlock/.indent.pro
postfix/postlog/.indent.pro
postfix/postmap/.indent.pro
postfix/postsuper/.indent.pro
postfix/qmgr/.indent.pro
postfix/sendmail/.indent.pro
postfix/showq/.indent.pro
postfix/smtp/.indent.pro
postfix/smtpd/.indent.pro
postfix/smtpd/smtpd_check.c
postfix/smtpstone/.indent.pro
postfix/spawn/.indent.pro [new file with mode: 0644]
postfix/spawn/.printfck [new file with mode: 0644]
postfix/spawn/Makefile.in [new file with mode: 0644]
postfix/spawn/spawn.c [new file with mode: 0644]
postfix/trivial-rewrite/.indent.pro
postfix/util/.indent.pro
postfix/util/Makefile.in
postfix/util/spawn_command.c [new file with mode: 0644]
postfix/util/spawn_command.h [new file with mode: 0644]

index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index 31cc0327f1ff0df70f6399fefc11ffda020fbdf6..147acda81ac13077b3bd8f054ffd484a4e2c3a12 100644 (file)
@@ -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.
index 4fceb90d33f894d1b060c72cdee28d884c37b9c8..d6a82f21fd1cecd61c42f3e85c9469213c34e355 100644 (file)
@@ -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
index 9733deb7ee50a7849cd71b71ce7fdfa22dc2f949..fd29c9d46879e288095f6c06d0a73cced5bbd49d 100644 (file)
@@ -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
 
index ebdd3f7833701124514ea1849676d747c0d87fa0..02eb24fa98b9eccaf80f1f20e35448e127e0fd93 100644 (file)
@@ -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.
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index e33946f035a0e90b87fe9ea98913ac9af14d9886..c3a9979fbf9fb2dde84460e864aba5bee2d1f911 100644 (file)
@@ -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
-
index cfcb2659ba21516439c6be3fec86fd4ce6ad641e..258a7b37dede338d21a734e3b3760dccfee21c42 100644 (file)
 # .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
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index 82bec8f2db0bf201542d4054bbde8442b3dbf7d1..9bce34df4dda123e39fd3c3aa22a569b29d2254a 100644 (file)
@@ -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
index d5f5d8e651a262cce28cefe867c5f4350710c4c2..fd4cce913e3f8efafc97c7d6f774d73b8a81e2d1 100644 (file)
@@ -76,7 +76,7 @@
 
 <li><a href="#root">Root's mail is delivered to nobody</a>
 
-<li><a href="#bogus">Postfix accepts mail for non-existing users</a>
+<li><a href="#bogus">Postfix accepts mail for non-existing local users</a>
 
 <li><a href="#duplicate">Postfix sends duplicate mail</a>
 
@@ -118,7 +118,7 @@ virtual domains</a>
 
 <li><a href="#root">Root's mail is delivered to nobody</a>
 
-<li><a href="#bogus">Postfix accepts mail for non-existing users</a>
+<li><a href="#bogus">Postfix accepts mail for non-existing local users</a>
 
 <li><a href="#some_local">Delivering some users locally while
 sending mail as user@domain</a>
@@ -569,6 +569,30 @@ Put the following command into your PPP or SLIP dialup scripts:
 The exact location of the <b>sendmail</b> command is system-specific.
 With some UNIX versions, use <b>/usr/lib/sendmail</b>.
 
+<p>
+
+In order to find out if the mail queue is flushed, use something
+like:
+
+<p>
+<pre>
+    #!/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 '^[^ ]*\*' &gt;/dev/null
+    do  
+        sleep 10
+    done
+</pre>
+
+<p>
+
 If you have disabled <a href="#spontaneous_smtp">spontaneous SMTP
 mail delivery</a>, 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:
     &lt;&lt;&lt; 250 Ok: queued as A958F5A15
 </pre>
 
-
 <p>
 
 Don't Panic!  Upgrade to a Postfix version of 19991227 or later.
 
 <p>
 
-Older Postfix versions would either <i>bounce</i> the mail because
-"test@some.other.site" is not a known local username (which is
-good), or they would <i>forward</i> 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, 
+
+<ol>
+
+<li>Good but confusing: a Postfix primary MX host for <i>some.site</i>
+accepts <i>test@some.other.site@some.site</i> then bounces it because
+<i>test@some.other.site</i> is not a known local username.
+
+<li>Good: a Postfix primary MX host for <i>some.site</i> rejects
+other source-routed addresses such as <i>test%some.other.site@some.site</i>
+or <i>some.other.site!test@some.site</i>.
+
+<li>Loophole: a Postfix backup MX host for <i>some.site</i> forwards
+source-routed addresses such as <i>test@some.other.site@some.site</i>
+or <i>test%some.other.site@some.site</i> to a primary MX host for
+<i>some.site</i>. Depending on the primary MX host's mailer
+configuration, the primary MX host could then spam the mail into
+the Internet.
+
+</ol>
+
+<p>
+
+With newer Postfix versions,
+
+<ol>
+
+<li>A Postfix primary MX host for <i>some.site</i> host rejects
+<i>test@some.other.site@some.site</i> just like it rejects
+<i>test%some.other.site@some.site</i>.  This ends the confusion
+mentioned in 1 above.
+
+<li>A Postfix backup MX host for <i>some.site</i> host rejects
+source-routed addresses including <i>test@some.other.site@some.site</i>.
+This closes the loophole mentioned in 3 above.
+
+</ol>
+
+<p>
+
+To be precise, Postfix UCE restrictions refuse to forward source-routed
+addresses under the following conditions:
+
+<p>
+
+<ul>
+
+<li> <a href="uce.html#check_relay_domains">check_relay_domains</a>:
+reject when the destination is not local and when the client hostname
+does not match <a href="uce.html#relay_domains">relay_domains</a>.
+
+<li> <a
+href="uce.html#permit_auth_destination">permit_auth_destination</a>:
+skip when the destination is not local.
+
+<li> <a
+href="uce.html#reject_unauth_destination">reject_unauth_destination</a>:
+reject when the destination is not local.
+
+<li> <a href="uce.html#permit_mx_backup">permit_mx_backup</a>:
+reject when the destination is not local.
+
+<li> Other UCE restrictions (e.g., SMTPD access maps) are not aware
+of sender-provided routing information.
+
+</ul>
+
+<p>
+
+However, a Postfix primary MX host for still forwards source-routed
+addresses <b>if received from a trusted client</b>, just like it
+did before.
+
+<p>
+
+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:
+
+<p>
+
+<pre>
+    <b>/etc/postfix/main.cf</b>:
+        smtpd_recipient_restrictions = 
+           regexp:/etc/postfix/regexp_access
+           <i>...other restrictions...</i>
+
+    <b>/etc/postfix/regexp_access</b>:
+        /[%!@].*[%!@]/ 550 Sender specified routing is not supported here.
+</pre>
+
+<p>
+
+This would be installed on all MX hosts.
 
 <hr>
 
@@ -1008,10 +1120,11 @@ To find out the location for your system, execute the command
 
 <hr>
 
-<a name="bogus"><h3>Postfix accepts mail for non-existing users</h3>
+<a name="bogus"><h3>Postfix accepts mail for non-existing local users</h3>
 
 The information in this section applies to Postfix versions 19991216
-and later.
+and later. See elsewhere for <a href="#unknown_virtual">unknown
+virtual</a> users.
 
 <p>
 
@@ -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 <b>local_recipient_maps</b>
 parameter:
 
@@ -1686,8 +1799,17 @@ host.
 
 <ul>
 
-<li>Specify that mail for, let's say, <i>some.domain</i>, should be
-delivered via UUCP, for example, to a host named <i>uucp-host</i>:
+<li>You need an <b>rmail</b> program that extracts the sender
+address from mail that arrives via UUCP, and that feeds the mail
+into the Postfix <b>sendmail<b> command.  Most UNIX systems come
+with an <b>rmail</b> utility. If you're in a pinch, try the one
+bundled with the Postfix source code in the <b>auxiliary</b>
+directory. Some day Postfix may have its own <b>rmail</b> command.
+
+<p>
+
+<li>Specify that mail for, let's say, <i>some.domain</i>, should
+be delivered via UUCP, for example, to a host named <i>uucp-host</i>:
 
 <p>
 
index b778c7b5d2f99fabc81f5857bfdffa9132a35422..bace70fa91db8c18569db5d08eca0751667cea2b 100644 (file)
@@ -37,12 +37,21 @@ TRANSPORT(5)                                         TRANSPORT(5)
               Mail for  any  subdomain  of  <i>domain</i>  is  delivered
               through <i>transport</i> to <i>nexthop</i>.
 
-              The  interpretation  of the <i>nexthop</i> field is trans-
-              port  dependent.  In  the  case  of  SMTP,  specify
-              <i>host</i>:<i>service</i> for a non-default server port, and use
-              [<i>host</i>] or [<i>host</i>:<i>port</i>] in order to disable MX  (mail
-              exchanger)  DNS  lookups.  The  [] form can also be
-              used with IP addresses instead of hostnames.
+       Note:  transport  map entries take precedence over domains
+       specified in the <b>mydestination</b> parameter. If you  use  the
+       optional  transport  map,  it  may  be  safer  to  specify
+       explicit entries for all domains specified  in  <b>mydestina-</b>
+       <b>tion</b>, for example:
+
+            <b>hostname.my.domain</b>       <b>local:</b>
+            <b>localhost.my.domain</b>      <b>local:</b>
+
+       The  interpretation  of  the  <i>nexthop</i>  field  is transport
+       dependent. In the case of SMTP, specify <i>host</i>:<i>service</i> for a
+       non-default  server port, and use [<i>host</i>] or [<i>host</i>:<i>port</i>] in
+       order to disable MX (mail exchanger) DNS lookups.  The  []
+       form  can  also be used with IP addresses instead of host-
+       names.
 
 <b>EXAMPLES</b>
        In order to send mail for <b>foo.org</b> and its subdomains
@@ -51,15 +60,6 @@ TRANSPORT(5)                                         TRANSPORT(5)
             <b>foo.org</b>      <b>uucp:foo</b>
             <b>.foo.org</b>     <b>uucp:foo</b>
 
-       When no <i>nexthop</i> host name is specified, the destination domain
-       name is used instead. For example, the following directs mail for
-       <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a mail
-       exchanger for <b>foo.org</b>.  The <b>slow</b> transport could be
-       something that runs at most one delivery process at a time:
-
-            <b>foo.org</b>      <b>slow:</b>
-
-
 
 
                                                                 1
@@ -71,6 +71,14 @@ TRANSPORT(5)                                         TRANSPORT(5)
 TRANSPORT(5)                                         TRANSPORT(5)
 
 
+       When no <i>nexthop</i> host name is specified, the destination domain
+       name is used instead. For example, the following directs mail for
+       <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a mail
+       exchanger for <b>foo.org</b>.  The <b>slow</b> transport could be
+       something that runs at most one delivery process at a time:
+
+            <b>foo.org</b>      <b>slow:</b>
+
        When no <i>transport</i> is specified, the default transport is
        used, as specified via the <b>default</b><i>_</i><b>transport</b> configuration
        parameter. The following sends all mail for <b>foo.org</b> and its
@@ -117,14 +125,6 @@ TRANSPORT(5)                                         TRANSPORT(5)
               The default host to send to when no transport table
               entry matches.
 
-<b>SEE</b> <b>ALSO</b>
-       <a href="postmap.1.html">postmap(1)</a> create mapping table
-       <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> rewrite and resolve addresses
-
-<b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
-       software.
-
 
 
 
@@ -137,6 +137,14 @@ TRANSPORT(5)                                         TRANSPORT(5)
 TRANSPORT(5)                                         TRANSPORT(5)
 
 
+<b>SEE</b> <b>ALSO</b>
+       <a href="postmap.1.html">postmap(1)</a> create mapping table
+       <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> rewrite and resolve addresses
+
+<b>LICENSE</b>
+       The  Secure  Mailer  license must be distributed with this
+       software.
+
 <b>AUTHOR(S)</b>
        Wietse Venema
        IBM T.J. Watson Research
@@ -177,14 +185,6 @@ TRANSPORT(5)                                         TRANSPORT(5)
 
 
 
-
-
-
-
-
-
-
-
 
 
 
index 7c82ad5d92130855cf46e66354c17b01dfdd497b..ce35418fba43b77eb7eec18ada8c7247f40fcebc 100644 (file)
@@ -584,7 +584,7 @@ href="#relay_domains">$relay_domains</a> or a subdomain thereof,
 <li>from untrusted clients to destinations that match <a
 href="#relay_domains"> $relay_domains</a> or a subdomain thereof,
 except for addresses that contain sender-specified routing
-(<i>user@there@here</i>).
+(<i>user@elsewhere@domain</i>).
 
 </ul>
 
@@ -656,7 +656,7 @@ or a subdomain thereof,
 <li>the resolved destination address matches <a
 href="#relay_domains">$relay_domains</a> or a subdomain thereof,
 and the address contains no sender-specified routing
-(<i>user@there@here</i>),
+(<i>user@elsewhere@domain</i>),
 
 <li>Postfix is the final destination:  any destination that matches
 <a href="basic.html#mydestination">$mydestination</a>, <a
@@ -674,14 +674,14 @@ parameter specifies the response code for rejected requests (default:
 <a name="permit_auth_destination">
 
 <dt> <b>permit_auth_destination</b> <dd> Ignore the client hostname.
-Permit the request when:
+Permit the request when one of the following is true:
 
 <ul>
 
 <li>the resolved destination address matches <a
 href="#relay_domains">$relay_domains</a> or a subdomain thereof,
 and the address contains no sender-specified routing
-(<i>user@there@here</i>),
+(<i>user@elsewhere@domain</i>),
 
 <li>Postfix is the final destination:  any destination that matches
 <a href="basic.html#mydestination">$mydestination</a>, <a
@@ -697,14 +697,14 @@ Otherwise proceed with the next restriction.
 <a name="reject_unauth_destination">
 
 <dt> <b>reject_unauth_destination</b> <dd> Ignore the client
-hostname.  Reject the request unless:
+hostname.  Reject the request unless one of the following is true:
 
 <ul>
 
 <li>the resolved destination address matches <a
 href="#relay_domains">$relay_domains</a> or a subdomain thereof,
 and the address contains no sender-specified routing
-(<i>user@there@here</i>),
+(<i>user@elsewhere@domain</i>),
 
 <li>Postfix is the final destination:  any destination that matches
 <a href="basic.html#mydestination">$mydestination</a>, <a
@@ -723,6 +723,12 @@ code for rejected requests (default:  <b>554</b>).
 <dt> <b>permit_mx_backup</b> <dd> Permit the request when the local
 mail system is MX host for the resolved destination. This includes
 the case that the local mail system is the final destination.
+However, the SMTP server will not forward mail with addresses that
+have sender-specified routing information (example:
+<i>user@elsewhere@domain</i>),
+
+<p>
+
 Relevant configuration parameters:  <a href="basic.html#mydestination">
 $mydestination</a>, <a href="basic.html#inet_interfaces">
 $inet_interfaces</a>.
@@ -1029,7 +1035,7 @@ href="#relay_domains">$relay_domains</a> or a subdomain thereof,
 <li>from untrusted clients to destinations that match <a
 href="#relay_domains"> $relay_domains</a> or a subdomain thereof,
 except for addresses that contain sender-specified routing
-(<i>user@there@here</i>).
+(<i>user@elsewhere@domain</i>).
 
 </ul>
 
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index d6293343f52a816b8ba58a32df556d76d4082545..78983c8f09c67e2ce7388e5a6bcdd01a6bd8473e 100644 (file)
@@ -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
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index 3be739290ceafe12f45ec150156bbad0c9a40ff6..0171c1e6aa4aa3cbb100e199dd2c045bcc1de62c 100644 (file)
@@ -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);
 }
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -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 (file)
index 0000000..4a83670
--- /dev/null
@@ -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 (file)
index 0000000..65eb6bf
--- /dev/null
@@ -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 (file)
index 0000000..b04e713
--- /dev/null
@@ -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 (file)
index 0000000..06e094b
--- /dev/null
@@ -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 <sys_defs.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <argv.h>
+#include <dict.h>
+#include <mymalloc.h>
+#include <spawn_command.h>
+#include <split_at.h>
+#include <timed_wait.h>
+#include <set_eugid.h>
+
+/* Single server skeleton. */
+
+#include <mail_params.h>
+#include <mail_server.h>
+#include <mail_conf.h>
+
+/* 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);
+}
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index dec6edcbf9bcdb1088a9c0766105b449e7f610f6..4a836704cf74f415327cd21225acc6ddb7b7524a 100644 (file)
@@ -96,6 +96,7 @@
 -TSMTP_SESSION
 -TSMTP_STATE
 -TSOCKADDR_SIZE
+-TSPAWN_ATTR
 -TSTRING_TABLE
 -TSYS_EXITS_TABLE
 -TTOK822
index 6d7c512463a5dba83500ae97665b448ee8e6ef08..b13d41aa22fa56528f97769bfed7afcc837c79bd 100644 (file)
@@ -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 (file)
index 0000000..3f009d3
--- /dev/null
@@ -0,0 +1,298 @@
+/*++
+/* NAME
+/*     spawn_command 3
+/* SUMMARY
+/*     run external command
+/* SYNOPSIS
+/*     #include <spawn_command.h>
+/*
+/*     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 <sys_defs.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+#include <syslog.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <timed_wait.h>
+#include <set_ugid.h>
+#include <argv.h>
+#include <spawn_command.h>
+#include <exec_command.h>
+#include <clean_env.h>
+
+/* 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 (file)
index 0000000..a6addf5
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _SPAWN_COMMAND_H_INCLUDED_
+#define _SPAWN_COMMAND_H_INCLUDED_
+
+/*++
+/* NAME
+/*     spawn_command 3h
+/* SUMMARY
+/*     run external command
+/* SYNOPSIS
+/*     #include <spawn_command.h>
+/* 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