From 318a1a2246a9f521e5a02313dcc1f6d68a0af7ec Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:16:14 +0200 Subject: Adding debian version 4.96-15+deb12u4. Signed-off-by: Daniel Baumann --- ...ing_is_ip_address-CVE-2023-42117-Bug-3031.patch | 309 +++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 debian/patches/76-01-fix-string_is_ip_address-CVE-2023-42117-Bug-3031.patch (limited to 'debian/patches/76-01-fix-string_is_ip_address-CVE-2023-42117-Bug-3031.patch') diff --git a/debian/patches/76-01-fix-string_is_ip_address-CVE-2023-42117-Bug-3031.patch b/debian/patches/76-01-fix-string_is_ip_address-CVE-2023-42117-Bug-3031.patch new file mode 100644 index 0000000..cbddde0 --- /dev/null +++ b/debian/patches/76-01-fix-string_is_ip_address-CVE-2023-42117-Bug-3031.patch @@ -0,0 +1,309 @@ +From a95acb1c19c2e3600ef327c71318e33316d34440 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" +Date: Thu, 5 Oct 2023 22:49:57 +0200 +Subject: [PATCH 1/3] fix: string_is_ip_address (CVE-2023-42117) Bug 3031 + +--- + doc/ChangeLog | 206 ++++++++++++++++++++++++++++++++++++++++++ + src/expand.c | 14 ++- + src/functions.h | 1 + + src/string.c | 200 +++++++++++++++++++++------------------- + 4 files changed, 323 insertions(+), 98 deletions(-) + +diff --git a/src/expand.c b/src/expand.c +index 36c9f423b..4986e4657 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -2646,17 +2646,25 @@ switch(cond_type = identify_operator(&s, &opname)) + } + *yield = (Ustat(sub[0], &statbuf) == 0) == testfor; + break; + + 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 */ + + case ECOND_PAM: + #ifdef SUPPORT_PAM + rc = auth_call_pam(sub[0], &expand_string_message); +diff --git a/src/functions.h b/src/functions.h +index 224666cb1..3c8104d25 100644 +--- a/src/functions.h ++++ b/src/functions.h +@@ -552,14 +552,15 @@ extern gstring *string_catn(gstring *, const uschar *, int) WARN_UNUSED_RESULT; + extern int string_compare_by_pointer(const void *, const void *); + extern uschar *string_copy_dnsdomain(uschar *); + extern uschar *string_copy_malloc(const uschar *); + extern uschar *string_dequote(const uschar **); + 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 + extern const uschar *string_printing2(const uschar *, int); + extern uschar *string_split_message(uschar *); + extern uschar *string_unprinting(uschar *); + #ifdef SUPPORT_I18N +diff --git a/src/string.c b/src/string.c +index a5161bb31..9aefc2b58 100644 +--- a/src/string.c ++++ b/src/string.c +@@ -25,131 +25,141 @@ address (assuming HAVE_IPV6 is set). If a mask is permitted and one is present, + and maskptr is not NULL, its offset is placed there. + + Arguments: + s a string + 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; ++string_is_ip_addressX(const uschar *ip_addr, int *maskptr, const uschar **errp) { ++ struct addrinfo hints; ++ struct addrinfo *res; + +-/* If an optional mask is permitted, check for it. If found, pass back the +-offset. */ ++ uschar *slash, *percent; + +-if (maskptr) ++ 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 */ +- +-if (Ustrchr(s, ':') != NULL) +- { +- 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++) ++ uschar *rest; ++ mask = Ustrtol(slash+1, &rest, 10); ++ if (*rest || mask < 0) + { +- /* 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 = "netmask not numeric or <0"; ++ 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. */ ++ *maskptr = slash - ip_addr; /* offset of the slash */ ++ endp = slash; ++ } else if (maskptr) *maskptr = 0; /* no slash found */ + +- if (!v4end) +- return (*s == 0 || *s == '%' || +- (*s == '/' && maskptr != NULL && *maskptr != 0))? yield : 0; ++ /* The interface-ID suffix (%) 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, '%')) ++ { ++ if (slash) ++ { ++ if (errp) *errp = "interface-ID and netmask are mutually exclusive"; ++ return 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. */ +- +-for (int i = 0; i < 4; i++) ++ /* 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; ++ ++ 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 */ + + + /************************************************* + * Format message size * + *************************************************/ + +-- +2.42.0 + -- cgit v1.2.3