diff --git a/CVE-2021-29425-1.patch b/CVE-2021-29425-1.patch new file mode 100644 index 0000000000000000000000000000000000000000..dce67f8e24a69ea904bd0070b0190e313741cac8 --- /dev/null +++ b/CVE-2021-29425-1.patch @@ -0,0 +1,182 @@ +From 2736b6fe0b3fa22ec8e2b4184897ecadb021fc78 Mon Sep 17 00:00:00 2001 +From: Stefan Bodewig +Date: Thu, 21 Dec 2017 13:49:06 +0100 +Subject: [PATCH 1/4] IO-559 verify hostname part of suspected UNC paths in + FileNameUtils + +--- + .../org/apache/commons/io/FilenameUtils.java | 147 +++++++++++++++++- + 1 files changed, 146 insertions(+), 1 deletion(-) + +diff --git a/src/main/java/org/apache/commons/io/FilenameUtils.java b/src/main/java/org/apache/commons/io/FilenameUtils.java +index 9cddebb6..cdbc41c2 100644 +--- a/src/main/java/org/apache/commons/io/FilenameUtils.java ++++ b/src/main/java/org/apache/commons/io/FilenameUtils.java +@@ -19,8 +19,12 @@ package org.apache.commons.io; + import java.io.File; + import java.io.IOException; + import java.util.ArrayList; ++import java.util.Arrays; + import java.util.Collection; ++import java.util.List; + import java.util.Stack; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; + + /** + * General filename and filepath manipulation utilities. +@@ -679,7 +683,9 @@ public class FilenameUtils { + } + posUnix = posUnix == NOT_FOUND ? posWin : posUnix; + posWin = posWin == NOT_FOUND ? posUnix : posWin; +- return Math.min(posUnix, posWin) + 1; ++ int pos = Math.min(posUnix, posWin) + 1; ++ String hostnamePart = filename.substring(2, pos - 1); ++ return isValidHostName(hostnamePart) ? pos : NOT_FOUND; + } else { + return isSeparator(ch0) ? 1 : 0; + } +@@ -1450,4 +1456,143 @@ public class FilenameUtils { + return list.toArray( new String[ list.size() ] ); + } + ++ /** ++ * Checks whether a given string is a valid host name according to ++ * RFC 3986. ++ * ++ *

Accepted are IP addresses (v4 and v6) as well as what the ++ * RFC calls a "reg-name". Percent encoded names don't seem to be ++ * valid names in UNC paths.

++ * ++ * @see "https://tools.ietf.org/html/rfc3986#section-3.2.2" ++ * @param name the hostname to validate ++ * @return true if the given name is a valid host name ++ */ ++ private static boolean isValidHostName(String name) { ++ return isIPv4Address(name) || isIPv6Address(name) || isRFC3986HostName(name); ++ } ++ ++ private static final Pattern IPV4_PATTERN = ++ Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"); ++ private static final int IPV4_MAX_OCTET_VALUE = 255; ++ ++ // mostly copied from org.apache.commons.validator.routines.InetAddressValidator#isValidInet4Address ++ private static boolean isIPv4Address(String name) { ++ Matcher m = IPV4_PATTERN.matcher(name); ++ if (!m.matches() || m.groupCount() != 4) { ++ return false; ++ } ++ ++ // verify that address subgroups are legal ++ for (int i = 1; i < 5; i++) { ++ String ipSegment = m.group(i); ++ if (ipSegment == null || ipSegment.length() == 0) { ++ return false; ++ } ++ ++ int iIpSegment = 0; ++ ++ try { ++ iIpSegment = Integer.parseInt(ipSegment); ++ } catch(NumberFormatException e) { ++ return false; ++ } ++ ++ if (iIpSegment > IPV4_MAX_OCTET_VALUE) { ++ return false; ++ } ++ ++ if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { ++ return false; ++ } ++ ++ } ++ ++ return true; ++ } ++ ++ private static final int IPV6_MAX_HEX_GROUPS = 8; ++ private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; ++ private static final int MAX_UNSIGNED_SHORT = 0xffff; ++ private static final int BASE_16 = 16; ++ ++ // copied from org.apache.commons.validator.routines.InetAddressValidator#isValidInet6Address ++ private static boolean isIPv6Address(String inet6Address) { ++ boolean containsCompressedZeroes = inet6Address.contains("::"); ++ if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { ++ return false; ++ } ++ if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::")) ++ || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) { ++ return false; ++ } ++ String[] octets = inet6Address.split(":"); ++ if (containsCompressedZeroes) { ++ List octetList = new ArrayList(Arrays.asList(octets)); ++ if (inet6Address.endsWith("::")) { ++ // String.split() drops ending empty segments ++ octetList.add(""); ++ } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { ++ octetList.remove(0); ++ } ++ octets = octetList.toArray(new String[octetList.size()]); ++ } ++ if (octets.length > IPV6_MAX_HEX_GROUPS) { ++ return false; ++ } ++ int validOctets = 0; ++ int emptyOctets = 0; // consecutive empty chunks ++ for (int index = 0; index < octets.length; index++) { ++ String octet = octets[index]; ++ if (octet.length() == 0) { ++ emptyOctets++; ++ if (emptyOctets > 1) { ++ return false; ++ } ++ } else { ++ emptyOctets = 0; ++ // Is last chunk an IPv4 address? ++ if (index == octets.length - 1 && octet.contains(".")) { ++ if (!isIPv4Address(octet)) { ++ return false; ++ } ++ validOctets += 2; ++ continue; ++ } ++ if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { ++ return false; ++ } ++ int octetInt = 0; ++ try { ++ octetInt = Integer.parseInt(octet, BASE_16); ++ } catch (NumberFormatException e) { ++ return false; ++ } ++ if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { ++ return false; ++ } ++ } ++ validOctets++; ++ } ++ if (validOctets > IPV6_MAX_HEX_GROUPS || (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes)) { ++ return false; ++ } ++ return true; ++ } ++ ++ private static final Pattern REG_NAME_PART_PATTERN = Pattern.compile("^[a-zA-Z0-9][a-zA-Z0-9-]*$"); ++ ++ private static boolean isRFC3986HostName(String name) { ++ String[] parts = name.split("\\.", -1); ++ for (int i = 0; i < parts.length; i++) { ++ if (parts[i].length() == 0) { ++ // trailing dot is legal, otherwise we've hit a .. sequence ++ return i == parts.length - 1; ++ } ++ if (!REG_NAME_PART_PATTERN.matcher(parts[i]).matches()) { ++ return false; ++ } ++ } ++ return true; ++ } + } diff --git a/CVE-2021-29425-2.patch b/CVE-2021-29425-2.patch new file mode 100644 index 0000000000000000000000000000000000000000..2acb61c1ea8614ee44e08e36bf6107d4d4485330 --- /dev/null +++ b/CVE-2021-29425-2.patch @@ -0,0 +1,57 @@ +From 71639e041876e4dca28785ac3e61d80ecc33db44 Mon Sep 17 00:00:00 2001 +From: Stefan Bodewig +Date: Wed, 16 May 2018 08:49:15 +0200 +Subject: [PATCH 2/4] checkstyle requires javadocs on private methods? + +--- + .../org/apache/commons/io/FilenameUtils.java | 20 +++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/src/main/java/org/apache/commons/io/FilenameUtils.java b/src/main/java/org/apache/commons/io/FilenameUtils.java +index cdbc41c2..a829f8c7 100644 +--- a/src/main/java/org/apache/commons/io/FilenameUtils.java ++++ b/src/main/java/org/apache/commons/io/FilenameUtils.java +@@ -1476,6 +1476,12 @@ public class FilenameUtils { + Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"); + private static final int IPV4_MAX_OCTET_VALUE = 255; + ++ /** ++ * Checks whether a given string represents a valid IPv4 address. ++ * ++ * @param name the name to validate ++ * @return true if the given name is a valid IPv4 address ++ */ + // mostly copied from org.apache.commons.validator.routines.InetAddressValidator#isValidInet4Address + private static boolean isIPv4Address(String name) { + Matcher m = IPV4_PATTERN.matcher(name); +@@ -1517,6 +1523,12 @@ public class FilenameUtils { + private static final int BASE_16 = 16; + + // copied from org.apache.commons.validator.routines.InetAddressValidator#isValidInet6Address ++ /** ++ * Checks whether a given string represents a valid IPv6 address. ++ * ++ * @param inet6Address the name to validate ++ * @return true if the given name is a valid IPv6 address ++ */ + private static boolean isIPv6Address(String inet6Address) { + boolean containsCompressedZeroes = inet6Address.contains("::"); + if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { +@@ -1582,6 +1594,14 @@ public class FilenameUtils { + + private static final Pattern REG_NAME_PART_PATTERN = Pattern.compile("^[a-zA-Z0-9][a-zA-Z0-9-]*$"); + ++ /** ++ * Checks whether a given string is a valid host name according to ++ * RFC 3986 - not accepting IP addresses. ++ * ++ * @see "https://tools.ietf.org/html/rfc3986#section-3.2.2" ++ * @param name the hostname to validate ++ * @return true if the given name is a valid host name ++ */ + private static boolean isRFC3986HostName(String name) { + String[] parts = name.split("\\.", -1); + for (int i = 0; i < parts.length; i++) { +-- +2.23.0 + diff --git a/CVE-2021-29425-3.patch b/CVE-2021-29425-3.patch new file mode 100644 index 0000000000000000000000000000000000000000..15011e08f0220a06159ed7df46376546ed71d0b3 --- /dev/null +++ b/CVE-2021-29425-3.patch @@ -0,0 +1,26 @@ +From bb388f116290c3e3ff244082fd5c376a45a5c798 Mon Sep 17 00:00:00 2001 +From: Stefan Bodewig +Date: Thu, 17 May 2018 21:03:26 +0200 +Subject: [PATCH 3/4] isRFC3986HostName applies to IPv4 addresses so we can + safe the test + +--- + src/main/java/org/apache/commons/io/FilenameUtils.java | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/main/java/org/apache/commons/io/FilenameUtils.java b/src/main/java/org/apache/commons/io/FilenameUtils.java +index a829f8c7..b93476bb 100644 +--- a/src/main/java/org/apache/commons/io/FilenameUtils.java ++++ b/src/main/java/org/apache/commons/io/FilenameUtils.java +@@ -1469,7 +1469,7 @@ public class FilenameUtils { + * @return true if the given name is a valid host name + */ + private static boolean isValidHostName(String name) { +- return isIPv4Address(name) || isIPv6Address(name) || isRFC3986HostName(name); ++ return isIPv6Address(name) || isRFC3986HostName(name); + } + + private static final Pattern IPV4_PATTERN = +-- +2.23.0 + diff --git a/CVE-2021-29425-4.patch b/CVE-2021-29425-4.patch new file mode 100644 index 0000000000000000000000000000000000000000..07f734fa04c2fe77d52887b0a7288dfb6564667e --- /dev/null +++ b/CVE-2021-29425-4.patch @@ -0,0 +1,40 @@ +From 0842e1f60a1ca36c8db76a00c6001a38174de21b Mon Sep 17 00:00:00 2001 +From: Stefan Bodewig +Date: Thu, 17 May 2018 21:04:34 +0200 +Subject: [PATCH 4/4] remove IPv4 checks that are unnnecessary due to matching + regex + +--- + .../java/org/apache/commons/io/FilenameUtils.java | 15 ++------------- + 1 file changed, 2 insertions(+), 13 deletions(-) + +diff --git a/src/main/java/org/apache/commons/io/FilenameUtils.java b/src/main/java/org/apache/commons/io/FilenameUtils.java +index b93476bb..5da8f786 100644 +--- a/src/main/java/org/apache/commons/io/FilenameUtils.java ++++ b/src/main/java/org/apache/commons/io/FilenameUtils.java +@@ -1490,20 +1490,9 @@ public class FilenameUtils { + } + + // verify that address subgroups are legal +- for (int i = 1; i < 5; i++) { ++ for (int i = 1; i <= 4; i++) { + String ipSegment = m.group(i); +- if (ipSegment == null || ipSegment.length() == 0) { +- return false; +- } +- +- int iIpSegment = 0; +- +- try { +- iIpSegment = Integer.parseInt(ipSegment); +- } catch(NumberFormatException e) { +- return false; +- } +- ++ int iIpSegment = Integer.parseInt(ipSegment); + if (iIpSegment > IPV4_MAX_OCTET_VALUE) { + return false; + } +-- +2.23.0 + diff --git a/apache-commons-io.spec b/apache-commons-io.spec index 2d577cb82aba2c1480bbf7319aa02f5ef651868c..4ef565c7ba78394bfc62ae56484712462af29c2a 100644 --- a/apache-commons-io.spec +++ b/apache-commons-io.spec @@ -1,11 +1,15 @@ Name: apache-commons-io Epoch: 1 Version: 2.6 -Release: 6 +Release: 7 Summary: A library of utilities for developing IO functionality. License: ASL 2.0 URL: http://commons.apache.org/proper/commons-io Source0: http://archive.apache.org/dist/commons/io/source/commons-io-%{version}-src.tar.gz +Patch0000: CVE-2021-29425-1.patch +Patch0001: CVE-2021-29425-2.patch +Patch0002: CVE-2021-29425-3.patch +Patch0003: CVE-2021-29425-4.patch Patch9000: Ignore-some-test-because-bep.patch BuildArch: noarch BuildRequires: mvn(org.apache.maven.plugins:maven-antrun-plugin) maven-local @@ -46,6 +50,9 @@ xmvn test --batch-mode --offline verify %doc RELEASE-NOTES.txt %changelog +* Sat May 8 2021 wangxiao - 1:2.6-7 +- Fix CVE-2021-29425 + * Mon Apr 26 2021 maminjie - 1:2.6-6 - Move the test to the %check stage