diff options
Diffstat (limited to 'debian/patches/75_14-Fix-smtp-response-timeout.patch')
-rw-r--r-- | debian/patches/75_14-Fix-smtp-response-timeout.patch | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/debian/patches/75_14-Fix-smtp-response-timeout.patch b/debian/patches/75_14-Fix-smtp-response-timeout.patch new file mode 100644 index 0000000..abf2da4 --- /dev/null +++ b/debian/patches/75_14-Fix-smtp-response-timeout.patch @@ -0,0 +1,325 @@ +From 0a5441fcd93ae4145c07b3ed138dfe0e107174e0 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Mon, 27 May 2019 23:44:31 +0100 +Subject: [PATCH 1/2] Fix smtp response timeout + +--- + doc/ChangeLog | 6 ++++++ + src/functions.h | 4 ++-- + src/ip.c | 16 +++++++--------- + src/malware.c | 26 +++++++++++++------------- + src/routers/iplookup.c | 2 +- + src/smtp_out.c | 9 +++++---- + src/spam.c | 2 +- + src/transports/smtp_socks.c | 6 +++--- + src/verify.c | 2 +- + 9 files changed, 39 insertions(+), 34 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -50,6 +50,13 @@ JH/27 Bug 2404: Use the main-section con + success-DSN messages. Previously the From: header was always the default + one for these; the option was ignored. + ++JH/28 Fix the timeout on smtp response to apply to the whole response. ++ Previously it was reset for every read, so a teergrubing peer sending ++ single bytes within the time limit could extend the connection for a ++ long time. Credit to Qualsys Security Advisory Team for the discovery. ++[from GIT master] ++ ++ + + Exim version 4.92 + ----------------- +--- a/src/functions.h ++++ b/src/functions.h +@@ -225,7 +225,7 @@ extern uschar *expand_string_copy(const + extern int_eximarith_t expand_string_integer(uschar *, BOOL); + extern void modify_variable(uschar *, void *); + +-extern BOOL fd_ready(int, int); ++extern BOOL fd_ready(int, time_t); + + extern int filter_interpret(uschar *, int, address_item **, uschar **); + extern BOOL filter_personal(string_item *, BOOL); +@@ -271,7 +271,7 @@ extern int ip_connectedsocket(int, c + int, host_item *, uschar **, const blob *); + extern int ip_get_address_family(int); + extern void ip_keepalive(int, const uschar *, BOOL); +-extern int ip_recv(client_conn_ctx *, uschar *, int, int); ++extern int ip_recv(client_conn_ctx *, uschar *, int, time_t); + extern int ip_socket(int, int); + + extern int ip_tcpsocket(const uschar *, uschar **, int); +--- a/src/ip.c ++++ b/src/ip.c +@@ -566,16 +566,15 @@ if (setsockopt(sock, SOL_SOCKET, SO_KEEP + /* + Arguments: + fd the file descriptor +- timeout the timeout, seconds ++ timelimit the timeout endpoint, seconds-since-epoch + Returns: TRUE => ready for i/o + FALSE => timed out, or other error + */ + BOOL +-fd_ready(int fd, int timeout) ++fd_ready(int fd, time_t timelimit) + { + fd_set select_inset; +-time_t start_recv = time(NULL); +-int time_left = timeout; ++int time_left = timelimit - time(NULL); + int rc; + + if (time_left <= 0) +@@ -609,8 +608,7 @@ do + DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n"); + + /* Watch out, 'continue' jumps to the condition, not to the loops top */ +- time_left = timeout - (time(NULL) - start_recv); +- if (time_left > 0) continue; ++ if ((time_left = timelimit - time(NULL)) > 0) continue; + } + + if (rc <= 0) +@@ -634,18 +632,18 @@ Arguments: + cctx the connection context (socket fd, possibly TLS context) + buffer to read into + bufsize the buffer size +- timeout the timeout ++ timelimit the timeout endpoint, seconds-since-epoch + + Returns: > 0 => that much data read + <= 0 on error or EOF; errno set - zero for EOF + */ + + int +-ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, int timeout) ++ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, time_t timelimit) + { + int rc; + +-if (!fd_ready(cctx->sock, timeout)) ++if (!fd_ready(cctx->sock, timelimit)) + return -1; + + /* The socket is ready, read from it (via TLS if it's active). On EOF (i.e. +--- a/src/malware.c ++++ b/src/malware.c +@@ -349,13 +349,13 @@ return cre; + -2 on timeout or error + */ + static int +-recv_line(int fd, uschar * buffer, int bsize, int tmo) ++recv_line(int fd, uschar * buffer, int bsize, time_t tmo) + { + uschar * p = buffer; + ssize_t rcv; + BOOL ok = FALSE; + +-if (!fd_ready(fd, tmo-time(NULL))) ++if (!fd_ready(fd, tmo)) + return -2; + + /*XXX tmo handling assumes we always get a whole line */ +@@ -382,9 +382,9 @@ return p - buffer; + + /* return TRUE iff size as requested */ + static BOOL +-recv_len(int sock, void * buf, int size, int tmo) ++recv_len(int sock, void * buf, int size, time_t tmo) + { +-return fd_ready(sock, tmo-time(NULL)) ++return fd_ready(sock, tmo) + ? recv(sock, buf, size, 0) == size + : FALSE; + } +@@ -430,7 +430,7 @@ for (;;) + } + + static inline int +-mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo) ++mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo) + { + client_conn_ctx cctx = {.sock = sock}; + int offset = 0; +@@ -438,7 +438,7 @@ int i; + + do + { +- i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL)); ++ i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo); + if (i <= 0) + { + (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); +@@ -497,7 +497,7 @@ switch (*line) + + static int + mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename, +- int tmo) ++ time_t tmo) + { + struct iovec iov[3]; + const char *cmd = "MSQ\n"; +@@ -746,7 +746,7 @@ if (!malware_ok) + if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0) + return m_panic_defer(scanent, CUS callout_address, errstr); + +- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); ++ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); + + if (bread <= 0) + return m_panic_defer_3(scanent, CUS callout_address, +@@ -1064,7 +1064,7 @@ badseek: err = errno; + if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) + return m_panic_defer(scanent, CUS callout_address, errstr); + +- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); ++ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); + if (bread > 0) av_buffer[bread]='\0'; + if (bread < 0) + return m_panic_defer_3(scanent, CUS callout_address, +@@ -1096,7 +1096,7 @@ badseek: err = errno; + { + errno = ETIMEDOUT; + i = av_buffer+sizeof(av_buffer)-p; +- if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo-time(NULL))) < 0) ++ if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0) + return m_panic_defer_3(scanent, CUS callout_address, + string_sprintf("unable to read result (%s)", strerror(errno)), + malware_daemon_ctx.sock); +@@ -1401,7 +1401,7 @@ badseek: err = errno; + + /* wait for result */ + memset(av_buffer, 0, sizeof(av_buffer)); +- if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) ++ if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0) + return m_panic_defer_3(scanent, CUS callout_address, + string_sprintf("unable to read from UNIX socket (%s)", scanner_options), + malware_daemon_ctx.sock); +@@ -1737,7 +1737,7 @@ b_seek: err = errno; + + /* Read the result */ + memset(av_buffer, 0, sizeof(av_buffer)); +- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); ++ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); + (void)close(malware_daemon_ctx.sock); + malware_daemon_ctx.sock = -1; + malware_daemon_ctx.tls_ctx = NULL; +@@ -1895,7 +1895,7 @@ b_seek: err = errno; + return m_panic_defer(scanent, CUS callout_address, errstr); + + /* Read the result */ +- bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo-time(NULL)); ++ bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo); + + if (bread <= 0) + return m_panic_defer_3(scanent, CUS callout_address, +--- a/src/routers/iplookup.c ++++ b/src/routers/iplookup.c +@@ -279,7 +279,7 @@ while ((hostname = string_nextinlist(&li + /* Read the response and close the socket. If the read fails, try the + next IP address. */ + +- count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, ob->timeout); ++ count = ip_recv(&query_cctx, reply, sizeof(reply) - 1, time(NULL) + ob->timeout); + (void)close(query_cctx.sock); + if (count <= 0) + { +--- a/src/smtp_out.c ++++ b/src/smtp_out.c +@@ -587,14 +587,14 @@ Arguments: + inblock the SMTP input block (contains holding buffer, socket, etc.) + buffer where to put the line + size space available for the line +- timeout the timeout to use when reading a packet ++ timelimit deadline for reading the lime, seconds past epoch + + Returns: length of a line that has been put in the buffer + -1 otherwise, with errno set + */ + + static int +-read_response_line(smtp_inblock *inblock, uschar *buffer, int size, int timeout) ++read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit) + { + uschar *p = buffer; + uschar *ptr = inblock->ptr; +@@ -637,7 +637,7 @@ for (;;) + + /* Need to read a new input packet. */ + +- if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timeout)) <= 0) ++ if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0) + { + DEBUG(D_deliver|D_transport|D_acl) + debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n", +@@ -694,6 +694,7 @@ smtp_read_response(void * sx0, uschar * + smtp_context * sx = sx0; + uschar * ptr = buffer; + int count = 0, rc; ++time_t timelimit = time(NULL) + timeout; + + errno = 0; /* Ensure errno starts out zero */ + +@@ -713,7 +714,7 @@ response. */ + + for (;;) + { +- if ((count = read_response_line(&sx->inblock, ptr, size, timeout)) < 0) ++ if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0) + return FALSE; + + HDEBUG(D_transport|D_acl|D_v) +--- a/src/spam.c ++++ b/src/spam.c +@@ -503,7 +503,7 @@ offset = 0; + while ((i = ip_recv(&spamd_cctx, + spamd_buffer + offset, + sizeof(spamd_buffer) - offset - 1, +- sd->timeout - time(NULL) + start)) > 0) ++ sd->timeout + start)) > 0) + offset += i; + spamd_buffer[offset] = '\0'; /* guard byte */ + +--- a/src/transports/smtp_socks.c ++++ b/src/transports/smtp_socks.c +@@ -129,7 +129,7 @@ switch(method) + #ifdef TCP_QUICKACK + (void) setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off)); + #endif +- if (!fd_ready(fd, tmo-time(NULL)) || read(fd, s, 2) != 2) ++ if (!fd_ready(fd, tmo) || read(fd, s, 2) != 2) + return FAIL; + HDEBUG(D_transport|D_acl|D_v) + debug_printf_indent(" SOCKS<< %02x %02x\n", s[0], s[1]); +@@ -320,7 +320,7 @@ HDEBUG(D_transport|D_acl|D_v) debug_prin + (void) setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off)); + #endif + +-if ( !fd_ready(fd, tmo-time(NULL)) ++if ( !fd_ready(fd, tmo) + || read(fd, buf, 2) != 2 + ) + goto rcv_err; +@@ -370,7 +370,7 @@ if (send(fd, buf, size, 0) < 0) + /* expect conn-reply (success, local(ipver, addr, port)) + of same length as conn-request, or non-success fail code */ + +-if ( !fd_ready(fd, tmo-time(NULL)) ++if ( !fd_ready(fd, tmo) + || (size = read(fd, buf, size)) < 2 + ) + goto rcv_err; +--- a/src/verify.c ++++ b/src/verify.c +@@ -2770,7 +2770,7 @@ for (;;) + int size = sizeof(buffer) - (p - buffer); + + if (size <= 0) goto END_OFF; /* Buffer filled without seeing \n. */ +- count = ip_recv(&ident_conn_ctx, p, size, rfc1413_query_timeout); ++ count = ip_recv(&ident_conn_ctx, p, size, time(NULL) + rfc1413_query_timeout); + if (count <= 0) goto END_OFF; /* Read error or EOF */ + + /* Scan what we just read, to see if we have reached the terminating \r\n. Be |