rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \
std-crypto.o store.o string.o tls.o tod.o transport.o tree.o verify.o \
+ environment.o \
$(OBJ_LOOKUPS) \
local_scan.o $(EXIM_PERL) $(OBJ_WITH_CONTENT_SCAN) \
$(OBJ_WITH_OLD_DEMIME) $(OBJ_EXPERIMENTAL)
enq.o: $(HDRS) enq.c
exim.o: $(HDRS) exim.c
expand.o: $(HDRS) expand.c
+environment.o: $(HDRS) environment.c
filter.o: $(HDRS) filter.c
filtertest.o: $(HDRS) filtertest.c
globals.o: $(HDRS) globals.c
+exim4 (4.84.2-2+deb8u3) jessie; urgency=medium
+
+ * 94_Fix-memory-leak-on-Gnu-TLS-close.patch from upstream exim-4_84_2+fixes
+ branch: Fix GnuTLS memory leak. (Thanks, Heiko Schlittermann!)
+ Closes: #845569
+
+ -- Andreas Metzler <ametzler@debian.org> Mon, 02 Jan 2017 19:18:05 +0100
+
+exim4 (4.84.2-2+deb8u2) jessie-security; urgency=high
+
+ * Non-maintainer upload by the Security Team.
+ * CVE-2016-9963: DKIM information leakage
+
+ -- Salvatore Bonaccorso <carnil@debian.org> Thu, 22 Dec 2016 12:17:01 +0100
+
+exim4 (4.84.2-2+deb8u1) jessie-security; urgency=high
+
+ * Non-maintainer upload by the Security Team.
+
+ [ Dominic Hargreaves ]
+ * eximstats: Remove . from @INC [CVE-2016-1238]
+
+ -- Salvatore Bonaccorso <carnil@debian.org> Mon, 25 Jul 2016 20:10:44 +0200
+
+exim4 (4.84.2-2) jessie; urgency=medium
+
+ * 90_Cutthrough-Fix-bug-with-dot-only-line.patch: JH/38 Fix cutthrough bug
+ with body lines having a single dot. The dot was incorrectly not doubled
+ on cutthrough transmission, hence seen as a body-termination at the
+ receiving system - resulting in truncated mails. Commonly the sender saw
+ a TCP-level error, and retransmitted the nessage via the normal
+ store-and-forward channel. This could result in duplicates received - but
+ deduplicating mailstores were liable to retain only the initial truncated
+ version.
+ * 91_Expansions-Fix-crash-in-crypteq-On-OpenBSD-a-bad-sec.patch: Fix crash
+ on "exim -be '${if crypteq{xxx}{\$aaa}{yes}{no}}'". Closes: #812585
+ * Improve on NEWS file. Closes: #818349
+ * Add 89_01_p_Delay-chdir-until-we-opened-the-main-config.patch. Backport
+ 3de973a29de6852d61ba9bf1845835d08ca5a5ab (Delay chdir(/) until we opened
+ the main config) to actually make $initial_cwd expansion work. Also unfuzz
+ 89_02_Store-the-initial-working-directory.diff.
+ (Thanks, Серж ИвановЪ for bugreport and pointer to missing patch) Closes:
+ #818897, #826646
+
+ -- Andreas Metzler <ametzler@debian.org> Sun, 12 Jun 2016 13:56:30 +0200
+
+exim4 (4.84.2-1) jessie-security; urgency=high
+
+ * New upstream security release.
+ + Fix CVE-2016-1531, a local privilege escalation issue when perl_startup
+ is used.
+ + New options keep_environment/add_environment which are empty by default,
+ i.e. any subprocesses start in a clean (empty) environment.
+ + -C requires an absolute path.
+ + Exim changes it's working directory to / right after startup.
+ * Add macros MAIN_KEEP_ENVIRONMENT and MAIN_ADD_ENVIRONMENT to set the new
+ options. Set "keep_environment =" by default to avoid a runtime warning.
+ Bump exim4-config Breaks to exim4-daemon-* (<< 4.84.2).
+ * 89_01_only_warn_on_nonempty_environment.diff,
+ 89_02_Store-the-initial-working-directory.diff: Upstream followups on the
+ CVE fix (Thanks, Heiko Schlittermann!):
+ + Runtime warning is only generated if (and only if) keep_environment
+ is unset and environment is nonempty.
+ + Store the initial working directory and make it available in the new
+ expansion variable $initial_cwd.
+ * Add NEWS entry to warn of potential breakage.
+
+ -- Andreas Metzler <ametzler@debian.org> Sat, 12 Mar 2016 08:17:40 +0100
+
+exim4 (4.84-8+deb8u2) jessie; urgency=medium
+
+ * 87_Fix-transport-results-pipe-for-multiple-recipients-c.patch: Pull and
+ unfuzz bd21a78 from upstream GIT, to fix a bug causing duplicate
+ deliveries especially on TLS connections. Closes: #805576
+
+ -- Andreas Metzler <ametzler@debian.org> Sat, 21 Nov 2015 11:24:46 +0100
+
+exim4 (4.84-8+deb8u1) jessie; urgency=medium
+
+ * Pull 85_Fix-crash-in-mime-acl-when-a-parameter-is-unterminat.patch
+ and 86_Avoid-crash-with-badly-terminated-non-recognised-mim.patch from
+ upstream GIT to fixup more MIME ACL related crashes. (Thanks, Lutz
+ Preßler) Closes: #803562
+
+ -- Andreas Metzler <ametzler@debian.org> Mon, 26 Oct 2015 17:42:16 +0100
+
exim4 (4.84-8) unstable; urgency=medium
* Pull 83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch and
Package: exim4-config
Architecture: all
-Breaks: exim4-daemon-light (<<4.82~rc1), exim4-daemon-heavy (<<4.82~rc1)
+Breaks: exim4-daemon-light (<<4.84.2), exim4-daemon-heavy (<<4.84.2)
Provides: exim4-config-2
Conflicts: exim, exim-tls, exim4-config, exim4-config-2, ${MTA-Conflicts}
Depends: ${shlibs:Depends}, ${misc:Depends}, adduser
# SMTP Banner. The example includes the Debian version in the SMTP dialog
# MAIN_SMTP_BANNER = "${primary_hostname} ESMTP Exim ${version_number} (Debian package MAIN_PACKAGE_VERSION) ${tod_full}"
# smtp_banner = $smtp_active_hostname ESMTP Exim $version_number $tod_full
+
+.ifdef MAIN_KEEP_ENVIRONMENT
+keep_environment = MAIN_KEEP_ENVIRONMENT
+.else
+# set option to empty value to avoid warning.
+keep_environment =
+.endif
+.ifdef MAIN_ADD_ENVIRONMENT
+add_environment = MAIN_ADD_ENVIRONMENT
+.endif
+exim4 (4.84.2-2) jessie; urgency=medium
+
+ As part of the fix for CVE-2016-1531 updated Exim versions clean
+ the complete execution environment by default, affecting Exim and
+ subprocesses such as routers calling other programs, and thus may break
+ existing installations. New configuration options (keep_environment,
+ add_environment) were introduced to adjust this behavior. Because of the
+ possible breakage Exim will show a runtime warning if keep_environment is
+ not set.
+
+ The Debian exim4 configuration does not rely on specific environment
+ variables and therefore sets 'keep_environment =' (i.e confirm empty
+ environment).
+
+ Users of custom Exim configurations will need to check whether their setup
+ continues to work with the abovementioned upstream change and modify the
+ Exim environment as needed otherwise. If the setup works fine with empty
+ environment it is still necessary to set the main configuration option
+ "keep_environment =" to quiet the runtime warning.
+
+ See <https://exim.org/static/doc/CVE-2016-1531.txt> for details.
+
+ -- Andreas Metzler <ametzler@debian.org> Mon, 28 Mar 2016 17:58:09 +0200
+
exim4 (4.68-1) unstable; urgency=low
In order to fix #420217, the handling of incoming messages to
--- /dev/null
+From bf485bf34df3fc2214765497a5552851c6a8977a Mon Sep 17 00:00:00 2001
+From: Jeremy Harris <jgh146exb@wizmail.org>
+Date: Tue, 30 Dec 2014 20:39:02 +0000
+Subject: [PATCH] Fix crash in mime acl when a parameter is unterminated
+
+Verified-by: Wolfgang Breyha <wbreyha@gmx.net>
+---
+ src/mime.c | 33 +++++++++++----------------------
+ test/confs/4000 | 1 +
+ test/log/4000 | 9 ++++++---
+ test/mail/4000.userx | 36 ++++++++++++++++++++++++++++++++++++
+ test/scripts/4000-scanning/4000 | 27 +++++++++++++++++++++++++++
+ test/stdout/4000 | 11 +++++++++++
+ 6 files changed, 92 insertions(+), 25 deletions(-)
+
+diff --git a/src/mime.c b/src/mime.c
+index a61e9f2..e5fe476 100644
+--- a/src/mime.c
++++ b/src/mime.c
+@@ -599,46 +599,35 @@ NEXT_PARAM_SEARCH:
+ /* found an interesting parameter? */
+ if (strncmpic(mp->name, p, mp->namelen) == 0)
+ {
+- uschar * q = p + mp->namelen;
+- int plen = 0;
+ int size = 0;
+ int ptr = 0;
+
+ /* yes, grab the value and copy to its corresponding expansion variable */
+- while(*q && *q != ';') /* ; terminates */
+- if (*q == '"')
++ p += mp->namelen;
++ while(*p && *p != ';') /* ; terminates */
++ if (*p == '"')
+ {
+- q++; /* skip leading " */
+- plen++; /* and account for the skip */
+- while(*q && *q != '"') /* " protects ; */
+- {
+- param_value = string_cat(param_value, &size, &ptr, q++, 1);
+- plen++;
+- }
+- if (*q)
+- {
+- q++; /* skip trailing " */
+- plen++;
+- }
++ p++; /* skip leading " */
++ while(*p && *p != '"') /* " protects ; */
++ param_value = string_cat(param_value, &size, &ptr, p++, 1);
++ if (*p) p++; /* skip trailing " */
+ }
+ else
+- {
+- param_value = string_cat(param_value, &size, &ptr, q++, 1);
+- plen++;
+- }
++ param_value = string_cat(param_value, &size, &ptr, p++, 1);
++ if (*p) p++; /* skip trailing ; */
+
+ if (param_value)
+ {
++ uschar * dummy;
+ param_value[ptr++] = '\0';
+
+ param_value = rfc2047_decode(param_value,
+- check_rfc2047_length, NULL, 32, NULL, &q);
++ check_rfc2047_length, NULL, 32, NULL, &dummy);
+ debug_printf("Found %s MIME parameter in %s header, "
+ "value is '%s'\n", mp->name, mime_header_list[i].name,
+ param_value);
+ }
+ *mp->value = param_value;
+- p += mp->namelen + plen + 1; /* name=, content, ; */
+ goto NEXT_PARAM_SEARCH;
+ }
+ }
--- /dev/null
+From e7c25d5b603a33e677efc4bccb6e5cac617e7ad5 Mon Sep 17 00:00:00 2001
+From: Jeremy Harris <jgh146exb@wizmail.org>
+Date: Thu, 1 Jan 2015 21:47:10 +0000
+Subject: [PATCH] Avoid crash with badly-terminated non-recognised mime
+ parameter
+
+---
+ src/mime.c | 18 +++++++++++-------
+ test/log/4000 | 3 +++
+ test/mail/4000.userx | 42 +++++++++++++++++++++++++++++++++++++++++
+ test/scripts/4000-scanning/4000 | 32 +++++++++++++++++++++++++++++++
+ test/stdout/4000 | 11 +++++++++++
+ 5 files changed, 99 insertions(+), 7 deletions(-)
+
+diff --git a/src/mime.c b/src/mime.c
+index e5fe476..948dd78 100644
+--- a/src/mime.c
++++ b/src/mime.c
+@@ -589,6 +589,7 @@ DECODE_HEADERS:
+ NEXT_PARAM_SEARCH:
+ while (*p)
+ {
++ /* debug_printf(" considering paramlist '%s'\n", p); */
+ mime_parameter * mp;
+ for (mp = mime_parameter_list;
+ mp < &mime_parameter_list[mime_parameter_list_size];
+@@ -623,7 +624,7 @@ NEXT_PARAM_SEARCH:
+
+ param_value = rfc2047_decode(param_value,
+ check_rfc2047_length, NULL, 32, NULL, &dummy);
+- debug_printf("Found %s MIME parameter in %s header, "
++ debug_printf(" Found %s MIME parameter in %s header, "
+ "value is '%s'\n", mp->name, mime_header_list[i].name,
+ param_value);
+ }
+@@ -631,14 +632,17 @@ NEXT_PARAM_SEARCH:
+ goto NEXT_PARAM_SEARCH;
+ }
+ }
+- /* There is something, but not one of our interesting parameters.
+- Advance to the next semicolon */
+- while(*p != ';')
++ /* There is something, but not one of our interesting parameters.
++ Advance to the next unquoted semicolon */
++ while(*p && *p != ';')
++ if (*p == '"')
+ {
+- if (*p == '"') while(*++p && *p != '"') ;
+- p++;
++ while(*++p && *p != '"') ;
++ if (*p) p++;
+ }
+- p++;
++ else
++ p++;
++ if (*p) p++;
+ }
+ }
+ }
--- /dev/null
+From bd21a787cdeef803334a6c7bf50d23b2a18cbd6f Mon Sep 17 00:00:00 2001
+From: Wolfgang Breyha <wbreyha@gmx.net>
+Date: Sun, 28 Sep 2014 13:40:45 +0100
+Subject: [PATCH] Fix transport-results pipe for multiple recipients combined
+ with certs.
+
+The previous parsing failed when a result item split over a buffer boundary;
+fix by prefixing sizes to items, and checking enough has been read as the
+initial parsing stage.
+---
+ doc/doc-txt/ChangeLog | 7 +++
+ src/deliver.c | 162 +++++++++++++++++++++++++++++++++++++-------------
+ src/macros.h | 4 ++
+ 3 files changed, 131 insertions(+), 42 deletions(-)
+
+# diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
+# index 46fed6f..76ecc20 100644
+# --- a/doc/doc-txt/ChangeLog
+# +++ b/doc/doc-txt/ChangeLog
+# @@ -37,6 +37,13 @@ TL/04 Bugzilla 1216: Add -M (related messages) option to exigrep.
+# TL/05 GitHub Issue 18: Adjust logic testing for true/false in redis lookups.
+# Merged patch from Sebastian Wiedenroth.
+#
+# +JH/05 Fix results-pipe from transport process. Several recipients, combined
+# + with certificate use, exposed issues where response data items split
+# + over buffer boundaries were not parsed properly. This eventually
+# + resulted in duplicates being sent. This issue only became common enough
+# + to notice due to the introduction of conection certificate information,
+# + the item size being so much larger. Found and fixed by Wolfgang Breyha.
+# +
+# Exim version 4.84
+# -----------------
+# TL/01 Bugzilla 1506: Re-add a 'return NULL' to silence complaints from static
+--- a/src/deliver.c
++++ b/src/deliver.c
+@@ -2823,6 +2823,8 @@ uschar *ptr = endptr;
+ uschar *msg = p->msg;
+ BOOL done = p->done;
+ BOOL unfinished = TRUE;
++/* minimum size to read is header size including id, subid and length */
++int required = PIPE_HEADER_SIZE;
+
+ /* Loop through all items, reading from the pipe when necessary. The pipe
+ is set up to be non-blocking, but there are two different Unix mechanisms in
+@@ -2845,12 +2847,15 @@ while (!done)
+ {
+ retry_item *r, **rp;
+ int remaining = endptr - ptr;
++ uschar header[PIPE_HEADER_SIZE + 1];
++ uschar id, subid;
++ uschar *endc;
+
+ /* Read (first time) or top up the chars in the buffer if necessary.
+ There will be only one read if we get all the available data (i.e. don't
+ fill the buffer completely). */
+
+- if (remaining < 2500 && unfinished)
++ if (remaining < required && unfinished)
+ {
+ int len;
+ int available = big_buffer_size - remaining;
+@@ -2883,17 +2888,64 @@ while (!done)
+ won't read any more, as "unfinished" will get set FALSE. */
+
+ endptr += len;
++ remaining += len;
+ unfinished = len == available;
+ }
+
+ /* If we are at the end of the available data, exit the loop. */
+-
+ if (ptr >= endptr) break;
+
++ /* copy and read header */
++ memcpy(header, ptr, PIPE_HEADER_SIZE);
++ header[PIPE_HEADER_SIZE] = '\0';
++ id = header[0];
++ subid = header[1];
++ required = Ustrtol(header + 2, &endc, 10) + PIPE_HEADER_SIZE; /* header + data */
++ if (*endc)
++ {
++ msg = string_sprintf("failed to read pipe from transport process "
++ "%d for transport %s: error reading size from header", pid, addr->transport->driver_name);
++ done = TRUE;
++ break;
++ }
++
++ DEBUG(D_deliver)
++ debug_printf("header read id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n",
++ id, subid, header+2, required, remaining, unfinished);
++
++ /* is there room for the dataset we want to read ? */
++ if (required > big_buffer_size - PIPE_HEADER_SIZE)
++ {
++ msg = string_sprintf("failed to read pipe from transport process "
++ "%d for transport %s: big_buffer too small! required size=%d buffer size=%d", pid, addr->transport->driver_name,
++ required, big_buffer_size - PIPE_HEADER_SIZE);
++ done = TRUE;
++ break;
++ }
++
++ /* we wrote all datasets with atomic write() calls
++ remaining < required only happens if big_buffer was too small
++ to get all available data from pipe. unfinished has to be true
++ as well. */
++ if (remaining < required)
++ if (unfinished)
++ continue;
++ else
++ {
++ msg = string_sprintf("failed to read pipe from transport process "
++ "%d for transport %s: required size=%d > remaining size=%d and unfinished=false",
++ pid, addr->transport->driver_name, required, remaining);
++ done = TRUE;
++ break;
++ }
++
++ /* step behind the header */
++ ptr += PIPE_HEADER_SIZE;
++
+ /* Handle each possible type of item, assuming the complete item is
+ available in store. */
+
+- switch (*ptr++)
++ switch (id)
+ {
+ /* Host items exist only if any hosts were marked unusable. Match
+ up by checking the IP address. */
+@@ -2990,7 +3042,7 @@ while (!done)
+ #ifdef SUPPORT_TLS
+ case 'X':
+ if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */
+- switch (*ptr++)
++ switch (subid)
+ {
+ case '1':
+ addr->cipher = NULL;
+@@ -3028,7 +3080,7 @@ while (!done)
+ #endif /*SUPPORT_TLS*/
+
+ case 'C': /* client authenticator information */
+- switch (*ptr++)
++ switch (subid)
+ {
+ case '1':
+ addr->authenticator = (*ptr)? string_copy(ptr) : NULL;
+@@ -3051,7 +3103,7 @@ while (!done)
+
+ #ifdef EXPERIMENTAL_DSN
+ case 'D':
+- if (addr == NULL) break;
++ if (addr == NULL) goto ADDR_MISMATCH;
+ memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
+ ptr += sizeof(addr->dsn_aware);
+ DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware);
+@@ -3119,7 +3171,7 @@ while (!done)
+ continue_hostname = NULL;
+ }
+ done = TRUE;
+- DEBUG(D_deliver) debug_printf("Z%c item read\n", *ptr);
++ DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr);
+ break;
+
+ /* Anything else is a disaster. */
+@@ -3572,9 +3624,40 @@ while (parcount > max)
+
+
+ static void
+-rmt_dlv_checked_write(int fd, void * buf, int size)
++rmt_dlv_checked_write(int fd, char id, char subid, void * buf, int size)
+ {
+-int ret = write(fd, buf, size);
++uschar writebuffer[PIPE_HEADER_SIZE + BIG_BUFFER_SIZE];
++int header_length;
++
++/* we assume that size can't get larger then BIG_BUFFER_SIZE which currently is set to 16k */
++/* complain to log if someone tries with buffer sizes we can't handle*/
++
++if (size > 99999)
++{
++ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
++ "Failed writing transport result to pipe: can't handle buffers > 99999 bytes. truncating!\n");
++ size = 99999;
++}
++
++/* to keep the write() atomic we build header in writebuffer and copy buf behind */
++/* two write() calls would increase the complexity of reading from pipe */
++
++/* convert size to human readable string prepended by id and subid */
++header_length = snprintf(writebuffer, PIPE_HEADER_SIZE+1, "%c%c%05d", id, subid, size);
++if (header_length != PIPE_HEADER_SIZE)
++{
++ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "header snprintf failed\n");
++ writebuffer[0] = '\0';
++}
++
++DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n",
++ id, subid, size, writebuffer);
++
++if (buf && size > 0)
++ memcpy(writebuffer + PIPE_HEADER_SIZE, buf, size);
++
++size += PIPE_HEADER_SIZE;
++int ret = write(fd, writebuffer, size);
+ if(ret != size)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed writing transport result to pipe: %s\n",
+ ret == -1 ? strerror(errno) : "short write");
+@@ -4100,8 +4183,8 @@ for (delivery_count = 0; addr_remote !=
+ for (h = addr->host_list; h != NULL; h = h->next)
+ {
+ if (h->address == NULL || h->status < hstatus_unusable) continue;
+- sprintf(CS big_buffer, "H%c%c%s", h->status, h->why, h->address);
+- rmt_dlv_checked_write(fd, big_buffer, Ustrlen(big_buffer+3) + 4);
++ sprintf(CS big_buffer, "%c%c%s", h->status, h->why, h->address);
++ rmt_dlv_checked_write(fd, 'H', '0', big_buffer, Ustrlen(big_buffer+2) + 3);
+ }
+
+ /* The number of bytes written. This is the same for each address. Even
+@@ -4109,9 +4192,8 @@ for (delivery_count = 0; addr_remote !=
+ size of each one is the same, and it's that value we have got because
+ transport_count gets reset before calling transport_write_message(). */
+
+- big_buffer[0] = 'S';
+- memcpy(big_buffer+1, &transport_count, sizeof(transport_count));
+- rmt_dlv_checked_write(fd, big_buffer, sizeof(transport_count) + 1);
++ memcpy(big_buffer, &transport_count, sizeof(transport_count));
++ rmt_dlv_checked_write(fd, 'S', '0', big_buffer, sizeof(transport_count));
+
+ /* Information about what happened to each address. Four item types are
+ used: an optional 'X' item first, for TLS information, then an optional "C"
+@@ -4131,7 +4213,7 @@ for (delivery_count = 0; addr_remote !=
+ if (addr->cipher)
+ {
+ ptr = big_buffer;
+- sprintf(CS ptr, "X1%.128s", addr->cipher);
++ sprintf(CS ptr, "%.128s", addr->cipher);
+ while(*ptr++);
+ if (!addr->peerdn)
+ *ptr++ = 0;
+@@ -4141,35 +4223,33 @@ for (delivery_count = 0; addr_remote !=
+ while(*ptr++);
+ }
+
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer);
+ }
+ if (addr->peercert)
+ {
+ ptr = big_buffer;
+- *ptr++ = 'X'; *ptr++ = '2';
+ if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert))
+ while(*ptr++);
+ else
+ *ptr++ = 0;
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'X', '2', big_buffer, ptr - big_buffer);
+ }
+ if (addr->ourcert)
+ {
+ ptr = big_buffer;
+- *ptr++ = 'X'; *ptr++ = '3';
+ if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert))
+ while(*ptr++);
+ else
+ *ptr++ = 0;
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'X', '3', big_buffer, ptr - big_buffer);
+ }
+ #ifndef DISABLE_OCSP
+ if (addr->ocsp > OCSP_NOT_REQ)
+ {
+ ptr = big_buffer;
+- sprintf(CS ptr, "X4%c", addr->ocsp + '0');
++ sprintf(CS ptr, "%c", addr->ocsp + '0');
+ while(*ptr++);
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer);
+ }
+ # endif
+ #endif /*SUPPORT_TLS*/
+@@ -4177,34 +4257,33 @@ for (delivery_count = 0; addr_remote !=
+ if (client_authenticator)
+ {
+ ptr = big_buffer;
+- sprintf(CS big_buffer, "C1%.64s", client_authenticator);
++ sprintf(CS big_buffer, "%.64s", client_authenticator);
+ while(*ptr++);
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer);
+ }
+ if (client_authenticated_id)
+ {
+ ptr = big_buffer;
+- sprintf(CS big_buffer, "C2%.64s", client_authenticated_id);
++ sprintf(CS big_buffer, "%.64s", client_authenticated_id);
+ while(*ptr++);
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer);
+ }
+ if (client_authenticated_sender)
+ {
+ ptr = big_buffer;
+- sprintf(CS big_buffer, "C3%.64s", client_authenticated_sender);
++ sprintf(CS big_buffer, "%.64s", client_authenticated_sender);
+ while(*ptr++);
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer);
+ }
+
+ #ifndef DISABLE_PRDR
+ if (addr->flags & af_prdr_used)
+- rmt_dlv_checked_write(fd, "P", 1);
++ rmt_dlv_checked_write(fd, 'P', '0', NULL, 0);
+ #endif
+
+ #ifdef EXPERIMENTAL_DSN
+- big_buffer[0] = 'D';
+- memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware));
+- rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1);
++ memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
++ rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
+ DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware);
+ #endif
+
+@@ -4213,7 +4292,7 @@ for (delivery_count = 0; addr_remote !=
+ for (r = addr->retries; r != NULL; r = r->next)
+ {
+ uschar *ptr;
+- sprintf(CS big_buffer, "R%c%.500s", r->flags, r->key);
++ sprintf(CS big_buffer, "%c%.500s", r->flags, r->key);
+ ptr = big_buffer + Ustrlen(big_buffer+2) + 3;
+ memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno));
+ ptr += sizeof(r->basic_errno);
+@@ -4224,13 +4303,13 @@ for (delivery_count = 0; addr_remote !=
+ sprintf(CS ptr, "%.512s", r->message);
+ while(*ptr++);
+ }
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
+ }
+
+ /* The rest of the information goes in an 'A' item. */
+
+- ptr = big_buffer + 3;
+- sprintf(CS big_buffer, "A%c%c", addr->transport_return,
++ ptr = big_buffer + 2;
++ sprintf(CS big_buffer, "%c%c", addr->transport_return,
+ addr->special_action);
+ memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno));
+ ptr += sizeof(addr->basic_errno);
+@@ -4265,7 +4344,7 @@ for (delivery_count = 0; addr_remote !=
+ : addr->host_used->dnssec==DS_NO ? '1' : '0';
+
+ }
+- rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
++ rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer);
+ }
+
+ /* Add termination flag, close the pipe, and that's it. The character
+@@ -4273,9 +4352,8 @@ for (delivery_count = 0; addr_remote !=
+ A change from non-NULL to NULL indicates a problem with a continuing
+ connection. */
+
+- big_buffer[0] = 'Z';
+- big_buffer[1] = (continue_transport == NULL)? '0' : '1';
+- rmt_dlv_checked_write(fd, big_buffer, 2);
++ big_buffer[0] = (continue_transport == NULL)? '0' : '1';
++ rmt_dlv_checked_write(fd, 'Z', '0', big_buffer, 1);
+ (void)close(fd);
+ exit(EXIT_SUCCESS);
+ }
+--- a/src/macros.h
++++ b/src/macros.h
+@@ -156,6 +156,10 @@ as long as the maximum path length. */
+ #define BIG_BUFFER_SIZE 16384
+ #endif
+
++/* header size of pipe content
++ currently: char id, char subid, char[5] length */
++#define PIPE_HEADER_SIZE 7
++
+ /* This limits the length of data returned by local_scan(). Because it is
+ written on the spool, it gets read into big_buffer. */
+
--- /dev/null
+Description: Don't issue env warning if env is empty
+ keep_environment needs to be mentioned in the runtime config.
+ Setting add_environment isn't enough to suppress the warning.
+
+ (cherry picked from commit 8e58ed807c77febfde61d3cf47928302f93cc99c)
+Origin: upstream
+
+--- exim4-4.84.2.orig/src/readconf.c
++++ exim4-4.84.2/src/readconf.c
+@@ -3418,10 +3418,10 @@ if (gnutls_require_kx || gnutls_require_
+ " are obsolete\n");
+ #endif /*SUPPORT_TLS*/
+
+-if ((!add_environment || *add_environment == '\0') && !keep_environment)
++if (!keep_environment && environ && *environ)
+ log_write(0, LOG_MAIN,
+- "WARNING: purging the environment.\n"
+- " Suggested action: use keep_environment and add_environment.\n");
++ "Warning: purging the environment.\n"
++ " Suggested action: use keep_environment.");
+ }
+
+
+--- exim4-4.84.2.orig/doc/spec.txt
++++ exim4-4.84.2/doc/spec.txt
+@@ -13516,8 +13516,10 @@ having FOO_HOME in your keep_environment
+ You may work around this using a regular expression that does not match the
+ macro name: ^[F]OO_HOME$.
+
+-Current versions of Exim issue a warning during startupif you do not mention
+-keep_environment or add_environment in your runtime configuration file.
++Current versions of Exim issue a warning during startup if you do not mention
++keep_environment in your runtime configuration file and if there is
++anything in your environment. Future versions may not issue that warning
++anymore.
+
+ +--------------+---------+----------+-----------+
+ |keep_malformed|Use: main|Type: time|Default: 4d|
--- /dev/null
+Backport of 3de973a29de6852d61ba9bf1845835d08ca5a5ab
+
+From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de>
+Date: Wed, 2 Mar 2016 22:07:45 +0100
+Subject: [PATCH] Delay chdir(/) until we opened the main config
+
+--- a/doc/spec.txt
++++ b/doc/spec.txt
+@@ -3361,8 +3361,6 @@ brief message about itself and exits.
+ first file that exists is used. Failure to open an existing file stops Exim
+ from proceeding any further along the list, and an error is generated.
+
+- The file names need to be absolute names.
+-
+ When this option is used by a caller other than root, and the list is
+ different from the compiled-in list, Exim gives up its root privilege
+ immediately, and runs with the real and effective uid and gid set to those
+--- a/src/exim.c
++++ b/src/exim.c
+@@ -3683,17 +3683,16 @@ init_lookup_list();
+
+ /* Read the main runtime configuration data; this gives up if there
+ is a failure. It leaves the configuration file open so that the subsequent
+-configuration data for delivery can be read if needed. */
++configuration data for delivery can be read if needed.
+
+-/* To be safe: change the working directory to /. */
+-if (Uchdir("/") < 0)
+- {
+- perror("exim: chdir `/': ");
+- exit(EXIT_FAILURE);
+- }
++NOTE: immediatly after opening the configuration file we change the working
++directory to "/"! Later we change to $spool_directory. We do it there, because
++during readconf_main() some expansion takes place already. */
+
+ readconf_main();
+
++/* Now in directory "/" */
++
+ if (cleanup_environment() == FALSE)
+ log_write(0, LOG_PANIC_DIE, "Can't cleanup environment");
+
+--- a/src/readconf.c
++++ b/src/readconf.c
+@@ -2969,14 +2969,6 @@ while((filename = string_nextinlist(&lis
+ != NULL)
+ {
+
+- /* To avoid confusion: Exim changes to / at the very beginning and
+- * and to $spool_directory later. */
+- if (filename[0] != '/')
+- {
+- fprintf(stderr, "-C %s: only absolute names are allowed\n", filename);
+- exit(EXIT_FAILURE);
+- }
+-
+ /* Cut out all the fancy processing unless specifically wanted */
+
+ #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
+@@ -3030,6 +3022,15 @@ while((filename = string_nextinlist(&lis
+ if (config_file != NULL || errno != ENOENT) break;
+ }
+
++/* Now, once we found and opened our configuration file, we change the directory
++to a safe place. Later we change to $spool_directory. */
++
++if (Uchdir("/") < 0)
++ {
++ perror("exim: chdir `/': ");
++ exit(EXIT_FAILURE);
++ }
++
+ /* On success, save the name for verification; config_filename is used when
+ logging configuration errors (it changes for .included files) whereas
+ config_main_filename is the name shown by -bP. Failure to open a configuration
--- /dev/null
+Description: Store the initial working directory, expand $initial_cwd.
+ Bug 1805 https://bugs.exim.org/show_bug.cgi?id=1805
+Origin: upstream
+
+--- a/src/globals.c
++++ b/src/globals.c
+@@ -759,6 +759,7 @@ BOOL ignore_fromline_local = FALSE;
+ uschar *ignore_fromline_hosts = NULL;
+ BOOL inetd_wait_mode = FALSE;
+ int inetd_wait_timeout = -1;
++uschar *initial_cwd = NULL;
+ uschar *interface_address = NULL;
+ int interface_port = -1;
+ BOOL is_inetd = FALSE;
+--- a/src/exim.c
++++ b/src/exim.c
+@@ -3689,6 +3689,13 @@ NOTE: immediatly after opening the confi
+ directory to "/"! Later we change to $spool_directory. We do it there, because
+ during readconf_main() some expansion takes place already. */
+
++/* Store the initial cwd before we change directories */
++if ((initial_cwd = getcwd(NULL, 0)) == NULL)
++ {
++ perror("exim: can't get the current working directory");
++ exit(EXIT_FAILURE);
++ }
++
+ readconf_main();
+
+ /* Now in directory "/" */
+@@ -3967,9 +3974,10 @@ if (((debug_selector & D_any) != 0 || (l
+ {
+ int i;
+ uschar *p = big_buffer;
+- char * dummy;
+ Ustrcpy(p, "cwd= (failed)");
+- dummy = /* quieten compiler */ getcwd(CS p+4, big_buffer_size - 4);
++
++ Ustrncpy(p + 4, initial_cwd, big_buffer_size-5);
++
+ while (*p) p++;
+ (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
+ while (*p) p++;
+--- a/src/globals.h
++++ b/src/globals.h
+@@ -486,6 +486,7 @@ extern BOOL ignore_fromline_local; /
+ extern uschar *ignore_fromline_hosts; /* Hosts permitted to send "From " */
+ extern BOOL inetd_wait_mode; /* Whether running in inetd wait mode */
+ extern int inetd_wait_timeout; /* Timeout for inetd wait mode */
++extern uschar *initial_cwd; /* The directory we where in at startup */
+ extern BOOL is_inetd; /* True for inetd calls */
+ extern uschar *iterate_item; /* Item from iterate list */
+
+--- a/src/expand.c
++++ b/src/expand.c
+@@ -501,6 +501,7 @@ static var_entry var_table[] = {
+ { "host_data", vtype_stringptr, &host_data },
+ { "host_lookup_deferred",vtype_int, &host_lookup_deferred },
+ { "host_lookup_failed", vtype_int, &host_lookup_failed },
++ { "initial_cwd", vtype_stringptr, &initial_cwd },
+ { "inode", vtype_ino, &deliver_inode },
+ { "interface_address", vtype_stringptr, &interface_address },
+ { "interface_port", vtype_int, &interface_port },
+--- a/doc/spec.txt
++++ b/doc/spec.txt
+@@ -10426,6 +10426,13 @@ $host_lookup_failed
+
+ See $host_lookup_deferred.
+
++$initial_cwd
++
++ This variable contains the full path name of the initial working
++ directory of the current Exim process. This may differ from the current
++ working directory, as Exim changes this to "/" during early startup, and
++ to $spool_directory later.
++
+ $inode
+
+ The only time this variable is set is while expanding the directory_file
--- /dev/null
+From 2d51a06458d4fb771dca34966cf2d19c6820ce61 Mon Sep 17 00:00:00 2001
+From: Jeremy Harris <jgh146exb@wizmail.org>
+Date: Thu, 21 Jan 2016 15:37:08 +0000
+Subject: [PATCH] Cutthrough: Fix bug with dot-only line
+ JH/38 Fix cutthrough bug with body lines having a single dot. The dot was
+ incorrectly not doubled on cutthrough transmission, hence seen as a
+ body-termination at the receiving system - resulting in truncated mails.
+ Commonly the sender saw a TCP-level error, and retransmitted the nessage
+ via the normal store-and-forward channel. This could result in duplicates
+ received - but deduplicating mailstores were liable to retain only the
+ initial truncated version.
+ (cherry picked from commit 1bc460a64a0de0766d21f4f8660c6597bc410cbc)
+
+--- exim4-4.84.2.orig/src/receive.c
++++ exim4-4.84.2/src/receive.c
+@@ -838,7 +838,15 @@ while ((ch = (receive_getc)()) != EOF)
+ ch_state = 4;
+ continue;
+ }
+- ch_state = 1; /* The dot itself is removed */
++ /* The dot was removed at state 3. For a doubled dot, here, reinstate
++ it to cutthrough. The current ch, dot or not, is passed both to cutthrough
++ and to file below. */
++ if (ch == '.')
++ {
++ uschar c= ch;
++ (void) cutthrough_puts(&c, 1);
++ }
++ ch_state = 1;
+ break;
+
+ case 4: /* After [CR] LF . CR */
--- /dev/null
+From 9dc2b215e83a63efa242f6acd3ab7af8b608e5a1 Mon Sep 17 00:00:00 2001
+From: Jeremy Harris <jgh146exb@wizmail.org>
+Date: Mon, 11 Jan 2016 15:50:22 +0000
+Subject: [PATCH] Expansions: Fix crash in crypteq: On OpenBSD a bad second-arg
+ results in an error-return from crypt(). Errorcheck that return.
+
+---
+ src/expand.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/src/expand.c b/src/expand.c
+index f144a75..2966c22 100644
+--- a/src/expand.c
++++ b/src/expand.c
+@@ -2791,7 +2791,7 @@ switch(cond_type)
+ #define XSTR(s) STR(s)
+ DEBUG(D_auth) debug_printf("crypteq: using %s()\n"
+ " subject=%s\n crypted=%s\n",
+- (which == 0)? XSTR(DEFAULT_CRYPT) : (which == 1)? "crypt" : "crypt16",
++ which == 0 ? XSTR(DEFAULT_CRYPT) : which == 1 ? "crypt" : "crypt16",
+ coded, sub[1]);
+ #undef STR
+ #undef XSTR
+@@ -2800,8 +2800,16 @@ switch(cond_type)
+ salt), force failure. Otherwise we get false positives: with an empty
+ string the yield of crypt() is an empty string! */
+
+- tempcond = (Ustrlen(sub[1]) < 2)? FALSE :
+- (Ustrcmp(coded, sub[1]) == 0);
++ if (coded)
++ tempcond = Ustrlen(sub[1]) < 2 ? FALSE : Ustrcmp(coded, sub[1]) == 0;
++ else if (errno == EINVAL)
++ tempcond = FALSE;
++ else
++ {
++ expand_string_message = string_sprintf("crypt error: %s\n",
++ US strerror(errno));
++ return NULL;
++ }
+ }
+ break;
+ #endif /* SUPPORT_CRYPTEQ */
+--
+2.8.0.rc3
+
--- /dev/null
+--- a/src/eximstats.src 2016-07-24 22:29:53.000000000 +0100
++++ b/src/eximstats.src 2016-07-24 22:33:49.763365395 +0100
+@@ -550,6 +550,8 @@
+
+ =cut
+
++BEGIN { pop @INC if $INC[-1] eq '.' }
++
+ use integer;
+ use strict;
+ use IO::File;
--- /dev/null
+From be2b8e517f4946d2ad0cb0100e7b078cb4d9b65f Mon Sep 17 00:00:00 2001
+From: Jeremy Harris <jgh@wizmail.org>
+Date: Fri, 16 Dec 2016 20:36:39 +0000
+Subject: [PATCH 1/2] Fix DKIM information leakage
+
+---
+
+--- a/src/dkim.c
++++ b/src/dkim.c
+@@ -521,6 +521,8 @@ uschar *dkim_exim_sign(int dkim_fd,
+ (char *)dkim_private_key_expanded
+ );
+
++ dkim_private_key_expanded[0] = '\0';
++
+ pdkim_set_debug_stream(ctx,debug_file);
+
+ pdkim_set_optional(ctx,
+--- a/src/transports/smtp.c
++++ b/src/transports/smtp.c
+@@ -282,6 +282,7 @@ static uschar *rf_names[] = { "NEVER", "
+ static uschar *smtp_command; /* Points to last cmd for error messages */
+ static uschar *mail_command; /* Points to MAIL cmd for error messages */
+ static BOOL update_waiting; /* TRUE to update the "wait" database */
++static uschar *data_command = US""; /* Points to DATA cmd for error messages */
+
+
+ /*************************************************
+@@ -1951,6 +1952,7 @@ if (ok || (smtp_use_pipelining && !mua_w
+ case -1: goto END_OFF; /* Timeout on RCPT */
+ default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */
+ }
++ data_command = string_copy(big_buffer); /* Save for later error message */
+ }
+
+ /* Save the first address of the next batch. */
+@@ -2136,7 +2138,7 @@ if (!ok) ok = TRUE; else
+ #else
+ "LMTP error after %s: %s",
+ #endif
+- big_buffer, string_printing(buffer));
++ data_command, string_printing(buffer));
+ setflag(addr, af_pass_message); /* Allow message to go to user */
+ if (buffer[0] == '5')
+ addr->transport_return = FAIL;
--- /dev/null
+From 867e8fe25dbfb1e31493488ad695bde55b890397 Mon Sep 17 00:00:00 2001
+From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de>
+Date: Wed, 23 Nov 2016 12:02:26 +0100
+Subject: [PATCH] Fix memory leak on (Gnu)TLS close.
+
+This leak doesn't show up under normal operation, as the process
+normally dies right after closing the session.
+
+But during callout repetitive TLS sessions are opened and closed from
+the same process (the process receiving the message). Depending on
+the amount of RAM and the number of callouts the same process does,
+this may be a problem. (On an amd64 machine with 4GB RAM, at about 1000
+recipients the memory is exhausted.)
+
+(cherry picked from commit ed62aae3051c9a713d35c8ae516fbd193d1401ba)
+---
+ src/tls-gnu.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/tls-gnu.c b/src/tls-gnu.c
+index 61ed0e81..670f8cbc 100644
+--- a/src/tls-gnu.c
++++ b/src/tls-gnu.c
+@@ -1729,6 +1729,7 @@ if (rc != GNUTLS_E_SUCCESS)
+
+ if (!sigalrm_seen)
+ {
++ gnutls_certificate_free_credentials(state->x509_cred);
+ (void)fclose(smtp_out);
+ (void)fclose(smtp_in);
+ }
+@@ -2014,6 +2015,8 @@ if (shutdown)
+ }
+
+ gnutls_deinit(state->session);
++gnutls_certificate_free_credentials(state->x509_cred);
++
+
+ state->tlsp->active = -1;
+ memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
+@@ -2074,6 +2077,8 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm)
+ receive_smtp_buffered = smtp_buffered;
+
+ gnutls_deinit(state->session);
++ gnutls_certificate_free_credentials(state->x509_cred);
++
+ state->session = NULL;
+ state->tlsp->active = -1;
+ state->tlsp->bits = 0;
+--
+2.11.0
+
82_quoted-or-r-2047-encoded.diff
83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch
84_Fix-truncation-of-items-in-headers_remove-lists-this.patch
+85_Fix-crash-in-mime-acl-when-a-parameter-is-unterminat.patch
+86_Avoid-crash-with-badly-terminated-non-recognised-mim.patch
+87_Fix-transport-results-pipe-for-multiple-recipients-c.patch
+89_01_only_warn_on_nonempty_environment.diff
+89_01_p_Delay-chdir-until-we-opened-the-main-config.patch
+89_02_Store-the-initial-working-directory.diff
+90_Cutthrough-Fix-bug-with-dot-only-line.patch
+91_Expansions-Fix-crash-in-crypteq-On-OpenBSD-a-bad-sec.patch
+92_CVE-2016-1238.diff
+93_CVE-2016-9963-Fix-DKIM-information-leakage.patch
+94_Fix-memory-leak-on-Gnu-TLS-close.patch
Change log file for Exim from version 4.21
-------------------------------------------
+Exim version 4.84.2
+-------------------
+Portability release
+
+
+Exim version 4.84.1
+-------------------
+HS/04 Add support for keep_environment and add_environment options.
+ Fix for CVE-2016-1531
+
Exim version 4.84
-----------------
settings can be obtained by using \fBrouters\fP, \fBtransports\fP, or
\fBauthenticators\fP.
.sp
+If \fBenvironment\fP is given as an argument, the set of environment
+variables is output, line by line. Using the \fB\-n\fP flag supresses the value of the
+variables.
+.sp
If invoked by an admin user, then \fBmacro\fP, \fBmacro_list\fP and \fBmacros\fP
are available, similarly to the drivers. Because macros are sometimes used
for storing passwords, this option is restricted.
file that exists is used. Failure to open an existing file stops Exim from
proceeding any further along the list, and an error is generated.
.sp
+The file names need to be absolute names.
+.sp
When this option is used by a caller other than root, and the list is different
from the compiled\-in list, Exim gives up its root privilege immediately, and
runs with the real and effective uid and gid set to those of the caller.
Copyright (c) 2014 University of Cambridge
-+-----------------------------------------------------------------------------+
-+-------------------------------------+--------------------------------+------+
-|Revision 4.84 |11 Aug 2014 |PH |
-+-------------------------------------+--------------------------------+------+
+Revision 4.84.2 02 Mar 2016 PH
+
-------------------------------------------------------------------------------
TABLE OF CONTENTS
This document describes the user interfaces to Exim's in-built mail filtering
facilities, and is copyright (c) University of Cambridge 2014. It corresponds
-to Exim version 4.84.
+to Exim version 4.84.2.
1.1 Introduction
Copyright (c) 2014 University of Cambridge
-+-----------------------------------------------------------------------------+
-+-------------------------------------+--------------------------------+------+
-|Revision 4.84 |11 Aug 2014 |EM |
-+-------------------------------------+--------------------------------+------+
+Revision 4.84.2 02 Mar 2016 EM
+
-------------------------------------------------------------------------------
TABLE OF CONTENTS
1.1 Exim documentation
----------------------
-This edition of the Exim specification applies to version 4.84 of Exim.
+This edition of the Exim specification applies to version 4.84.2 of Exim.
Substantive changes from the 4.83 edition are marked in some renditions of the
document; this paragraph is so marked if the rendition is capable of showing a
change indicator.
Exim is distributed as a gzipped or bzipped tar file which, when unpacked,
creates a directory with the name of the current release (for example,
-exim-4.84) into which the following files are placed:
+exim-4.84.2) into which the following files are placed:
ACKNOWLEDGMENTS contains some acknowledgments
CHANGES contains a reference to where changes are documented
For the utility programs, old versions are renamed by adding the suffix .O to
their names. The Exim binary itself, however, is handled differently. It is
installed under a name that includes the version number and the compile number,
-for example exim-4.84-1. The script then arranges for a symbolic link called
+for example exim-4.84.2-1. The script then arranges for a symbolic link called
exim to point to the binary. If you are updating a previous version of Exim,
the script takes care to ensure that the name exim is never absent from the
directory (as seen by other processes).
authenticator_list, and a complete list of all drivers with their option
settings can be obtained by using routers, transports, or authenticators.
+ If environment is given as an argument, the set of environment variables is
+ output, line by line. Using the -n flag supresses the value of the
+ variables.
+
If invoked by an admin user, then macro, macro_list and macros are
available, similarly to the drivers. Because macros are sometimes used for
storing passwords, this option is restricted. The output format is one item
first file that exists is used. Failure to open an existing file stops Exim
from proceeding any further along the list, and an error is generated.
+ The file names need to be absolute names.
+
When this option is used by a caller other than root, and the list is
different from the compiled-in list, Exim gives up its root privilege
immediately, and runs with the real and effective uid and gid set to those
This option defines the ACL that is run when an SMTP VRFY command is received.
See chapter 42 for further details.
++---------------+---------+-----------------+--------------+
+|add_environment|Use: main|Type: string list|Default: empty|
++---------------+---------+-----------------+--------------+
+
+This option allows to set individual environment variables that the currently
+linked libraries and programs in child processes use. The default list is
+empty,
+
+------------+---------+------------------+--------------+
|admin_groups|Use: main|Type: string list*|Default: unset|
+------------+---------+------------------+--------------+
See ignore_fromline_hosts above.
++----------------+---------+-----------------+--------------+
+|keep_environment|Use: main|Type: string list|Default: unset|
++----------------+---------+-----------------+--------------+
+
+This option contains a string list of environment variables to keep. You have
+to trust these variables or you have to be sure that these variables do not
+impose any security risk. Keep in mind that during the startup phase Exim is
+running with an effective UID 0 in most installations. As the default value is
+an empty list, the default environment for using libraries, running embedded
+Perl code, or running external binaries is empty, and does not not even contain
+PATH or HOME.
+
+Actually the list is interpreted as a list of patterns (10.1), except that it
+is not expanded first.
+
+WARNING: Macro substitution is still done first, so having a macro FOO and
+having FOO_HOME in your keep_environment option may have unexpected results.
+You may work around this using a regular expression that does not match the
+macro name: ^[F]OO_HOME$.
+
+Current versions of Exim issue a warning during startupif you do not mention
+keep_environment or add_environment in your runtime configuration file.
+
+--------------+---------+----------+-----------+
|keep_malformed|Use: main|Type: time|Default: 4d|
+--------------+---------+----------+-----------+
sender_unqualified_hosts, or if the message was submitted locally (not using
TCP/IP), and the -bnq option was not set.
++---------------+---------+-----------------+--------------+
+|set_environment|Use: main|Type: string list|Default: empty|
++---------------+---------+-----------------+--------------+
+
+This option allows to set individual environment variables that the currently
+linked libraries and programs in child processes use. The default list is
+empty,
+
+---------------------+---------+-------------+-------------+
|smtp_accept_keepalive|Use: main|Type: boolean|Default: true|
+---------------------+---------+-------------+-------------+
ln -s ../src/drtables.c drtables.c
ln -s ../src/dummies.c dummies.c
ln -s ../src/enq.c enq.c
+ln -s ../src/environment.c environment.c
ln -s ../src/exim.c exim.c
ln -s ../src/exim_dbmbuild.c exim_dbmbuild.c
ln -s ../src/exim_dbutil.c exim_dbutil.c
--- /dev/null
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Copyright (c) Heiko Schlittermann 2016
+ * hs@schlittermann.de
+ * See the file NOTICE for conditions of use and distribution.
+ */
+
+#include "exim.h"
+
+extern char **environ;
+
+/* The cleanup_environment() function is used during the startup phase
+of the Exim process, right after reading the configurations main
+part, before any expansions take place. It retains the environment
+variables we trust (via the keep_environment option) and allows to
+set additional variables (via add_environment).
+
+Returns: TRUE if successful
+ FALSE otherwise
+*/
+
+BOOL
+cleanup_environment()
+{
+if (!keep_environment || *keep_environment == '\0')
+ {
+ /* From: https://github.com/dovecot/core/blob/master/src/lib/env-util.c#L55
+ Try to clear the environment.
+ a) environ = NULL crashes on OS X.
+ b) *environ = NULL doesn't work on FreeBSD 7.0.
+ c) environ = emptyenv doesn't work on Haiku OS
+ d) environ = calloc() should work everywhere */
+
+ if (environ) *environ = NULL;
+
+ }
+else if (Ustrcmp(keep_environment, "*") != 0)
+ {
+ uschar **p;
+ if (environ) for (p = USS environ; *p; /* see below */)
+ {
+ /* It's considered broken if we do not find the '=', according to
+ Florian Weimer. For now we ignore such strings. unsetenv() would complain,
+ getenv() would complain. */
+ uschar *eqp = Ustrchr(*p, '=');
+
+ if (eqp)
+ {
+ uschar *name = string_copyn(*p, eqp - *p);
+ if (OK != match_isinlist(name, USS &keep_environment,
+ 0, NULL, NULL, MCL_NOEXPAND, FALSE, NULL))
+ if (unsetenv(CS name) < 0) return FALSE;
+ else p = USS environ; /* RESTART from the beginning */
+ else p++;
+ store_reset(name);
+ }
+ }
+ }
+if (add_environment)
+ {
+ uschar *p;
+ int sep = 0;
+ uschar* envlist = add_environment;
+ while ((p = string_nextinlist(&envlist, &sep, NULL, 0)))
+ putenv(CS p);
+ }
+
+ return TRUE;
+}
is a failure. It leaves the configuration file open so that the subsequent
configuration data for delivery can be read if needed. */
+/* To be safe: change the working directory to /. */
+if (Uchdir("/") < 0)
+ {
+ perror("exim: chdir `/': ");
+ exit(EXIT_FAILURE);
+ }
+
readconf_main();
+if (cleanup_environment() == FALSE)
+ log_write(0, LOG_PANIC_DIE, "Can't cleanup environment");
+
+
/* If an action on specific messages is requested, or if a daemon or queue
runner is being started, we need to know if Exim was called by an admin user.
This is the case if the real user is root or exim, or if the real group is
#ifdef TMPDIR
{
uschar **p;
- for (p = USS environ; *p != NULL; p++)
+ if (environ) for (p = USS environ; *p != NULL; p++)
{
if (Ustrncmp(*p, "TMPDIR=", 7) == 0 &&
Ustrcmp(*p+7, TMPDIR) != 0)
uschar **new;
uschar **newp;
int count = 0;
- while (*p++ != NULL) count++;
+ if (environ) while (*p++ != NULL) count++;
if (envtz == NULL) count++;
newp = new = malloc(sizeof(uschar *) * (count + 1));
- for (p = USS environ; *p != NULL; p++)
+ if (environ) for (p = USS environ; *p != NULL; p++)
{
if (Ustrncmp(*p, "TZ=", 3) == 0) continue;
*newp++ = *p;
(Ustrcmp(argv[i], "router") == 0 ||
Ustrcmp(argv[i], "transport") == 0 ||
Ustrcmp(argv[i], "authenticator") == 0 ||
- Ustrcmp(argv[i], "macro") == 0))
+ Ustrcmp(argv[i], "macro") == 0 ||
+ Ustrcmp(argv[i], "environment") == 0))
{
readconf_print(argv[i+1], argv[i], flag_n);
i++;
extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...);
extern pid_t child_open_uid(uschar **, uschar **, int, uid_t *, gid_t *,
int *, int *, uschar *, BOOL);
+extern BOOL cleanup_environment(void);
extern uschar *cutthrough_finaldot(void);
extern BOOL cutthrough_flush_send(void);
extern BOOL cutthrough_headers_send(void);
extern uschar *string_append_listele(uschar *, uschar, const uschar *);
extern uschar *string_base62(unsigned long int);
extern uschar *string_cat(uschar *, int *, int *, const uschar *, int);
+extern int string_compare_by_pointer(const void *, const void *);
extern uschar *string_copy_dnsdomain(uschar *);
extern uschar *string_copy_malloc(uschar *);
extern uschar *string_copylc(uschar *);
BOOL active_local_sender_retain = FALSE;
int body_8bitmime = 0;
BOOL accept_8bitmime = TRUE; /* deliberately not RFC compliant */
+uschar *add_environment = NULL;
address_item *addr_duplicate = NULL;
address_item address_defaults = {
int journal_fd = -1;
+uschar *keep_environment = NULL;
+
int keep_malformed = 4*24*60*60; /* 4 days */
uschar *eldap_dn = NULL;
/* General global variables */
extern BOOL accept_8bitmime; /* Allow *BITMIME incoming */
+extern uschar *add_environment; /* List of environment variables to add */
extern int body_8bitmime; /* sender declared BODY= ; 7=7BIT, 8=8BITMIME */
extern header_line *acl_added_headers; /* Headers added by an ACL */
extern tree_node *acl_anchor; /* Tree of named ACLs */
extern int journal_fd; /* Fd for journal file */
+extern uschar *keep_environment; /* Whitelist for environment variables */
extern int keep_malformed; /* Time to keep malformed messages */
extern uschar *eldap_dn; /* Where LDAP DNs are left */
#define US (unsigned char *)
#define CUS (const unsigned char *)
#define USS (unsigned char **)
+#define CUSS (const unsigned char **)
/* The C library string functions expect "char *" arguments. Use macros to
avoid having to write a cast each time. We do this for string and file
#include "exim.h"
+extern char **environ;
+
+
#define CSTATE_STACK_SIZE 10
{ "acl_smtp_starttls", opt_stringptr, &acl_smtp_starttls },
#endif
{ "acl_smtp_vrfy", opt_stringptr, &acl_smtp_vrfy },
+ { "add_environment", opt_stringptr, &add_environment },
{ "admin_groups", opt_gidlist, &admin_groups },
{ "allow_domain_literals", opt_bool, &allow_domain_literals },
{ "allow_mx_to_ip", opt_bool, &allow_mx_to_ip },
{ "ignore_bounce_errors_after", opt_time, &ignore_bounce_errors_after },
{ "ignore_fromline_hosts", opt_stringptr, &ignore_fromline_hosts },
{ "ignore_fromline_local", opt_bool, &ignore_fromline_local },
+ { "keep_environment", opt_stringptr, &keep_environment },
{ "keep_malformed", opt_time, &keep_malformed },
#ifdef LOOKUP_LDAP
{ "ldap_ca_cert_dir", opt_stringptr, &eldap_ca_cert_dir },
macro_list print a list of macro names
+name print a named list item
local_scan print the local_scan options
+ environment print the used execution environment
If the second argument is not NULL, it must be one of "router", "transport",
"authenticator" or "macro" in which case the first argument identifies the
names_only = TRUE;
}
+ else if (Ustrcmp(name, "environment") == 0)
+ {
+ if (environ)
+ {
+ uschar **p;
+ size_t n;
+ for (p = USS environ; *p; p++) ;
+ n = p - USS environ;
+ qsort(environ, p - USS environ, sizeof(*p), string_compare_by_pointer);
+
+ for (p = USS environ; *p; p++)
+ {
+ if (no_labels) *(Ustrchr(*p, '=')) = '\0';
+ puts(*p);
+ }
+ }
+ return;
+ }
+
else
{
print_ol(find_option(name, optionlist_config, optionlist_config_size),
while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
!= NULL)
{
+
+ /* To avoid confusion: Exim changes to / at the very beginning and
+ * and to $spool_directory later. */
+ if (filename[0] != '/')
+ {
+ fprintf(stderr, "-C %s: only absolute names are allowed\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
/* Cut out all the fancy processing unless specifically wanted */
#if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
" gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols"
" are obsolete\n");
#endif /*SUPPORT_TLS*/
+
+if ((!add_environment || *add_environment == '\0') && !keep_environment)
+ log_write(0, LOG_MAIN,
+ "WARNING: purging the environment.\n"
+ " Suggested action: use keep_environment and add_environment.\n");
}
#endif /* COMPILE_UTILITY */
+#ifndef COMPILE_UTILITY
+/* qsort(3), currently used to sort the environment variables
+for -bP environment output, needs a function to compare two pointers to string
+pointers. Here it is. */
+
+int
+string_compare_by_pointer(const void *a, const void *b)
+{
+return Ustrcmp(* CUSS a, * CUSS b);
+}
+#endif /* COMPILE_UTILITY */
{
extern char ** environ;
uschar ** p;
- for (p = USS environ; *p != NULL; p++)
+ if (environ) for (p = USS environ; *p != NULL; p++)
if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
{
DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
-# automatically generated file - see ../scripts/reversion
-EXIM_RELEASE_VERSION="4.84"
-EXIM_VARIANT_VERSION=""
-EXIM_COMPILE_NUMBER="1"
+# initial version automatically generated from release-process/scripts/mk_exim_release.pl
+EXIM_RELEASE_VERSION=4.84
+EXIM_VARIANT_VERSION=_2
+EXIM_COMPILE_NUMBER=0