From: Wietse Venema Date: Mon, 15 Nov 1999 05:00:00 +0000 (-0500) Subject: snapshot-19991115 X-Git-Tag: v20010228~91 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6dea8a0d53eed44b57556a7f501c8f74a8a0c0c4;p=thirdparty%2Fpostfix.git snapshot-19991115 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 2f8367cb2..9996bd434 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. diff --git a/postfix/INSTALL.sh b/postfix/INSTALL.sh index 0b740f319..6684494e6 100644 --- a/postfix/INSTALL.sh +++ b/postfix/INSTALL.sh @@ -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 </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 diff --git a/postfix/Makefile.in b/postfix/Makefile.in index 9c14f10a5..3e685f736 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -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; \ diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf index 521e9c45a..b5cb64508 100644 --- a/postfix/conf/sample-smtpd.cf +++ b/postfix/conf/sample-smtpd.cf @@ -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. diff --git a/postfix/dns/dns_lookup.c b/postfix/dns/dns_lookup.c index 90344cd52..e548f2c0f 100644 --- a/postfix/dns/dns_lookup.c +++ b/postfix/dns/dns_lookup.c @@ -100,6 +100,7 @@ #include /* BSDI stdarg.h uses abort() */ #include #include +#include /* Utility library. */ @@ -107,6 +108,7 @@ #include #include #include +#include /* 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)); } diff --git a/postfix/dns/snd.h b/postfix/dns/snd.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/postfix/dns/test_dns_lookup.c b/postfix/dns/test_dns_lookup.c index 5f1a9e63e..aabdd98ec 100644 --- a/postfix/dns/test_dns_lookup.c +++ b/postfix/dns/test_dns_lookup.c @@ -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: diff --git a/postfix/global/mail_params.h b/postfix/global/mail_params.h index 6561a2d36..53c2f1800 100644 --- a/postfix/global/mail_params.h +++ b/postfix/global/mail_params.h @@ -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" diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index 287086ab2..070732ad2 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -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 diff --git a/postfix/html/uce.html b/postfix/html/uce.html index 84abddfbc..fdabb0c01 100644 --- a/postfix/html/uce.html +++ b/postfix/html/uce.html @@ -537,20 +537,30 @@ reject_unauth_destination
check_relay_domains
Permit the request when the client hostname matches $relay_domains, -or when the resolved destination address matches -$relay_domains, otherwise reject. The relay_domains_reject_code -parameter specifies the response code for rejected requests (default: +or when the resolved destination address matches the the local +machine or $relay_domains, otherwise +reject the request. The relay_domains_reject_code parameter +specifies the response code for rejected requests (default: 554).

+ + +

permit_auth_destination
Ignore the client hostname. +Permit the request when the resolved destination address matches +the local machine or $relay_domains. + +

+

reject_unauth_destination
Ignore the client hostname. Reject the request when the resolved destination address -does not match $relay_domains. The -relay_domains_reject_code parameter specifies the response -code for rejected requests (default: 554). +does not match the local machine or +$relay_domains. The relay_domains_reject_code parameter +specifies the response code for rejected requests (default: +554).

diff --git a/postfix/local/recipient.c b/postfix/local/recipient.c index f962c7608..3a6a2991f 100644 --- a/postfix/local/recipient.c +++ b/postfix/local/recipient.c @@ -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. */ diff --git a/postfix/local/resolve.c b/postfix/local/resolve.c index 4c410461b..b6b1837b6 100644 --- a/postfix/local/resolve.c +++ b/postfix/local/resolve.c @@ -62,6 +62,7 @@ #include #include #include +#include /* 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); diff --git a/postfix/postconf/extract.awk b/postfix/postconf/extract.awk index 9d3a33635..43cb9604c 100644 --- a/postfix/postconf/extract.awk +++ b/postfix/postconf/extract.awk @@ -18,3 +18,7 @@ print | "sort -u >bool_table.h" } } + +# Workaround for broken gawk versions. + +END { exit(0); } diff --git a/postfix/qmgr/qmgr_message.c b/postfix/qmgr/qmgr_message.c index a5bfaff2c..522a0dcd9 100644 --- a/postfix/qmgr/qmgr_message.c +++ b/postfix/qmgr/qmgr_message.c @@ -110,6 +110,7 @@ #include #include #include +#include /* 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 diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c index 32468aa8d..a5f65544e 100644 --- a/postfix/smtpd/smtpd.c +++ b/postfix/smtpd/smtpd.c @@ -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:

"); 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:
"); 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[] = { diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c index 4cc27582e..8febad8c2 100644 --- a/postfix/smtpd/smtpd_check.c +++ b/postfix/smtpd/smtpd_check.c @@ -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); diff --git a/postfix/smtpd/smtpd_check.in2 b/postfix/smtpd/smtpd_check.in2 index ba539f00e..68d1d31a4 100644 --- a/postfix/smtpd/smtpd_check.in2 +++ b/postfix/smtpd/smtpd_check.in2 @@ -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 diff --git a/postfix/smtpd/smtpd_check.ref b/postfix/smtpd/smtpd_check.ref index 9c7727171..1ed6fca90 100644 --- a/postfix/smtpd/smtpd_check.ref +++ b/postfix/smtpd/smtpd_check.ref @@ -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= -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 ; from= +554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com, reason: EBlackholed - see >>> # >>> # Hybrids >>> # diff --git a/postfix/smtpd/smtpd_check.ref2 b/postfix/smtpd/smtpd_check.ref2 index f6cb0c849..cbb4a3280 100644 --- a/postfix/smtpd/smtpd_check.ref2 +++ b/postfix/smtpd/smtpd_check.ref2 @@ -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= -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 ; from= +554 Service unavailable; [127.0.0.2] blocked using rbl.maps.vix.com, reason: EBlackholed - see >>> # >>> # unknown sender/recipient domain >>> # @@ -193,3 +193,24 @@ OK >>> rcpt wietse@porcupine.org ./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 : Sender address rejected: Domain not found; from= to= 554 : 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 : Recipient address rejected: Access denied; from= to= +554 : 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 : Relay access denied; from= to= +554 : Relay access denied +>>> rcpt user@foo.com +OK diff --git a/postfix/smtpd/smtpd_token.c b/postfix/smtpd/smtpd_token.c index 7de161fa6..ed0f5c360 100644 --- a/postfix/smtpd/smtpd_token.c +++ b/postfix/smtpd/smtpd_token.c @@ -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 == '<') { /* */ 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); } diff --git a/postfix/smtpd/smtpd_token.h b/postfix/smtpd/smtpd_token.h index 841ddb3ce..88489fa22 100644 --- a/postfix/smtpd/smtpd_token.h +++ b/postfix/smtpd/smtpd_token.h @@ -17,6 +17,7 @@ * External interface. */ typedef struct SMTPD_TOKEN { + int tokval; char *strval; VSTRING *vstrval; } SMTPD_TOKEN; diff --git a/postfix/smtpd/smtpd_token.in b/postfix/smtpd/smtpd_token.in index 3ef8f7596..d67d5d04d 100644 --- a/postfix/smtpd/smtpd_token.in +++ b/postfix/smtpd/smtpd_token.in @@ -7,3 +7,6 @@ mail from:<"wietse venema" > 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: +mail to: diff --git a/postfix/smtpd/smtpd_token.ref b/postfix/smtpd/smtpd_token.ref index 5d962fbf6..bba391549 100644 --- a/postfix/smtpd/smtpd_token.ref +++ b/postfix/smtpd/smtpd_token.ref @@ -1,36 +1,84 @@ mail from: +Token type: other Token value: mail +Token type: other Token value: from: +Token type: other Token value: 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: +Token type: other Token value: mail +Token type: other Token value: from: +Token type: other Token value: 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" > +Token type: other Token value: mail +Token type: other Token value: from: +Token type: other Token value: <"wietse venema" > 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: +Token type: other +Token value: mail +Token type: other +Token value: to: +Token type: other +Token value: +mail to: +Token type: other +Token value: mail +Token type: other +Token value: to: +Token type: error +Token value: diff --git a/postfix/trivial-rewrite/resolve.c b/postfix/trivial-rewrite/resolve.c index 5f185882c..f298da479 100644 --- a/postfix/trivial-rewrite/resolve.c +++ b/postfix/trivial-rewrite/resolve.c @@ -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.