2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-31 14:17:41 +00:00

snapshot-19991115

This commit is contained in:
Wietse Venema
1999-11-15 00:00:00 -05:00
parent 1b37fc863c
commit 6dea8a0d53
24 changed files with 480 additions and 115 deletions

View File

@@ -3170,8 +3170,56 @@ Apologies for any names omitted.
Bugfix: LDAP lookup timeout settings were ignored. Patch
by John Hensley. File: util/dict_ldap.c.
19991110
19991108
Bugfix: when doing a fresh install, INSTALL.sh didn't set
main.cf:mail_owner properly (Simon J. Mudd).
19991109
Bugfix: when doing a fresh install, INSTALL.sh no longer
worked (missing main.cf file). Fix: add "-c" argument to
the postmap commands (Lars Hecking @ nmrc.ucc.ie).
Documentation: removed spurious "do not edit" comments from
the sample pcre and regexp configuration files.
19991110-13
Code cleanup: greatly simplified the SMTPD command parser
and somewhat simplified the code that groks RFC 822-style
address syntax in MAIL FROM and RCPT TO commands.
New parameter: strict_rfc821_envelopes (default: no) to
reject RFC 822 address forms (with comments etc.) in SMTP
envelopes. By default, the Postfix SMTP server only logs
a warning.
19991113
Oops, also updated the SMTP VRFY code in the light of
changes to the SMTPD command parser.
Cleanup: the local delivery agent now explicitly rejects
recipients with an empty username.
19991114
Workaround: with some gawk versions, postconf/extract.awk
reportedly returns a non-zero exit status upon success.
Added an explicit exit(0) statement.
19991115
Feature: DNS TXT record lookup support, based on initial
code by Simon J Mudd. File: dns/dns_lookup.c.
Feature: RBL TXT record lookups, based on initial code by
Simon J Mudd. File: smtpd/smtpd_check.c.
Feature: permit_auth_destination restriction based on code
by Jesper Skriver @ skriver.dk.
Code cleanup: the transport table now can override local
deliveries. Postfix no longer uses the "empty next-hop
hostname hack" to remember that a destination is local.

View File

@@ -6,6 +6,9 @@
PATH=/bin:/usr/bin:/usr/sbin:/usr/etc:/sbin:/etc
umask 022
# Workaround, should edit main.cf in place.
trap 'rm -f ./main.cf; exit' 0 1 2 3 15
cat <<EOF
Warning: this script replaces existing sendmail or Postfix programs.
@@ -138,7 +141,14 @@ do
esac
done
bin/postmap -c ./conf -q "$owner" unix:passwd.byname >/dev/null || {
# Workaround, should edit main.cf in place.
test -f $config_directory/main.cf || {
echo "mail_owner = $owner" >./main.cf
echo "myhostname = xx.yy" >>./main.cf
alt_main="-c ."
}
bin/postmap $alt_main -q "$owner" unix:passwd.byname >/dev/null || {
echo "$owner needs an entry in the passwd file" 1>&2
echo "Remember, $owner must have a dedicated user id and group id." 1>&2
exit 1
@@ -146,7 +156,7 @@ bin/postmap -c ./conf -q "$owner" unix:passwd.byname >/dev/null || {
case $setgid in
no) ;;
*) bin/postmap -c ./conf -q "$setgid" unix:group.byname >/dev/null || {
*) bin/postmap $alt_main -q "$setgid" unix:group.byname >/dev/null || {
echo "$setgid needs an entry in the group file" 1>&2
echo "Remember, $setgid must have a dedicated group id." 1>&2
exit 1
@@ -239,6 +249,16 @@ compare_or_replace a+x,go-w $postfix_script $config_directory/postfix-script ||
case $manpages in
no) ;;
*) test -d $manpages || mkdir -p $manpages || exit 1
(cd man && tar cf - man?) | (cd $manpages && tar xf -)
*) (
cd man || exit 1
for dir in man?
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
done
)
esac

View File

@@ -22,6 +22,9 @@ update printfck:
printfck: update
install: update
sh INSTALL.sh
depend clean:
set -e; for i in $(DIRS); do \
(set -e; echo "[$$i]"; cd $$i; $(MAKE) $@) || exit 1; \

View File

@@ -191,8 +191,10 @@ smtpd_sender_restrictions =
# reject_invalid_hostname: reject HELO hostname with bad syntax.
# reject_unknown_hostname: reject HELO hostname without DNS A or MX record.
# reject_unknown_sender_domain: reject sender domain without A or MX record.
# check_relay_domains: permit only mail from/to domains in $relay_domains.
# reject_unauth_destination: reject mail not to domains in $relay_domains.
# check_relay_domains: permit only mail from/to domains in $relay_domains
or to the local machine.
# permit_auth_destination: permit mail to self or to $relay_domains.
# reject_unauth_destination: reject mail not to self or to $relay_domains.
# reject_unauth_pipelining: reject mail from improperly pipelining spamware
# permit_mx_backup: accept mail for sites that list me as MX host.
# reject_unknown_recipient_domain: reject domains without A or MX record.

View File

@@ -100,6 +100,7 @@
#include <stdlib.h> /* BSDI stdarg.h uses abort() */
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
/* Utility library. */
@@ -107,6 +108,7 @@
#include <vstring.h>
#include <msg.h>
#include <valid_hostname.h>
#include <stringops.h>
/* DNS library. */
@@ -246,8 +248,13 @@ static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
char *rr_name, DNS_FIXED *fixed)
{
char temp[DNS_NAME_LEN];
int data_len = fixed->length;
int data_len;
unsigned pref = 0;
unsigned char *src;
unsigned char *dst;
int ch;
#define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b))
if (pos + fixed->length > reply->end)
return (0);
@@ -287,6 +294,14 @@ static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
memcpy(temp, pos, fixed->length);
data_len = fixed->length;
break;
case T_TXT:
data_len = MIN2(fixed->length + 1, sizeof(temp));
for (src = pos, dst = temp; dst < temp + data_len - 1; /* void */ ) {
ch = *src++;
*dst++ = (ISPRINT(ch) ? ch : ' ');
}
*dst = 0;
break;
}
return (dns_rr_create(rr_name, fixed, pref, temp, data_len));
}

View File

View File

@@ -55,6 +55,7 @@ static void print_rr(DNS_RR *rr)
case T_MR:
case T_NS:
case T_PTR:
case T_TXT:
printf("%s: %s\n", dns_strtype(rr->type), rr->data);
break;
case T_MX:

View File

@@ -208,11 +208,11 @@ extern char *var_db_type;
extern char *var_always_bcc;
/*
* Standards violation: permit RFC 822-style addresses in SMTP commands.
* Standards violation: allow/permit RFC 822-style addresses in SMTP commands.
*/
#define VAR_ALLOW_RFC822_ENV "allow_rfc822_envelopes"
#define DEF_ALLOW_RFC822_ENV 1
extern bool var_allow_rfc822_envelopes;
#define VAR_STRICT_RFC821_ENV "strict_rfc821_envelopes"
#define DEF_STRICT_RFC821_ENV 0
extern bool var_strict_rfc821_env;
/*
* trivial rewrite/resolve service: mapping tables.
@@ -773,6 +773,7 @@ extern int var_non_fqdn_code;
#define DEF_UNK_ADDR_CODE 450
extern int var_unk_addr_code;
#define PERMIT_AUTH_DEST "permit_auth_destination"
#define REJECT_UNAUTH_DEST "reject_unauth_destination"
#define CHECK_RELAY_DOMAINS "check_relay_domains"
#define VAR_RELAY_CODE "relay_domains_reject_code"

View File

@@ -15,7 +15,7 @@
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-19991110"
#define DEF_MAIL_VERSION "Snapshot-19991115"
extern char *var_mail_version;
/* LICENSE

View File

@@ -537,20 +537,30 @@ reject_unauth_destination</b>
<dt> <b>check_relay_domains</b> <dd> Permit the request when the
client hostname matches <a href="#relay_domains">$relay_domains</a>,
or when the resolved destination address matches <a href="#relay_domains">
$relay_domains</a>, otherwise reject. The <b>relay_domains_reject_code</b>
parameter specifies the response code for rejected requests (default:
or when the resolved destination address matches the the local
machine or <a href="#relay_domains"> $relay_domains</a>, otherwise
reject the request. The <b>relay_domains_reject_code</b> parameter
specifies the response code for rejected requests (default:
<b>554</b>).
<p>
<a name="permit_auth_destination">
<dt> <b>permit_auth_destination</b> <dd> Ignore the client hostname.
Permit the request when the resolved destination address matches
the local machine or <a href="#relay_domains"> $relay_domains</a>.
<p>
<a name="reject_unauth_destination">
<dt> <b>reject_unauth_destination</b> <dd> Ignore the client
hostname. Reject the request when the resolved destination address
does not match <a href="#relay_domains"> $relay_domains</a>. The
<b>relay_domains_reject_code</b> parameter specifies the response
code for rejected requests (default: <b>554</b>).
does not match the local machine or <a href="#relay_domains">
$relay_domains</a>. The <b>relay_domains_reject_code</b> parameter
specifies the response code for rejected requests (default:
<b>554</b>).
<p>

View File

@@ -221,6 +221,13 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
state.msg_attr.extension = 0;
state.msg_attr.unmatched = state.msg_attr.extension;
/*
* Do not allow null usernames.
*/
if (state.msg_attr.user[0] == 0)
return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
"null username in %s", state.msg_attr.recipient));
/*
* Run the recipient through the delivery switch.
*/

View File

@@ -62,6 +62,7 @@
#include <rewrite_clnt.h>
#include <tok822.h>
#include <mail_params.h>
#include <resolve_local.h>
/* Application-specific. */
@@ -138,7 +139,7 @@ int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr
* 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 (VSTRING_LEN(reply.nexthop) == 0) {
if (resolve_local(STR(reply.nexthop))) {
status = deliver_recipient(state, usr_attr);
} else {
status = deliver_indirect(state);

View File

@@ -18,3 +18,7 @@
print | "sort -u >bool_table.h"
}
}
# Workaround for broken gawk versions.
END { exit(0); }

View File

@@ -110,6 +110,7 @@
#include <deliver_completed.h>
#include <mail_addr_find.h>
#include <opened.h>
#include <resolve_local.h>
/* Client stubs. */
@@ -476,7 +477,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
/*
* Bounce mail to non-existent users in virtual domains.
*/
if (VSTRING_LEN(reply.nexthop) > 0
if (!resolve_local(STR(reply.nexthop))
&& qmgr_virtual != 0
&& (at = strrchr(recipient->address, '@')) != 0) {
domain = lowercase(mystrdup(at + 1));
@@ -515,7 +516,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
* job requires knowledge of local aliases. Yuck! I don't want to
* duplicate delivery-agent specific knowledge in the queue manager.
*/
if (VSTRING_LEN(reply.nexthop) == 0) {
if (resolve_local(STR(reply.nexthop))) {
vstring_strcpy(reply.nexthop, STR(reply.recipient));
(void) split_at_right(STR(reply.nexthop), '@');
#if 0

View File

@@ -52,6 +52,12 @@
/* 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
@@ -284,6 +290,7 @@ char *var_always_bcc;
char *var_error_rcpt;
int var_smtpd_delay_reject;
char *var_rest_classes;
int var_strict_rfc821_env;
/*
* Global state, for stand-alone mode queue file cleanup. When this is
@@ -291,17 +298,23 @@ char *var_rest_classes;
*/
char *smtpd_path;
/*
* Silly little macros.
*/
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
/* collapse_args - put arguments together again */
static void collapse_args(int argc, SMTPD_TOKEN *argv)
{
int i;
for (i = 2; i < argc; i++) {
vstring_strcat(argv[1].vstrval, " ");
vstring_strcat(argv[1].vstrval, argv[i].strval);
for (i = 1; i < argc; i++) {
vstring_strcat(argv[0].vstrval, " ");
vstring_strcat(argv[0].vstrval, argv[i].strval);
}
argv[1].strval = vstring_str(argv[1].vstrval);
argv[0].strval = STR(argv[0].vstrval);
}
/* helo_cmd - process HELO command */
@@ -320,7 +333,8 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "503 Duplicate HELO/EHLO");
return (-1);
}
collapse_args(argc, argv);
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
@@ -349,7 +363,8 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "503 Error: duplicate HELO/EHLO");
return (-1);
}
collapse_args(argc, argv);
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
@@ -427,46 +442,94 @@ static void mail_open_stream(SMTPD_STATE *state)
/* extract_addr - extract address from rubble */
static VSTRING *extract_addr(SMTPD_STATE *state, VSTRING *buf)
static char *extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
int allow_empty_addr)
{
char *myname = "extract_addr";
TOK822 *tree;
TOK822 *tp;
TOK822 *addr = 0;
int naddr;
int non_addr;
char *err = 0;
/*
* Special case.
*/
#define PERMIT_EMPTY_ADDR 1
#define REJECT_EMPTY_ADDR 0
if (allow_empty_addr && strcmp(STR(arg->vstrval), "<>") == 0) {
if (msg_verbose)
msg_info("%s: empty address", myname);
VSTRING_RESET(arg->vstrval);
VSTRING_TERMINATE(arg->vstrval);
arg->strval = STR(arg->vstrval);
return (0);
}
/*
* Some mailers send RFC822-style address forms (with comments and such)
* in SMTP envelopes. We cannot blame users for this: the blame is with
* programmers violating the RFC, and with sendmail for being permissive.
*
* Extract the address from any surrounding junk. XXX Because of this, the
* SMTP command tokenizer must leave the address in externalized (quoted)
* form.
* XXX The SMTP command tokenizer must leave the address in externalized
* (quoted) form, so that the address parser can correctly extract the
* address from surrounding junk.
*
* XXX We have only one address parser, written according to the rules of
* RFC 822. That standard differs subtly from RFC 821.
*/
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
if (msg_verbose)
msg_info("%s: input: %s", myname, STR(buf));
tree = tok822_parse(STR(buf));
msg_info("%s: input: %s", myname, STR(arg->vstrval));
tree = tok822_parse(STR(arg->vstrval));
/*
* Find trouble.
*/
for (naddr = non_addr = 0, tp = tree; tp != 0; tp = tp->next) {
if (tp->type == TOK822_ADDR) {
if (++naddr == 1)
tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
else if (naddr == 2)
msg_warn("Multiple addresses from %s in %s command: %s",
state->namaddr, state->where, STR(buf));
} else if (tp->type != '<' && tp->type != '>') {
if (++non_addr == 1)
msg_warn("Non-RFC 821 syntax from %s in %s command: %s",
state->namaddr, state->where, STR(buf));
addr = tp;
naddr += 1; /* count address forms */
} else if (tp->type == '<' || tp->type == '>') {
/* void */ ; /* ignore brackets */
} else {
non_addr += 1; /* count non-address forms */
}
}
/*
* Report trouble.
*/
if (naddr != 1) { /* sorry, no can do */
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where, STR(arg->vstrval));
err = "501 Bad address syntax";
} else if (non_addr > 0) { /* it works with Sendmail... */
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where, STR(arg->vstrval));
err = (var_strict_rfc821_env ? "501 Bad address syntax" : 0);
}
/*
* Overwrite the input with the extracted address. This seems bad design,
* but we really are not going to use the original data anymore. What we
* start with is quoted (external) form, and what we need is unquoted
* (internal form).
*/
if (addr)
tok822_internalize(arg->vstrval, addr->head, TOK822_STR_DEFL);
else
vstring_strcat(arg->vstrval, "");
arg->strval = STR(arg->vstrval);
/*
* Cleanup.
*/
tok822_free_tree(tree);
if (msg_verbose)
msg_info("%s: result: %s", myname, STR(buf));
return (buf);
msg_info("%s: result: %s", myname, STR(arg->vstrval));
return (err);
}
/* mail_cmd - process MAIL command */
@@ -498,7 +561,16 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "501 Syntax: MAIL FROM: <address>");
return (-1);
}
argv[2].strval = STR(extract_addr(state, argv[2].vstrval));
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad address syntax");
return (-1);
}
if ((err = extract_addr(state, argv + 2, PERMIT_EMPTY_ADDR)) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
if (strcasecmp(arg, "BODY=8BITMIME") == 0
@@ -596,7 +668,16 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "501 Syntax: RCPT TO: <address>");
return (-1);
}
argv[2].strval = STR(extract_addr(state, argv[2].vstrval));
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad address syntax");
return (-1);
}
if ((err = extract_addr(state, argv + 2, REJECT_EMPTY_ADDR)) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (var_smtpd_rcpt_limit && state->rcpt_count >= var_smtpd_rcpt_limit) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "452 Error: too many recipients");
@@ -845,13 +926,25 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
* The SMTP standard (RFC 821) disallows unquoted special characters in
* the VRFY argument. Common practice violates the standard, however.
* Postfix accomodates common practice where it violates the standard.
*
* XXX Impedance mismatch! The SMTP command tokenizer preserves quoting,
* whereas the recipient restrictions checks expect unquoted (internal)
* address forms. Therefore we must parse out the address, or we must
* stop doing recipient restriction checks and lose the opportunity to
* say "user unknown" at the SMTP port.
*/
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: VRFY address");
return (-1);
}
collapse_args(argc, argv);
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if ((err = extract_addr(state, argv + 1, REJECT_EMPTY_ADDR)) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0)
err = smtpd_check_rcpt(state, argv[1].strval);
@@ -1216,6 +1309,7 @@ int main(int argc, char **argv)
static CONFIG_BOOL_TABLE bool_table[] = {
VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
VAR_STRICT_RFC821_ENV, DEF_STRICT_RFC821_ENV, &var_strict_rfc821_env,
0,
};
static CONFIG_STR_TABLE str_table[] = {

View File

@@ -124,10 +124,13 @@
/* parameter. Reject the request otherwise.
/* The \fIrelay_domains_reject_code\fR configuration parameter specifies
/* the reject status code (default: 554).
/* .IP permit_auth_destination
/* Permit the request when the resolved recipient domain matches
/* the local machine or the \fIrelay_domains\fR configuration parameter.
/* .IP reject_unauth_destination
/* Reject the request when the resolved recipient domain does not match
/* the \fIrelay_domains\fR configuration parameter. Same error code as
/* check_relay_domains.
/* the local machine or the \fIrelay_domains\fR configuration parameter.
/* Same error code as check_relay_domains.
/* .IP reject_unauth_pipelining
/* Reject the request when the client has already sent the next request
/* without being told that the server implements SMTP command pipelining.
@@ -709,7 +712,7 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
* Permit if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
if (STR(reply.nexthop)[0] == 0
if (resolve_local(STR(reply.nexthop))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
@@ -728,6 +731,43 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
var_relay_code, reply_name, reply_class));
}
/* permit_auth_destination - OK for message relaying */
static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
{
char *myname = "permit_auth_destination";
char *domain;
if (msg_verbose)
msg_info("%s: %s", myname, recipient);
/*
* Resolve the address.
*/
canon_addr_internal(query, recipient);
resolve_clnt_query(STR(query), &reply);
/*
* Permit if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
if (resolve_local(STR(reply.nexthop))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
/*
* Permit if the destination matches the relay_domains list.
*/
if (domain_list_match(relay_domains, domain))
return (SMTPD_CHECK_OK);
/*
* Skip when not matched
*/
return (SMTPD_CHECK_DUNNO);
}
/* reject_unauth_destination - FAIL for message relaying */
static int reject_unauth_destination(SMTPD_STATE *state, char *recipient)
@@ -748,7 +788,7 @@ static int reject_unauth_destination(SMTPD_STATE *state, char *recipient)
* Pass if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
if (STR(reply.nexthop)[0] == 0
if (resolve_local(STR(reply.nexthop))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
@@ -850,7 +890,7 @@ static int permit_mx_backup(SMTPD_STATE *unused_state, const char *recipient)
* If the destination is local, it is acceptable, because we are
* supposedly MX for our own address.
*/
if (STR(reply.nexthop)[0] == 0
if (resolve_local(STR(reply.nexthop))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
@@ -984,7 +1024,7 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr,
/*
* Skip local destinations and non-DNS forms.
*/
if (STR(reply.nexthop)[0] == 0
if (resolve_local(STR(reply.nexthop))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
@@ -1024,7 +1064,7 @@ static int permit_rcpt_map(char *table, char *reply_name)
return (SMTPD_CHECK_DUNNO);
domain += 1;
if (domain[0] == '#' || domain[0] == '[')
if (STR(reply.nexthop)[0] != 0)
if (!resolve_local(STR(reply.nexthop)))
return (SMTPD_CHECK_DUNNO);
/*
@@ -1338,6 +1378,9 @@ static int reject_maps_rbl(SMTPD_STATE *state)
char *saved_domains = mystrdup(var_maps_rbl_domains);
char *bp = saved_domains;
char *rbl_domain;
char *rbl_reason;
char *rbl_fodder;
DNS_RR *txt_list;
int reverse_len;
int dns_status = DNS_FAIL;
int i;
@@ -1371,11 +1414,22 @@ static int reject_maps_rbl(SMTPD_STATE *state)
/*
* Report the result.
*/
if (dns_status == DNS_OK)
if (dns_status == DNS_OK) {
if (dns_lookup(STR(query), T_TXT, 0, &txt_list,
(VSTRING *) 0, (VSTRING *) 0) == DNS_OK) {
rbl_fodder = ", reason: ";
rbl_reason = (char *) txt_list->data;
} else {
txt_list = 0;
rbl_fodder = rbl_reason = "";
}
result = smtpd_check_reject(state, MAIL_ERROR_POLICY,
"%d Service unavailable; [%s] blocked using %s",
var_maps_rbl_code, state->addr, rbl_domain);
else
"%d Service unavailable; [%s] blocked using %s%s%s",
var_maps_rbl_code, state->addr, rbl_domain,
rbl_fodder, rbl_reason);
if (txt_list)
dns_rr_free(txt_list);
} else
result = SMTPD_CHECK_DUNNO;
/*
@@ -1549,6 +1603,9 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
} else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) {
if (state->recipient)
status = permit_mx_backup(state, state->recipient);
} else if (strcasecmp(name, PERMIT_AUTH_DEST) == 0) {
if (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);

View File

@@ -94,3 +94,14 @@ rcpt wietse@porcupine.org
rcpt wietse@no.recipient.domain
mail wietse@no.sender.domain
rcpt wietse@porcupine.org
#
# {permit_auth,reject_unauth}_destination
#
relay_domains foo.com,bar.com
mail user@some.where
recipient_restrictions permit_auth_destination,reject
rcpt user@foo.org
rcpt user@foo.com
recipient_restrictions reject_unauth_destination,permit
rcpt user@foo.org
rcpt user@foo.com

View File

@@ -182,8 +182,8 @@ OK
>>> client spike.porcupine.org 168.100.189.2
OK
>>> client foo 127.0.0.2
./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com; from=<foo@friend.bad.domain>
554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com, reason: EBlackholed - see <URL:http://mail-abuse.org/cgi-bin/lookup?127.0.0.2>; from=<foo@friend.bad.domain>
554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com, reason: EBlackholed - see <URL:http://mail-abuse.org/cgi-bin/lookup?127.0.0.2>
>>> #
>>> # Hybrids
>>> #

View File

@@ -172,8 +172,8 @@ OK
>>> client spike.porcupine.org 168.100.189.2
OK
>>> client foo 127.0.0.2
./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com; from=<foo@friend.bad.domain>
554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com
./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com, reason: EBlackholed - see <URL:http://mail-abuse.org/cgi-bin/lookup?127.0.0.2>; from=<foo@friend.bad.domain>
554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com, reason: EBlackholed - see <URL:http://mail-abuse.org/cgi-bin/lookup?127.0.0.2>
>>> #
>>> # unknown sender/recipient domain
>>> #
@@ -193,3 +193,24 @@ OK
>>> rcpt wietse@porcupine.org
./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 <wietse@no.sender.domain>: Sender address rejected: Domain not found; from=<wietse@no.sender.domain> to=<wietse@porcupine.org>
554 <wietse@no.sender.domain>: Sender address rejected: Domain not found
>>> #
>>> # {permit_auth,reject_unauth}_destination
>>> #
>>> relay_domains foo.com,bar.com
OK
>>> mail user@some.where
OK
>>> recipient_restrictions permit_auth_destination,reject
OK
>>> rcpt user@foo.org
./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 <user@foo.org>: Recipient address rejected: Access denied; from=<user@some.where> to=<user@foo.org>
554 <user@foo.org>: Recipient address rejected: Access denied
>>> rcpt user@foo.com
OK
>>> recipient_restrictions reject_unauth_destination,permit
OK
>>> rcpt user@foo.org
./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 <user@foo.org>: Relay access denied; from=<user@some.where> to=<user@foo.org>
554 <user@foo.org>: Relay access denied
>>> rcpt user@foo.com
OK

View File

@@ -31,7 +31,7 @@
/* It understands backslash escapes, white space, quoted strings,
/* and addresses (including quoted text) enclosed by < and >.
/* The input is broken up into tokens by whitespace, except for
/* whitespace that is protected by quites etc.
/* whitespace that is protected by quotes etc.
/* LICENSE
/* .ad
/* .fi
@@ -64,12 +64,22 @@
static char *smtp_quoted(char *cp, SMTPD_TOKEN *arg, int start, int last)
{
static VSTRING *stack;
int wanted;
int c;
/*
* Parser stack. `ch' is always the most-recently entered character.
*/
#define ENTER_CHAR(buf, ch) VSTRING_ADDCH(buf, ch);
#define LEAVE_CHAR(buf, ch) { \
vstring_truncate(buf, VSTRING_LEN(buf) - 1); \
ch = vstring_end(buf)[-1]; \
}
if (stack == 0)
stack = vstring_alloc(1);
VSTRING_RESET(stack);
VSTRING_ADDCH(stack, last);
ENTER_CHAR(stack, wanted = last);
VSTRING_ADDCH(arg->vstrval, start);
for (;;) {
@@ -77,25 +87,22 @@ static char *smtp_quoted(char *cp, SMTPD_TOKEN *arg, int start, int last)
break;
cp++;
VSTRING_ADDCH(arg->vstrval, c);
if (c == vstring_end(stack)[-1]) { /* closing quote etc. */
vstring_truncate(stack, VSTRING_LEN(stack) - 1);
if (VSTRING_LEN(stack) == 0)
if (c == '\\') { /* parse escape sequence */
if ((c = *cp) == 0)
break;
} else {
if (c == '\\') { /* parse escape sequence */
if ((c = *cp) == 0)
break;
cp++;
VSTRING_ADDCH(arg->vstrval, c);
} else if (c == '"') {
VSTRING_ADDCH(stack, '"'); /* highest precedence */
} else if (c == '(' && vstring_end(stack)[-1] != '"') {
VSTRING_ADDCH(stack, ')'); /* medium precedence */
} else if (c == '<' && vstring_end(stack)[-1] == '>') {
VSTRING_ADDCH(stack, '>'); /* lowest precedence */
}
cp++;
VSTRING_ADDCH(arg->vstrval, c);
} else if (c == wanted) { /* closing quote etc. */
if (VSTRING_LEN(stack) == 1)
return (cp);
LEAVE_CHAR(stack, wanted);
} else if (c == '"') {
ENTER_CHAR(stack, wanted = '"'); /* highest precedence */
} else if (c == '<' && wanted == '>') {
ENTER_CHAR(stack, wanted = '>'); /* lowest precedence */
}
}
arg->tokval = SMTPD_TOK_ERROR; /* missing end */
return (cp);
}
@@ -106,12 +113,15 @@ static char *smtp_next_token(char *cp, SMTPD_TOKEN *arg)
int c;
VSTRING_RESET(arg->vstrval);
arg->tokval = SMTPD_TOK_OTHER;
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
#define STREQ(x,y,l) ((x)[0] == (x)[0] && strncasecmp((x), (y), (l)) == 0)
#define STREQ(x,y,l) (strncasecmp((x), (y), (l)) == 0)
while ((c = *cp) != 0) {
for (;;) {
if ((c = *cp) == 0) /* end of input */
break;
cp++;
if (ISSPACE(c)) { /* whitespace, skip */
while (*cp && ISSPACE(*cp))
@@ -120,8 +130,6 @@ static char *smtp_next_token(char *cp, SMTPD_TOKEN *arg)
break;
} else if (c == '<') { /* <stuff> */
cp = smtp_quoted(cp, arg, c, '>');
} else if (c == '[') { /* [stuff] */
cp = smtp_quoted(cp, arg, c, ']');
} else if (c == '"') { /* "stuff" */
cp = smtp_quoted(cp, arg, c, c);
} else if (c == ':') { /* this is gross, but... */
@@ -138,7 +146,7 @@ static char *smtp_next_token(char *cp, SMTPD_TOKEN *arg)
VSTRING_ADDCH(arg->vstrval, c);
}
}
if (LEN(arg->vstrval) == 0) /* no token found */
if (LEN(arg->vstrval) <= 0) /* no token found */
return (0);
VSTRING_TERMINATE(arg->vstrval);
arg->strval = vstring_str(arg->vstrval);
@@ -197,15 +205,20 @@ main(int unused_argc, char **unused_argv)
if (isatty(STDIN_FILENO))
vstream_printf("enter SMTPD command: ");
vstream_fflush(VSTREAM_OUT);
if (vstring_fgets(vp, VSTREAM_IN) == 0)
if (vstring_get_nonl(vp, VSTREAM_IN) == VSTREAM_EOF)
break;
if (*vstring_str(vp) == '#')
continue;
if (!isatty(STDIN_FILENO))
vstream_fputs(vstring_str(vp), VSTREAM_OUT);
vstream_printf("%s\n", vstring_str(vp));
tok_argc = smtpd_token(vstring_str(vp), &tok_argv);
for (i = 0; i < tok_argc; i++)
for (i = 0; i < tok_argc; i++) {
vstream_printf("Token type: %s\n",
tok_argv[i].tokval == SMTPD_TOK_OTHER ? "other" :
tok_argv[i].tokval == SMTPD_TOK_ERROR ? "error" :
"unknown");
vstream_printf("Token value: %s\n", tok_argv[i].strval);
}
}
exit(0);
}

View File

@@ -17,6 +17,7 @@
* External interface.
*/
typedef struct SMTPD_TOKEN {
int tokval;
char *strval;
VSTRING *vstrval;
} SMTPD_TOKEN;

View File

@@ -7,3 +7,6 @@ mail from:<"wietse venema" <wietse@porcupine.org>>
mail from:<"wietse venema"@porcupine.org ( ("wietse ) venema") )>
mail from:"wietse venema"@porcupine.org
mail from:wietse\ venema@porcupine.org
mail to:<"wietse venema>
mail to:<wietse@[stuff>
mail to:<wietse@["stuff]>

View File

@@ -1,36 +1,84 @@
mail from:<wietse@porcupine.org>
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: <wietse@porcupine.org>
mail from:<"wietse venema"@porcupine.org>
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: <"wietse venema"@porcupine.org>
mail from:wietse@porcupine.org
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: wietse@porcupine.org
mail from:<wietse @ porcupine.org>
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: <wietse @ porcupine.org>
mail from:<"wietse venema"@porcupine.org ("wietse ) venema")>
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: <"wietse venema"@porcupine.org ("wietse ) venema")>
mail from:<"wietse venema" <wietse@porcupine.org>>
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: <"wietse venema" <wietse@porcupine.org>>
mail from:<"wietse venema"@porcupine.org ( ("wietse ) venema") )>
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: <"wietse venema"@porcupine.org ( ("wietse ) venema") )>
mail from:"wietse venema"@porcupine.org
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: "wietse venema"@porcupine.org
mail from:wietse\ venema@porcupine.org
Token type: other
Token value: mail
Token type: other
Token value: from:
Token type: other
Token value: wietse venema@porcupine.org
mail to:<"wietse venema>
Token type: other
Token value: mail
Token type: other
Token value: to:
Token type: error
Token value: <"wietse venema>
mail to:<wietse@[stuff>
Token type: other
Token value: mail
Token type: other
Token value: to:
Token type: other
Token value: <wietse@[stuff>
mail to:<wietse@["stuff]>
Token type: other
Token value: mail
Token type: other
Token value: to:
Token type: error
Token value: <wietse@["stuff]>

View File

@@ -151,39 +151,43 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop,
}
/*
* Non-local delivery: if no transport is specified, assume the transport
* specified in var_def_transport. If no mail relay is specified in
* var_relayhost, forward to the domain's mail exchanger.
* The transport map, if specified, overrides default routing.
*/
if (domain != 0) {
if (*var_transport_maps == 0
|| (tok822_internalize(addr_buf, domain->next, TOK822_STR_DEFL),
transport_lookup(STR(addr_buf), channel, nexthop) == 0)) {
if (*var_transport_maps == 0
|| (tok822_internalize(addr_buf, domain->next, TOK822_STR_DEFL),
transport_lookup(STR(addr_buf), channel, nexthop)) == 0) {
/*
* Non-local delivery. Use the default transport specified in
* var_def_transport. If no default mail relay is specified in
* var_relayhost, forward to the domain's mail exchanger.
*/
if (domain != 0) {
vstring_strcpy(channel, var_def_transport);
if (*var_relayhost)
vstring_strcpy(nexthop, var_relayhost);
else
tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL);
}
tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
}
/*
* Local delivery: if no domain was specified, assume the local machine.
* See above for what happens with an empty localpart.
*/
else {
vstring_strcpy(channel, MAIL_SERVICE_LOCAL);
vstring_strcpy(nexthop, "");
if (saved_domain) {
tok822_sub_append(tree, saved_domain);
saved_domain = 0;
} else {
tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
/*
* Local delivery. Use the default transport and next-hop hostname.
* If no domain was specified, assume the local machine. See above
* for what happens with an empty localpart.
*/
else {
vstring_strcpy(channel, MAIL_SERVICE_LOCAL);
vstring_strcpy(nexthop, var_myhostname);
if (saved_domain) {
tok822_sub_append(tree, saved_domain);
saved_domain = 0;
} else {
tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
}
}
tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
}
tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
/*
* Clean up.