[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#992599: marked as done (buster-pu: package commons-io/2.6-2)



Your message dated Sat, 09 Oct 2021 12:11:43 +0100
with message-id <896b7609401ceb0e1c537222e26587ea2351415d.camel@adam-barratt.org.uk>
and subject line Closing bugs for fixes included in the 10.11 point release
has caused the Debian Bug report #992599,
regarding buster-pu: package commons-io/2.6-2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
992599: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=992599
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: buster
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: apo@debian.org

[ Reason ]

Fixing CVE-2021-29425 in Buster which has been marked no-dsa by the
security team.

[ Impact ]

Buster would still be vulnerable to CVE-2021-29425.

[ Tests ]

I have manually tested the code and it works.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

Regards,

Markus
diff -Nru commons-io-2.6/debian/changelog commons-io-2.6/debian/changelog
--- commons-io-2.6/debian/changelog	2018-02-05 14:41:54.000000000 +0100
+++ commons-io-2.6/debian/changelog	2021-08-20 22:25:28.000000000 +0200
@@ -1,3 +1,15 @@
+commons-io (2.6-2+deb10u1) buster; urgency=medium
+
+  * Team upload.
+  * Fix CVE-2021-29425:
+    When invoking the method FileNameUtils.normalize with an improper input
+    string, like "//../foo", or "\\..\foo", the result would be the same
+    value, thus possibly providing access to files in the parent directory,
+    but not further above (thus "limited" path traversal), if the calling code
+    would use the result to construct a path value.
+
+ -- Markus Koschany <apo@debian.org>  Fri, 20 Aug 2021 22:25:28 +0200
+
 commons-io (2.6-2) unstable; urgency=medium
 
   * Team upload.
diff -Nru commons-io-2.6/debian/patches/CVE-2021-29425.patch commons-io-2.6/debian/patches/CVE-2021-29425.patch
--- commons-io-2.6/debian/patches/CVE-2021-29425.patch	1970-01-01 01:00:00.000000000 +0100
+++ commons-io-2.6/debian/patches/CVE-2021-29425.patch	2021-08-20 22:25:28.000000000 +0200
@@ -0,0 +1,245 @@
+From: Markus Koschany <apo@debian.org>
+Date: Mon, 2 Aug 2021 12:30:01 +0200
+Subject: CVE-2021-29425
+
+Origin: https://github.com/apache/commons-io/pull/52
+---
+ .../java/org/apache/commons/io/FilenameUtils.java  | 156 ++++++++++++++++++++-
+ .../apache/commons/io/FilenameUtilsTestCase.java   |  36 +++++
+ 2 files changed, 191 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 9cddebb..f3bd5a6 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,152 @@ public class FilenameUtils {
+         return list.toArray( new String[ list.size() ] );
+     }
+ 
++    /**
++     * Checks whether a given string is a valid host name according to
++     * RFC 3986.
++     *
++     * <p>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.</p>
++     *
++     * @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 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;
++
++    /**
++     * 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);
++        if (!m.matches() || m.groupCount() != 4) {
++            return false;
++        }
++
++        // verify that address subgroups are legal
++        for (int i = 1; i <= 4; i++) {
++            String ipSegment = m.group(i);
++            int iIpSegment = Integer.parseInt(ipSegment);
++            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
++    /**
++     * 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("::"))) {
++            return false;
++        }
++        if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::"))
++                || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) {
++            return false;
++        }
++        String[] octets = inet6Address.split(":");
++        if (containsCompressedZeroes) {
++            List<String> octetList = new ArrayList<String>(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-]*$");
++
++    /**
++     * 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++) {
++            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/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java b/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
+index 234c25e..a8ede91 100644
+--- a/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
++++ b/src/test/java/org/apache/commons/io/FilenameUtilsTestCase.java
+@@ -244,6 +244,33 @@ public class FilenameUtilsTestCase {
+         assertEquals(null, FilenameUtils.normalize("//server/../a"));
+         assertEquals(null, FilenameUtils.normalize("//server/.."));
+         assertEquals(SEP + SEP + "server" + SEP + "", FilenameUtils.normalize("//server/"));
++
++        assertEquals(SEP + SEP + "127.0.0.1" + SEP + "a" + SEP + "b" + SEP + "c.txt", FilenameUtils.normalize("\\\\127.0.0.1\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "::1" + SEP + "a" + SEP + "b" + SEP + "c.txt", FilenameUtils.normalize("\\\\::1\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "1::" + SEP + "a" + SEP + "b" + SEP + "c.txt", FilenameUtils.normalize("\\\\1::\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "server.example.org" + SEP + "a" + SEP + "b" + SEP + "c.txt", FilenameUtils.normalize("\\\\server.example.org\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "server.sub.example.org" + SEP + "a" + SEP + "b" + SEP + "c.txt", FilenameUtils.normalize("\\\\server.sub.example.org\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "server." + SEP + "a" + SEP + "b" + SEP + "c.txt", FilenameUtils.normalize("\\\\server.\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "1::127.0.0.1" + SEP + "a" + SEP + "b" + SEP + "c.txt",
++            FilenameUtils.normalize("\\\\1::127.0.0.1\\a\\b\\c.txt"));
++
++        // not valid IPv4 addresses but technically a valid "reg-name"s according to RFC1034
++        assertEquals(SEP + SEP + "127.0.0.256" + SEP + "a" + SEP + "b" + SEP + "c.txt",
++            FilenameUtils.normalize("\\\\127.0.0.256\\a\\b\\c.txt"));
++        assertEquals(SEP + SEP + "127.0.0.01" + SEP + "a" + SEP + "b" + SEP + "c.txt",
++            FilenameUtils.normalize("\\\\127.0.0.01\\a\\b\\c.txt"));
++
++        assertEquals(null, FilenameUtils.normalize("\\\\-server\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\.\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\..\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\127.0..1\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\::1::2\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\:1\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\1:\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\1:2:3:4:5:6:7:8:9\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\g:2:3:4:5:6:7:8\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\1ffff:2:3:4:5:6:7:8\\a\\b\\c.txt"));
++        assertEquals(null, FilenameUtils.normalize("\\\\1:2\\a\\b\\c.txt"));
+     }
+ 
+     @Test
+@@ -560,6 +587,15 @@ public class FilenameUtilsTestCase {
+         assertEquals(1, FilenameUtils.getPrefixLength("/:foo"));
+         assertEquals(1, FilenameUtils.getPrefixLength("/:/"));
+         assertEquals(1, FilenameUtils.getPrefixLength("/:::::::.txt"));
++
++        assertEquals(12, FilenameUtils.getPrefixLength("\\\\127.0.0.1\\a\\b\\c.txt"));
++        assertEquals(6, FilenameUtils.getPrefixLength("\\\\::1\\a\\b\\c.txt"));
++        assertEquals(21, FilenameUtils.getPrefixLength("\\\\server.example.org\\a\\b\\c.txt"));
++        assertEquals(10, FilenameUtils.getPrefixLength("\\\\server.\\a\\b\\c.txt"));
++
++        assertEquals(-1, FilenameUtils.getPrefixLength("\\\\-server\\a\\b\\c.txt"));
++        assertEquals(-1, FilenameUtils.getPrefixLength("\\\\.\\a\\b\\c.txt"));
++        assertEquals(-1, FilenameUtils.getPrefixLength("\\\\..\\a\\b\\c.txt"));
+     }
+ 
+     @Test
diff -Nru commons-io-2.6/debian/patches/series commons-io-2.6/debian/patches/series
--- commons-io-2.6/debian/patches/series	1970-01-01 01:00:00.000000000 +0100
+++ commons-io-2.6/debian/patches/series	2021-08-20 22:25:28.000000000 +0200
@@ -0,0 +1 @@
+CVE-2021-29425.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 10.11

Hi,

The updates relating to these bugs were included in this morning's
10.11 point release for buster.

Regards,

Adam

--- End Message ---

Reply to: