ihrachyshka pushed to openstack-neutron (f20). "CVE-2014-7821: Fix hostname validation for nameservers, rhbz#1165887 (..more)"

notifications at fedoraproject.org notifications at fedoraproject.org
Thu Apr 9 18:10:52 UTC 2015


>From 3813847d4d2d2b644e25e0b47c0104e05328fff3 Mon Sep 17 00:00:00 2001
From: Ihar Hrachyshka <ihrachys at redhat.com>
Date: Thu, 9 Apr 2015 20:03:25 +0200
Subject: CVE-2014-7821: Fix hostname validation for nameservers, rhbz#1165887

Resolves: rhbz#1165887

Changelog:
- CVE-2014-7821: Fix hostname validation for nameservers, rhbz#1165887
- CVE-2014-7821: Fix hostname regex pattern, rhbz#1165887

diff --git a/0006-Fix-hostname-regex-pattern.patch b/0006-Fix-hostname-regex-pattern.patch
new file mode 100644
index 0000000..594d905
--- /dev/null
+++ b/0006-Fix-hostname-regex-pattern.patch
@@ -0,0 +1,44 @@
+From 10f637ce2ac8835f3bcf0d6fbea46070a82d8dc3 Mon Sep 17 00:00:00 2001
+From: John Perkins <john.perkins at rackspace.com>
+Date: Mon, 6 Oct 2014 16:24:57 -0500
+Subject: [PATCH] Fix hostname regex pattern
+
+Current hostname_pattern regex complexity grows exponentially
+when given a string of just digits, which can be exploited to
+cause neutron-server to freeze.
+
+Change-Id: I886c6d883a9cb0acd9908495eec50bf0411d8ba8
+Closes-bug: #1378450
+(cherry picked from commit ab7ea069de5cecf1c26af50996a26e1a7f86def4)
+---
+ neutron/api/v2/attributes.py          | 4 ++--
+ neutron/tests/unit/test_attributes.py | 1 +
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py
+index 512255f..625c99c 100644
+--- a/neutron/api/v2/attributes.py
++++ b/neutron/api/v2/attributes.py
+@@ -494,8 +494,8 @@ def convert_to_list(data):
+         return [data]
+ 
+ 
+-HOSTNAME_PATTERN = ("(?=^.{1,254}$)(^(?:(?!\d+\.|-)[a-zA-Z0-9_\-]"
+-                    "{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)")
++HOSTNAME_PATTERN = ("(?=^.{1,254}$)(^(?:(?!\d+.|-)[a-zA-Z0-9_\-]{1,62}"
++                    "[a-zA-Z0-9]\.?)+(?:[a-zA-Z]{2,})$)")
+ 
+ HEX_ELEM = '[0-9A-Fa-f]'
+ UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
+diff --git a/neutron/tests/unit/test_attributes.py b/neutron/tests/unit/test_attributes.py
+index 800c1f1..ef37512 100644
+--- a/neutron/tests/unit/test_attributes.py
++++ b/neutron/tests/unit/test_attributes.py
+@@ -246,6 +246,7 @@ class TestAttributes(base.BaseTestCase):
+                     ['www.hostname.com', 'www.hostname.com'],
+                     ['77.hostname.com'],
+                     ['1000.0.0.1'],
++                    ['111111111111111111111111111111111111111111111111111111111111'],  # noqa
+                     None]
+ 
+         for ns in ns_pools:
diff --git a/0007-Fix-hostname-validation-for-nameservers.patch b/0007-Fix-hostname-validation-for-nameservers.patch
new file mode 100644
index 0000000..f8aebd2
--- /dev/null
+++ b/0007-Fix-hostname-validation-for-nameservers.patch
@@ -0,0 +1,185 @@
+From cfb4609ca6f7f55b82c20b7611fe5fd58284d412 Mon Sep 17 00:00:00 2001
+From: Kevin Benton <blak111 at gmail.com>
+Date: Thu, 27 Nov 2014 01:45:29 -0800
+Subject: [PATCH] Fix hostname validation for nameservers
+
+Fixes the hostname validation to align with the RFC's demands[1].
+This was done by replacing the full regex with a function that
+broke the FQDN into individual components that were easier to
+reason about with regular expressions.
+
+Also added several test cases for domains so if someone wants
+to convert it back to pure regex there will be better test vectors.
+
+1. RFC 1123 says an all-digit hostname is allowed in section 2.1. It
+   says that this more liberal syntax MUST be supported.
+
+Conflicts:
+	neutron/api/v2/attributes.py
+	neutron/tests/unit/test_attributes.py
+
+Closes-Bug: #1396932
+Change-Id: I003cf14d95070707e43e40d55da62e11a28dfa4e
+(cherry picked from commit 35662d07628452d14306f5197871ad64f6396ff3)
+(cherry picked from commit d39349cf47dfe9ac3f52089402d69cd1be8b013b)
+---
+ neutron/api/v2/attributes.py          | 73 ++++++++++++++++++++++++++++-------
+ neutron/tests/unit/test_attributes.py | 22 +++++++----
+ 2 files changed, 72 insertions(+), 23 deletions(-)
+
+diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py
+index 625c99c..283a3f5 100644
+--- a/neutron/api/v2/attributes.py
++++ b/neutron/api/v2/attributes.py
+@@ -206,27 +206,38 @@ def _validate_fixed_ips(data, valid_values=None):
+                 return msg
+ 
+ 
++def _validate_ip_or_hostname(host):
++    ip_err = _validate_ip_address(host)
++    if not ip_err:
++        return
++    name_err = _validate_hostname(host)
++    if not name_err:
++        return
++    msg = _("%(host)s is not a valid IP or hostname. Details: "
++            "%(ip_err)s, %(name_err)s") % {'ip_err': ip_err, 'host': host,
++                                           'name_err': name_err}
++    return msg
++
++
+ def _validate_nameservers(data, valid_values=None):
+     if not hasattr(data, '__iter__'):
+         msg = _("Invalid data format for nameserver: '%s'") % data
+         LOG.debug(msg)
+         return msg
+ 
+-    ips = []
+-    for ip in data:
+-        msg = _validate_ip_address(ip)
++    hosts = []
++    for host in data:
++        # This may be an IP or a hostname
++        msg = _validate_ip_or_hostname(host)
+         if msg:
+-            # This may be a hostname
+-            msg = _validate_regex(ip, HOSTNAME_PATTERN)
+-            if msg:
+-                msg = _("'%s' is not a valid nameserver") % ip
+-                LOG.debug(msg)
+-                return msg
+-        if ip in ips:
+-            msg = _("Duplicate nameserver '%s'") % ip
++            msg = _("'%(host)s' is not a valid nameserver. %(msg)s") % {
++                'host': host, 'msg': msg}
++            return msg
++        if host in hosts:
++            msg = _("Duplicate nameserver '%s'") % host
+             LOG.debug(msg)
+             return msg
+-        ips.append(ip)
++        hosts.append(host)
+ 
+ 
+ def _validate_hostroutes(data, valid_values=None):
+@@ -297,6 +308,41 @@ def _validate_subnet_list(data, valid_values=None):
+             return msg
+ 
+ 
++def _validate_hostname(data):
++    # NOTE: An individual name regex instead of an entire FQDN was used
++    # because its easier to make correct. Feel free to replace with a
++    # full regex solution. The logic should validate that the hostname
++    # matches RFC 1123 (section 2.1) and RFC 952.
++    hostname_pattern = "[a-zA-Z0-9-]{1,63}$"
++    try:
++        # Trailing periods are allowed to indicate that a name is fully
++        # qualified per RFC 1034 (page 7).
++        trimmed = data if data[-1] != '.' else data[:-1]
++        if len(trimmed) > 255:
++            raise TypeError(
++                _("'%s' exceeds the 255 character hostname limit") % trimmed)
++        names = trimmed.split('.')
++        for name in names:
++            if not name:
++                raise TypeError(_("Encountered an empty component."))
++            if name[-1] == '-' or name[0] == '-':
++                raise TypeError(
++                    _("Name '%s' must not start or end with a hyphen.") % name)
++            if not re.match(hostname_pattern, name):
++                raise TypeError(
++                    _("Name '%s' must be 1-63 characters long, each of "
++                      "which can only be alphanumeric or a hyphen.") % name)
++        # RFC 1123 hints that a TLD can't be all numeric. last is a TLD if
++        # it's an FQDN.
++        if len(names) > 1 and re.match("^[0-9]+$", names[-1]):
++            raise TypeError(_("TLD '%s' must not be all numeric") % names[-1])
++    except TypeError as e:
++        msg = _("'%(data)s' is not a valid hostname. Reason: %(reason)s") % {
++            'data': data, 'reason': e.message}
++        LOG.debug(msg)
++        return msg
++
++
+ def _validate_regex(data, valid_values=None):
+     try:
+         if re.match(valid_values, data):
+@@ -494,9 +540,6 @@ def convert_to_list(data):
+         return [data]
+ 
+ 
+-HOSTNAME_PATTERN = ("(?=^.{1,254}$)(^(?:(?!\d+.|-)[a-zA-Z0-9_\-]{1,62}"
+-                    "[a-zA-Z0-9]\.?)+(?:[a-zA-Z]{2,})$)")
+-
+ HEX_ELEM = '[0-9A-Fa-f]'
+ UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
+                          HEX_ELEM + '{4}', HEX_ELEM + '{4}',
+diff --git a/neutron/tests/unit/test_attributes.py b/neutron/tests/unit/test_attributes.py
+index ef37512..4dd6a5a 100644
+--- a/neutron/tests/unit/test_attributes.py
++++ b/neutron/tests/unit/test_attributes.py
+@@ -244,9 +244,7 @@ class TestAttributes(base.BaseTestCase):
+     def test_validate_nameservers(self):
+         ns_pools = [['1.1.1.2', '1.1.1.2'],
+                     ['www.hostname.com', 'www.hostname.com'],
+-                    ['77.hostname.com'],
+                     ['1000.0.0.1'],
+-                    ['111111111111111111111111111111111111111111111111111111111111'],  # noqa
+                     None]
+ 
+         for ns in ns_pools:
+@@ -257,6 +255,8 @@ class TestAttributes(base.BaseTestCase):
+                     ['www.hostname.com'],
+                     ['www.great.marathons.to.travel'],
+                     ['valid'],
++                    ['77.hostname.com'],
++                    ['1' * 59],
+                     ['www.internal.hostname.com']]
+ 
+         for ns in ns_pools:
+@@ -307,13 +307,19 @@ class TestAttributes(base.BaseTestCase):
+         self.assertEqual(msg, "'%s' is not a valid IP address" % ip_addr)
+ 
+     def test_hostname_pattern(self):
+-        data = '@openstack'
+-        msg = attributes._validate_regex(data, attributes.HOSTNAME_PATTERN)
+-        self.assertIsNotNone(msg)
++        bad_values = ['@openstack', 'ffff.abcdefg' * 26, 'f' * 80, '-hello',
++                      'goodbye-', 'example..org']
++        for data in bad_values:
++            msg = attributes._validate_hostname(data)
++            self.assertIsNotNone(msg)
+ 
+-        data = 'www.openstack.org'
+-        msg = attributes._validate_regex(data, attributes.HOSTNAME_PATTERN)
+-        self.assertIsNone(msg)
++        # All numeric hostnames are allowed per RFC 1123 section 2.1
++        good_values = ['www.openstack.org', '1234x', '1234',
++                       'openstack-1', 'v.xyz', '1' * 50, 'a1a',
++                       'x.x1x', 'x.yz', 'example.org.']
++        for data in good_values:
++            msg = attributes._validate_hostname(data)
++            self.assertIsNone(msg)
+ 
+     def test_uuid_pattern(self):
+         data = 'garbage'
diff --git a/openstack-neutron.spec b/openstack-neutron.spec
index 3fef315..cc382f8 100644
--- a/openstack-neutron.spec
+++ b/openstack-neutron.spec
@@ -2,7 +2,7 @@
 
 Name:		openstack-neutron
 Version:	2013.2.4
-Release:	7%{?dist}
+Release:	8%{?dist}
 Provides:	openstack-quantum = %{version}-%{release}
 Obsoletes:	openstack-quantum < 2013.2-0.4.b3
 Summary:	OpenStack Networking Service
@@ -24,6 +24,8 @@ Patch0002: 0002-Removed-signing_dir-from-neutron.conf.patch
 Patch0003: 0003-Notify-systemd-when-starting-Neutron-server.patch
 Patch0004: 0004-Forbid-regular-users-to-reset-admin-only-attrs-to-de.patch
 Patch0005: 0005-use-parallel-installed-versions-in-RHEL6.patch
+Patch0006: 0006-Fix-hostname-regex-pattern.patch
+Patch0007: 0007-Fix-hostname-validation-for-nameservers.patch
 
 # systemd units
 Source10:	neutron-server.service
@@ -462,6 +464,8 @@ IPSec.
 %if 0%{?rhel} == 6
 %patch0005 -p1
 %endif
+%patch0006 -p1
+%patch0007 -p1
 
 find neutron -name \*.py -exec sed -i '/\/usr\/bin\/env python/{d;q}' {} +
 
@@ -1182,6 +1186,10 @@ fi
 
 
 %changelog
+* Thu Apr 09 2015 Ihar Hrachyshka <ihrachys at redhat.com> 2013.2.4-8
+- CVE-2014-7821: Fix hostname validation for nameservers, rhbz#1165887
+- CVE-2014-7821: Fix hostname regex pattern, rhbz#1165887
+
 * Fri Oct 10 2014 Ihar Hrachyshka <ihrachys at redhat.com> 2013.2.4-7
 - Readded python-pbr as dependency (was dropped during el6-havana merge).
 
-- 
cgit v0.10.2


	http://pkgs.fedoraproject.org/cgit/openstack-neutron.git/commit/?h=f20&id=3813847d4d2d2b644e25e0b47c0104e05328fff3


More information about the scm-commits mailing list