ph10 2007/02/20 15:58:02 GMT
Modified files:
exim-doc/doc-txt ChangeLog
exim-src/src smtp_in.c
exim-test README
Added files:
exim-test/confs 0556
exim-test/log 0556
exim-test/rejectlog 0556
exim-test/scripts/0000-Basic 0556
exim-test/stdout 0556
Log:
Add extra sync checks after ACLs that might delay.
Revision Changes Path
1.484 +8 -0 exim/exim-doc/doc-txt/ChangeLog
1.55 +90 -32 exim/exim-src/src/smtp_in.c
1.8 +7 -4 exim/exim-test/README
1.1 +39 -0 exim/exim-test/confs/0556 (new)
1.1 +5 -0 exim/exim-test/log/0556 (new)
1.1 +3 -0 exim/exim-test/rejectlog/0556 (new)
1.1 +55 -0 exim/exim-test/scripts/0000-Basic/0556 (new)
1.1 +59 -0 exim/exim-test/stdout/0556 (new)
Index: ChangeLog
===================================================================
RCS file: /home/cvs/exim/exim-doc/doc-txt/ChangeLog,v
retrieving revision 1.483
retrieving revision 1.484
diff -u -r1.483 -r1.484
--- ChangeLog 20 Feb 2007 11:37:16 -0000 1.483
+++ ChangeLog 20 Feb 2007 15:58:02 -0000 1.484
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-doc/doc-txt/ChangeLog,v 1.483 2007/02/20 11:37:16 ph10 Exp $
+$Cambridge: exim/exim-doc/doc-txt/ChangeLog,v 1.484 2007/02/20 15:58:02 ph10 Exp $
Change log file for Exim from version 4.21
-------------------------------------------
@@ -122,6 +122,14 @@
in the case of a MAIL command with additional options following the
address, for example: MAIL FROM:<foo@bar> SIZE=1234. The option settings
were accidentally chopped off.
+
+PH/29 SMTP synchronization checks are implemented when a command is read -
+ there is a check that no more input is waiting when there shouldn't be
+ any. However, for some commands, a delay in an ACL can mean that it is
+ some time before the response is written. In this time, more input might
+ arrive, invalidly. So now there are extra checks after an ACL has run for
+ HELO/EHLO and after the predata ACL, and likewise for MAIL and RCPT when
+ pipelining has not been advertised.
Exim version 4.66
Index: smtp_in.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/smtp_in.c,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -r1.54 -r1.55
--- smtp_in.c 20 Feb 2007 11:37:16 -0000 1.54
+++ smtp_in.c 20 Feb 2007 15:58:02 -0000 1.55
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/smtp_in.c,v 1.54 2007/02/20 11:37:16 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/smtp_in.c,v 1.55 2007/02/20 15:58:02 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -469,6 +469,7 @@
+
/*************************************************
* Read one command line *
*************************************************/
@@ -604,6 +605,60 @@
/*************************************************
+* Recheck synchronization *
+*************************************************/
+
+/* Synchronization checks can never be perfect because a packet may be on its
+way but not arrived when the check is done. Such checks can in any case only be
+done when TLS is not in use. Normally, the checks happen when commands are
+read: Exim ensures that there is no more input in the input buffer. In normal
+cases, the response to the command will be fast, and there is no further check.
+
+However, for some commands an ACL is run, and that can include delays. In those
+cases, it is useful to do another check on the input just before sending the
+response. This also applies at the start of a connection. This function does
+that check by means of the select() function, as long as the facility is not
+disabled or inappropriate. A failure of select() is ignored.
+
+When there is unwanted input, we read it so that it appears in the log of the
+error.
+
+Arguments: none
+Returns: TRUE if all is well; FALSE if there is input pending
+*/
+
+static BOOL
+check_sync(void)
+{
+int fd, rc;
+fd_set fds;
+struct timeval tzero;
+
+if (!smtp_enforce_sync || sender_host_address == NULL ||
+ sender_host_notsocket || tls_active >= 0)
+ return TRUE;
+
+fd = fileno(smtp_in);
+FD_ZERO(&fds);
+FD_SET(fd, &fds);
+tzero.tv_sec = 0;
+tzero.tv_usec = 0;
+rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero);
+
+if (rc <= 0) return TRUE; /* Not ready to read */
+rc = smtp_getc();
+if (rc < 0) return TRUE; /* End of file or error */
+
+smtp_ungetc(rc);
+rc = smtp_inend - smtp_inptr;
+if (rc > 150) rc = 150;
+smtp_inptr[rc] = 0;
+return FALSE;
+}
+
+
+
+/*************************************************
* Forced closedown of call *
*************************************************/
@@ -1760,30 +1815,14 @@
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
-if (smtp_enforce_sync && sender_host_address != NULL && !sender_host_notsocket)
+if (!check_sync())
{
- fd_set fds;
- struct timeval tzero;
- tzero.tv_sec = 0;
- tzero.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(fileno(smtp_in), &fds);
- if (select(fileno(smtp_in) + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL,
- &tzero) > 0)
- {
- int rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size);
- if (rc > 0)
- {
- if (rc > 150) rc = 150;
- smtp_inbuffer[rc] = 0;
- log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
- "synchronization error (input sent without waiting for greeting): "
- "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
- string_printing(smtp_inbuffer));
- smtp_printf("554 SMTP synchronization error\r\n");
- return FALSE;
- }
- }
+ log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+ "synchronization error (input sent without waiting for greeting): "
+ "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+ string_printing(smtp_inptr));
+ smtp_printf("554 SMTP synchronization error\r\n");
+ return FALSE;
}
/* Now output the banner */
@@ -2731,7 +2770,8 @@
spf_init(sender_helo_name, sender_host_address);
#endif
- /* Apply an ACL check if one is defined */
+ /* 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)
{
@@ -2743,6 +2783,7 @@
host_build_sender_fullhost(); /* Rebuild */
break;
}
+ else if (!check_sync()) goto SYNC_FAILURE;
}
/* Generate an OK reply. The default string includes the ident if present,
@@ -3236,10 +3277,16 @@
}
}
- /* Apply an ACL check if one is defined, before responding */
-
- rc = (acl_smtp_mail == NULL)? OK :
- acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
+ /* Apply an ACL check if one is defined, before responding. Afterwards,
+ when pipelining is not advertised, do another sync check in case the ACL
+ delayed and the client started sending in the meantime. */
+
+ if (acl_smtp_mail == NULL) rc = OK; else
+ {
+ rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
+ if (rc == OK && !pipelining_advertised && !check_sync())
+ goto SYNC_FAILURE;
+ }
if (rc == OK || rc == DISCARD)
{
@@ -3395,10 +3442,17 @@
}
/* If the MAIL ACL discarded all the recipients, we bypass ACL checking
- for them. Otherwise, check the access control list for this recipient. */
+ for them. Otherwise, check the access control list for this recipient. As
+ there may be a delay in this, re-check for a synchronization error
+ afterwards, unless pipelining was advertised. */
- rc = recipients_discarded? DISCARD :
- acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg, &log_msg);
+ if (recipients_discarded) rc = DISCARD; else
+ {
+ rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg,
+ &log_msg);
+ if (rc == OK && !pipelining_advertised && !check_sync())
+ goto SYNC_FAILURE;
+ }
/* The ACL was happy */
@@ -3472,12 +3526,16 @@
break;
}
+ /* If there is an ACL, re-check the synchronization afterwards, since the
+ ACL may have delayed. */
+
if (acl_smtp_predata == NULL) rc = OK; else
{
enable_dollar_recipients = TRUE;
rc = acl_check(ACL_WHERE_PREDATA, NULL, acl_smtp_predata, &user_msg,
&log_msg);
enable_dollar_recipients = FALSE;
+ if (rc == OK && !check_sync()) goto SYNC_FAILURE;
}
if (rc == OK)
@@ -3493,7 +3551,6 @@
else
done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg);
-
break;
@@ -3942,6 +3999,7 @@
case BADSYN_CMD:
+ SYNC_FAILURE:
if (smtp_inend >= smtp_inbuffer + in_buffer_size)
smtp_inend = smtp_inbuffer + in_buffer_size - 1;
c = smtp_inend - smtp_inptr;
Index: README
===================================================================
RCS file: /home/cvs/exim/exim-test/README,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- README 31 Jan 2007 16:52:12 -0000 1.7
+++ README 20 Feb 2007 15:58:02 -0000 1.8
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-test/README,v 1.7 2007/01/31 16:52:12 ph10 Exp $
+$Cambridge: exim/exim-test/README,v 1.8 2007/02/20 15:58:02 ph10 Exp $
EXPORTABLE EXIM TEST SUITE
--------------------------
@@ -6,7 +6,7 @@
This document last updated for:
Test Suite Version: 4.67
-Date: 31 January 2007
+Date: 20 February 2007
BACKGROUND
@@ -765,9 +765,12 @@
This command runs the auxiliary "client" program that simulates an SMTP client.
It is controlled by a script read from its standard input, details of which are
-given below. The only option is -t, which must be followed by a number, to
-specify the command timeout in seconds. The program connects to the given IP
-address and port, using the specified interface, if one is given.
+given below. There are two options. One is -t, which must be followed directly
+by a number, to specify the command timeout in seconds (e.g. -t5). The default
+timeout is 1 second. The other option is -tls-on-connect, which causes the
+client to try to start up a TLS session as soon as it has connected, without
+using the STARTTLS command. The client program connects to the given IP address
+and port, using the specified interface, if one is given.
client-ssl [<options>] <ip address> <port> [<outgoing interface>] \
Index: 0556
====================================================================
# Exim test configuration 0556
ACL_MAIL=accept
ACL_RCPT=accept
ACL_PREDATA=accept
PAH=127.0.0.1
exim_path = EXIM_PATH
host_lookup_order = bydns
primary_hostname = myhost.test.ex
rfc1413_query_timeout = 0s
spool_directory = DIR/spool
log_file_path = DIR/spool/log/%slog
gecos_pattern = ""
gecos_name = CALLER_NAME
# ----- Main settings -----
acl_smtp_predata = ACL_PREDATA
acl_smtp_mail = ACL_MAIL
acl_smtp_rcpt = ACL_RCPT
pipelining_advertise_hosts = PAH
queue_only
# ----- ACL -----
begin ACL
check_predata:
accept delay = 2s
check_mail:
accept delay = 2s
check_rcpt:
accept delay = 2s
# End
Index: 0556
====================================================================
1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
1999-03-02 09:44:33 SMTP protocol synchronization error (next input sent too soon: pipelining was advertised): rejected "data" H=(abcd) [127.0.0.1] next input="Start: sent early ...\r\n"
1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
1999-03-02 09:44:33 SMTP protocol synchronization error (next input sent too soon: pipelining was not advertised): rejected "mail from:<userx@???>" H=(abcd) [127.0.0.1] next input="rcpt to:<userx@???>\r\n"
1999-03-02 09:44:33 SMTP protocol synchronization error (next input sent too soon: pipelining was not advertised): rejected "rcpt to:<userx@???>" H=(abcd) [127.0.0.1] next input="data\r\n"
Index: 0556
====================================================================
1999-03-02 09:44:33 SMTP protocol synchronization error (next input sent too soon: pipelining was advertised): rejected "data" H=(abcd) [127.0.0.1] next input="Start: sent early ...\r\n"
1999-03-02 09:44:33 SMTP protocol synchronization error (next input sent too soon: pipelining was not advertised): rejected "mail from:<userx@???>" H=(abcd) [127.0.0.1] next input="rcpt to:<userx@???>\r\n"
1999-03-02 09:44:33 SMTP protocol synchronization error (next input sent too soon: pipelining was not advertised): rejected "rcpt to:<userx@???>" H=(abcd) [127.0.0.1] next input="data\r\n"
Index: 0556
====================================================================
# SMTP synchronization checks before sending responses
need_ipv4
#
exim -DSERVER=server -DACL_PREDATA=check_predata -bd -oX PORT_D
****
# The pause (+++ 1) in the middle of this is so that there is no pending
# input when DATA is received, but we start sending the data itself too
# early (the server will be waiting 2 seconds in the predata ACL).
#
client 127.0.0.1 PORT_D
??? 220
ehlo abcd
??? 250-
??? 250-
??? 250-
??? 250
rset\r\nmail from:<userx@???>\r\nrcpt to:<userx@???>\r\ndata
+++ 1
Start: sent early ...
??? 250
??? 250
??? 250
??? 554
****
sleep 1
killdaemon
# This time turn off pipelining to check MAIL and RCPT
exim -DSERVER=server -DACL_MAIL=check_mail -DACL_RCPT=check_rcpt -DPAH= \
-bd -oX PORT_D
****
client -t5 127.0.0.1 PORT_D
??? 220
ehlo abcd
??? 250-
??? 250-
??? 250
mail from:<userx@???>
+++ 1
rcpt to:<userx@???>
??? 554
****
client -t5 127.0.0.1 PORT_D
??? 220
ehlo abcd
??? 250-
??? 250-
??? 250
mail from:<userx@???>
??? 250
rcpt to:<userx@???>
+++ 1
data
??? 554
****
killdaemon
Index: 0556
====================================================================
Connecting to 127.0.0.1 port 1225 ... connected
??? 220
<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
>>> ehlo abcd
??? 250-
<<< 250-myhost.test.ex Hello abcd [127.0.0.1]
??? 250-
<<< 250-SIZE 52428800
??? 250-
<<< 250-PIPELINING
??? 250
<<< 250 HELP
>>> rset\r\nmail from:<userx@???>\r\nrcpt to:<userx@???>\r\ndata
+++ 1
>>> Start: sent early ...
??? 250
<<< 250 Reset OK
??? 250
<<< 250 OK
??? 250
<<< 250 Accepted
??? 554
<<< 554 SMTP synchronization error
End of script
Connecting to 127.0.0.1 port 1225 ... connected
??? 220
<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
>>> ehlo abcd
??? 250-
<<< 250-myhost.test.ex Hello abcd [127.0.0.1]
??? 250-
<<< 250-SIZE 52428800
??? 250
<<< 250 HELP
>>> mail from:<userx@???>
+++ 1
>>> rcpt to:<userx@???>
??? 554
<<< 554 SMTP synchronization error
End of script
Connecting to 127.0.0.1 port 1225 ... connected
??? 220
<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
>>> ehlo abcd
??? 250-
<<< 250-myhost.test.ex Hello abcd [127.0.0.1]
??? 250-
<<< 250-SIZE 52428800
??? 250
<<< 250 HELP
>>> mail from:<userx@???>
??? 250
<<< 250 OK
>>> rcpt to:<userx@???>
+++ 1
>>> data
??? 554
<<< 554 SMTP synchronization error
End of script