Commit 3813847d authored by Ihar Hrachyshka's avatar Ihar Hrachyshka
Browse files

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
parent fcf342a8
From 10f637ce2ac8835f3bcf0d6fbea46070a82d8dc3 Mon Sep 17 00:00:00 2001
From: John Perkins <john.perkins@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:
From cfb4609ca6f7f55b82c20b7611fe5fd58284d412 Mon Sep 17 00:00:00 2001
From: Kevin Benton <blak111@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'
......@@ -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@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@redhat.com> 2013.2.4-7
- Readded python-pbr as dependency (was dropped during el6-havana merge).
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment