20010403
Workaround: the mysql library can return null pointers
- rather than zero-length strings.
+ rather than zero-length strings. File: util/dict_mysql.c.
20010404
- Logging: log additional information about why "mail for
- XXX loops back to myself", when the local machine is the
- best MX host. File: smtp/smtp_addr.c.
+ Ergonomics: log additional information about the reason
+ why "mail for XXX loops back to myself" when the local
+ machine is the best MX host. File: smtp/smtp_addr.c.
20010406
20010413
Ergonomics: Postfix applications now warn when a DB or DBM
- file is out of date, and recommends to re-run postmap or
- postalias. Files: util/dict_db.c, util/dict_dbm.c.
+ file is out of date, and recommend to rebuild the table.
+ Files: util/dict_db.c, util/dict_dbm.c.
20010414
Workaround: the SMTP server did not really parse invalid
addresses such as <first last <user@domain>> well. I
thought this was taken care of years ago. File: smtpd/smtpd.c.
+
+20010427
+
+ Bugfix: smtpd would reject mail instead of replying with
+ a 4xx temporary error code when, for example, an LDAP or
+ mysql server was unavailable. Remotely based on a fix by
+ Robert Kiessling @ de.easynet.net. File: smtpd/smtpd_check.c.
+
+20010429
+
+ Feature: the Postfix SMTP client now by default randomly
+ shuffles destination IP addresses of equal preference.
+ File: smtp/smtp_addr.c. Based on an idea by Aleph1.
Pay particular attention to messages that are labeled as <b>fatal</b>
and <b>panic</b>. These describe catastrophic failures that need
to be addressed before Postfix is happy. Problems labeled as
-<b>fatal</b> as fixed by adjusting configurations. Problems labeled
-as <b>panic</b> are fixed by changing Postfix source code.
+<b>fatal</b> are fixed by you, by adjusting configuration files,
+file permissions and so on. Problems labeled as <b>panic</b> are
+fixed by the Postfix author, by changing Postfix source code.
<hr>
For security reasons, Postfix tries to avoid using <b>root</b>
privileges where possible. In particular, Postfix virtual mapping
is done by an unprivileged daemon, so there is no secure way to
-execute commands or to deliver to files found in virtual maps.
+execute commands or to deliver to files specified in virtual maps.
<hr>
<p>
To avoid queue file name collisions when copying queue files,
-restore queue files in the maildrop directory instead.
+restore the incoming, active and deferred queue files under the
+maildrop directory instead.
+
+<p>
+
+As of late 2000, Postfix queues are all hashed (for example, file
+ABCDEF is stored as A/B/ABCDEF), so you need an additional step to
+move files down from their subdirectories.
<p>
<pre>
# postfix stop
- ... restore queue files under the maildrop directory...
+ # cd /var/spool/postfix/maildrop
+ ... restore incoming/active/deferred queue files under the maildrop directory...
+ # find incoming active deferred -type f -exec mv '{}' . ';'
+ # rm -rf incoming active deferred
# postfix start
</pre>
+While all this is going on, don't submit new mail locally, because
+that could collide with the files you are restoring under the
+maildrop directory.
+
<p>
When Postfix is started, it will pick up queue files from the
#define DEF_SMTP_BIND_ADDR ""
extern char *var_smtp_bind_addr;
+#define VAR_SMTP_RAND_ADDR "smtp_randomize_address"
+#define DEF_SMTP_RAND_ADDR 1
+extern bool var_smtp_rand_addr;
+
/*
* SMTP server. The soft error limit determines how many errors an SMTP
* client may make before we start to slow down; the hard error limit
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20010426"
+#define DEF_MAIL_VERSION "Snapshot-20010429"
extern char *var_mail_version;
/* LICENSE
int lmtp_lhlo(LMTP_STATE *state)
{
- char *myname = "lmtp_lhlo";
LMTP_SESSION *session = state->session;
LMTP_RESP *resp;
int except;
char *lines;
char *words;
char *word;
- SOCKOPT_SIZE optlen = sizeof(state->sndbufsize);
/*
* Prepare for disaster.
/* fax machines, and so on.
/*
/* To prevent Postfix from sending multiple recipients per delivery
-/* request, specify
+/* request, specify
/*
/* .ti +4
/* \fItransport\fB_destination_recipient_limit = 1\fR
/*
-/* in the Postfix \fBmain.cf\fR file, where \fItransport\fR
+/* in the Postfix \fBmain.cf\fR file, where \fItransport\fR
/* is the name in the first column of the Postfix \fBmaster.cf\fR
/* entry for the pipe-based delivery transport.
/* COMMAND ATTRIBUTE SYNTAX
/* expand_argv - expand macros in the argument vector */
-static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list, long data_size)
+static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
{
VSTRING *buf = vstring_alloc(100);
ARGV *result;
vstring_sprintf(buf, "%ld", (long) request->data_size);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
vstring_free(buf);
- expanded_argv = expand_argv(attr.command, rcpt_list, request->data_size);
+ expanded_argv = expand_argv(attr.command, rcpt_list);
export_env = argv_split(var_export_environ, ", \t\r\n");
command_status = pipe_command(request->fp, why,
char *var_smtp_sasl_passwd;
bool var_smtp_sasl_enable;
char *var_smtp_bind_addr;
+bool var_smtp_rand_addr;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
VAR_SMTP_ALWAYS_EHLO, DEF_SMTP_ALWAYS_EHLO, &var_smtp_always_ehlo,
VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo,
VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
+ VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
0,
};
#include <mymalloc.h>
#include <inet_addr_list.h>
#include <stringops.h>
+#include <myrand.h>
/* Global library. */
msg_info("end %s address list", what);
}
+/* smtp_rand_addr - randomize equal-preference resource records */
+
+static int smtp_rand_addr(DNS_RR *a, DNS_RR *b)
+{
+ int diff;
+
+ /*
+ * XXX Equal-preference records are made to appear different. The bogus
+ * difference is not consistent from one call to the next. Code based on
+ * an idea by Aleph1.
+ */
+ return ((diff = a->pref - b->pref != 0) ? diff : (myrand() & 1) ? -1 : 1);
+}
+
/* smtp_addr_one - address lookup for one host name */
static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRING *why)
return (addr_list);
}
-/* smtp_compare_mx - compare resource records by preference */
+/* smtp_compare_pref - compare resource records by preference */
-static int smtp_compare_mx(DNS_RR *a, DNS_RR *b)
+static int smtp_compare_pref(DNS_RR *a, DNS_RR *b)
{
return (a->pref - b->pref);
}
addr_list = smtp_host_addr(name, why);
break;
case DNS_OK:
- mx_names = dns_rr_sort(mx_names, smtp_compare_mx);
+ mx_names = dns_rr_sort(mx_names, smtp_compare_pref);
best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE);
addr_list = smtp_addr_list(mx_names, why);
dns_rr_free(mx_names);
}
}
}
+ if (addr_list && var_smtp_rand_addr)
+ addr_list = dns_rr_sort(addr_list, smtp_rand_addr);
break;
case DNS_NOTFOUND:
addr_list = smtp_host_addr(name, why);
*/
#define PREF0 0
addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why);
+ if (addr_list && var_smtp_rand_addr)
+ addr_list = dns_rr_sort(addr_list, smtp_rand_addr);
if (msg_verbose)
smtp_print_addr(host, addr_list);
return (addr_list);
/* Allow untrusted clients to specify addresses with sender-specified
/* routing. Enabling this opens up nasty relay loopholes involving
/* trusted backup MX hosts.
-/* .IP \fBrestriction_classes\fR
+/* .IP \fBsmtpd_restriction_classes\fR
/* Declares the name of zero or more parameters that contain a
/* list of UCE restrictions. The names of these parameters can
/* then be used instead of the restriction lists that they represent.
return (SMTPD_CHECK_REJECT);
}
+/* reject_dict_retry - reject with temporary failure if dict lookup fails */
+
+static void reject_dict_retry(SMTPD_STATE *state, const char *reply_name)
+{
+ longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_RESOURCE,
+ "%d <%s>: Temporary lookup failure",
+ 451, reply_name));
+}
+
+/* check_maps_find - reject with temporary failure if dict lookup fails */
+
+static const char *check_maps_find(SMTPD_STATE *state, const char *reply_name,
+ MAPS *maps, const char *key, int flags)
+{
+ const char *result;
+
+ dict_errno = 0;
+ if ((result = maps_find(maps, key, flags)) == 0
+ && dict_errno == DICT_ERR_RETRY)
+ reject_dict_retry(state, reply_name);
+ return (result);
+}
+
+/* check_mail_addr_find - reject with temporary failure if dict lookup fails */
+
+static const char *check_mail_addr_find(SMTPD_STATE *state, const char *reply_name,
+ MAPS *maps, const char *key, char **ext)
+{
+ const char *result;
+
+ dict_errno = 0;
+ if ((result = mail_addr_find(maps, key, ext)) == 0
+ && dict_errno == DICT_ERR_RETRY)
+ reject_dict_retry(state, reply_name);
+ return (result);
+}
+
/* reject_unknown_client - fail if client hostname is unknown */
static int reject_unknown_client(SMTPD_STATE *state)
return (SMTPD_CHECK_DUNNO);
}
-static int permit_auth_destination(char *recipient);
+static int permit_auth_destination(SMTPD_STATE *state, char *recipient);
/* check_relay_domains - OK/FAIL for message relaying */
/*
* Permit authorized destinations.
*/
- if (permit_auth_destination(recipient) == SMTPD_CHECK_OK)
+ if (permit_auth_destination(state, recipient) == SMTPD_CHECK_OK)
return (SMTPD_CHECK_OK);
/*
/* permit_auth_destination - OK for message relaying */
-static int permit_auth_destination(char *recipient)
+static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
{
char *myname = "permit_auth_destination";
char *domain;
* virtual_maps.
*/
if (resolve_local(domain)
- || (*var_virtual_maps && maps_find(virtual_maps, domain, 0)))
+ || (*var_virtual_maps
+ && check_maps_find(state, recipient, virtual_maps, domain, 0)))
return (SMTPD_CHECK_OK);
/*
/*
* Skip authorized destination.
*/
- if (permit_auth_destination(recipient) == SMTPD_CHECK_OK)
+ if (permit_auth_destination(state, recipient) == SMTPD_CHECK_OK)
return (SMTPD_CHECK_DUNNO);
/*
/* 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 *state, const char *recipient)
{
char *myname = "permit_mx_backup";
char *domain;
return (SMTPD_CHECK_OK);
domain += 1;
if (resolve_local(domain)
- || (*var_virtual_maps && maps_find(virtual_maps, domain, 0)))
+ || (*var_virtual_maps
+ && check_maps_find(state, recipient, virtual_maps, domain, 0)))
return (SMTPD_CHECK_OK);
if (msg_verbose)
return (SMTPD_CHECK_DUNNO);
domain += 1;
if (resolve_local(domain)
- || (*var_virtual_maps && maps_find(virtual_maps, domain, 0)))
+ || (*var_virtual_maps
+ && check_maps_find(state, reply_name, virtual_maps, domain, 0)))
return (SMTPD_CHECK_DUNNO);
if (domain[0] == '#')
return (SMTPD_CHECK_DUNNO);
status = permit_mx_backup(state, state->recipient);
} else if (strcasecmp(name, PERMIT_AUTH_DEST) == 0) {
if (state->recipient)
- status = permit_auth_destination(state->recipient);
+ status = permit_auth_destination(state, state->recipient);
} else if (strcasecmp(name, REJECT_UNAUTH_DEST) == 0) {
if (state->recipient)
status = reject_unauth_destination(state, state->recipient);
char *myname = "smtpd_check_rcptmap";
char *saved_recipient;
char *domain;
+ int status;
/*
* XXX This module does a lot of unnecessary guessing. This functionality
*/
SMTPD_CHECK_PUSH(saved_recipient, state->recipient, recipient);
+ /*
+ * Return here in case of serious trouble.
+ */
+ if ((status = setjmp(smtpd_check_buf)) != 0)
+ SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ?
+ STR(error_text) : 0);
+
/*
* Resolve the address.
*/
SMTPD_CHECK_RCPT_RETURN(0);
#define NOMATCH(map, rcpt) \
- (mail_addr_find(map, rcpt, (char **) 0) == 0 && dict_errno == 0)
+ (check_mail_addr_find(state, recipient, map, rcpt, (char **) 0) == 0)
/*
* Reject mail to unknown addresses in Postfix-style virtual domains.
*/
- if (*var_virtual_maps && maps_find(virtual_maps, domain, 0)) {
+ if (*var_virtual_maps
+ && (check_maps_find(state, recipient, virtual_maps, domain, 0))) {
if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
&& NOMATCH(canonical_maps, STR(reply.recipient))
&& NOMATCH(relocated_maps, STR(reply.recipient))
* String-valued configuration parameters.
*/
char *var_maps_rbl_domains;
+char *var_myorigin;
char *var_mydest;
char *var_inet_interfaces;
+char *var_rcpt_delim;
char *var_rest_classes;
char *var_alias_maps;
char *var_rcpt_canon_maps;
static STRING_TABLE string_table[] = {
VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains,
+ VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin,
VAR_MYDEST, DEF_MYDEST, &var_mydest,
VAR_INET_INTERFACES, DEF_INET_INTERFACES, &var_inet_interfaces,
+ VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim,
VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes,
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps,
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps,
reply->recipient = vstring_alloc(100);
}
+#ifdef USE_SASL_AUTH
+
+bool var_smtpd_sasl_enable = 0;
+
+/* smtpd_sasl_connect - stub */
+
+void smtpd_sasl_connect(SMTPD_STATE *state)
+{
+ msg_panic("smtpd_sasl_connect was called");
+}
+
+/* smtpd_sasl_disconnect - stub */
+
+void smtpd_sasl_disconnect(SMTPD_STATE *state)
+{
+ msg_panic("smtpd_sasl_disconnect was called");
+}
+
+/* permit_sasl_auth - stub */
+
+int permit_sasl_auth(SMTPD_STATE *state, int ifyes, int ifnot)
+{
+ return (ifnot);
+}
+
+#endif
+
/* canon_addr_internal - stub */
VSTRING *canon_addr_internal(VSTRING *result, const char *addr)
/*
* Try config settings.
*/
+#define UPDATE_MAPS(ptr, var, val, lock) \
+ { if (ptr) maps_free(ptr); ptr = maps_create(var, val, lock); }
+
case 2:
+ if (strcasecmp(args->argv[0], "virtual_maps") == 0) {
+ UPDATE_STRING(var_virtual_maps, args->argv[1]);
+ UPDATE_MAPS(virtual_maps, VAR_VIRTUAL_MAPS,
+ var_virtual_maps, DICT_FLAG_LOCK);
+ resp = 0;
+ break;
+ }
+ if (strcasecmp(args->argv[0], "local_recipient_maps") == 0) {
+ UPDATE_STRING(var_local_rcpt_maps, args->argv[1]);
+ UPDATE_MAPS(local_rcpt_maps, VAR_LOCAL_RCPT_MAPS,
+ var_local_rcpt_maps, DICT_FLAG_LOCK);
+ resp = 0;
+ break;
+ }
+ if (strcasecmp(args->argv[0], "canonical_maps") == 0) {
+ UPDATE_STRING(var_canonical_maps, args->argv[1]);
+ UPDATE_MAPS(canonical_maps, VAR_CANONICAL_MAPS,
+ var_canonical_maps, DICT_FLAG_LOCK);
+ resp = 0;
+ break;
+ }
if (strcasecmp(args->argv[0], "mynetworks") == 0) {
namadr_list_free(mynetworks);
mynetworks = namadr_list_init(args->argv[1]);
UPDATE_STRING(state.sender, args->argv[1]);
} else if (strcasecmp(args->argv[0], "rcpt") == 0) {
state.where = "RCPT";
- resp = smtpd_check_rcpt(&state, args->argv[1]);
+ (resp = smtpd_check_rcpt(&state, args->argv[1]))
+ || (resp = smtpd_check_rcptmap(&state, args->argv[1]));
}
break;
--- /dev/null
+#msg_verbose 1
+smtpd_delay_reject 0
+mynetworks 127.0.0.0/8,168.100.189.0/28
+relay_domains porcupine.org
+local_recipient_maps unix:passwd.byname
+client unknown 131.155.210.17
+canonical_maps tcp:localhost:200
+#
+recipient_restrictions permit
+rcpt no.such.user@[127.0.0.1]
+#
+virtual_maps tcp:localhost:100
+#
+recipient_restrictions permit_mx_backup
+rcpt wietse@nowhere1.com
+#
+recipient_restrictions check_relay_domains
+rcpt wietse@nowhere2.com
+#
+recipient_restrictions reject_unknown_recipient_domain
+rcpt wietse@nowhere3.com
+#
+recipient_restrictions permit_auth_destination
+rcpt wietse@nowhere4.com
+#
+recipient_restrictions reject_unauth_destination
+rcpt wietse@nowhere5.com
clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \
sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
- sane_socketpair.c
+ sane_socketpair.c myrand.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 \
clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \
sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
- sane_socketpair.o
+ sane_socketpair.o myrand.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 \
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 spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
- sane_time.h sane_socketpair.h
+ sane_time.h sane_socketpair.h myrand.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 \
mymalloc.o: sys_defs.h
mymalloc.o: msg.h
mymalloc.o: mymalloc.h
+myrand.o: myrand.c
+myrand.o: sys_defs.h
+myrand.o: myrand.h
mystrtok.o: mystrtok.c
mystrtok.o: sys_defs.h
mystrtok.o: stringops.h
static void dict_unix_close(DICT *dict)
{
- DICT_UNIX *dict_unix = (DICT_UNIX *) dict;
-
dict_free(dict);
}
--- /dev/null
+/*++
+/* NAME
+/* myrand 3
+/* SUMMARY
+/* rand wrapper
+/* SYNOPSIS
+/* #include <myrand.h>
+/*
+/* void mysrand(seed)
+/* int seed;
+/*
+/* int myrand()
+/* DESCRIPTION
+/* This module implements a wrapper for the portable, pseudo-random
+/* number generator.
+/*
+/* mysrand() performs initialization. This call may be skipped.
+/*
+/* myrand() returns a pseudo-random number in the range [0, RAND_MAX].
+/* If mysrand() was not called, it is invoked with the process ID.
+/* 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 <stdlib.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <myrand.h>
+
+static int myrand_initdone = 0;
+
+/* mysrand - initialize */
+
+void mysrand(int seed)
+{
+ srand(seed);
+ myrand_initdone = 1;
+}
+
+/* myrand - pseudo-random number */
+
+int myrand(void)
+{
+ if (myrand_initdone == 0)
+ mysrand(getpid());
+ return (rand());
+}
--- /dev/null
+#ifndef _MYRAND_H_INCLUDED_
+#define _MYRAND_H_INCLUDED_
+
+/*++
+/* NAME
+/* myrand 3h
+/* SUMMARY
+/* rand wrapper
+/* SYNOPSIS
+/* #include <myrand.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+extern void mysrand(int);
+extern int myrand(void);
+
+/* 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
/* Utility library. */
#include <msg.h>
+#include <myrand.h>
#include <iostuff.h>
/* rand_sleep - block for random time */
void rand_sleep(unsigned delay, unsigned variation)
{
char *myname = "rand_sleep";
- static pid_t my_pid;
unsigned usec;
/*
/*
* Use the semi-crappy random number generator.
*/
- if (my_pid == 0)
- srand((my_pid = getpid()) ^ time((time_t *) 0));
- usec = (delay - variation / 2) + variation * (double) rand() / RAND_MAX;
+ usec = (delay - variation / 2) + variation * (double) myrand() / RAND_MAX;
doze(usec);
}