-Incompatible changes with snapshot 19990909
+Incompatible changes with snapshot 19990910
===========================================
- You can not longer use virtual, canonical or aliases tables as
-SMTPD access control tables. Use the permit_address_map feature
+SMTPD access control tables. Use the permit_recipient_map feature
instead. The loss is compensated for.
-Major changes with snapshot 19990909
+Major changes with snapshot 19990910
====================================
- Per-client/helo/sender/recipient UCE restrictions: you can now
specify arbitrary restrictions on the right-hand side of SMTPD
-access tables. The only anomaly in this scheme is that header
-checks are the same for every message.
+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
- Reject mail for non-existent local accounts. You can now use
passwd/canonical/virtual/aliases tables for SMTPD access control.
-For example, specify in main.cf:
- relay_domains = $mydestination other domains...
+For example, a non-relaying site would specify in main.cf:
+
smtpd_recipient_restrictions =
- reject_unauth_destination
- permit_address_map unix:passwd.byname
- permit_address_map hash:/etc/canonical
- permit_address_map hash:/etc/postfix/virtual
- permit_address_map hash:/etc/aliases
+ permit_recipient_map unix:passwd.byname
+ permit_recipient_map hash:/etc/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.
-Unfortunately, permit_address_map does not combine well with
-check_relay_domains, because that restriction ALWAYS permits mail
-for local destinations. Instead of check_relay_domains, use some
-combination of permit_mynetworks and reject_unauth_destination.
+All this is great for non-relaying sites. A good example with
+permit_recipient_map for relaying sites still needs to be found.
+
+Unfortunately, permit_recipient_map does not combine well with
+permit_mynetworks, because permit_mynetworks accepts mail for ALL
+destinations, including ALL LOCAL destinations.
+
+Unfortunately, permit_recipient_map does not combine well with
+check_relay_domains, because check_relay_domains permits mail for
+ALL LOCAL destinations.
Incompatible changes with postfix-19990906
==========================================
/* const char *map_names;
/* int flags;
/*
+/* MAPS *maps_append(maps, map_name, dict_handle)
+/* MAPS *maps;
+/* const char *map_name;
+/* DICT *dict_handle;
+/*
/* const char *maps_find(maps, key, flags)
/* MAPS *maps;
/* const char *key;
/* other maps_xxx() operations.
/* See dict_open(3) for a description of flags.
/*
+/* maps_append() appends a dictionary to an existing handle
+/* under the given name. If dict_handle is a null pointer,
+/* the named dictionary is opened on the fly.
+/*
/* maps_find() searches the specified list of dictionaries
/* in the specified order for the named key. The result is in
/* memory that is overwritten upon each call.
MAPS *maps_create(const char *title, const char *map_names, int flags)
{
- char *myname = "maps_create";
- char *temp = mystrdup(map_names);
- char *bufp = temp;
+ char *temp;
+ char *bufp;
static char sep[] = " \t,\r\n";
MAPS *maps;
- DICT *dict;
char *map_type_name;
/*
maps = (MAPS *) mymalloc(sizeof(*maps));
maps->title = mystrdup(title);
maps->argv = argv_alloc(2);
+ maps->flags = flags;
/*
* For each specified type:name pair, either register a new dictionary,
* or increment the reference count of an existing one.
*/
- while ((map_type_name = mystrtok(&bufp, sep)) != 0) {
- if (msg_verbose)
- msg_info("%s: %s", myname, map_type_name);
- if ((dict = dict_handle(map_type_name)) == 0)
- dict = dict_open(map_type_name, O_RDONLY, flags);
- else if ((dict->flags & flags) != flags)
- msg_warn("%s: map %s has flags 0%o, want flags 0%o",
- myname, map_type_name, dict->flags, flags);
- dict_register(map_type_name, dict);
- argv_add(maps->argv, map_type_name, ARGV_END);
+ if (*map_names) {
+ bufp = temp = mystrdup(map_names);
+ while ((map_type_name = mystrtok(&bufp, sep)) != 0)
+ maps_append(maps, map_type_name, dict_handle(map_type_name));
+ myfree(temp);
}
- myfree(temp);
+ return (maps);
+}
+
+/* maps_append - append dictionary */
+
+MAPS *maps_append(MAPS *maps, const char *map_type_name, DICT *dict)
+{
+ char *myname = "maps_append";
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, map_type_name);
+ if (dict == 0)
+ dict = dict_open(map_type_name, O_RDONLY, maps->flags);
+ if ((dict->flags & maps->flags) != maps->flags)
+ msg_warn("%s: map %s has flags 0%o, want flags 0%o",
+ myname, map_type_name, dict->flags, maps->flags);
+ dict_register(map_type_name, dict);
+ argv_add(maps->argv, map_type_name, ARGV_END);
argv_terminate(maps->argv);
return (maps);
}
/* Allow the request when the local mail system is mail exchanger
/* for the recipient domain (this includes the case where the local
/* system is the final destination).
-/* .IP permit_address_map maptype:mapname
+/* .IP permit_recipient_map maptype:mapname
/* Permit the request when the recipient address matches the named
-/* table. Lookups are done in the same way as with virtual and
+/* table. Lookups are done in the same way as with virtual and
/* canonical maps.
/* .IP restriction_classes
/* Defines a list of parameter names, each parameter being a list
static HTABLE *smtpd_rest_classes;
-static HTABLE *smtpd_addr_maps;
+static HTABLE *smtpd_rcpt_maps;
/*
* The routine that recursively applies restrictions.
}
/*
- * This is the place to specify definitions for complex restrictions
- * such as check_relay_domains in terms of more elementary restrictions.
+ * This is the place to specify definitions for complex restrictions such
+ * as check_relay_domains in terms of more elementary restrictions.
*/
#if 0
htable_enter("check_relay_domains",
/*
* Other one-off initializations.
*/
- smtpd_addr_maps = htable_create(1);
+ smtpd_rcpt_maps = htable_create(1);
}
/* smtpd_check_reject - do the boring things that must be done */
return (reject_unknown_mailhost(state, domain, reply_name, reply_class));
}
-/* permit_addr_map - permit if address matches rewriting table */
+/* permit_rcpt_map - permit if recipient address matches rewriting table */
-static int permit_addr_map(SMTPD_STATE *state, char *table,
- char *reply_name, char *reply_class)
+static int permit_rcpt_map(char *table, char *reply_name)
{
- char *myname = "permit_addr_map";
+ char *myname = "permit_rcpt_map";
char *domain;
MAPS *map;
+ DICT *dict;
if (msg_verbose)
msg_info("%s: %s %s", myname, table, reply_name);
/*
* Look up the name in the specified table, using the usual magic of
- * canonical and virtual maps.
+ * 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.
*/
- if ((map = (MAPS *) htable_find(smtpd_addr_maps, table)) == 0) {
- map = maps_create("addr_map", table, DICT_FLAG_LOCK);
- htable_enter(smtpd_addr_maps, table, (char *) map);
+ if ((map = (MAPS *) htable_find(smtpd_rcpt_maps, table)) == 0) {
+ if ((dict = dict_handle(table)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, table);
+ map = maps_create("rcpt_map", "", DICT_FLAG_LOCK);
+ maps_append(map, table, dict);
+ htable_enter(smtpd_rcpt_maps, table, (char *) map);
}
#define TOSS_THE_EXTENSION ((char **) 0)
return (SMTPD_CHECK_OK);
/*
+ * Unfortunately, maps must be declared ahead of time so they can be
+ * opened before we go to jail. We could insist that the RHS can only
+ * contain a pre-defined restriction class name, but that would be too
+ * restrictive. Instead we warn if an access table references any map.
+ *
* XXX Don't use passwd files or address rewriting maps as access tables.
*/
+ if (strchr(value, ':') != 0) {
+ msg_warn("SMTPD access map %s has entry with lookup table: %s",
+ table, value);
+ msg_warn("do not specify lookup tables inside SMTPD access maps");
+ msg_warn("define a restriction class and specify its name instead");
+ longjmp(smtpd_check_buf, -1);
+ }
restrictions = argv_split(value, " \t\r\n,");
status = generic_checks(state, restrictions, reply_name,
reply_class, def_acl);
msg_info("%s: %s", myname, domain);
/*
- * Try the name and its parent domains. Don't try top-level domains.
+ * Try the name and its parent domains. Including top-level domains.
*/
#define CHK_DOMAIN_RETURN(x) { myfree(low_domain); return(x); }
- for (name = low_domain; (next = strchr(name, '.')) != 0; name = next + 1) {
- if ((dict = dict_handle(table)) == 0)
- msg_panic("%s: dictionary not found: %s", myname, table);
+ if ((dict = dict_handle(table)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, table);
+ for (name = low_domain; /* void */ ; name = next + 1) {
if (flags == 0 || (flags & dict->flags) != 0) {
if ((value = dict_get(dict, name)) != 0)
CHK_DOMAIN_RETURN(check_table_result(state, table, value,
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", table);
}
+ if ((next = strchr(name, '.')) == 0)
+ break;
flags = PARTIAL;
}
CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO);
*/
addr = STR(vstring_strcpy(error_text, address));
+ if ((dict = dict_handle(table)) == 0)
+ msg_panic("%s: dictionary not found: %s", myname, table);
do {
- if ((dict = dict_handle(table)) == 0)
- msg_panic("%s: dictionary not found: %s", myname, table);
if (flags == 0 || (flags & dict->flags) != 0) {
if ((value = dict_get(dict, addr)) != 0)
return (check_table_result(state, table, value, address,
char *myname = "generic_checks";
char **cpp;
char *name;
- int status;
+ int status = 0;
ARGV *list;
- status = setjmp(smtpd_check_buf);
- if (status != 0)
- return (0);
-
if (msg_verbose)
msg_info("%s: START", myname);
if (state->recipient)
status = reject_non_fqdn_address(state, state->recipient,
state->recipient, SMTPD_NAME_RECIPIENT);
- } else if (is_map_command(name, PERMIT_ADDR_MAP, &cpp)) {
+ } else if (is_map_command(name, PERMIT_RCPT_MAP, &cpp)) {
if (state->recipient)
- status = permit_addr_map(state, *cpp,
- state->recipient, SMTPD_NAME_RECIPIENT);
+ status = permit_rcpt_map(*cpp, state->recipient);
}
/*
/*
* Apply restrictions in the order as specified.
*/
- status = generic_checks(state, client_restrctions, state->namaddr,
- SMTPD_NAME_CLIENT, CHECK_CLIENT_ACL);
+ status = setjmp(smtpd_check_buf);
+ if (status == 0 && client_restrctions->argc)
+ status = generic_checks(state, client_restrctions, state->namaddr,
+ SMTPD_NAME_CLIENT, CHECK_CLIENT_ACL);
return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
/*
* Apply restrictions in the order as specified.
*/
- status = generic_checks(state, helo_restrctions, state->helo_name,
- SMTPD_NAME_HELO, CHECK_HELO_ACL);
+ status = setjmp(smtpd_check_buf);
+ if (status == 0 && helo_restrctions->argc)
+ status = generic_checks(state, helo_restrctions, state->helo_name,
+ SMTPD_NAME_HELO, CHECK_HELO_ACL);
SMTPD_CHECK_HELO_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
/*
* Apply restrictions in the order as specified.
*/
- status = generic_checks(state, mail_restrctions, sender,
- SMTPD_NAME_SENDER, CHECK_SENDER_ACL);
+ status = setjmp(smtpd_check_buf);
+ if (status == 0 && mail_restrctions->argc)
+ status = generic_checks(state, mail_restrctions, sender,
+ SMTPD_NAME_SENDER, CHECK_SENDER_ACL);
SMTPD_CHECK_MAIL_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
/*
* Apply restrictions in the order as specified.
*/
- status = generic_checks(state, rcpt_restrctions,
+ status = setjmp(smtpd_check_buf);
+ if (status == 0 && rcpt_restrctions->argc)
+ status = generic_checks(state, rcpt_restrctions,
recipient, SMTPD_NAME_RECIPIENT, CHECK_RECIP_ACL);
SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
/*
* Apply restrictions in the order as specified.
*/
- status = generic_checks(state, etrn_restrctions, domain,
- SMTPD_NAME_ETRN, CHECK_ETRN_ACL);
+ status = setjmp(smtpd_check_buf);
+ if (status == 0 && etrn_restrctions->argc)
+ status = generic_checks(state, etrn_restrctions, domain,
+ SMTPD_NAME_ETRN, CHECK_ETRN_ACL);
SMTPD_CHECK_ETRN_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
return (STR(error_text));
}
fsspace(".", &fsbuf);
-
if (msg_verbose)
msg_info("%s: blocks %lu avail %lu min_free %lu size %lu",
myname,
typedef struct {
char *name;
ARGV **target;
-} SMTPD_REST_TABLE;
+} REST_TABLE;
-static SMTPD_REST_TABLE smtpd_rest_table[] = {
+static REST_TABLE rest_table[] = {
"client_restrictions", &client_restrctions,
"helo_restrictions", &helo_restrctions,
"sender_restrictions", &mail_restrctions,
0,
};
-/* smtpd_rest_update - update restriction */
+/* rest_update - update restriction */
-static int smtpd_rest_update(char **argv)
+static int rest_update(char **argv)
{
- SMTPD_REST_TABLE *rp;
+ REST_TABLE *rp;
- for (rp = smtpd_rest_table; rp->name; rp++) {
+ for (rp = rest_table; rp->name; rp++) {
if (strcasecmp(rp->name, argv[0]) == 0) {
argv_free(rp->target[0]);
rp->target[0] = smtpd_check_parse(argv[1]);
}
if (int_update(args->argv)
|| string_update(args->argv)
- || smtpd_rest_update(args->argv)) {
+ || rest_update(args->argv)) {
resp = 0;
break;
}