This patch is not final, it needs some pointers/input.
So I found myself wanting to have an ACL run on every single exit of an
SMTP connection for whatever reason so that I could use it to figure out
if a host was being nice and using quit or just dropping the connection,
and finding that information out sooner rather than later.
I have delved into the code and tried to produce something that wont
blow up any old configurations and should work to fill pretty much
everyone's wishes. It's a little bit ugly, but I can work on that if
this is deemed to not be a complete waste of everyone's time.
It has a few limitations due to the number of ways that a connection can
fail and the complexity of some of exit functions that I was not game to
touch for fear of really screwing something up. I should revisit
smtp_handle_acl_fail in smtp_in.c and see if I can more closely tie in
the exit acl instead of its present implementation where the user_msg
and log_msg are not used.
When the connection is terminated due to a local configuration or system
problem, the exit acl is also not called as it makes a pair already
cyclic function calls into a self calling triple.
A variable $smtp_notquit_reason is available to the acl for the user to
figure out what went wrong.
command-timeout - exim timed out waiting for next SMTP command
sigterm - got sigterm while in initial conversation
acl-drop - an ACL in the conversation returned "drop"
tls-failed - TLS failed to start
connection-lost - connection was lost at any stage
synchronization-error - sync error
bad-commands - too many SMTP commands in a row, or too many non mail
logging-error - one of the log errors I could handle
data-timeout - no data in DATA section in a long time
config-eror - something went boom in your config
signal-exit - got sigterm or sigint while receiving message body
I did not make a new ACL for sync errors as, I thought it was a bad
idea. If people really want it I can probably add it in a matter of
minutes now that I know how this section of the exim code works.
I want to blacklist the "delay" term from being used in the acl as it's
completely useless and may screw things over as this acl is called
during a number of signal handlers. From what I understand of how the
signals have been setup in an exim receiving process, this isn't really
that big an issue as the signal is left disabled during the processing
of the signal, but it still leaves an opening for people to be stupid.
What little I tried to this front ended up not working in the slightest.
I guess now the question is, what do I need to do to this make it good
enough to be accepted or should I just /dev/null it?
I also added the QUIT ACL being called in one extra place where I
thought it should be, just for completeness. There was another place I
wanted to add it but I can't find it right at this very moment.
Ted.
--
The Exim Manual
http://www.exim.org/docs.html
http://www.exim.org/exim-html-current/doc/html/spec_html/index.html
diff -Naur exim-4.67/src/expand.c exim-4.67-mod/src/expand.c
--- exim-4.67/src/expand.c 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/expand.c 2007-07-09 01:27:37.000000000 +1000
@@ -547,6 +547,7 @@
{ "smtp_command", vtype_stringptr, &smtp_cmd_buffer },
{ "smtp_command_argument", vtype_stringptr, &smtp_cmd_argument },
{ "smtp_count_at_connection_start", vtype_int, &smtp_accept_count },
+ { "smtp_notquit_reason", vtype_stringptr, &smtp_notquit_reason },
{ "sn0", vtype_filter_int, &filter_sn[0] },
{ "sn1", vtype_filter_int, &filter_sn[1] },
{ "sn2", vtype_filter_int, &filter_sn[2] },
diff -Naur exim-4.67/src/functions.h exim-4.67-mod/src/functions.h
--- exim-4.67/src/functions.h 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/functions.h 2007-07-12 17:10:16.000000000 +1000
@@ -210,7 +210,7 @@
extern void readconf_rest(BOOL);
extern uschar *readconf_retry_error(uschar *, uschar *, int *, int *);
extern void read_message_body(BOOL);
-extern void receive_bomb_out(uschar *);
+extern void receive_bomb_out(uschar *, uschar *);
extern BOOL receive_check_fs(int);
extern BOOL receive_check_set_sender(uschar *);
extern BOOL receive_msg(BOOL);
@@ -279,6 +279,7 @@
extern void smtp_respond(uschar *, int, BOOL, uschar *);
extern void smtp_send_prohibition_message(int, uschar *);
extern int smtp_setup_msg(void);
+extern void smtp_run_notquit_acl(uschar *, uschar *, uschar *);
extern BOOL smtp_start_session(void);
extern int smtp_ungetc(int);
extern BOOL smtp_verify_helo(void);
diff -Naur exim-4.67/src/globals.c exim-4.67-mod/src/globals.c
--- exim-4.67/src/globals.c 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/globals.c 2007-07-09 01:22:28.000000000 +1000
@@ -188,6 +188,7 @@
#endif
uschar *acl_smtp_predata = NULL;
uschar *acl_smtp_quit = NULL;
+uschar *acl_smtp_notquit = NULL;
uschar *acl_smtp_rcpt = NULL;
uschar *acl_smtp_starttls = NULL;
uschar *acl_smtp_vrfy = NULL;
@@ -213,6 +214,7 @@
US"EHLO or HELO",
US"MAILAUTH",
US"non-SMTP-start",
+ US"NOTQUIT",
US"QUIT",
US"STARTTLS",
US"VRFY"
@@ -231,6 +233,7 @@
US"550", /* HELO/EHLO */
US"0", /* MAILAUTH; not relevant */
US"0", /* not SMTP; not relevant */
+ US"0", /* NOTQUIT; also not relevant */
US"0", /* QUIT; not relevant */
US"550", /* STARTTLS */
US"252" /* VRFY */
@@ -1099,6 +1102,8 @@
BOOL smtp_use_pipelining = FALSE;
BOOL smtp_use_size = FALSE;
+uschar *smtp_notquit_reason = NULL;
+
#ifdef WITH_CONTENT_SCAN
uschar *spamd_address = US"127.0.0.1 783";
uschar *spam_bar = NULL;
diff -Naur exim-4.67/src/globals.h exim-4.67-mod/src/globals.h
--- exim-4.67/src/globals.h 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/globals.h 2007-07-09 02:53:17.000000000 +1000
@@ -128,6 +128,7 @@
#endif
extern uschar *acl_smtp_predata; /* ACL run for DATA command */
extern uschar *acl_smtp_quit; /* ACL run for QUIT */
+extern uschar *acl_smtp_notquit; /* ACL run for disconnects */
extern uschar *acl_smtp_rcpt; /* ACL run for RCPT */
extern uschar *acl_smtp_starttls; /* ACL run for STARTTLS */
extern uschar *acl_smtp_vrfy; /* ACL run for VRFY */
@@ -678,6 +679,8 @@
extern BOOL smtp_use_pipelining; /* Global for passed connections */
extern BOOL smtp_use_size; /* Global for passed connections */
+extern uschar *smtp_notquit_reason; /* Global for disconnect reason */
+
#ifdef WITH_CONTENT_SCAN
extern uschar *spamd_address; /* address for the spamassassin daemon */
extern uschar *spam_bar; /* the spam "bar" (textual representation of spam_score) */
diff -Naur exim-4.67/src/log.c exim-4.67-mod/src/log.c
--- exim-4.67/src/log.c 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/log.c 2007-07-12 17:03:00.000000000 +1000
@@ -155,7 +155,9 @@
if (log_stderr != NULL && log_stderr != debug_file)
fprintf(log_stderr, "%s\n", s1);
}
-if (receive_call_bombout) receive_bomb_out(s2); /* does not return */
+/* call not-quit ACL in bombout - will not return */
+if (receive_call_bombout)
+ receive_bomb_out(US"logging-error", s2); /* does not return */
if (smtp_input) smtp_closedown(s2);
exim_exit(EXIT_FAILURE);
}
diff -Naur exim-4.67/src/macros.h exim-4.67-mod/src/macros.h
--- exim-4.67/src/macros.h 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/macros.h 2007-07-09 01:12:06.000000000 +1000
@@ -800,6 +800,7 @@
ACL_WHERE_HELO,
ACL_WHERE_MAILAUTH,
ACL_WHERE_NOTSMTP_START,
+ ACL_WHERE_NOTQUIT,
ACL_WHERE_QUIT,
ACL_WHERE_STARTTLS,
ACL_WHERE_VRFY
diff -Naur exim-4.67/src/readconf.c exim-4.67-mod/src/readconf.c
--- exim-4.67/src/readconf.c 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/readconf.c 2007-07-09 10:35:23.000000000 +1000
@@ -150,6 +150,7 @@
#ifdef WITH_CONTENT_SCAN
{ "acl_smtp_mime", opt_stringptr, &acl_smtp_mime },
#endif
+ { "acl_smtp_notquit", opt_stringptr, &acl_smtp_notquit },
{ "acl_smtp_predata", opt_stringptr, &acl_smtp_predata },
{ "acl_smtp_quit", opt_stringptr, &acl_smtp_quit },
{ "acl_smtp_rcpt", opt_stringptr, &acl_smtp_rcpt },
diff -Naur exim-4.67/src/receive.c exim-4.67-mod/src/receive.c
--- exim-4.67/src/receive.c 2007-04-17 23:06:39.000000000 +1000
+++ exim-4.67-mod/src/receive.c 2007-07-12 17:10:18.000000000 +1000
@@ -288,7 +288,7 @@
*/
void
-receive_bomb_out(uschar *msg)
+receive_bomb_out(uschar *reason, uschar *msg)
{
/* If spool_name is set, it contains the name of the data file that is being
written. Unlink it before closing so that it cannot be picked up by a delivery
@@ -312,8 +312,13 @@
{
if (!smtp_batched_input)
{
- smtp_printf("421 %s %s - closing connection.\r\n", smtp_active_hostname,
- msg);
+ /* SMTP connection about to be closed without a QUIT */
+ uschar local_buffer[512];
+ snprintf(local_buffer, 512, "%s %s - closing connection.",
+ smtp_active_hostname, msg);
+ smtp_run_notquit_acl(reason, US"421", local_buffer);
+ /* smtp_printf("421 %s %s - closing connection.\r\n", smtp_active_hostname,
+ msg); */
mac_smtp_fflush();
}
@@ -362,7 +367,7 @@
LOG_MAIN, "timed out while reading local message");
}
-receive_bomb_out(msg); /* Does not return */
+receive_bomb_out(US"data-timeout", msg); /* Does not return */
}
@@ -384,7 +389,8 @@
sig = sig; /* Keep picky compilers happy */
log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - "
"message temporarily rejected (size %d)", message_size);
-receive_bomb_out(US"local verification problem"); /* Does not return */
+/* Does not return */
+receive_bomb_out(US"config-error", US"local verification problem");
}
@@ -405,7 +411,8 @@
{
log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with "
"signal %d - message temporarily rejected (size %d)", sig, message_size);
-receive_bomb_out(US"local verification problem"); /* Does not return */
+/* Does not return */
+receive_bomb_out(US"config-error", US"local verification problem");
}
@@ -442,7 +449,7 @@
}
}
-receive_bomb_out(msg); /* Does not return */
+receive_bomb_out(US"signal-exit", msg); /* Does not return */
}
diff -Naur exim-4.67/src/smtp_in.c exim-4.67-mod/src/smtp_in.c
--- exim-4.67/src/smtp_in.c 2007-04-17 23:06:40.000000000 +1000
+++ exim-4.67-mod/src/smtp_in.c 2007-07-13 11:30:37.000000000 +1000
@@ -127,6 +127,7 @@
static int unknown_command_count;
static int sync_cmd_limit;
static int smtp_write_error = 0;
+static int smtp_exit_function_called = 0;
static uschar *rcpt_smtp_response;
static uschar *smtp_data_buffer;
@@ -470,8 +471,12 @@
host_and_ident(FALSE));
if (smtp_batched_input)
moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
-smtp_printf("421 %s: SMTP command timeout - closing connection\r\n",
- smtp_active_hostname);
+/* smtp_printf("421 %s: SMTP command timeout - closing connection\r\n",
+ smtp_active_hostname); */
+uschar local_buffer[512];
+snprintf(local_buffer, 512, "%s: SMTP command timeout - closing "
+ "connection\r\n", smtp_active_hostname);
+smtp_run_notquit_acl(US"command-timeout", US"421", local_buffer);
mac_smtp_fflush();
exim_exit(EXIT_FAILURE);
}
@@ -495,8 +500,13 @@
log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
if (smtp_batched_input)
moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
-smtp_printf("421 %s: Service not available - closing connection\r\n",
+
+uschar local_buffer[512];
+snprintf(local_buffer, 512, "%s: Service not available - closing connection",
smtp_active_hostname);
+smtp_run_notquit_acl(US"sigterm", US"421", local_buffer);
+/* smtp_printf("421 %s: Service not available - closing connection\r\n",
+ smtp_active_hostname); */
exim_exit(EXIT_FAILURE);
}
@@ -1299,7 +1309,6 @@
break;
}
}
-
return done - 2; /* Convert yield values */
}
@@ -1344,6 +1353,8 @@
pipelining_advertised = FALSE;
pipelining_enable = TRUE;
sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
+/* flag so we only call exit once (process should exit after that!!) */
+smtp_exit_function_called = 0;
memset(sender_host_cache, 0, sizeof(sender_host_cache));
@@ -2266,6 +2277,11 @@
log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL",
smtp_get_connection_info());
+
+/* No easy way to integrate this in.. just let the above do the talking. Means
+no custom messages in this particular case. Actually, unfortunatly I should
+get this tied into the rest of the code in this function */
+smtp_run_notquit_acl(US"acl-drop", NULL, NULL);
return 2;
}
@@ -3787,13 +3803,28 @@
log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF",
smtp_get_connection_info());
done = 2;
+ /* Possible acl_smtp_notquit call here - it's still a disconnection */
+ smtp_run_notquit_acl(US"tls-failed", NULL, NULL);
break;
case QUIT_CMD:
- smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+ /* smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);*/
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
smtp_get_connection_info());
done = 2;
+ /* The REAL QUIT ACL should be called here? */
+ if (acl_smtp_quit != NULL)
+ {
+ rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit,&user_msg,&log_msg);
+ if (rc == ERROR)
+ log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
+ log_msg);
+ }
+
+ if (user_msg == NULL)
+ smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+ else
+ smtp_respond(US"221", 3, TRUE, user_msg);
break;
default:
@@ -3882,7 +3913,15 @@
case EOF_CMD:
incomplete_transaction_log(US"connection lost");
- smtp_printf("421 %s lost input connection\r\n", smtp_active_hostname);
+
+ {
+ uschar local_buffer[512];
+ snprintf(local_buffer, 512, "%s lost input connection",
+ smtp_active_hostname);
+ smtp_run_notquit_acl(US"connection-lost", US"421", local_buffer);
+ }
+
+ /*smtp_printf("421 %s lost input connection\r\n", smtp_active_hostname);*/
/* Don't log by default unless in the middle of a message, as some mailers
just drop the call rather than sending QUIT, and it clutters up the logs.
@@ -4088,7 +4127,12 @@
pipelining_advertised? "" : " not",
smtp_cmd_buffer, host_and_ident(TRUE),
string_printing(smtp_inptr));
- smtp_printf("554 SMTP synchronization error\r\n");
+
+ smtp_run_notquit_acl(US"synchronization-error", US"544",
+ US"SMTP synchronization error");
+
+ /* smtp_printf("554 SMTP synchronization error\r\n"); */
+
done = 1; /* Pretend eof - drops connection */
break;
@@ -4100,7 +4144,11 @@
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
"nonmail commands (last was \"%.*s\")", host_and_ident(FALSE),
s - smtp_cmd_buffer, smtp_cmd_buffer);
- smtp_printf("554 Too many nonmail commands\r\n");
+
+ smtp_run_notquit_acl(US"bad-commands", US"554",
+ US"Too many nonmail commands");
+
+ /* smtp_printf("554 Too many nonmail commands\r\n"); */
done = 1; /* Pretend eof - drops connection */
break;
@@ -4113,7 +4161,10 @@
string_printing(smtp_cmd_buffer), host_and_ident(TRUE),
US"unrecognized command");
incomplete_transaction_log(US"unrecognized command");
- smtp_printf("500 Too many unrecognized commands\r\n");
+
+ smtp_run_notquit_acl(US"bad-commands", US"500",
+ US"Too many unrecognized commands");
+ /* smtp_printf("500 Too many unrecognized commands\r\n"); */
done = 2;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
"unrecognized commands (last was \"%s\")", host_and_ident(FALSE),
@@ -4134,7 +4185,72 @@
continue;
}
+
return done - 2; /* Convert yield values */
}
+/*************************************************
+* Run an ACL when SMTP transaction dies *
+*************************************************/
+
+/* Provides a logging/statistics hook for when an SMTP connection is dropped
+on the floor or the other end goes away.
+
+It's seperated out like this to save coding the exact same thing 10 times.
+
+Arguments:
+ reason What the smtp_notquit_reason will be set to in the ACL
+ code The error code to return as part of the response
+ defaultrespond The exim default if there's no user_msg
+
+Returns: nada
+
+*/
+
+void
+smtp_run_notquit_acl(uschar *reason, uschar *code, uschar *defaultrespond)
+{
+ int rc;
+ uschar *user_msg = NULL;
+ uschar *log_msg = NULL;
+
+ if (smtp_exit_function_called > 0) {
+ /* this has already been called */
+ log_write(0, LOG_PANIC, "non quit acl called more than once %d (%s)",
+ smtp_exit_function_called, reason);
+ return;
+ }
+ smtp_exit_function_called++;
+
+ /* Call not-QUIT ACL */
+ if (acl_smtp_notquit != NULL)
+ {
+ /* set the reason - this mem will auto-release with the connection */
+ smtp_notquit_reason = store_get(strlen(reason) + 1);
+ sprintf(smtp_notquit_reason, reason);
+
+ rc = acl_check(ACL_WHERE_NOTQUIT, NULL, acl_smtp_notquit,&user_msg,&log_msg);
+ if (rc == ERROR)
+ log_write(0, LOG_MAIN|LOG_PANIC, "ACL for not-QUIT returned ERROR: %s",
+ log_msg);
+
+ /* write out the log_msg if the acl came back accept. This gives some
+ determination on whether to use log_message or not. Should probably be the
+ other way round */
+ if (rc == OK && log_msg != NULL)
+ log_write(0, LOG_MAIN, "not-QUIT: %s", log_msg);
+
+ }
+
+ if (code != NULL && defaultrespond != NULL)
+ {
+ if (user_msg == NULL)
+ smtp_printf("%s %s\r\n", code, defaultrespond);
+ else
+ smtp_respond(code, 3, TRUE, user_msg);
+ }
+
+ /* end not-QUIT ACL */
+}
+
/* End of smtp_in.c */