19991116
Code cleanup: a new "local_transports" configuration
- parameter explicitly lists all transports that deliver
- mail locally. The first name listed there is the default
- local transport. This is the end of the "empty next-hop
- hostname" hack to indicate that a destination is local.
- Files: trivial-rewrite/resolve.c, global/local_transport.[hc]
+ parameter explicitly lists all transports that deliver mail
+ locally. The first name listed there is the default local
+ transport. This is the end of the "empty next-hop hostname"
+ hack to indicate that a destination is local. Files:
+ trivial-rewrite/resolve.c, global/local_transport.[hc]
Feature: "postconf -m" shows what lookup table types are
available. Code by Scott Cotton, Internet Consultants
19991117
+ Portability: SunOS 4 has no SA_RESTART. File: util/watchdog.c.
+
Feature: on systems with h_errno, the "reject_unknown_client"
restriction now distinguishes between soft errors (always
reply with 450) and hard errors (use the user-specified
Robustness: postconf no longer aborts when it can't figure
out the local domain name; it prints a warning instead.
This allows you to use "postconf -e" to fix the problem.
+
+19991118
+
+ Bugfix: the RFC822 address parser would misparse a leading
+ \ as an atom all by itself. Problem reported by Keith
+ Stevenson @ louisville.edu. File: global/tok822_parse.c.
+
+19991119
+
+ Bugfix: tiny memory leak in pipe_command() when fork()
+ fails. File: global/pipe_command.c.
+
+19991120
+
+ Bugfix: reversed test for all-numerical results in SMTPD
+ access maps. File: smtpd/smtpd_check.c.
+
+19991121
+
+ Robustness: INSTALL.sh no longer uses postmap for sanity checks.
+
+ Feature: INSTALL.sh now has an install_root option.
PATH=/bin:/usr/bin:/usr/sbin:/usr/etc:/sbin:/etc
umask 022
-# Default settings, edit to taste or change interactively. Once this
-# script has run it saves settings to $config_directory/install.cf.
-
-config_directory=/etc/postfix
-daemon_directory=/usr/libexec/postfix
-command_directory=/usr/sbin
-queue_directory=/var/spool/postfix
-sendmail_path=/usr/sbin/sendmail
-newaliases_path=/usr/bin/newaliases
-mailq_path=/usr/bin/mailq
-mail_owner=postfix
-setgid=no
-manpages=no
-
-# Load defaults from existing installation.
-
-test -f $config_directory/main.cf && {
- for name in daemon_directory command_directory queue_directory mail_owner
- do
- eval "$name=\"\`bin/postconf -h $name || kill \$\$\`\""
- done
-}
-
-test -f $config_directory/install.cf && . $config_directory/install.cf
-
cat <<EOF
Warning: this script replaces existing sendmail or Postfix programs.
You can either edit this script ahead of time, or you can specify
your changes interactively.
+ install_root - prepend to installed file names (for package building)
+
config_directory - directory with Postfix configuration files.
daemon_directory - directory with Postfix daemon programs.
command_directory - directory with Postfix administrative commands.
compare_or_replace() {
cmp $2 $3 >/dev/null 2>&1 || {
+ rm -f junk || exit 1
cp $2 junk || exit 1
mv -f junk $3 || exit 1
chmod $1 $3 || exit 1
compare_or_symlink() {
cmp $1 $2 >/dev/null 2>&1 || {
+ rm -f junk || exit 1
ln -s $1 junk || exit 1
mv -f junk $2 || exit 1
}
*) n=; c='\c';;
esac
+# Default settings, edit to taste or change interactively. Once this
+# script has run it saves settings to $config_directory/install.cf.
+
+config_directory=/etc/postfix
+daemon_directory=/usr/libexec/postfix
+command_directory=/usr/sbin
+queue_directory=/var/spool/postfix
+sendmail_path=/usr/sbin/sendmail
+newaliases_path=/usr/bin/newaliases
+mailq_path=/usr/bin/mailq
+mail_owner=postfix
+setgid=no
+manpages=no
+
+while :
+do
+ echo $n "install_root: [/] $c"
+ read ans
+ case $ans in
+""|/|no) install_root=; break;;
+ /*) install_root=$ans; break;;
+ *) echo "install_root should be an absolute path name" 1>&2; exit 1;;
+ esac
+done
+
+# Load defaults from existing installation.
+
+CONFIG_DIRECTORY=$install_root$config_directory
+
+test -f $CONFIG_DIRECTORY/main.cf && {
+ for name in daemon_directory command_directory queue_directory mail_owner
+ do
+ eval "$name=\"\`bin/postconf -c $CONFIG_DIRECTORY -h $name || kill \$\$\`\""
+ done
+}
+
+test -f $CONFIG_DIRECTORY/install.cf && . $CONFIG_DIRECTORY/install.cf
+
for name in config_directory daemon_directory command_directory \
queue_directory sendmail_path newaliases_path mailq_path mail_owner \
setgid manpages
# Sanity checks
+rm -f foobar-
+touch foobar-
+
+chown $mail_owner foobar- >/dev/null 2>&1 || {
+ echo "Error: $mail_owner needs an entry in the passwd file" 1>&2
+ echo "Remember, $mail_owner must have a dedicated user id and group id." 1>&2
+ exit 1
+}
+
+case $setgid in
+no) ;;
+ *) chgrp "$setgid" foobar- >/dev/null 2>&1 || {
+ echo "Error: $setgid needs an entry in the group file" 1>&2
+ echo "Remember, $setgid must have a dedicated group id." 1>&2
+ exit 1
+ }
+esac
+
+rm -f foobar-
+
for path in $config_directory $daemon_directory $command_directory \
$queue_directory $sendmail_path $newaliases_path $mailq_path $manpages
do
# Create any missing directories.
-test -d $config_directory || mkdir -p $config_directory || exit 1
-test -d $daemon_directory || mkdir -p $daemon_directory || exit 1
-test -d $command_directory || mkdir -p $command_directory || exit 1
-test -d $queue_directory || mkdir -p $queue_directory || exit 1
+CONFIG_DIRECTORY=$install_root$config_directory
+DAEMON_DIRECTORY=$install_root$daemon_directory
+COMMAND_DIRECTORY=$install_root$command_directory
+QUEUE_DIRECTORY=$install_root$queue_directory
+SENDMAIL_PATH=$install_root$sendmail_path
+NEWALIASES_PATH=$install_root$newaliases_path
+MAILQ_PATH=$install_root$mailq_path
+MANPAGES=$install_root$manpages
+
+case $install_root in
+ /?*) test -d $install_root || mkdir -p $install_root || exit 1
+esac
+
+test -d $CONFIG_DIRECTORY || mkdir -p $CONFIG_DIRECTORY || exit 1
+test -d $DAEMON_DIRECTORY || mkdir -p $DAEMON_DIRECTORY || exit 1
+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
+ mkdir -p `echo $path|sed 's/[^/]*[/]*$//'`
+done
# Install files. Be careful to not copy over running programs.
for file in `ls libexec`
do
- compare_or_replace a+x,go-w libexec/$file $daemon_directory/$file || exit 1
+ compare_or_replace a+x,go-w libexec/$file $DAEMON_DIRECTORY/$file || exit 1
done
for file in `ls bin | grep '^post'`
do
- compare_or_replace a+x,go-w bin/$file $command_directory/$file || exit 1
+ compare_or_replace a+x,go-w bin/$file $COMMAND_DIRECTORY/$file || exit 1
done
test -f bin/sendmail && {
- compare_or_replace a+x,go-w bin/sendmail $sendmail_path || exit 1
- compare_or_symlink $sendmail_path $newaliases_path
- compare_or_symlink $sendmail_path $mailq_path
+ compare_or_replace a+x,go-w bin/sendmail $SENDMAIL_PATH || exit 1
+ compare_or_symlink $sendmail_path $NEWALIASES_PATH
+ compare_or_symlink $sendmail_path $MAILQ_PATH
}
-compare_or_replace a+r,go-w conf/LICENSE $config_directory/LICENSE || exit 1
+compare_or_replace a+r,go-w conf/LICENSE $CONFIG_DIRECTORY/LICENSE || exit 1
-test -f $config_directory/main.cf || {
- cp conf/* $config_directory || exit 1
- chmod a+r,go-w $config_directory/* || exit 1
+test -f $CONFIG_DIRECTORY/main.cf || {
+ cp conf/* $CONFIG_DIRECTORY || exit 1
+ chmod a+r,go-w $CONFIG_DIRECTORY/* || exit 1
echo "Warning: you still need to edit myorigin/mydestination in" 1>&2
- echo "$config_directory/main.cf. See also html/faq.html for dialup" 1>&2
+ echo "$CONFIG_DIRECTORY/main.cf. See also html/faq.html for dialup" 1>&2
echo "sites or for sites inside a firewalled network." 1>&2
echo "" 1>&2
echo "BTW, Edit your alias database and be sure to set up aliases" 1>&2
do
eval echo $name=\$$name
done) >junk || exit 1
-compare_or_move a+x,go-w junk $config_directory/install.cf || exit 1
+compare_or_move a+x,go-w junk $CONFIG_DIRECTORY/install.cf || exit 1
rm -f junk
# Use set-gid privileges instead of writable maildrop (optional).
-test -d $queue_directory/maildrop || {
- mkdir -p $queue_directory/maildrop || exit 1
- chown $owner $queue_directory/maildrop || exit 1
+test -d $QUEUE_DIRECTORY/maildrop || {
+ mkdir -p $QUEUE_DIRECTORY/maildrop || exit 1
+ chown $mail_owner $QUEUE_DIRECTORY/maildrop || exit 1
}
case $setgid in
no)
- chmod 1733 $queue_directory/maildrop || exit 1
- chmod g-s $command_directory/postdrop || exit 1
+ chmod 1733 $QUEUE_DIRECTORY/maildrop || exit 1
+ chmod g-s $COMMAND_DIRECTORY/postdrop || exit 1
postfix_script=conf/postfix-script-nosgid
;;
*)
- chgrp $setgid $command_directory/postdrop || exit 1
- chmod g+s $command_directory/postdrop || exit 1
- chgrp $setgid $queue_directory/maildrop || exit 1
- chmod 1730 $queue_directory/maildrop || exit 1
+ chgrp $setgid $COMMAND_DIRECTORY/postdrop || exit 1
+ chmod g+s $COMMAND_DIRECTORY/postdrop || exit 1
+ chgrp $setgid $QUEUE_DIRECTORY/maildrop || exit 1
+ chmod 1730 $QUEUE_DIRECTORY/maildrop || exit 1
postfix_script=conf/postfix-script-sgid
;;
esac
-compare_or_replace a+x,go-w $postfix_script $config_directory/postfix-script ||
+compare_or_replace a+x,go-w $postfix_script $CONFIG_DIRECTORY/postfix-script ||
exit 1
# Install manual pages (optional). We just clobber whatever is there.
*) (
cd man || exit 1
for dir in man?
- do mkdir -p $manpages/$dir || exit 1
+ do mkdir -p $MANPAGES/$dir || exit 1
done
for file in man?/*
do
- rm -f $manpages/$file
- cp $file $manpages/$file || exit 1
- chmod 644 $manpages/$file || exit 1
+ rm -f $MANPAGES/$file
+ cp $file $MANPAGES/$file || exit 1
+ chmod 644 $MANPAGES/$file || exit 1
done
)
esac
-Incompatible changes with snapshot XXXXXXXX
+Incompatible changes with snapshot 19991120
===========================================
+- 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.
+
- Recipient addresses may no longer begin with `-'. In order to
reinstate the old behavior, specify "allow_min_user = yes" in
main.cf.
-- You can not longer use virtual, canonical or aliases tables as
+- You can no longer use virtual, canonical or aliases tables as
SMTPD access control tables. Use the permit_recipient_map feature
-instead. The loss is compensated for.
+instead. The loss is compensated for (see below).
-Major changes with snapshot XXXXXXXX
+Major changes with snapshot 19991120
====================================
-- Per-client/helo/sender/recipient UCE restrictions: you can now
-specify arbitrary restrictions on the right-hand side of SMTPD
-access tables. The only anomalies in this scheme are (1) header
-checks are the same for every message, and (2) you must use a
-restriction class (see below) in order to specify a lookup table
-on the right-hand side of an access table.
-
-- Restriction classes allow you to conveniently group restrictions
-under one name. This is great for per-client/helo/sender/recipient
-UCE restrictions. For example in main.cf:
-
- smtpd_restriction_classes = restrictive, permissive
- restrictive = reject_unknown_sender reject_unknown_client ...
- permissive = permit
+- The Postfix SMTP server now understands a wider range of illegal
+address formats in MAIL FROM and RCPT TO commands. In order to
+disable those forms, specify "strict_rfc821_envelopes = yes".
-Then use "restrictive" or "permissive" on the right-hand side of
-your per-client/helo/sender/recipient SMTPD access tables.
+- Per-client/helo/sender/recipient UCE restrictions (fully-recursive
+UCE restriction parser). See the RESTRICTION_CLASS file for details.
-- Reject mail for non-existent local accounts. You can now use
-passwd/canonical/virtual/aliases tables for SMTPD access control.
-
-For example, a non-relaying site would specify in main.cf:
+- Block mail for non-existent users at the SMTP port. On a non-relay
+host, use the following to reject mail for non-existent users and
+for non-local destinations.
smtpd_recipient_restrictions =
- permit_recipient_map unix:passwd.byname
- permit_recipient_map hash:/etc/canonical
+ permit_recipient_map unix:passwd .byname
+ permit_recipient_map hash:/etc/postfix/canonical
permit_recipient_map hash:/etc/postfix/virtual
permit_recipient_map hash:/etc/aliases
reject
-That should stop a lot of the mail to non-existent recipients. It
-won't stop mail to broken aliases or to users with broken .forward
-files, though.
+- "postconf -e name=value..." edits the main.cf file. This is
+easier and safer than editing the main.cf file by hand. The edits
+are done on a temporary copy that is renamed into place.
+
+- "postconf -m" displays all supported lookup table types (Scott
+Cotton).
-For a relaying site, a good configuration with permit_recipient_map
-still needs to be found. Something that permits relaying to all
-sites except $mydestination.
+- It is now relatively safe to configure 550 status codes for the
+main.cf unknown_address_reject_code or unknown_client_reject_code
+parameters. The SMTP server now always sends a 450 (try again)
+reply code when an UCE restriction fails due to a soft DNS error.
-Unfortunately, permit_recipient_map does not combine well with
-permit_mynetworks, because permit_mynetworks accepts mail for
-non-existent local recipients.
+- The RBL checks now show the content of TXT records (Simon J Mudd).
-Unfortunately, permit_recipient_map does not combine well with
-check_relay_domains, because check_relay_domains either rejects
-mail, or accepts mail regardless of whether a recipient exists.
+- New "permit_auth_destination" UCE restriction for finer-grained
+control (Jesper Skriver).
Incompatible changes with postfix-19990906
==========================================
--- /dev/null
+Per client/helo/sender/recipient UCE restrictions
+=================================================
+
+The Postfix SMTP server allows you to specify UCE restrictions on
+the right-hand side of SMTPD access tables, so that you can have
+different UCE restrictions for different clients or users.
+
+The only anomalies in this scheme are that (1) message header checks
+are still the same for every message, and (2) you must use a
+restriction class name (see below) if you want to specify a lookup
+table on the right-hand side of an access table (this is because
+Postfix needs to open those tables ahead of time).
+
+Restriction classes allow you to give easy-to-remember names to
+groups of UCE restrictions (such as permissive, restrictive, and
+so on). For example in main.cf:
+
+ smtpd_restriction_classes = restrictive, permissive
+ restrictive = reject_unknown_sender reject_unknown_client ...
+ permissive = permit
+
+With this in place, you can use "restrictive" or "permissive" on
+the right-hand side of your per-client/helo/sender/recipient SMTPD
+access tables.
--- /dev/null
+#!/bin/sh
+
+# Dummy UUCP rmail command for postfix/qmail systems
+
+SENDMAIL="/usr/sbin/sendmail"
+IFS=" " read junk from junk
+
+exec $SENDMAIL -f "$from" -- "$@"
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
+ flags=F. user=foo argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient
# reject_non_fqdn_recipient: reject recipient address that is not in FQDN form
# permit_recipient_map maptype:mapname: permit if the recipient matches
# the table. Matching is as with virtual/canonical tables.
+# You can also use unix:passwd.byname or alias maps.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
#
* only if the name server told us so.
*/
len = res_search((char *) name, C_IN, type, reply->buf, sizeof(reply->buf));
- reply_header = (HEADER *) reply->buf;
if (len < 0) {
- if (reply_header->rcode == SERVFAIL)
- h_errno = NO_RECOVERY;
if (why)
vstring_sprintf(why, "Name service error for domain %s: %s",
name, dns_strerror(h_errno));
*/
if ((reply->end = reply->buf + len) > reply->buf + sizeof(reply->buf))
reply->end = reply->buf + sizeof(reply->buf);
+ reply_header = (HEADER *) reply->buf;
reply->query_start = reply->buf + sizeof(HEADER);
reply->answer_start = 0;
reply->query_count = ntohs(reply_header->qdcount);
static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
char *rr_name, DNS_FIXED *fixed)
{
- unsigned char temp[DNS_NAME_LEN];
+ char temp[DNS_NAME_LEN];
int data_len;
unsigned pref = 0;
unsigned char *src;
int ch;
#define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b))
+#define UC(x) ((unsigned char *) (x))
if (pos + fixed->length > reply->end)
return (0);
break;
case T_TXT:
data_len = MIN2(fixed->length + 1, sizeof(temp));
- for (src = pos, dst = temp; dst < temp + data_len - 1; /* void */ ) {
+ for (src = pos, dst = UC(temp); dst < UC(temp) + data_len - 1; /* */ ) {
ch = *src++;
*dst++ = (ISPRINT(ch) ? ch : ' ');
}
/*
/* const char *def_local_transport()
/*
-/* int local_transport(transport)
+/* int match_def_local_transport(transport)
+/* const char *transport;
+/*
+/* int match_any_local_transport(transport)
/* const char *transport;
/* DESCRIPTION
/* This module uses the information kept in the "local_transports"
/* configuration parameter, which lists the name of the default
-/* local transport, followed by zero or more other transports that
-/* deliver locally.
+/* local transport, followed by the names of zero or more other
+/* transports that deliver locally.
/*
/* def_local_transport() returns the name of the default local
-/* transport, that is, the first transport name specified with
+/* transport, that is, the first transport name specified with
/* the "local_transports" configuration parameter.
/*
-/* local_transport() determines if the named transport is listed
-/* in the "local_transports" configuration parameter.
+/* match_def_local_transport() determines if the named transport is
+/* identical to the default local transport.
+/*
+/* match_any_local_transport() determines if the named transport is
+/* listed in the "local_transports" configuration parameter.
/* SEE ALSO
/* resolve_local(3), see if address resolves locally.
/* LICENSE
/*
* Sanity check.
*/
- if (!local_transport(local_transport_name))
+ if (!match_any_local_transport(local_transport_name))
msg_panic("%s: unable to intialize", myname);
}
return (local_transport_name);
}
-/* local_transport - match address against list of local destinations */
+/* match_def_local_transport - match against default local transport */
+
+int match_def_local_transport(const char *transport)
+{
+
+ /*
+ * Initialize on the fly.
+ */
+ if (local_transport_list == 0)
+ local_transport_init();
+
+ /*
+ * Compare the transport against the list of transports that are listed
+ * as delivering locally.
+ */
+ return (strcmp(transport, local_transport_name) == 0);
+}
+
+/* match_any_local_transport - match against list of local transports */
-int local_transport(const char *transport)
+int match_any_local_transport(const char *transport)
{
/*
if (argc != 2)
msg_fatal("usage: %s transport", argv[0]);
mail_conf_read();
- vstream_printf("%s\n", local_transport(argv[1]) ? "yes" : "no");
+ vstream_printf("%s\n", match_any_local_transport(argv[1]) ? "yes" : "no");
vstream_fflush(VSTREAM_OUT);
}
* External interface.
*/
extern const char *def_local_transport(void);
-extern int local_transport(const char *);
+extern int match_def_local_transport(const char *);
+extern int match_any_local_transport(const char *);
/* LICENSE
/* .ad
msg_fatal("chdir %s: %m", var_queue_dir);
path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
- if ((result = mail_addr_map(path, STR(buffer))) != 0)
+ if ((result = mail_addr_map(path, STR(buffer), 1)) != 0)
argv_free(result);
}
vstring_free(buffer);
static const char *check_myhostname(void)
{
- const char *name;
+ static const char *name;
const char *dot;
const char *domain;
+ /*
+ * Use cached result.
+ */
+ if (name)
+ return (name);
+
/*
* If the local machine name is not in FQDN form, try to append the
* contents of $mydomain.
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-19991117"
+#define DEF_MAIL_VERSION "Snapshot-19991120"
extern char *var_mail_version;
/* LICENSE
*/
if (pipe(cmd_in_pipe) < 0 || pipe(cmd_out_pipe) < 0)
msg_fatal("%s: pipe: %m", myname);
- cmd_in_stream = vstream_fdopen(cmd_in_pipe[1], O_WRONLY);
- cmd_out_stream = vstream_fdopen(cmd_out_pipe[0], O_RDONLY);
non_blocking(cmd_out_pipe[1], NON_BLOCKING);
/*
close(cmd_in_pipe[0]);
close(cmd_out_pipe[1]);
+ cmd_in_stream = vstream_fdopen(cmd_in_pipe[1], O_WRONLY);
+ cmd_out_stream = vstream_fdopen(cmd_out_pipe[0], O_RDONLY);
+
/*
* Give the command a limited amount of time to run, by enforcing
* timeouts on all I/O from and to it.
} else if (ch == '"') {
tp = tok822_alloc(TOK822_QSTRING, (char *) 0);
COLLECT_SKIP_LAST(tp, str, ch, ch != '"');
- } else if (strchr(tok822_opchar, ch)) {
+ } else if (ch != '\\' && strchr(tok822_opchar, ch)) {
tp = tok822_alloc(ch, (char *) 0);
} else {
tp = tok822_alloc(TOK822_ATOM, (char *) 0);
- VSTRING_ADDCH(tp->vstr, ch);
+ str -= 1; /* \ may be first */
COLLECT(tp, str, ch, !ISSPACE(ch) && !strchr(tok822_opchar, ch));
tok822_quote_atom(tp);
}
postconf - Postfix configuration utility
<b>SYNOPSIS</b>
- <b>postconf</b> [<b>-c</b> <i>config_dir</i>] [<b>-d</b>] [<b>-h</b>] [<b>-n</b>] [<b>-v</b>] [<i>parameter</i>
- <i>...</i>]
+ <b>postconf</b> [<b>-dhmnv</b>] [<b>-c</b> <i>config_dir</i>] [<i>parameter</i> <i>...</i>]
+
+ <b>postconf</b> [<b>-ev</b>] [<b>-c</b> <i>config_dir</i>] [<i>parameter=value</i> <i>...</i>]
<b>DESCRIPTION</b>
- The <b>postconf</b> command prints the actual value of <i>parameter</i>
- (all known parameters by default), one parameter per line.
+ The <b>postconf</b> command prints the actual value of <i>parameter</i>
+ (all known parameters by default), one parameter per line,
+ changes its value, or prints other information about the
+ Postfix mail system.
Options:
<b>-d</b> Print default parameter settings instead of actual
settings.
+ <b>-e</b> Edit the <b>main.cf</b> configuration file. The file is
+ copied to a temporary file then renamed into place.
+ Parameters and values are specified on the command
+ line. Use quotes in order to protect shell
+ metacharacters and whitespace.
+
<b>-h</b> Show parameter values only, not the ``name = ''
label that normally precedes the value.
+ <b>-m</b> List the names of all supported lookup table types.
+
<b>-n</b> Print non-default parameter settings only.
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
- tiple <b>-v</b> options make the software increasingly
+ tiple <b>-v</b> options make the software increasingly
verbose.
<b>DIAGNOSTICS</b>
Problems are reported to the standard error stream.
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
-
-
-
-
-
-
-
-
-
-
-
1
command after a configuration change.
+<b>Compatibility</b> <b>controls</b>
+ <b>strict</b><i>_</i><b>rfc821</b><i>_</i><b>envelopes</b>
+ Disallow non-RFC 821 style addresses in envelopes.
+ For example, allow RFC822-style address forms with
+ comments, like Sendmail does.
+
<b>Miscellaneous</b>
<b>always</b><i>_</i><b>bcc</b>
Address to send a copy of each message that enters
<b>smtpd</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
Restrict the number of recipients that the SMTP
- server accepts per message delivery.
-
- <b>smtpd</b><i>_</i><b>timeout</b>
- Limit the time to send a server response and to
- receive a client request.
-
SMTPD(8) SMTPD(8)
+ server accepts per message delivery.
+
+ <b>smtpd</b><i>_</i><b>timeout</b>
+ Limit the time to send a server response and to
+ receive a client request.
+
<b>Resource</b> <b>controls</b>
<b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
Limit the amount of memory in bytes used for the
Restrict what recipient addresses are allowed in
<b>RCPT</b> <b>TO</b> commands.
- <b>smtpd</b><i>_</i><b>etrn</b><i>_</i><b>restrictions</b>
- Restrict what domain names can be used in <b>ETRN</b> com-
- mands, and what clients may issue <b>ETRN</b> commands.
-
-
-
3
SMTPD(8) SMTPD(8)
+ <b>smtpd</b><i>_</i><b>etrn</b><i>_</i><b>restrictions</b>
+ Restrict what domain names can be used in <b>ETRN</b> com-
+ mands, and what clients may issue <b>ETRN</b> commands.
+
<b>restriction</b><i>_</i><b>classes</b>
Declares the name of zero or more parameters that
contain a list of UCE restrictions. The names of
<b>SEE</b> <b>ALSO</b>
<a href="cleanup.8.html">cleanup(8)</a> message canonicalization
- <a href="master.8.html">master(8)</a> process manager
- syslogd(8) system logging
-
-
SMTPD(8) SMTPD(8)
+ <a href="master.8.html">master(8)</a> process manager
+ syslogd(8) system logging
+
<b>LICENSE</b>
The Secure Mailer license must be distributed with this
software.
-
-
-
domains, client address, or networks obtained by stripping least
significant octets. Reject the request if the result is <b>REJECT</b>
or "[<b>45</b>]<i>XX text</i>". Permit the request if the result
-is <b>OK</b> or <b>RELAY</b>. Otherwise, treat the result as another
-list of UCE restrictions. The <b>access_map_reject_code</b>
-parameter specifies the response code for <b>REJECT</b> results
-(default: <b>554</b>).
+is <b>OK</b> or <b>RELAY</b> or all-numerical. Otherwise, treat the
+result as another list of UCE restrictions. The
+<b>access_map_reject_code</b> parameter specifies the response code for
+<b>REJECT</b> results (default: <b>554</b>).
<p>
href="access.5.html">access database</a> for the <b>HELO</b> hostname
or parent domains in the specified table. Reject the request if
the result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>". Permit
-the request when the result is <b>OK</b> or <b>RELAY</b>. Otherwise,
-treat the result as another list of UCE restrictions. The
-<b>access_map_reject_code </b> parameter specifies the response
-code for <b>REJECT</b> results (default: <b>554</b>).
+the request when the result is <b>OK</b> or <b>RELAY</b> or
+all-numerical. Otherwise, treat the result as another list of UCE
+restrictions. The <b>access_map_reject_code </b> parameter specifies
+the response code for <b>REJECT</b> results (default: <b>554</b>).
<p>
href="access.5.html">access database</a> for the sender mail address,
parent domain, or <i>localpart</i>@. Reject the request if the
result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>". Permit the
-request if the result is <b>OK</b> or <b>RELAY</b>. Otherwise,
-treat the result as another list of UCE restrictions. The
-<b>access_map_reject_code </b> parameter specifies the result code
-for rejected requests (default: <b>554</b>).
+request if the result is <b>OK</b> or <b>RELAY</b> or all-numerical.
+Otherwise, treat the result as another list of UCE restrictions. The
+<b>access_map_reject_code </b> parameter specifies the result code for
+rejected requests (default: <b>554</b>).
<p>
<dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
href="access.5.html">access database</a> for the resolved destination
-address, parent domain, or <i>localpart</i>@. Reject the request
-if the result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>".
-Permit the request if the result is <b>OK</b> or <b>RELAY</b>.
-Otherwise, treat the result as another list of UCE restrictions.
-The <b>access_map_reject_code </b> parameter specifies the result
-code for rejected requests (default: <b>554</b>).
+address, parent domain, or <i>localpart</i>@. Reject the request if the
+result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>". Permit the
+request if the result is <b>OK</b> or <b>RELAY</b> or all-numerical.
+Otherwise, treat the result as another list of UCE restrictions. The
+<b>access_map_reject_code </b> parameter specifies the result code for
+rejected requests (default: <b>554</b>).
<p>
href="access.5.html">access database</a> for the domain specified
in the ETRN command, or its parent domains. Reject the request if
the result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>". Permit
-the request if the result is <b>OK</b> or <b>RELAY</b>. Otherwise,
-treat the result as another list of UCE restrictions. The
-<b>access_map_reject_code </b> parameter specifies the result code
-for rejected requests (default: <b>554</b>).
+the request if the result is <b>OK</b> or <b>RELAY</b> or
+all-numerical. Otherwise, treat the result as another list of UCE
+restrictions. The <b>access_map_reject_code </b> parameter specifies
+the result code for rejected requests (default: <b>554</b>).
<p>
* ugly code to force local recursive alias expansions on a host with no
* authority over the local domain, but that code was just too unclean.
*/
- if (local_transport(STR(reply.transport))) {
+ if (match_def_local_transport(STR(reply.transport))) {
status = deliver_recipient(state, usr_attr);
} else {
status = deliver_indirect(state);
.na
.nf
.fi
-\fBpostconf\fR [\fB-c \fIconfig_dir\fR] [\fB-d\fR] [\fB-h\fR]
-[\fB-n\fR] [\fB-v\fR] [\fIparameter ...\fR]
+\fBpostconf\fR [\fB-dhmnv\fR] [\fB-c \fIconfig_dir\fR]
+[\fIparameter ...\fR]
+
+\fBpostconf\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
+[\fIparameter=value ...\fR]
.SH DESCRIPTION
.ad
.fi
The \fBpostconf\fR command prints the actual value of
\fIparameter\fR (all known parameters by default), one
-parameter per line.
+parameter per line, changes its value, or prints other
+information about the Postfix mail system.
Options:
.IP "\fB-c \fIconfig_dir\fR"
The \fBmain.cf\fR configuration file is in the named directory.
.IP \fB-d\fR
Print default parameter settings instead of actual settings.
+.IP \fB-e\fR
+Edit the \fBmain.cf\fR configuration file. The file is copied
+to a temporary file then renamed into place. Parameters and
+values are specified on the command line. Use quotes in order
+to protect shell metacharacters and whitespace.
.IP \fB-h\fR
Show parameter values only, not the ``name = '' label
that normally precedes the value.
+.IP \fB-m\fR
+List the names of all supported lookup table types.
.IP \fB-n\fR
Print non-default parameter settings only.
.IP \fB-v\fR
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 "Compatibility controls"
+.ad
+.fi
+.IP \fBstrict_rfc821_envelopes\fR
+Disallow non-RFC 821 style addresses in envelopes. For example,
+allow RFC822-style address forms with comments, like Sendmail does.
.SH Miscellaneous
.ad
.fi
/* Postfix configuration utility
/* SYNOPSIS
/* .fi
-/* \fBpostconf\fR [\fB-dhnv\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostconf\fR [\fB-dhmnv\fR] [\fB-c \fIconfig_dir\fR]
/* [\fIparameter ...\fR]
/*
/* \fBpostconf\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
static const char *check_myhostname(void)
{
- const char *name;
+ static const char *name;
const char *dot;
const char *domain;
+ /*
+ * Use cached result.
+ */
+ if (name)
+ return (name);
+
/*
* If the local machine name is not in FQDN form, try to append the
* contents of $mydomain.
/*
* Edit main.cf.
*/
- if (mode & EDIT_MAIN) {
+ else if (mode & EDIT_MAIN) {
edit_parameters(argc - optind, argv + optind);
}
/*
* Bounce mail to non-existent users in virtual domains.
*/
- if (!local_transport(STR(reply.transport))
+ if (!match_def_local_transport(STR(reply.transport))
&& qmgr_virtual != 0
&& (at = strrchr(recipient->address, '@')) != 0) {
domain = lowercase(mystrdup(at + 1));
* job requires knowledge of local aliases. Yuck! I don't want to
* duplicate delivery-agent specific knowledge in the queue manager.
*/
- if (local_transport(STR(reply.transport))) {
+ if (match_def_local_transport(STR(reply.transport))) {
vstring_strcpy(reply.nexthop, STR(reply.recipient));
(void) split_at_right(STR(reply.nexthop), '@');
#if 0
session->best = (addr->pref == addr_list->pref);
break;
}
- msg_info("%s; address %s port %d", vstring_str(why),
- inet_ntoa(*((struct in_addr *) addr->data)), ntohs(port));
+ msg_info("%s (port %d)", vstring_str(why), ntohs(port));
}
dns_rr_free(addr_list);
return (session);
done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
@make -f Makefile.in Makefile
-tests: smtpd_check_test smtpd_check_test2 smtpd_token_test
+tests: smtpd_check_test smtpd_check_test2 smtpd_check_test3 smtpd_token_test
smtpd_check_test: smtpd_check smtpd_check.in smtpd_check.ref
../postmap/postmap smtpd_check_access
diff smtpd_check.ref2 smtpd_check.tmp
rm -f smtpd_check.tmp smtpd_check_access.*
+smtpd_check_test3: smtpd_check smtpd_check.in3 smtpd_check.ref3
+ ../postmap/postmap smtpd_check_access
+ ./smtpd_check <smtpd_check.in3 >smtpd_check.tmp 2>&1
+ diff smtpd_check.ref3 smtpd_check.tmp
+ rm -f smtpd_check.tmp smtpd_check_access.*
+
smtpd_token_test: smtpd_token smtpd_token.in smtpd_token.ref
./smtpd_token <smtpd_token.in >smtpd_token.tmp 2>&1
diff smtpd_token.ref smtpd_token.tmp
char *etrn_name;
char *protocol;
char *where;
+ int recursion;
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
/*
* Pre-opened access control lists.
*/
-static DOMAIN_LIST *relay_domains;
-static NAMADR_LIST *mynetworks;
+static DOMAIN_LIST *relay_domains;
+static NAMADR_LIST *mynetworks;
/*
* Pre-parsed restriction lists.
*/
-static ARGV *client_restrctions;
+static ARGV *client_restrctions;
static ARGV *helo_restrctions;
static ARGV *mail_restrctions;
static ARGV *rcpt_restrctions;
static ARGV *etrn_restrctions;
-static HTABLE *smtpd_rest_classes;
+static HTABLE *smtpd_rest_classes;
static HTABLE *smtpd_rcpt_maps;
/* smtpd_check_reject - do the boring things that must be done */
-static int smtpd_check_reject(SMTPD_STATE *state, int error_class,
+static int smtpd_check_reject(SMTPD_STATE * state, int error_class,
char *format,...)
{
va_list ap;
/* reject_unknown_client - fail if client hostname is unknown */
-static int reject_unknown_client(SMTPD_STATE *state)
+static int reject_unknown_client(SMTPD_STATE * state)
{
char *myname = "reject_unknown_client";
/* permit_mynetworks - succeed if client is in a trusted network */
-static int permit_mynetworks(SMTPD_STATE *state)
+static int permit_mynetworks(SMTPD_STATE * state)
{
char *myname = "permit_mynetworks";
/* reject_invalid_hostaddr - fail if host address is incorrect */
-static int reject_invalid_hostaddr(SMTPD_STATE *state, char *addr,
+static int reject_invalid_hostaddr(SMTPD_STATE * state, char *addr,
char *reply_name, char *reply_class)
{
char *myname = "reject_invalid_hostaddr";
/* reject_invalid_hostname - fail if host/domain syntax is incorrect */
-static int reject_invalid_hostname(SMTPD_STATE *state, char *name,
+static int reject_invalid_hostname(SMTPD_STATE * state, char *name,
char *reply_name, char *reply_class)
{
char *myname = "reject_invalid_hostname";
/* reject_non_fqdn_hostname - fail if host name is not in fqdn form */
-static int reject_non_fqdn_hostname(SMTPD_STATE *state, char *name,
+static int reject_non_fqdn_hostname(SMTPD_STATE * state, char *name,
char *reply_name, char *reply_class)
{
char *myname = "reject_non_fqdn_hostname";
/* reject_unknown_hostname - fail if name has no A or MX record */
-static int reject_unknown_hostname(SMTPD_STATE *state, char *name,
+static int reject_unknown_hostname(SMTPD_STATE * state, char *name,
char *reply_name, char *reply_class)
{
char *myname = "reject_unknown_hostname";
/* reject_unknown_mailhost - fail if name has no A or MX record */
-static int reject_unknown_mailhost(SMTPD_STATE *state, char *name,
+static int reject_unknown_mailhost(SMTPD_STATE * state, char *name,
char *reply_name, char *reply_class)
{
char *myname = "reject_unknown_mailhost";
/* check_relay_domains - OK/FAIL for message relaying */
-static int check_relay_domains(SMTPD_STATE *state, char *recipient,
+static int check_relay_domains(SMTPD_STATE * state, char *recipient,
char *reply_name, char *reply_class)
{
char *myname = "check_relay_domains";
* Permit if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
- if (local_transport(STR(reply.transport))
+ if (match_any_local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
* Permit if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
- if (local_transport(STR(reply.transport))
+ if (match_any_local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
/* reject_unauth_destination - FAIL for message relaying */
-static int reject_unauth_destination(SMTPD_STATE *state, char *recipient)
+static int reject_unauth_destination(SMTPD_STATE * state, char *recipient)
{
char *myname = "reject_unauth_destination";
char *domain;
* Pass if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
- if (local_transport(STR(reply.transport))
+ if (match_any_local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
/* reject_unauth_pipelining - reject improper use of SMTP command pipelining */
-static int reject_unauth_pipelining(SMTPD_STATE *state)
+static int reject_unauth_pipelining(SMTPD_STATE * state)
{
char *myname = "reject_unauth_pipelining";
/* permit_mx_backup - permit use of me as MX backup for recipient domain */
-static int permit_mx_backup(SMTPD_STATE *unused_state, const char *recipient)
+static int permit_mx_backup(SMTPD_STATE * unused_state, const char *recipient)
{
char *myname = "permit_mx_backup";
char *domain;
+
DNS_RR *mx_list;
DNS_RR *mx;
int dns_status;
* If the destination is local, it is acceptable, because we are
* supposedly MX for our own address.
*/
- if (local_transport(STR(reply.transport))
+ if (match_any_local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
/* reject_non_fqdn_address - fail if address is not in fqdn form */
-static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr,
+static int reject_non_fqdn_address(SMTPD_STATE * state, char *addr,
char *reply_name, char *reply_class)
{
char *myname = "reject_non_fqdn_address";
/* reject_unknown_address - fail if address does not resolve */
-static int reject_unknown_address(SMTPD_STATE *state, char *addr,
+static int reject_unknown_address(SMTPD_STATE * state, char *addr,
char *reply_name, char *reply_class)
{
char *myname = "reject_unknown_address";
/*
* Skip local destinations and non-DNS forms.
*/
- if (local_transport(STR(reply.transport))
+ if (match_any_local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
{
char *myname = "permit_rcpt_map";
char *domain;
- MAPS *map;
- DICT *dict;
+
+ MAPS *map;
+ DICT *dict;
if (msg_verbose)
msg_info("%s: %s %s", myname, table, reply_name);
return (SMTPD_CHECK_DUNNO);
domain += 1;
if (domain[0] == '#' || domain[0] == '[')
- if (!local_transport(STR(reply.transport)))
+ if (!match_any_local_transport(STR(reply.transport)))
return (SMTPD_CHECK_DUNNO);
/*
* canonical and virtual maps. Be sure this map was declared in a main.cf
* restriction or restriction class. The maps must be opened before the
* process enters a chroot jail.
+ *
+ * XXX What follows is a kludge to wrap up the recipient map in a form
+ * usable by mail_addr_find(). Perhaps we should have a mail_addr_find()
+ * interface that will search just one map instead of a list of maps.
*/
if ((map = (MAPS *) htable_find(smtpd_rcpt_maps, table)) == 0) {
if ((dict = dict_handle(table)) == 0)
/* check_table_result - translate table lookup result into pass/reject */
-static int check_table_result(SMTPD_STATE *state, char *table,
+static int check_table_result(SMTPD_STATE * state, char *table,
const char *value, const char *datum,
char *reply_name, char *reply_class,
char *def_acl)
{
char *myname = "check_table_result";
int code;
- ARGV *restrictions;
+ ARGV *restrictions;
int status;
if (msg_verbose)
* All-numeric result probably means OK - some out-of-band authentication
* mechanism uses this as time stamp.
*/
- if (value[strcspn(value, "0123456789")] == 0)
+ if (*value && value[strspn(value, "0123456789")] == 0)
return (SMTPD_CHECK_OK);
/*
msg_warn("define a restriction class and specify its name instead");
longjmp(smtpd_check_buf, -1);
}
+
+ /*
+ * Don't get carried away with recursion.
+ */
+ if (state->recursion++ > 100) {
+ msg_warn("SMTPD access map %s entry %s causes unreasonable recursion",
+ table, value);
+ longjmp(smtpd_check_buf, -1);
+ }
+
+ /*
+ * Recursively evaluate the restrictions given in the right-hand side.
+ */
restrictions = argv_split(value, " \t\r\n,");
status = generic_checks(state, restrictions, reply_name,
reply_class, def_acl);
/* check_access - table lookup without substring magic */
-static int check_access(SMTPD_STATE *state, char *table, char *name, int flags,
+static int check_access(SMTPD_STATE * state, char *table, char *name, int flags,
char *reply_name, char *reply_class, char *def_acl)
{
char *myname = "check_access";
/* check_domain_access - domainname-based table lookup */
-static int check_domain_access(SMTPD_STATE *state, char *table,
+static int check_domain_access(SMTPD_STATE * state, char *table,
char *domain, int flags,
char *reply_name, char *reply_class,
char *def_acl)
/* check_addr_access - address-based table lookup */
-static int check_addr_access(SMTPD_STATE *state, char *table,
+static int check_addr_access(SMTPD_STATE * state, char *table,
char *address, int flags,
char *reply_name, char *reply_class,
char *def_acl)
/* check_namadr_access - OK/FAIL based on host name/address lookup */
-static int check_namadr_access(SMTPD_STATE *state, char *table,
+static int check_namadr_access(SMTPD_STATE * state, char *table,
char *name, char *addr, int flags,
char *reply_name, char *reply_class,
char *def_acl)
/* check_mail_access - OK/FAIL based on mail address lookup */
-static int check_mail_access(SMTPD_STATE *state, char *table, char *addr,
+static int check_mail_access(SMTPD_STATE * state, char *table, char *addr,
char *reply_name, char *reply_class,
char *def_acl)
{
/* reject_maps_rbl - reject if client address in real-time blackhole list */
-static int reject_maps_rbl(SMTPD_STATE *state)
+static int reject_maps_rbl(SMTPD_STATE * state)
{
char *myname = "reject_maps_rbl";
ARGV *octets = argv_split(state->addr, ".");
/* generic_checks - generic restrictions */
-static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
+static int generic_checks(SMTPD_STATE * state, ARGV *restrictions,
char *reply_name, char *reply_class, char *def_acl)
{
char *myname = "generic_checks";
/* smtpd_check_client - validate client name or address */
-char *smtpd_check_client(SMTPD_STATE *state)
+char *smtpd_check_client(SMTPD_STATE * state)
{
int status;
/*
* Apply restrictions in the order as specified.
*/
+ state->recursion = 0;
status = setjmp(smtpd_check_buf);
if (status == 0 && client_restrctions->argc)
status = generic_checks(state, client_restrctions, state->namaddr,
/* smtpd_check_helo - validate HELO hostname */
-char *smtpd_check_helo(SMTPD_STATE *state, char *helohost)
+char *smtpd_check_helo(SMTPD_STATE * state, char *helohost)
{
int status;
char *saved_helo;
/*
* Apply restrictions in the order as specified.
*/
+ state->recursion = 0;
status = setjmp(smtpd_check_buf);
if (status == 0 && helo_restrctions->argc)
status = generic_checks(state, helo_restrctions, state->helo_name,
/* smtpd_check_mail - validate sender address, driver */
-char *smtpd_check_mail(SMTPD_STATE *state, char *sender)
+char *smtpd_check_mail(SMTPD_STATE * state, char *sender)
{
int status;
char *saved_sender;
/*
* Apply restrictions in the order as specified.
*/
+ state->recursion = 0;
status = setjmp(smtpd_check_buf);
if (status == 0 && mail_restrctions->argc)
status = generic_checks(state, mail_restrctions, sender,
/* smtpd_check_rcpt - validate recipient address, driver */
-char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
+char *smtpd_check_rcpt(SMTPD_STATE * state, char *recipient)
{
int status;
char *saved_recipient;
/*
* Apply restrictions in the order as specified.
*/
+ state->recursion = 0;
status = setjmp(smtpd_check_buf);
if (status == 0 && rcpt_restrctions->argc)
status = generic_checks(state, rcpt_restrctions,
/* smtpd_check_etrn - validate ETRN request */
-char *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
+char *smtpd_check_etrn(SMTPD_STATE * state, char *domain)
{
int status;
char *saved_etrn_name;
/*
* Apply restrictions in the order as specified.
*/
+ state->recursion = 0;
status = setjmp(smtpd_check_buf);
if (status == 0 && etrn_restrctions->argc)
status = generic_checks(state, etrn_restrctions, domain,
/* smtpd_check_size - check optional SIZE parameter value */
-char *smtpd_check_size(SMTPD_STATE *state, off_t size)
+char *smtpd_check_size(SMTPD_STATE * state, off_t size)
{
char *myname = "smtpd_check_size";
struct fsspace fsbuf;
char *name;
char *defval;
char **target;
-} STRING_TABLE;
+} STRING_TABLE;
static STRING_TABLE string_table[] = {
VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains,
char *name;
int defval;
int *target;
-} INT_TABLE;
+} INT_TABLE;
int var_unk_client_code;
int var_bad_name_code;
return (0);
}
+/* rest_class - (re)define a restriction class */
+
+static void rest_class(char *class)
+{
+ char *cp = class;
+ char *name;
+ HTABLE_INFO *entry;
+
+ if (smtpd_rest_classes == 0)
+ smtpd_rest_classes = htable_create(1);
+
+ if ((name = mystrtok(&cp, " \t\r\n,")) == 0)
+ msg_panic("rest_class: null class name");
+ if ((entry = htable_locate(smtpd_rest_classes, name)) != 0)
+ argv_free((ARGV *) entry->value);
+ else
+ entry = htable_enter(smtpd_rest_classes, name, (char *) 0);
+ entry->value = (char *) smtpd_check_parse(cp);
+}
+
/* resolve_clnt_init - initialize reply */
-void resolve_clnt_init(RESOLVE_REPLY *reply)
+void resolve_clnt_init(RESOLVE_REPLY * reply)
{
reply->transport = vstring_alloc(100);
reply->nexthop = vstring_alloc(100);
/* canon_addr_internal - stub */
-VSTRING *canon_addr_internal(VSTRING *result, const char *addr)
+VSTRING *canon_addr_internal(VSTRING * result, const char *addr)
{
if (addr == STR(result))
msg_panic("canon_addr_internal: result clobbers input");
/* resolve_clnt_query - stub */
-void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply)
+void resolve_clnt_query(const char *addr, RESOLVE_REPLY * reply)
{
if (addr == STR(reply->recipient))
msg_panic("resolve_clnt_query: result clobbers input");
/* smtpd_chat_reset - stub */
-void smtpd_chat_reset(SMTPD_STATE *unused_state)
+void smtpd_chat_reset(SMTPD_STATE * unused_state)
{
}
resp = 0;
break;
}
+ if (strcasecmp(args->argv[0], "restriction_class") == 0) {
+ rest_class(args->argv[1]);
+ resp = 0;
+ break;
+ }
if (int_update(args->argv)
|| string_update(args->argv)
|| rest_update(args->argv)) {
helo_restrictions <restrictions>\n\
sender_restrictions <restrictions>\n\
recipient_restrictions <restrictions>\n\
+ restriction_class name,<restrictions>\n\
\n\
Note: no address rewriting \n";
break;
#
# Test the client restrictions.
#
-client_restrictions permit_mynetworks,reject_unknown_client,hash:./smtpd_check_access
+client_restrictions permit_mynetworks,reject_unknown_client,dbm:./smtpd_check_access
client unknown 131.155.210.17
client unknown 168.100.189.13
client random.bad.domain 123.123.123.123
#
# Test the helo restrictions
#
-helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,hash:./smtpd_check_access
+helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,dbm:./smtpd_check_access
client unknown 131.155.210.17
helo foo.
client foo 123.123.123.123
helo foo.
helo foo
helo spike.porcupine.org
-helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access
+helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,dbm:./smtpd_check_access
helo random.bad.domain
helo friend.bad.domain
helo_restrictions reject_invalid_hostname,reject_unknown_hostname
sender_restrictions reject_unknown_address
mail foo@watson.ibm.com
mail foo@bad.domain
-sender_restrictions hash:./smtpd_check_access
+sender_restrictions dbm:./smtpd_check_access
mail bad-sender@any.domain
mail bad-sender@good.domain
mail reject@this.address
client foo 123.123.123.123
rcpt foo@watson.ibm.com
rcpt foo@porcupine.org
-recipient_restrictions hash:./smtpd_check_access
+recipient_restrictions dbm:./smtpd_check_access
mail bad-sender@any.domain
mail bad-sender@good.domain
mail reject@this.address
recipient_restrictions check_relay_domains
client foo 131.155.210.17
rcpt foo@watson.ibm.com
-recipient_restrictions check_client_access,hash:./smtpd_check_access,check_relay_domains
+recipient_restrictions check_client_access,dbm:./smtpd_check_access,check_relay_domains
client foo 131.155.210.17
rcpt foo@porcupine.org
-helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access
-recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_relay_domains
+helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,dbm:./smtpd_check_access
+recipient_restrictions check_helo_access,dbm:./smtpd_check_access,check_relay_domains
helo bad.domain
rcpt foo@porcupine.org
helo 131.155.210.17
rcpt foo@porcupine.org
-recipient_restrictions check_sender_access,hash:./smtpd_check_access,check_relay_domains
+recipient_restrictions check_sender_access,dbm:./smtpd_check_access,check_relay_domains
mail foo@bad.domain
rcpt foo@porcupine.org
mail foo@friend.bad.domain
client_restrictions permit
helo_restrictions permit
sender_restrictions permit
-recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_sender_access,hash:./smtpd_check_access
+recipient_restrictions check_helo_access,dbm:./smtpd_check_access,check_sender_access,dbm:./smtpd_check_access
helo bad.domain
mail foo@good.domain
rcpt foo@porcupine.org
#
# Test the client restrictions.
#
-client_restrictions permit_mynetworks,reject_unknown_client,check_client_access,hash:./smtpd_check_access
+client_restrictions permit_mynetworks,reject_unknown_client,check_client_access,dbm:./smtpd_check_access
client unknown 131.155.210.17
client unknown 168.100.189.13
client random.bad.domain 123.123.123.123
#
# Test the helo restrictions
#
-helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,check_helo_access,hash:./smtpd_check_access
+helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,check_helo_access,dbm:./smtpd_check_access
client unknown 131.155.210.17
helo foo.
client foo 123.123.123.123
helo foo.
helo foo
helo spike.porcupine.org
-helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,hash:./smtpd_check_access
+helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,dbm:./smtpd_check_access
helo random.bad.domain
helo friend.bad.domain
#
sender_restrictions reject_unknown_address
mail foo@watson.ibm.com
mail foo@bad.domain
-sender_restrictions check_sender_access,hash:./smtpd_check_access
+sender_restrictions check_sender_access,dbm:./smtpd_check_access
mail bad-sender@any.domain
mail bad-sender@good.domain
mail reject@this.address
client foo 123.123.123.123
rcpt foo@watson.ibm.com
rcpt foo@porcupine.org
-recipient_restrictions check_recipient_access,hash:./smtpd_check_access
+recipient_restrictions check_recipient_access,dbm:./smtpd_check_access
mail bad-sender@any.domain
mail bad-sender@good.domain
mail reject@this.address
>>> #
>>> # Test the client restrictions.
>>> #
->>> client_restrictions permit_mynetworks,reject_unknown_client,hash:./smtpd_check_access
+>>> client_restrictions permit_mynetworks,reject_unknown_client,dbm:./smtpd_check_access
OK
>>> client unknown 131.155.210.17
./smtpd_check: reject: CONNECT from unknown[131.155.210.17]: 450 Client host rejected: cannot find your hostname, [131.155.210.17]
>>> #
>>> # Test the helo restrictions
>>> #
->>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,hash:./smtpd_check_access
+>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,dbm:./smtpd_check_access
OK
>>> client unknown 131.155.210.17
OK
450 <foo>: Helo command rejected: Host not found
>>> helo spike.porcupine.org
OK
->>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access
+>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,dbm:./smtpd_check_access
OK
>>> helo random.bad.domain
./smtpd_check: reject: HELO from foo[123.123.123.123]: 554 <random.bad.domain>: Helo command rejected: match bad.domain
>>> mail foo@bad.domain
./smtpd_check: reject: MAIL from foo[123.123.123.123]: 450 <foo@bad.domain>: Sender address rejected: Domain not found; from=<foo@bad.domain>
450 <foo@bad.domain>: Sender address rejected: Domain not found
->>> sender_restrictions hash:./smtpd_check_access
+>>> sender_restrictions dbm:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 <bad-sender@any.domain>: Sender address rejected: match bad-sender@; from=<bad-sender@any.domain>
554 <foo@watson.ibm.com>: Recipient address rejected: Relay access denied
>>> rcpt foo@porcupine.org
OK
->>> recipient_restrictions hash:./smtpd_check_access
+>>> recipient_restrictions dbm:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 <bad-sender@any.domain>: Sender address rejected: match bad-sender@; from=<bad-sender@any.domain>
>>> rcpt foo@watson.ibm.com
./smtpd_check: reject: RCPT from foo[131.155.210.17]: 554 <foo@watson.ibm.com>: Recipient address rejected: Relay access denied; from=<foo@friend.bad.domain> to=<foo@watson.ibm.com>
554 <foo@watson.ibm.com>: Recipient address rejected: Relay access denied
->>> recipient_restrictions check_client_access,hash:./smtpd_check_access,check_relay_domains
+>>> recipient_restrictions check_client_access,dbm:./smtpd_check_access,check_relay_domains
OK
>>> client foo 131.155.210.17
OK
>>> rcpt foo@porcupine.org
OK
->>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,hash:./smtpd_check_access
+>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,dbm:./smtpd_check_access
OK
->>> recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_relay_domains
+>>> recipient_restrictions check_helo_access,dbm:./smtpd_check_access,check_relay_domains
OK
>>> helo bad.domain
./smtpd_check: reject: HELO from foo[131.155.210.17]: 554 <bad.domain>: Helo command rejected: match bad.domain; from=<foo@friend.bad.domain>
OK
>>> rcpt foo@porcupine.org
OK
->>> recipient_restrictions check_sender_access,hash:./smtpd_check_access,check_relay_domains
+>>> recipient_restrictions check_sender_access,dbm:./smtpd_check_access,check_relay_domains
OK
>>> mail foo@bad.domain
./smtpd_check: reject: MAIL from foo[131.155.210.17]: 554 <foo@bad.domain>: Sender address rejected: match bad.domain; from=<foo@bad.domain>
OK
>>> sender_restrictions permit
OK
->>> recipient_restrictions check_helo_access,hash:./smtpd_check_access,check_sender_access,hash:./smtpd_check_access
+>>> recipient_restrictions check_helo_access,dbm:./smtpd_check_access,check_sender_access,dbm:./smtpd_check_access
OK
>>> helo bad.domain
OK
>>> #
>>> # Test the client restrictions.
>>> #
->>> client_restrictions permit_mynetworks,reject_unknown_client,check_client_access,hash:./smtpd_check_access
+>>> client_restrictions permit_mynetworks,reject_unknown_client,check_client_access,dbm:./smtpd_check_access
OK
>>> client unknown 131.155.210.17
./smtpd_check: reject: CONNECT from unknown[131.155.210.17]: 450 Client host rejected: cannot find your hostname, [131.155.210.17]
>>> #
>>> # Test the helo restrictions
>>> #
->>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,check_helo_access,hash:./smtpd_check_access
+>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,reject_unknown_hostname,check_helo_access,dbm:./smtpd_check_access
OK
>>> client unknown 131.155.210.17
OK
450 <foo>: Helo command rejected: Host not found
>>> helo spike.porcupine.org
OK
->>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,hash:./smtpd_check_access
+>>> helo_restrictions permit_mynetworks,reject_unknown_client,reject_invalid_hostname,check_helo_access,dbm:./smtpd_check_access
OK
>>> helo random.bad.domain
./smtpd_check: reject: HELO from foo[123.123.123.123]: 554 <random.bad.domain>: Helo command rejected: match bad.domain
>>> mail foo@bad.domain
./smtpd_check: reject: MAIL from foo[123.123.123.123]: 450 <foo@bad.domain>: Sender address rejected: Domain not found; from=<foo@bad.domain>
450 <foo@bad.domain>: Sender address rejected: Domain not found
->>> sender_restrictions check_sender_access,hash:./smtpd_check_access
+>>> sender_restrictions check_sender_access,dbm:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 <bad-sender@any.domain>: Sender address rejected: match bad-sender@; from=<bad-sender@any.domain>
554 <foo@watson.ibm.com>: Recipient address rejected: Relay access denied
>>> rcpt foo@porcupine.org
OK
->>> recipient_restrictions check_recipient_access,hash:./smtpd_check_access
+>>> recipient_restrictions check_recipient_access,dbm:./smtpd_check_access
OK
>>> mail bad-sender@any.domain
./smtpd_check: reject: MAIL from foo[123.123.123.123]: 554 <bad-sender@any.domain>: Sender address rejected: match bad-sender@; from=<bad-sender@any.domain>
131.155.210 554 match 131.155.210
131.155.210.17 OK
reject@this.address 554 match reject@this.address
+open_user@some.site open
+strict_user@some.site strict
+auth_client 123456
/* error (no address->name mapping or no name->address mapping).
/* .IP 5
/* The name lookup or verification failed with an unrecoverable
-/* error (no address->name mapping, bad hostname syntax, no
+/* error (no address->name mapping, bad hostname syntax, no
/* name->address mapping, client address not listed for hostname).
/* .RE
/* .PP
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <stdio.h> /* strerror() */
+#include <errno.h>
#include <netdb.h>
#include <string.h>
+ /*
+ * Older systems don't have h_errno. Even modern systems don't have
+ * hstrerror().
+ */
#ifdef NO_HERRNO
+
static int h_errno = TRY_AGAIN;
-define hstrerror(x) "Host not found"
+
+#define HSTRERROR(err) "Host not found"
+
+#else
+
+#define HSTRERROR(err) (\
+ err == TRY_AGAIN ? "Host not found, try again" : \
+ err == HOST_NOT_FOUND ? "Host not found" : \
+ err == NO_DATA ? "Host name has no address" : \
+ err == NO_RECOVERY ? "Name server failure" : \
+ strerror(errno) \
+ )
#endif
/* Utility library. */
/* smtpd_peer_init - initialize peer information */
-void smtpd_peer_init(SMTPD_STATE *state)
+void smtpd_peer_init(SMTPD_STATE * state)
{
struct sockaddr_in sin;
SOCKADDR_SIZE len = sizeof(sin);
int i;
/*
- * If it's not networked assume local.
+ * Look up the peer address information.
*/
if (getpeername(vstream_fileno(state->client),
- (struct sockaddr *) & sin, &len) < 0)
- sin.sin_family = AF_UNSPEC;
-
- switch (sin.sin_family) {
+ (struct sockaddr *) & sin, &len) >= 0) {
+ errno = 0;
+ }
- /*
- * If it's not Internet, assume the client is local, and avoid using
- * the naming service because that can hang when the machine is
- * disconnected.
- */
- default:
- state->name = mystrdup("localhost");
- state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */
- state->peer_code = 2;
- break;
+ /*
+ * If peer went away, give up.
+ */
+ if (errno == ECONNRESET || errno == ECONNABORTED) {
+ msg_info("errno %d %m", errno);
+ state->name = mystrdup("unknown");
+ state->addr = mystrdup("unknown");
+ state->peer_code = 5;
+ }
- /*
- * Look up and "verify" the client hostname.
- */
- case AF_INET:
+ /*
+ * Look up and "verify" the client hostname.
+ */
+ else if (errno == 0 && sin.sin_family == AF_INET) {
state->addr = mystrdup(inet_ntoa(sin.sin_addr));
hp = gethostbyaddr((char *) &(sin.sin_addr),
sizeof(sin.sin_addr), AF_INET);
if (hp == 0) {
state->name = mystrdup("unknown");
state->peer_code = (h_errno == TRY_AGAIN ? 4 : 5);
- break;
- }
- if (!valid_hostname(hp->h_name)) {
+ } else if (!valid_hostname(hp->h_name)) {
state->name = mystrdup("unknown");
state->peer_code = 5;
- break;
- }
- state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
- state->peer_code = 2;
+ } else {
+ state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
+ state->peer_code = 2;
+
+ /*
+ * Reject the hostname if it does not list the peer address.
+ */
+#define REJECT_PEER_NAME(state, code) { \
+ myfree(state->name); \
+ state->name = mystrdup("unknown"); \
+ state->peer_code = 5; \
+ }
- /*
- * Reject the hostname if it does not list the peer address.
- */
- hp = gethostbyname(state->name); /* clobbers hp->name!! */
- if (hp == 0) {
- msg_warn("hostname %s verification failed: %s",
- state->name, hstrerror(h_errno));
- myfree(state->name);
- state->name = mystrdup("unknown");
- state->peer_code = (h_errno == TRY_AGAIN ? 4 : 5);
- break;
- }
- if (hp->h_length != sizeof(sin.sin_addr)) {
- msg_warn("hostname %s verification failed: bad address size %d",
- state->name, hp->h_length);
- myfree(state->name);
- state->name = mystrdup("unknown");
- state->peer_code = 5;
- break;
+ hp = gethostbyname(state->name); /* clobbers hp->name!! */
+ if (hp == 0) {
+ msg_warn("%s: hostname %s verification failed: %s",
+ state->addr, state->name, HSTRERROR(h_errno));
+ REJECT_PEER_NAME(state, (h_errno == TRY_AGAIN ? 4 : 5));
+ } else if (hp->h_length != sizeof(sin.sin_addr)) {
+ msg_warn("%s: hostname %s verification failed: bad address size %d",
+ state->addr, state->name, hp->h_length);
+ REJECT_PEER_NAME(state, 5);
+ } else {
+ for (i = 0; /* void */ ; i++) {
+ if (hp->h_addr_list[i] == 0) {
+ msg_warn("%s: address not listed for hostname %s",
+ state->addr, state->name);
+ REJECT_PEER_NAME(state, 5);
+ break;
+ }
+ if (memcmp(hp->h_addr_list[i],
+ (char *) &sin.sin_addr,
+ sizeof(sin.sin_addr)) == 0)
+ break; /* keep peer name */
+ }
+ }
}
- for (i = 0; hp->h_addr_list[i]; i++) {
- if (memcmp(hp->h_addr_list[i],
- (char *) &sin.sin_addr,
- sizeof(sin.sin_addr)) == 0)
- break;
- }
- if (hp->h_addr_list[i] == 0) {
- msg_warn("address %s not listed for name %s",
- state->addr, state->name);
- myfree(state->name);
- state->name = mystrdup("unknown");
- state->peer_code = 5;
- break;
- }
- break;
}
+
+ /*
+ * If it's not Internet, assume the client is local, and avoid using the
+ * naming service because that can hang when the machine is disconnected.
+ */
+ else {
+ state->name = mystrdup("localhost");
+ state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */
+ state->peer_code = 2;
+ }
+
+ /*
+ * Do the name[addr] formatting for pretty reports.
+ */
state->namaddr =
concatenate(state->name, "[", state->addr, "]", (char *) 0);
}
/* smtpd_peer_reset - destroy peer information */
-void smtpd_peer_reset(SMTPD_STATE *state)
+void smtpd_peer_reset(SMTPD_STATE * state)
{
- if (state->name)
- myfree(state->name);
- if (state->addr)
- myfree(state->addr);
- if (state->namaddr)
- myfree(state->namaddr);
+ myfree(state->name);
+ myfree(state->addr);
+ myfree(state->namaddr);
}
state->etrn_name = 0;
state->protocol = "SMTP";
state->where = SMTPD_AFTER_CONNECT;
+ state->recursion = 0;
/*
* Initialize peer information.