Bug#378131: allow overriding smtp codes
Robert Millan
rmh at aybabtu.com
Thu Jul 13 15:05:00 UTC 2006
Package: exim4
Version: 4.62-2
Severity: wishlist
Tags: patch
This is a backport of the following (post-4.62) CVS commit:
http://www.exim.org/mail-archives/exim-cvs/2006-July/msg00009.html
ChangeLog reads:
PH/16 Recognize SMTP codes at the start of "message" in ACLs and after :fail:
and :defer: in a redirect router. Add forbid_smtp_code to suppress the
latter.
NewStuff reads:
3. When an SMTP error message is specified in a "message" modifier in an ACL,
or in a :fail: or :defer: message in a redirect router, Exim now checks the
start of the message for an SMTP error code. This consists of three digits
followed by a space, optionally followed by an extended code of the form
n.n.n, also followed by a space. If this is the case and the very first
digit is the same as the default error code, the code from the message is
used instead. If the very first digit is incorrect, a panic error is logged,
and the default code is used. This is an incompatible change, but it is not
expected to affect many (if any) configurations. It is possible to suppress
the use of the supplied code in a redirect router by setting the
smtp_error_code option false. In this case, any SMTP code is quietly
ignored.
-- System Information:
Debian Release: testing/unstable
APT prefers testing
APT policy: (500, 'testing'), (1, 'experimental')
Architecture: amd64 (x86_64)
Shell: /bin/sh linked to /bin/bash
Kernel: Linux 2.6.17-1-amd64-k8
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL set to en_US.UTF-8)
Versions of packages exim4 depends on:
ii exim4-base 4.62-2 support files for all exim MTA (v4
ii exim4-daemon-light 4.62-2 lightweight exim MTA (v4) daemon
exim4 recommends no packages.
-- no debconf information
-------------- next part --------------
This is a backport of the following (post-4.62) CVS commit:
http://www.exim.org/mail-archives/exim-cvs/2006-July/msg00009.html
ChangeLog reads:
PH/16 Recognize SMTP codes at the start of "message" in ACLs and after :fail:
and :defer: in a redirect router. Add forbid_smtp_code to suppress the
latter.
NewStuff reads:
3. When an SMTP error message is specified in a "message" modifier in an ACL,
or in a :fail: or :defer: message in a redirect router, Exim now checks the
start of the message for an SMTP error code. This consists of three digits
followed by a space, optionally followed by an extended code of the form
n.n.n, also followed by a space. If this is the case and the very first
digit is the same as the default error code, the code from the message is
used instead. If the very first digit is incorrect, a panic error is logged,
and the default code is used. This is an incompatible change, but it is not
expected to affect many (if any) configurations. It is possible to suppress
the use of the supplied code in a redirect router by setting the
smtp_error_code option false. In this case, any SMTP code is quietly
ignored.
diff -ur exim4-4.62.old/README.UPDATING exim4-4.62/README.UPDATING
--- exim4-4.62.old/README.UPDATING 2006-04-28 10:32:21.000000000 +0000
+++ exim4-4.62/README.UPDATING 2006-07-13 14:33:27.000000000 +0000
@@ -28,6 +28,22 @@
that might affect a running system.
+Exim version 4.63
+-----------------
+
+When an SMTP error message is specified in a "message" modifier in an ACL, or
+in a :fail: or :defer: message in a redirect router, Exim now checks the start
+of the message for an SMTP error code. This consists of three digits followed
+by a space, optionally followed by an extended code of the form n.n.n, also
+followed by a space. If this is the case and the very first digit is the same
+as the default error code, the code from the message is used instead. If the
+very first digit is incorrect, a panic error is logged, and the default code is
+used. This is an incompatible change, but it is not expected to affect many (if
+any) configurations. It is possible to suppress the use of the supplied code in
+a redirect router by setting the smtp_error_code option false. In this case,
+any SMTP code is quietly ignored.
+
+
Exim version 4.61
-----------------
diff -ur exim4-4.62.old/src/exim.c exim4-4.62/src/exim.c
--- exim4-4.62.old/src/exim.c 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/exim.c 2006-07-13 14:33:27.000000000 +0000
@@ -1492,6 +1492,13 @@
regex_ismsgid =
regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE);
+/* Precompile the regular expression that is used for matching an SMTP error
+code, possibly extended, at the start of an error message. */
+
+regex_smtp_code =
+ regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?",
+ FALSE, TRUE);
+
/* If the program is called as "mailq" treat it as equivalent to "exim -bp";
this seems to be a generally accepted convention, since one finds symbolic
links called "mailq" in standard OS configurations. */
diff -ur exim4-4.62.old/src/functions.h exim4-4.62/src/functions.h
--- exim4-4.62.old/src/functions.h 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/functions.h 2006-07-13 14:33:27.000000000 +0000
@@ -266,7 +266,7 @@
extern int smtp_getc(void);
extern int smtp_handle_acl_fail(int, int, uschar *, uschar *);
extern BOOL smtp_read_response(smtp_inblock *, uschar *, int, int, int);
-extern void smtp_respond(int, BOOL, uschar *);
+extern void smtp_respond(uschar *, int, BOOL, uschar *);
extern void smtp_send_prohibition_message(int, uschar *);
extern int smtp_setup_msg(void);
extern BOOL smtp_start_session(void);
diff -ur exim4-4.62.old/src/globals.c exim4-4.62/src/globals.c
--- exim4-4.62.old/src/globals.c 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/globals.c 2006-07-13 14:34:54.000000000 +0000
@@ -209,21 +209,21 @@
US"VRFY"
};
-int acl_wherecodes[] = { 550, /* RCPT */
- 550, /* MAIL */
- 550, /* PREDATA */
- 550, /* MIME */
- 550, /* DATA */
- 0, /* not SMTP; not relevant */
- 503, /* AUTH */
- 550, /* connect */
- 458, /* ETRN */
- 550, /* EXPN */
- 550, /* HELO/EHLO */
- 0, /* MAILAUTH; not relevant */
- 0, /* QUIT; not relevant */
- 550, /* STARTTLS */
- 252 /* VRFY */
+uschar *acl_wherecodes[] = { US"550", /* RCPT */
+ US"550", /* MAIL */
+ US"550", /* PREDATA */
+ US"550", /* MIME */
+ US"550", /* DATA */
+ US"0", /* not SMTP; not relevant */
+ US"503", /* AUTH */
+ US"550", /* connect */
+ US"458", /* ETRN */
+ US"550", /* EXPN */
+ US"550", /* HELO/EHLO */
+ US"0", /* MAILAUTH; not relevant */
+ US"0", /* QUIT; not relevant */
+ US"550", /* STARTTLS */
+ US"252" /* VRFY */
};
BOOL active_local_from_check = FALSE;
@@ -863,6 +863,7 @@
const pcre *regex_IGNOREQUOTA = NULL;
const pcre *regex_PIPELINING = NULL;
const pcre *regex_SIZE = NULL;
+const pcre *regex_smtp_code = NULL;
const pcre *regex_ismsgid = NULL;
#ifdef WITH_CONTENT_SCAN
uschar *regex_match_string = NULL;
diff -ur exim4-4.62.old/src/globals.h exim4-4.62/src/globals.h
--- exim4-4.62.old/src/globals.h 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/globals.h 2006-07-13 14:33:27.000000000 +0000
@@ -130,7 +130,7 @@
extern uschar *acl_var[ACL_CVARS+ACL_MVARS]; /* User ACL variables */
extern uschar *acl_verify_message; /* User message for verify failure */
extern string_item *acl_warn_logged; /* Logged lines */
-extern int acl_wherecodes[]; /* Response codes for ACL fails */
+extern uschar *acl_wherecodes[]; /* Response codes for ACL fails */
extern uschar *acl_wherenames[]; /* Names for messages */
extern BOOL active_local_from_check;/* For adding Sender: (switchable) */
extern BOOL active_local_sender_retain; /* For keeping Sender: (switchable) */
@@ -555,6 +555,7 @@
extern const pcre *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
extern const pcre *regex_PIPELINING; /* For recognizing PIPELINING */
extern const pcre *regex_SIZE; /* For recognizing SIZE settings */
+extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */
extern const pcre *regex_ismsgid; /* Compiled r.e. for message it */
#ifdef WITH_CONTENT_SCAN
extern uschar *regex_match_string; /* regex that matched a line (regex ACL condition) */
diff -ur exim4-4.62.old/src/receive.c exim4-4.62/src/receive.c
--- exim4-4.62.old/src/receive.c 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/receive.c 2006-07-13 14:33:27.000000000 +0000
@@ -1067,7 +1067,7 @@
"acl_smtp_mime: error while creating mbox spool file, message temporarily rejected.");
Uunlink(spool_name);
unspool_mbox();
- smtp_respond(451, TRUE, US"temporary local problem");
+ smtp_respond(US"451", 3, TRUE, US"temporary local problem");
message_id[0] = 0; /* Indicate no message accepted */
*smtp_reply_ptr = US""; /* Indicate reply already sent */
return FALSE; /* Indicate skip to end of receive function */
@@ -3110,9 +3110,9 @@
{
uschar *istemp = US"";
uschar *s = NULL;
+ uschar *smtp_code;
int size = 0;
int sptr = 0;
- int code;
errmsg = local_scan_data;
@@ -3129,7 +3129,7 @@
/* Fall through */
case LOCAL_SCAN_REJECT:
- code = 550;
+ smtp_code = US"550";
if (errmsg == NULL) errmsg = US"Administrative prohibition";
break;
@@ -3139,7 +3139,7 @@
case LOCAL_SCAN_TEMPREJECT:
TEMPREJECT:
- code = 451;
+ smtp_code = US"451";
if (errmsg == NULL) errmsg = US"Temporary local problem";
istemp = US"temporarily ";
break;
@@ -3157,14 +3157,14 @@
{
if (!smtp_batched_input)
{
- smtp_respond(code, TRUE, errmsg);
+ smtp_respond(smtp_code, 3, TRUE, errmsg);
message_id[0] = 0; /* Indicate no message accepted */
smtp_reply = US""; /* Indicate reply already sent */
goto TIDYUP; /* Skip to end of function */
}
else
{
- moan_smtp_batch(NULL, "%d %s", code, errmsg);
+ moan_smtp_batch(NULL, "%s %s", smtp_code, errmsg);
/* Does not return */
}
}
@@ -3483,8 +3483,8 @@
if (smtp_reply == NULL)
{
if (fake_response != OK)
- smtp_respond(fake_response == DEFER ? 450 : 550,
- TRUE, fake_response_text);
+ smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE,
+ fake_response_text);
else
smtp_printf("250 OK id=%s\r\n", message_id);
if (host_checking)
@@ -3494,8 +3494,8 @@
else if (smtp_reply[0] != 0)
{
if (fake_response != OK && (smtp_reply[0] == '2'))
- smtp_respond(fake_response == DEFER ? 450 : 550,
- TRUE, fake_response_text);
+ smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE,
+ fake_response_text);
else
smtp_printf("%.1024s\r\n", smtp_reply);
}
diff -ur exim4-4.62.old/src/routers/redirect.c exim4-4.62/src/routers/redirect.c
--- exim4-4.62.old/src/routers/redirect.c 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/routers/redirect.c 2006-07-13 14:33:27.000000000 +0000
@@ -69,6 +69,8 @@
(void *)offsetof(redirect_router_options_block, forbid_pipe) },
{ "forbid_sieve_filter",opt_bit | (RDON_SIEVE_FILTER << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
+ { "forbid_smtp_code", opt_bool,
+ (void *)offsetof(redirect_router_options_block, forbid_smtp_code) },
{ "hide_child_in_errmsg", opt_bool,
(void *)offsetof(redirect_router_options_block, hide_child_in_errmsg) },
{ "ignore_eacces", opt_bit | (RDON_EACCES << 16),
@@ -167,6 +169,7 @@
FALSE, /* forbid_file */
FALSE, /* forbid_filter_reply */
FALSE, /* forbid_pipe */
+ FALSE, /* forbid_smtp_code */
FALSE, /* hide_child_in_errmsg */
FALSE, /* one_time */
FALSE, /* qualify_preserve_domain */
@@ -709,26 +712,39 @@
break;
/* FF_DEFER and FF_FAIL can arise only as a result of explicit commands
- (:fail: in an alias file or "fail" in a filter). If a configured message was
- supplied, allow it to be included in an SMTP response after verifying. */
+ (:defer: or :fail: in an alias file or "fail" in a filter). If a configured
+ message was supplied, allow it to be included in an SMTP response after
+ verifying. Remove any SMTP code if it is not allowed. */
case FF_DEFER:
- if (addr->message == NULL) addr->message = US"forced defer";
- else addr->user_message = addr->message;
- return DEFER;
+ yield = DEFER;
+ goto SORT_MESSAGE;
case FF_FAIL:
if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
return xrc;
add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
+ yield = FAIL;
+
+ SORT_MESSAGE:
if (addr->message == NULL)
- addr->message = US"forced rejection";
+ addr->message = (yield == FAIL)? US"forced rejection" : US"forced defer";
else
{
+ int ovector[3];
+ if (ob->forbid_smtp_code &&
+ pcre_exec(regex_smtp_code, NULL, CS addr->message,
+ Ustrlen(addr->message), 0, PCRE_EOPT,
+ ovector, sizeof(ovector)/sizeof(int)) >= 0)
+ {
+ DEBUG(D_route) debug_printf("SMTP code at start of error message "
+ "is ignored because forbid_smtp_code is set\n");
+ addr->message += ovector[1];
+ }
addr->user_message = addr->message;
setflag(addr, af_pass_message);
}
- return FAIL;
+ return yield;
/* As in the case of a system filter, a freeze does not happen after a manual
thaw. In case deliveries were set up by the filter, we set the child count
diff -ur exim4-4.62.old/src/routers/redirect.h exim4-4.62/src/routers/redirect.h
--- exim4-4.62.old/src/routers/redirect.h 2006-04-28 10:32:22.000000000 +0000
+++ exim4-4.62/src/routers/redirect.h 2006-07-13 14:33:27.000000000 +0000
@@ -51,6 +51,7 @@
BOOL forbid_file;
BOOL forbid_filter_reply;
BOOL forbid_pipe;
+ BOOL forbid_smtp_code;
BOOL hide_child_in_errmsg;
BOOL one_time;
BOOL qualify_preserve_domain;
diff -ur exim4-4.62.old/src/smtp_in.c exim4-4.62/src/smtp_in.c
--- exim4-4.62.old/src/smtp_in.c 2006-04-28 10:32:23.000000000 +0000
+++ exim4-4.62/src/smtp_in.c 2006-07-13 14:33:27.000000000 +0000
@@ -1767,7 +1767,8 @@
output nothing for non-final calls, and only the first line for anything else.
Arguments:
- code SMTP code
+ code SMTP code, may involve extended status codes
+ codelen length of smtp code; uf > 3 there's an ESC
final FALSE if the last line isn't the final line
msg message text, possibly containing newlines
@@ -1775,26 +1776,36 @@
*/
void
-smtp_respond(int code, BOOL final, uschar *msg)
+smtp_respond(uschar* code, int codelen, BOOL final, uschar *msg)
{
+int esclen = 0;
+uschar *esc = US"";
+
if (!final && no_multiline_responses) return;
+if (codelen > 3)
+ {
+ esc = code + 4;
+ esclen = codelen - 4;
+ }
+
for (;;)
{
uschar *nl = Ustrchr(msg, '\n');
if (nl == NULL)
{
- smtp_printf("%d%c%s\r\n", code, final? ' ':'-', msg);
+ smtp_printf("%.3s%c%.*s%s\r\n", code, final? ' ':'-', esclen, esc, msg);
return;
}
else if (nl[1] == 0 || no_multiline_responses)
{
- smtp_printf("%d%c%.*s\r\n", code, final? ' ':'-', (int)(nl - msg), msg);
+ smtp_printf("%.3s%c%.*s%.*s\r\n", code, final? ' ':'-', esclen, esc,
+ (int)(nl - msg), msg);
return;
}
else
{
- smtp_printf("%d-%.*s\r\n", code, (int)(nl - msg), msg);
+ smtp_printf("%.3s-%.*s%.*s\r\n", code, esclen, esc, (int)(nl - msg), msg);
msg = nl + 1;
while (isspace(*msg)) msg++;
}
@@ -1814,13 +1825,18 @@
newlines is turned into a multiline SMTP response, but for logging, only the
first line is used.
-There's a table of the response codes to use in globals.c, along with the table
-of names. VFRY is special. Despite RFC1123 it 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.
+There's a table of default permanent failure response codes to use in
+globals.c, along with the table of names. VFRY is special. Despite RFC1123 it
+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.
+
+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
@@ -1837,8 +1853,10 @@
int
smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg)
{
-int code = acl_wherecodes[where];
BOOL drop = rc == FAIL_DROP;
+int codelen = 3;
+int ovector[3];
+uschar *smtp_code;
uschar *lognl;
uschar *sender_info = US"";
uschar *what =
@@ -1853,6 +1871,41 @@
if (drop) rc = FAIL;
+/* Set the default SMTP code */
+
+smtp_code = (rc != FAIL)? US"451" : acl_wherecodes[where];
+
+/* Check a user message for starting with a response code and optionally an
+extended status code. If found, check that the first digit is valid, and if so,
+use it instead of the default code. */
+
+if (user_msg != NULL)
+ {
+ int n = pcre_exec(regex_smtp_code, NULL, CS user_msg, Ustrlen(user_msg), 0,
+ PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int));
+ if (n >= 0)
+ {
+ if (user_msg[0] != smtp_code[0])
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with "
+ "incorrect digit (expected %c) in \"%s\"", smtp_code[0], user_msg);
+
+ /* If log_msg == user_msg (the default set in acl.c if no log message is
+ specified, we must adjust the log message to show the code that is
+ actually going to be used. */
+
+ if (log_msg == user_msg)
+ log_msg = string_sprintf("%s %s", smtp_code, log_msg + ovector[1]);
+ }
+ else
+ {
+ smtp_code = user_msg;
+ codelen = ovector[1]; /* Includes final space */
+ }
+ user_msg += ovector[1]; /* Chop the code off the message */
+ }
+ }
+
/* 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
fixed, sender_address at this point became the rewritten address. I'm not sure
@@ -1888,7 +1941,7 @@
string_sprintf(": %s", sender_verified_failed->message));
if (rc == FAIL && sender_verified_failed->user_message != NULL)
- smtp_respond(code, FALSE, string_sprintf(
+ smtp_respond(smtp_code, codelen, FALSE, string_sprintf(
testflag(sender_verified_failed, af_verify_pmfail)?
"Postmaster verification failed while checking <%s>\n%s\n"
"Several RFCs state that you are required to have a postmaster\n"
@@ -1918,7 +1971,7 @@
always a 5xx one - see comments at the start of this function. If the original
rc was FAIL_DROP we drop the connection and yield 2. */
-if (rc == FAIL) smtp_respond(code, TRUE, (user_msg == NULL)?
+if (rc == FAIL) smtp_respond(smtp_code, codelen, TRUE, (user_msg == NULL)?
US"Administrative prohibition" : user_msg);
/* Send temporary failure response to the command. Don't give any details,
@@ -1937,12 +1990,13 @@
sender_verified_failed != NULL &&
sender_verified_failed->message != NULL)
{
- smtp_respond(451, FALSE, sender_verified_failed->message);
+ smtp_respond(smtp_code, codelen, FALSE, sender_verified_failed->message);
}
- smtp_respond(451, TRUE, user_msg);
+ smtp_respond(smtp_code, codelen, TRUE, user_msg);
}
else
- smtp_printf("451 Temporary local problem - please try later\r\n");
+ smtp_respond(smtp_code, codelen, TRUE,
+ US"Temporary local problem - please try later");
}
/* Log the incident. If the connection is not forcibly to be dropped, return 0.
More information about the Pkg-exim4-maintainers
mailing list