Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package exim for openSUSE:Factory checked in at 2023-10-16 22:33:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/exim (Old) and /work/SRC/openSUSE:Factory/.exim.new.20540 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "exim" Mon Oct 16 22:33:24 2023 rev:77 rq:1117952 version:4.96.2 Changes: -------- --- /work/SRC/openSUSE:Factory/exim/exim.changes 2023-10-02 20:06:45.559436481 +0200 +++ /work/SRC/openSUSE:Factory/.exim.new.20540/exim.changes 2023-10-16 22:33:39.961296830 +0200 @@ -1,0 +2,7 @@ +Mon Oct 16 08:45:01 UTC 2023 - Peter Wullinger <wullin...@rz.uni-kiel.de> + +- security update to exim 4.96.2 + * fixes CVE-2023-42117 (bsc#1215787) + * fixes CVE-2023-42119 (bsc#1215789) + +------------------------------------------------------------------- Old: ---- exim-4.96.1.tar.bz2 exim-4.96.1.tar.bz2.asc New: ---- exim-4.96.2.tar.bz2 exim-4.96.2.tar.bz2.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ exim.spec ++++++ --- /var/tmp/diff_new_pack.Q6FJhI/_old 2023-10-16 22:33:40.965333046 +0200 +++ /var/tmp/diff_new_pack.Q6FJhI/_new 2023-10-16 22:33:40.965333046 +0200 @@ -74,7 +74,7 @@ %endif Requires(pre): fileutils textutils %endif -Version: 4.96.1 +Version: 4.96.2 Release: 0 %if %{with_mysql} BuildRequires: mysql-devel ++++++ exim-4.96.1.tar.bz2 -> exim-4.96.2.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/doc/ChangeLog new/exim-4.96.2/doc/ChangeLog --- old/exim-4.96.1/doc/ChangeLog 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/doc/ChangeLog 2023-10-14 23:55:23.000000000 +0200 @@ -2,6 +2,15 @@ affect Exim's operation, with an unchanged configuration file. For new options, and new features, see the NewStuff file next to this ChangeLog. +Exim version 4.96.2 +------------------- + +JH/01 Bug 3033: Harden dnsdb lookups against crafted DNS responses. + CVE-2023-42219 + +HS/01 Fix string_is_ip_address() CVE-2023-42117 (Bug 3031) + + Exim version 4.96.1 ------------------- @@ -19,6 +28,10 @@ be triggered by externally-controlled input. Found by Trend Micro. CVE-2023-42114 +JH/04 Bug 2903: avoid exit on an attempt to rewrite a malformed address. + Make the rewrite never match and keep the logging. Trust the + admin to be using verify=header-syntax (to actually reject the message). + Exim version 4.96 ----------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/doc/filter.txt new/exim-4.96.2/doc/filter.txt --- old/exim-4.96.1/doc/filter.txt 2023-10-02 12:25:25.000000000 +0200 +++ new/exim-4.96.2/doc/filter.txt 2023-10-15 17:20:38.000000000 +0200 @@ -4,7 +4,7 @@ Copyright (c) 2021 The Exim Maintainers -Revision 4.96.1 02 Oct 2023 PH +Revision 4.96.2 15 Oct 2023 PH ------------------------------------------------------------------------------- @@ -72,7 +72,7 @@ This document describes the user interfaces to Exim's in-built mail filtering facilities, and is copyright (c) The Exim Maintainers 2021. It corresponds to -Exim version 4.96.1. +Exim version 4.96.2. 1.1 Introduction diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/doc/spec.txt new/exim-4.96.2/doc/spec.txt --- old/exim-4.96.1/doc/spec.txt 2023-10-02 12:25:25.000000000 +0200 +++ new/exim-4.96.2/doc/spec.txt 2023-10-15 17:20:37.000000000 +0200 @@ -4,7 +4,7 @@ Copyright (c) 2022 The Exim Maintainers -Revision 4.96.1 02 Oct 2023 EM +Revision 4.96.2 15 Oct 2023 EM ------------------------------------------------------------------------------- @@ -596,7 +596,7 @@ 1.1 Exim documentation ---------------------- -This edition of the Exim specification applies to version 4.96.1 of Exim. +This edition of the Exim specification applies to version 4.96.2 of Exim. Substantive changes from the 4.95 edition are marked in some renditions of this document; this paragraph is so marked if the rendition is capable of showing a change indicator. @@ -1723,7 +1723,7 @@ 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.96.1) into which the following files are placed: +exim-4.96.2) into which the following files are placed: ACKNOWLEDGMENTS contains some acknowledgments CHANGES contains a reference to where changes are documented @@ -2345,7 +2345,7 @@ 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.96.1-1. The script then arranges for a symbolic link called +for example, exim-4.96.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). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/dns.c new/exim-4.96.2/src/dns.c --- old/exim-4.96.1/src/dns.c 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/src/dns.c 2023-10-14 23:55:23.000000000 +0200 @@ -304,7 +304,7 @@ static BOOL dnss_inc_aptr(const dns_answer * dnsa, dns_scan * dnss, unsigned delta) { -return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen; +return (dnss->aptr += delta) > dnsa->answer + dnsa->answerlen; } /************************************************* @@ -388,7 +388,7 @@ TRACE trace = "A-hdr"; if (dnss_inc_aptr(dnsa, dnss, namelen+8)) goto null_return; GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */ - /* skip over it */ + /* skip over it, checking for a bogus size */ TRACE trace = "A-skip"; if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) goto null_return; } @@ -428,10 +428,9 @@ GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */ dnss->srr.data = dnss->aptr; /* The record's data follows */ -/* Unchecked increment ok here since no further access on this iteration; -will be checked on next at "R-name". */ - -dnss->aptr += dnss->srr.size; /* Advance to next RR */ +/* skip over it, checking for a bogus size */ +if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) + goto null_return; /* Return a pointer to the dns_record structure within the dns_answer. This is for convenience so that the scans can use nice-looking for loops. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/expand.c new/exim-4.96.2/src/expand.c --- old/exim-4.96.1/src/expand.c 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/src/expand.c 2023-10-14 23:55:23.000000000 +0200 @@ -2650,9 +2650,17 @@ case ECOND_ISIP: case ECOND_ISIP4: case ECOND_ISIP6: - rc = string_is_ip_address(sub[0], NULL); - *yield = ((cond_type == ECOND_ISIP)? (rc != 0) : - (cond_type == ECOND_ISIP4)? (rc == 4) : (rc == 6)) == testfor; + { + const uschar *errp; + const uschar **errpp; + DEBUG(D_expand) errpp = &errp; else errpp = 0; + if (0 == (rc = string_is_ip_addressX(sub[0], NULL, errpp))) + DEBUG(D_expand) debug_printf("failed: %s\n", errp); + + *yield = ( cond_type == ECOND_ISIP ? rc != 0 : + cond_type == ECOND_ISIP4 ? rc == 4 : rc == 6) == testfor; + } + break; /* Various authentication tests - all optionally compiled */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/functions.h new/exim-4.96.2/src/functions.h --- old/exim-4.96.1/src/functions.h 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/src/functions.h 2023-10-14 23:55:23.000000000 +0200 @@ -556,6 +556,7 @@ extern uschar *string_format_size(int, uschar *); extern int string_interpret_escape(const uschar **); extern int string_is_ip_address(const uschar *, int *); +extern int string_is_ip_addressX(const uschar *, int *, const uschar **); #ifdef SUPPORT_I18N extern BOOL string_is_utf8(const uschar *); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/lookups/dnsdb.c new/exim-4.96.2/src/lookups/dnsdb.c --- old/exim-4.96.1/src/lookups/dnsdb.c 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/src/lookups/dnsdb.c 2023-10-14 23:55:23.000000000 +0200 @@ -398,43 +398,60 @@ if (type == T_TXT || type == T_SPF) { - if (outsep2 == NULL) /* output only the first item of data */ - yield = string_catn(yield, US (rr->data+1), (rr->data)[0]); + if (!outsep2) /* output only the first item of data */ + { + uschar n = (rr->data)[0]; + /* size byte + data bytes must not excced the RRs length */ + if (n + 1 <= rr->size) + yield = string_catn(yield, US (rr->data+1), n); + } else { /* output all items */ int data_offset = 0; while (data_offset < rr->size) { - uschar chunk_len = (rr->data)[data_offset++]; - if (outsep2[0] != '\0' && data_offset != 1) + uschar chunk_len = (rr->data)[data_offset]; + int remain = rr->size - data_offset; + + /* Apparently there are resolvers that do not check RRs before passing + them on, and glibc fails to do so. So every application must... + Check for chunk len exceeding RR */ + + if (chunk_len > remain) + chunk_len = remain; + + if (*outsep2 && data_offset != 0) yield = string_catn(yield, outsep2, 1); - yield = string_catn(yield, US ((rr->data)+data_offset), chunk_len); + yield = string_catn(yield, US ((rr->data) + ++data_offset), --chunk_len); data_offset += chunk_len; } } } else if (type == T_TLSA) - { - uint8_t usage, selector, matching_type; - uint16_t payload_length; - uschar s[MAX_TLSA_EXPANDED_SIZE]; - uschar * sp = s; - uschar * p = US rr->data; - - usage = *p++; - selector = *p++; - matching_type = *p++; - /* What's left after removing the first 3 bytes above */ - payload_length = rr->size - 3; - sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2, - selector, *outsep2, matching_type, *outsep2); - /* Now append the cert/identifier, one hex char at a time */ - while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4)) - sp += sprintf(CS sp, "%02x", *p++); + if (rr->size < 3) + continue; + else + { + uint8_t usage, selector, matching_type; + uint16_t payload_length; + uschar s[MAX_TLSA_EXPANDED_SIZE]; + uschar * sp = s; + uschar * p = US rr->data; + + usage = *p++; + selector = *p++; + matching_type = *p++; + /* What's left after removing the first 3 bytes above */ + payload_length = rr->size - 3; + sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2, + selector, *outsep2, matching_type, *outsep2); + /* Now append the cert/identifier, one hex char at a time */ + while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4)) + sp += sprintf(CS sp, "%02x", *p++); - yield = string_cat(yield, s); - } + yield = string_cat(yield, s); + } else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */ { int priority, weight, port; @@ -444,17 +461,20 @@ switch (type) { case T_MXH: + if (rr->size < sizeof(u_int16_t)) continue; /* mxh ignores the priority number and includes only the hostnames */ GETSHORT(priority, p); break; case T_MX: + if (rr->size < sizeof(u_int16_t)) continue; GETSHORT(priority, p); sprintf(CS s, "%d%c", priority, *outsep2); yield = string_cat(yield, s); break; case T_SRV: + if (rr->size < 3*sizeof(u_int16_t)) continue; GETSHORT(priority, p); GETSHORT(weight, p); GETSHORT(port, p); @@ -464,6 +484,7 @@ break; case T_CSA: + if (rr->size < 3*sizeof(u_int16_t)) continue; /* See acl_verify_csa() for more comments about CSA. */ GETSHORT(priority, p); GETSHORT(weight, p); @@ -514,7 +535,7 @@ if (type == T_SOA && outsep2 != NULL) { - unsigned long serial, refresh, retry, expire, minimum; + unsigned long serial = 0, refresh = 0, retry = 0, expire = 0, minimum = 0; p += rc; yield = string_catn(yield, outsep2, 1); @@ -530,8 +551,11 @@ else yield = string_cat(yield, s); p += rc; - GETLONG(serial, p); GETLONG(refresh, p); - GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p); + if (rr->size >= p - rr->data - 5*sizeof(u_int32_t)) + { + GETLONG(serial, p); GETLONG(refresh, p); + GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p); + } sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu", *outsep2, serial, *outsep2, refresh, *outsep2, retry, *outsep2, expire, *outsep2, minimum); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/spf.c new/exim-4.96.2/src/spf.c --- old/exim-4.96.1/src/spf.c 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/src/spf.c 2023-10-14 23:55:23.000000000 +0200 @@ -120,6 +120,7 @@ switch(rr_type) { case T_MX: + if (rr->size < 2) continue; s += 2; /* skip the MX precedence field */ case T_PTR: { @@ -135,6 +136,7 @@ gstring * g = NULL; uschar chunk_len; + if (rr->size < 1+6) continue; /* min for version str */ if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0) { HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n", @@ -142,9 +144,12 @@ continue; } - for (int off = 0; off < rr->size; off += chunk_len) + /* require 1 byte for the chunk_len */ + for (int off = 0; off < rr->size - 1; off += chunk_len) { - if (!(chunk_len = s[off++])) break; + if ( !(chunk_len = s[off++]) + || rr->size < off + chunk_len /* ignore bogus size chunks */ + ) break; g = string_catn(g, s+off, chunk_len); } if (!g) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/string.c new/exim-4.96.2/src/string.c --- old/exim-4.96.1/src/string.c 2023-09-30 22:52:59.000000000 +0200 +++ new/exim-4.96.2/src/string.c 2023-10-14 23:55:23.000000000 +0200 @@ -29,123 +29,133 @@ maskptr NULL if no mask is permitted to follow otherwise, points to an int where the offset of '/' is placed if there is no / followed by trailing digits, *maskptr is set 0 + errp NULL if no diagnostic information is required, and if the netmask + length should not be checked. Otherwise it is set pointing to a short + descriptive text. Returns: 0 if the string is not a textual representation of an IP address 4 if it is an IPv4 address 6 if it is an IPv6 address -*/ +The legacy string_is_ip_address() function follows below. +*/ int -string_is_ip_address(const uschar *s, int *maskptr) -{ -int yield = 4; - -/* If an optional mask is permitted, check for it. If found, pass back the -offset. */ - -if (maskptr) +string_is_ip_addressX(const uschar *ip_addr, int *maskptr, const uschar **errp) { + struct addrinfo hints; + struct addrinfo *res; + + uschar *slash, *percent; + + uschar *endp = 0; + long int mask = 0; + const uschar *addr = 0; + + /* If there is a slash, but we didn't request a (optional) netmask, + we return failure, as we do if the mask isn't a pure numerical value, + or if it is negative. The actual length is checked later, once we know + the address family. */ + if (slash = Ustrchr(ip_addr, '/')) { - const uschar *ss = s + Ustrlen(s); - *maskptr = 0; - if (s != ss && isdigit(*(--ss))) + if (!maskptr) { - while (ss > s && isdigit(ss[-1])) ss--; - if (ss > s && *(--ss) == '/') *maskptr = ss - s; + if (errp) *errp = "netmask found, but not requested"; + return 0; } - } -/* A colon anywhere in the string => IPv6 address */ + uschar *rest; + mask = Ustrtol(slash+1, &rest, 10); + if (*rest || mask < 0) + { + if (errp) *errp = "netmask not numeric or <0"; + return 0; + } -if (Ustrchr(s, ':') != NULL) + *maskptr = slash - ip_addr; /* offset of the slash */ + endp = slash; + } else if (maskptr) *maskptr = 0; /* no slash found */ + + /* The interface-ID suffix (%<id>) is optional (for IPv6). If it + exists, we check it syntactically. Later, if we know the address + family is IPv4, we might reject it. + The interface-ID is mutually exclusive with the netmask, to the + best of my knowledge. */ + if (percent = Ustrchr(ip_addr, '%')) { - BOOL had_double_colon = FALSE; - BOOL v4end = FALSE; - - yield = 6; - - /* An IPv6 address must start with hex digit or double colon. A single - colon is invalid. */ - - if (*s == ':' && *(++s) != ':') return 0; - - /* Now read up to 8 components consisting of up to 4 hex digits each. There - may be one and only one appearance of double colon, which implies any number - of binary zero bits. The number of preceding components is held in count. */ - - for (int count = 0; count < 8; count++) + if (slash) { - /* If the end of the string is reached before reading 8 components, the - address is valid provided a double colon has been read. This also applies - if we hit the / that introduces a mask or the % that introduces the - interface specifier (scope id) of a link-local address. */ - - if (*s == 0 || *s == '%' || *s == '/') return had_double_colon ? yield : 0; - - /* If a component starts with an additional colon, we have hit a double - colon. This is permitted to appear once only, and counts as at least - one component. The final component may be of this form. */ - - if (*s == ':') - { - if (had_double_colon) return 0; - had_double_colon = TRUE; - s++; - continue; - } - - /* If the remainder of the string contains a dot but no colons, we - can expect a trailing IPv4 address. This is valid if either there has - been no double-colon and this is the 7th component (with the IPv4 address - being the 7th & 8th components), OR if there has been a double-colon - and fewer than 6 components. */ - - if (Ustrchr(s, ':') == NULL && Ustrchr(s, '.') != NULL) - { - if ((!had_double_colon && count != 6) || - (had_double_colon && count > 6)) return 0; - v4end = TRUE; - yield = 6; - break; - } - - /* Check for at least one and not more than 4 hex digits for this - component. */ - - if (!isxdigit(*s++)) return 0; - if (isxdigit(*s) && isxdigit(*(++s)) && isxdigit(*(++s))) s++; - - /* If the component is terminated by colon and there is more to - follow, skip over the colon. If there is no more to follow the address is - invalid. */ - - if (*s == ':' && *(++s) == 0) return 0; + if (errp) *errp = "interface-ID and netmask are mutually exclusive"; + return 0; } - - /* If about to handle a trailing IPv4 address, drop through. Otherwise - all is well if we are at the end of the string or at the mask or at a percent - sign, which introduces the interface specifier (scope id) of a link local - address. */ - - if (!v4end) - return (*s == 0 || *s == '%' || - (*s == '/' && maskptr != NULL && *maskptr != 0))? yield : 0; + for (uschar *p = percent+1; *p; p++) + if (!isalnum(*p) && !ispunct(*p)) + { + if (errp) *errp = "interface-ID must match [[:alnum:][:punct:]]"; + return 0; + } + endp = percent; } -/* Test for IPv4 address, which may be the tail-end of an IPv6 address. */ + /* inet_pton() can't parse netmasks and interface IDs, so work on a shortened copy + allocated on the current stack */ + if (endp) { + ptrdiff_t l = endp - ip_addr; + if (l > 255) + { + if (errp) *errp = "rudiculous long ip address string"; + return 0; + } + addr = alloca(l+1); /* *BSD does not have strndupa() */ + Ustrncpy((uschar *)addr, ip_addr, l); + ((uschar*)addr)[l] = '\0'; + } else addr = ip_addr; + + int af; + union { /* we do not need this, but inet_pton() needs a place for storage */ + struct in_addr sa4; + struct in6_addr sa6; + } sa; -for (int i = 0; i < 4; i++) + af = Ustrchr(addr, ':') ? AF_INET6 : AF_INET; + if (!inet_pton(af, addr, &sa)) { - long n; - uschar * end; - - if (i != 0 && *s++ != '.') return 0; - n = strtol(CCS s, CSS &end, 10); - if (n > 255 || n < 0 || end <= s || end > s+3) return 0; - s = end; + if (errp) *errp = af == AF_INET6 ? "IP address string not parsable as IPv6" + : "IP address string not parsable IPv4"; + return 0; } + /* we do not check the values of the mask here, as + this is done on the callers side (but I don't understand why), so + actually I'd like to do it here, but it breaks at least 0002 */ + switch (af) + { + case AF_INET6: + if (errp && mask > 128) + { + *errp = "IPv6 netmask value must not be >128"; + return 0; + } + return 6; + case AF_INET: + if (percent) + { + if (errp) *errp = "IPv4 address string must not have an interface-ID"; + return 0; + } + if (errp && mask > 32) { + *errp = "IPv4 netmask value must not be >32"; + return 0; + } + return 4; + default: + if (errp) *errp = "unknown address family (should not happen)"; + return 0; + } +} -return !*s || (*s == '/' && maskptr && *maskptr != 0) ? yield : 0; +int +string_is_ip_address(const uschar *ip_addr, int *maskptr) { + return string_is_ip_addressX(ip_addr, maskptr, 0); } + #endif /* COMPILE_UTILITY */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/version.h new/exim-4.96.2/src/version.h --- old/exim-4.96.1/src/version.h 2023-10-02 12:24:59.000000000 +0200 +++ new/exim-4.96.2/src/version.h 2023-10-15 17:20:11.000000000 +0200 @@ -1,5 +1,5 @@ /* automatically generated file - see ../scripts/reversion */ -#define EXIM_RELEASE_VERSION "4.96.1" +#define EXIM_RELEASE_VERSION "4.96.2" #ifdef EXIM_VARIANT_VERSION #define EXIM_VERSION_STR EXIM_RELEASE_VERSION "-" EXIM_VARIANT_VERSION #else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/exim-4.96.1/src/version.sh new/exim-4.96.2/src/version.sh --- old/exim-4.96.1/src/version.sh 2023-10-02 12:24:59.000000000 +0200 +++ new/exim-4.96.2/src/version.sh 2023-10-15 17:20:11.000000000 +0200 @@ -1,3 +1,3 @@ # automatically generated file - see ../scripts/reversion -EXIM_RELEASE_VERSION="4.96.1" +EXIM_RELEASE_VERSION="4.96.2" EXIM_COMPILE_NUMBER="1"