summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2016-01-16 22:17:33 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2016-01-16 23:20:24 +0000
commit4f6ae5c314e5c3e462313f3b53c917f36b131bf4 (patch)
tree1d46ff1a265a21c1a0ff3d795d95eb02b6989086
parentfa01e4f81d44f68f0a92be34b687c05f2ddbc2e9 (diff)
VRFY: Permit an ACL to override the default 252 response, to support
verify-by-ACL instead of the more usual verify-by-routers. Bug 1769
-rw-r--r--doc/doc-docbook/spec.xfpt11
-rw-r--r--doc/doc-txt/ChangeLog3
-rw-r--r--src/src/acl.c6
-rw-r--r--src/src/functions.h2
-rw-r--r--src/src/receive.c10
-rw-r--r--src/src/smtp_in.c161
-rw-r--r--test/confs/00415
-rw-r--r--test/scripts/0000-Basic/00411
-rw-r--r--test/stderr/004115
-rw-r--r--test/stdout/00411
10 files changed, 122 insertions, 93 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index aa1e67712..cb913d6f1 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -34046,13 +34046,20 @@ specific badly-behaved hosts that you have to live with.
When Exim receives a VRFY or EXPN command on a TCP/IP connection, it
runs the ACL specified by &%acl_smtp_vrfy%& or &%acl_smtp_expn%& (as
appropriate) in order to decide whether the command should be accepted or not.
-If no ACL is defined, the command is rejected.
+.new
.cindex "VRFY" "processing"
+When no ACL is defined for VRFY, or if it rejects without
+setting an explicit response code, the command is accepted
+(with a 252 SMTP response code)
+in order to support awkward clients that do a VRFY before every RCPT.
+.wen
When VRFY is accepted, it runs exactly the same code as when Exim is
-called with the &%-bv%& option.
+called with the &%-bv%& option, and returns 250/451/550
+SMTP response codes.
.cindex "EXPN" "processing"
+If no ACL for EXPN is defined, the command is rejected.
When EXPN is accepted, a single-level expansion of the address is done.
EXPN is treated as an &"address test"& (similar to the &%-bt%& option) rather
than a verification (the &%-bv%& option). If an unqualified local part is given
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 28fe75447..9f275a9db 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -153,6 +153,9 @@ JH/36 Fix a longstanding bug in memory use by the ${run } expansion: A fresh
concluded, but leaving the global pointer active for it. Possibly
involved in Bug 1778.
+JH/37 Bug 1769: Permit a VRFY ACL to override the default 252 response,
+ and to use the domains and local_parts ACL conditions.
+
Exim version 4.86
-----------------
diff --git a/src/src/acl.c b/src/src/acl.c
index 1456cc724..684b93bbb 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -482,6 +482,7 @@ static unsigned int cond_forbids[] = {
(unsigned int)
~((1<<ACL_WHERE_RCPT) /* domains */
+ |(1<<ACL_WHERE_VRFY)
#ifndef DISABLE_PRDR
|(1<<ACL_WHERE_PRDR)
#endif
@@ -499,6 +500,7 @@ static unsigned int cond_forbids[] = {
(unsigned int)
~((1<<ACL_WHERE_RCPT) /* local_parts */
+ |(1<<ACL_WHERE_VRFY)
#ifndef DISABLE_PRDR
|(1<<ACL_WHERE_PRDR)
#endif
@@ -4454,9 +4456,9 @@ ratelimiters_cmd = NULL;
log_reject_target = LOG_MAIN|LOG_REJECT;
#ifndef DISABLE_PRDR
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR)
+if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY || where==ACL_WHERE_PRDR)
#else
-if (where == ACL_WHERE_RCPT)
+if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY)
#endif
{
adb = address_defaults;
diff --git a/src/src/functions.h b/src/src/functions.h
index 1d2d6b8ae..97d95dd3d 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -390,7 +390,7 @@ extern BOOL smtp_get_port(uschar *, address_item *, int *, uschar *);
extern int smtp_getc(void);
extern int smtp_handle_acl_fail(int, int, uschar *, uschar *);
extern void smtp_log_no_mail(void);
-extern void smtp_message_code(uschar **, int *, uschar **, uschar **);
+extern void smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL);
extern BOOL smtp_read_response(smtp_inblock *, uschar *, int, int, int);
extern void smtp_respond(uschar *, int, BOOL, uschar *);
extern void smtp_notquit_exit(uschar *, uschar *, uschar *, ...);
diff --git a/src/src/receive.c b/src/src/receive.c
index 585cb9025..f2a94e0f8 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -525,7 +525,7 @@ static void
smtp_user_msg(uschar *code, uschar *user_msg)
{
int len = 3;
-smtp_message_code(&code, &len, &user_msg, NULL);
+smtp_message_code(&code, &len, &user_msg, NULL, TRUE);
smtp_respond(code, len, TRUE, user_msg);
}
#endif
@@ -1279,10 +1279,12 @@ else if (rc != OK)
#ifdef EXPERIMENTAL_DCC
dcc_ok = 0;
#endif
- if (smtp_input && smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0) {
+ if ( smtp_input
+ && smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0)
+ {
*smtp_yield_ptr = FALSE; /* No more messsages after dropped connection */
*smtp_reply_ptr = US""; /* Indicate reply already sent */
- }
+ }
message_id[0] = 0; /* Indicate no message accepted */
return FALSE; /* Cause skip to end of receive function */
}
@@ -4078,7 +4080,7 @@ if (smtp_input)
{
uschar *code = US"250";
int len = 3;
- smtp_message_code(&code, &len, &user_msg, NULL);
+ smtp_message_code(&code, &len, &user_msg, NULL, TRUE);
smtp_respond(code, len, TRUE, user_msg);
}
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index b48e436e3..0498ecb18 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -2364,7 +2364,7 @@ else
{
int codelen = 3;
s = user_msg;
- smtp_message_code(&code, &codelen, &s, NULL);
+ smtp_message_code(&code, &codelen, &s, NULL, TRUE);
if (codelen > 4)
{
esc = code + 4;
@@ -2626,23 +2626,24 @@ Arguments:
codelen length of smtp code; if > 4 there's an ESC
msg message text
log_msg optional log message, to be adjusted with the new SMTP code
+ check_valid if true, verify the response code
Returns: nothing
*/
void
-smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg)
+smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg,
+ BOOL check_valid)
{
int n;
int ovector[3];
-if (msg == NULL || *msg == NULL) return;
+if (!msg || !*msg) return;
-n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0,
- PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int));
-if (n < 0) return;
+if ((n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0,
+ PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int))) < 0) return;
-if ((*msg)[0] != (*code)[0])
+if (check_valid && (*msg)[0] != (*code)[0])
{
log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with "
"incorrect digit (expected %c) in \"%s\"", (*code)[0], *msg);
@@ -2677,18 +2678,19 @@ defaults disabled in Exim. However, discussion in connection with RFC 821bis
(aka RFC 2821) has concluded that the response should be 252 in the disabled
state, because there are broken clients that try VRFY before RCPT. A 5xx
response should be given only when the address is positively known to be
-undeliverable. Sigh. Also, for ETRN, 458 is given on refusal, and for AUTH,
-503.
+undeliverable. Sigh. We return 252 if there is no VRFY ACL or it provides
+no explicit code, but if there is one we let it know best.
+Also, for ETRN, 458 is given on refusal, and for AUTH, 503.
From Exim 4.63, it is possible to override the response code details by
providing a suitable response code string at the start of the message provided
in user_msg. The code's first digit is checked for validity.
Arguments:
- where where the ACL was called from
- rc the failure code
- user_msg a message that can be included in an SMTP response
- log_msg a message for logging
+ where where the ACL was called from
+ rc the failure code
+ user_msg a message that can be included in an SMTP response
+ log_msg a message for logging
Returns: 0 in most cases
2 if the failure code was FAIL_DROP, in which case the
@@ -2721,8 +2723,9 @@ if (drop) rc = FAIL;
/* Set the default SMTP code, and allow a user message to change it. */
-smtp_code = (rc != FAIL)? US"451" : acl_wherecodes[where];
-smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg);
+smtp_code = rc == FAIL ? acl_wherecodes[where] : US"451";
+smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg,
+ where != ACL_WHERE_VRFY);
/* We used to have sender_address here; however, there was a bug that was not
updating sender_address after a rewrite during a verify. When this bug was
@@ -3096,7 +3099,7 @@ static void
smtp_user_msg(uschar *code, uschar *user_msg)
{
int len = 3;
-smtp_message_code(&code, &len, &user_msg, NULL);
+smtp_message_code(&code, &len, &user_msg, NULL, TRUE);
smtp_respond(code, len, TRUE, user_msg);
}
@@ -3305,14 +3308,13 @@ while (done <= 0)
)
{
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
- if (acl_smtp_auth)
+ if ( acl_smtp_auth
+ && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
+ &user_msg, &log_msg)) != OK
+ )
{
- rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg);
- if (rc != OK)
- {
- done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
- continue;
- }
+ done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
+ continue;
}
for (au = auths; au; au = au->next)
@@ -3371,14 +3373,13 @@ while (done <= 0)
/* Check the ACL */
- if (acl_smtp_auth)
+ if ( acl_smtp_auth
+ && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
+ &user_msg, &log_msg)) != OK
+ )
{
- rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg);
- if (rc != OK)
- {
- done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
- break;
- }
+ done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
+ break;
}
/* Find the name of the requested authentication mechanism. */
@@ -3550,10 +3551,9 @@ while (done <= 0)
/* Apply an ACL check if one is defined; afterwards, recheck
synchronization in case the client started sending in a delay. */
- if (acl_smtp_helo != NULL)
- {
- rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg);
- if (rc != OK)
+ if (acl_smtp_helo)
+ if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo,
+ &user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
sender_helo_name = NULL;
@@ -3561,7 +3561,6 @@ while (done <= 0)
break;
}
else if (!check_sync()) goto SYNC_FAILURE;
- }
/* Generate an OK reply. The default string includes the ident if present,
and also the IP address if present. Reflecting back the ident is intended
@@ -3609,7 +3608,7 @@ while (done <= 0)
{
char *ss;
int codelen = 4;
- smtp_message_code(&smtp_code, &codelen, &user_msg, NULL);
+ smtp_message_code(&smtp_code, &codelen, &user_msg, NULL, TRUE);
s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg);
if ((ss = strpbrk(CS s, "\r\n")) != NULL)
{
@@ -4581,51 +4580,49 @@ while (done <= 0)
case VRFY_CMD:
- HAD(SCH_VRFY);
- rc = acl_check(ACL_WHERE_VRFY, NULL, acl_smtp_vrfy, &user_msg, &log_msg);
- if (rc != OK)
- done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
- else
{
- uschar *address;
- uschar *s = NULL;
+ uschar * address;
- /* rfc821_domains = TRUE; << no longer needed */
- address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end,
- &recipient_domain, FALSE);
- /* rfc821_domains = FALSE; << no longer needed */
+ HAD(SCH_VRFY);
- if (address == NULL)
- s = string_sprintf("501 %s", errmess);
+ if(!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end,
+ &recipient_domain, FALSE)))
+ smtp_printf("501 %s\r\n", errmess);
+
+ else if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy,
+ &user_msg, &log_msg)) != OK)
+ done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
else
- {
- address_item *addr = deliver_make_addr(address, FALSE);
- switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1,
- -1, -1, NULL, NULL, NULL))
- {
- case OK:
- s = string_sprintf("250 <%s> is deliverable", address);
- break;
+ {
+ uschar *s = NULL;
- case DEFER:
- s = (addr->user_message != NULL)?
- string_sprintf("451 <%s> %s", address, addr->user_message) :
- string_sprintf("451 Cannot resolve <%s> at this time", address);
- break;
+ address_item *addr = deliver_make_addr(address, FALSE);
+ switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1,
+ -1, -1, NULL, NULL, NULL))
+ {
+ case OK:
+ s = string_sprintf("250 <%s> is deliverable", address);
+ break;
- case FAIL:
- s = (addr->user_message != NULL)?
- string_sprintf("550 <%s> %s", address, addr->user_message) :
- string_sprintf("550 <%s> is not deliverable", address);
- log_write(0, LOG_MAIN, "VRFY failed for %s %s",
- smtp_cmd_argument, host_and_ident(TRUE));
- break;
- }
- }
+ case DEFER:
+ s = (addr->user_message != NULL)?
+ string_sprintf("451 <%s> %s", address, addr->user_message) :
+ string_sprintf("451 Cannot resolve <%s> at this time", address);
+ break;
- smtp_printf("%s\r\n", s);
+ case FAIL:
+ s = (addr->user_message != NULL)?
+ string_sprintf("550 <%s> %s", address, addr->user_message) :
+ string_sprintf("550 <%s> is not deliverable", address);
+ log_write(0, LOG_MAIN, "VRFY failed for %s %s",
+ smtp_cmd_argument, host_and_ident(TRUE));
+ break;
+ }
+
+ smtp_printf("%s\r\n", s);
+ }
+ break;
}
- break;
case EXPN_CMD:
@@ -4659,15 +4656,13 @@ while (done <= 0)
/* Apply an ACL check if one is defined */
- if (acl_smtp_starttls != NULL)
+ if ( acl_smtp_starttls
+ && (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls,
+ &user_msg, &log_msg)) != OK
+ )
{
- rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, &user_msg,
- &log_msg);
- if (rc != OK)
- {
- done = smtp_handle_acl_fail(ACL_WHERE_STARTTLS, rc, user_msg, log_msg);
- break;
- }
+ done = smtp_handle_acl_fail(ACL_WHERE_STARTTLS, rc, user_msg, log_msg);
+ break;
}
/* RFC 2487 is not clear on when this command may be sent, though it
@@ -4910,8 +4905,8 @@ while (done <= 0)
log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument,
host_and_ident(FALSE));
- rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, &user_msg, &log_msg);
- if (rc != OK)
+ if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn,
+ &user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_ETRN, rc, user_msg, log_msg);
break;
diff --git a/test/confs/0041 b/test/confs/0041
index 51cba04ac..ec1067584 100644
--- a/test/confs/0041
+++ b/test/confs/0041
@@ -12,6 +12,7 @@ tls_advertise_hosts =
domainlist local_domains = test.ex
+acl_smtp_vrfy = check_vrfy
acl_smtp_expn = check_expn
qualify_domain = test.ex
no_write_rejectlog
@@ -21,6 +22,10 @@ no_write_rejectlog
begin acl
+check_vrfy:
+ deny local_parts = hardfail
+ message = 599 custom reject
+
check_expn:
accept hosts = 2.2.2.2
diff --git a/test/scripts/0000-Basic/0041 b/test/scripts/0000-Basic/0041
index 5601d6506..3495375cb 100644
--- a/test/scripts/0000-Basic/0041
+++ b/test/scripts/0000-Basic/0041
@@ -1,6 +1,7 @@
# VRFY & EXPN blocking
exim -bh 1.1.1.1
vrfy userx@test.ex
+vrfy hardfail@test.ex
expn postmaster
quit
****
diff --git a/test/stderr/0041 b/test/stderr/0041
index 4aacae883..b00305212 100644
--- a/test/stderr/0041
+++ b/test/stderr/0041
@@ -7,8 +7,21 @@
>>> host in helo_try_verify_hosts? no (option unset)
>>> host in helo_accept_junk_hosts? no (option unset)
>>> host in smtp_accept_max_nonmail_hosts? yes (matched "*")
->>> ACL is NULL: implicit DENY
+>>> using ACL "check_vrfy"
+>>> processing "deny"
+>>> check local_parts = hardfail
+>>> userx in "hardfail"? no (end of list)
+>>> deny: condition test failed in ACL "check_vrfy"
+>>> end of ACL "check_vrfy": implicit DENY
LOG: H=[1.1.1.1] rejected VRFY userx@test.ex
+>>> using ACL "check_vrfy"
+>>> processing "deny"
+>>> check local_parts = hardfail
+>>> hardfail in "hardfail"? yes (matched "hardfail")
+>>> message: 599 custom reject
+>>> deny: condition test succeeded in ACL "check_vrfy"
+>>> end of ACL "check_vrfy": DENY
+LOG: H=[1.1.1.1] rejected VRFY hardfail@test.ex: 599 custom reject
>>> using ACL "check_expn"
>>> processing "accept"
>>> check hosts = 2.2.2.2
diff --git a/test/stdout/0041 b/test/stdout/0041
index d43b4501a..b88c93ac7 100644
--- a/test/stdout/0041
+++ b/test/stdout/0041
@@ -5,6 +5,7 @@
220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
252 Administrative prohibition
+599 custom reject
550 Administrative prohibition
221 the.local.host.name closing connection