--- a/components/openstack/nova/patches/07-CVE-2015-0259.patch Fri Feb 05 11:09:10 2016 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,336 +0,0 @@
-Upstream patch to address CVE-2015-0259. This fix will be included in
-the future 2014.2.3 (juno) release.
-
-From 676ba7bbc788a528b0fe4c87c1c4bf94b4bb6eb1 Mon Sep 17 00:00:00 2001
-From: Dave McCowan <[email protected]>
-Date: Tue, 24 Feb 2015 21:35:48 -0500
-Subject: [PATCH] Websocket Proxy should verify Origin header
-
-If the Origin HTTP header passed in the WebSocket handshake does
-not match the host, this could indicate an attempt at a
-cross-site attack. This commit adds a check to verify
-the origin matches the host.
-
-Change-Id: Ica6ec23d6f69a236657d5ba0c3f51b693c633649
-Closes-Bug: 1409142
----
- nova/console/websocketproxy.py | 45 +++++++
- nova/tests/console/test_websocketproxy.py | 185 ++++++++++++++++++++++++++++-
- 2 files changed, 226 insertions(+), 4 deletions(-)
-
-diff --git a/nova/console/websocketproxy.py b/nova/console/websocketproxy.py
-index ef684f5..7a1e056 100644
---- a/nova/console/websocketproxy.py
-+++ b/nova/console/websocketproxy.py
-@@ -22,17 +22,40 @@ import Cookie
- import socket
- import urlparse
-
-+from oslo.config import cfg
- import websockify
-
- from nova.consoleauth import rpcapi as consoleauth_rpcapi
- from nova import context
-+from nova import exception
- from nova.i18n import _
- from nova.openstack.common import log as logging
-
- LOG = logging.getLogger(__name__)
-
-+CONF = cfg.CONF
-+CONF.import_opt('novncproxy_base_url', 'nova.vnc')
-+CONF.import_opt('html5proxy_base_url', 'nova.spice', group='spice')
-+CONF.import_opt('base_url', 'nova.console.serial', group='serial_console')
-+
-
- class NovaProxyRequestHandlerBase(object):
-+ def verify_origin_proto(self, console_type, origin_proto):
-+ if console_type == 'novnc':
-+ expected_proto = \
-+ urlparse.urlparse(CONF.novncproxy_base_url).scheme
-+ elif console_type == 'spice-html5':
-+ expected_proto = \
-+ urlparse.urlparse(CONF.spice.html5proxy_base_url).scheme
-+ elif console_type == 'serial':
-+ expected_proto = \
-+ urlparse.urlparse(CONF.serial_console.base_url).scheme
-+ else:
-+ detail = _("Invalid Console Type for WebSocketProxy: '%s'") % \
-+ console_type
-+ raise exception.ValidationError(detail=detail)
-+ return origin_proto == expected_proto
-+
- def new_websocket_client(self):
- """Called after a new WebSocket connection has been established."""
- # Reopen the eventlet hub to make sure we don't share an epoll
-@@ -62,6 +85,28 @@ class NovaProxyRequestHandlerBase(object):
- if not connect_info:
- raise Exception(_("Invalid Token"))
-
-+ # Verify Origin
-+ expected_origin_hostname = self.headers.getheader('Host')
-+ if ':' in expected_origin_hostname:
-+ e = expected_origin_hostname
-+ expected_origin_hostname = e.split(':')[0]
-+ origin_url = self.headers.getheader('Origin')
-+ # missing origin header indicates non-browser client which is OK
-+ if origin_url is not None:
-+ origin = urlparse.urlparse(origin_url)
-+ origin_hostname = origin.hostname
-+ origin_scheme = origin.scheme
-+ if origin_hostname == '' or origin_scheme == '':
-+ detail = _("Origin header not valid.")
-+ raise exception.ValidationError(detail=detail)
-+ if expected_origin_hostname != origin_hostname:
-+ detail = _("Origin header does not match this host.")
-+ raise exception.ValidationError(detail=detail)
-+ if not self.verify_origin_proto(connect_info['console_type'],
-+ origin.scheme):
-+ detail = _("Origin header protocol does not match this host.")
-+ raise exception.ValidationError(detail=detail)
-+
- self.msg(_('connect info: %s'), str(connect_info))
- host = connect_info['host']
- port = int(connect_info['port'])
-diff --git a/nova/tests/console/test_websocketproxy.py b/nova/tests/console/test_websocketproxy.py
-index 1e51a4d..66913c2 100644
---- a/nova/tests/console/test_websocketproxy.py
-+++ b/nova/tests/console/test_websocketproxy.py
-@@ -16,10 +16,14 @@
-
-
- import mock
-+from oslo.config import cfg
-
- from nova.console import websocketproxy
-+from nova import exception
- from nova import test
-
-+CONF = cfg.CONF
-+
-
- class NovaProxyRequestHandlerBaseTestCase(test.TestCase):
-
-@@ -31,15 +35,82 @@ class NovaProxyRequestHandlerBaseTestCase(test.TestCase):
- self.wh.msg = mock.MagicMock()
- self.wh.do_proxy = mock.MagicMock()
- self.wh.headers = mock.MagicMock()
-+ CONF.set_override('novncproxy_base_url',
-+ 'https://example.net:6080/vnc_auto.html')
-+ CONF.set_override('html5proxy_base_url',
-+ 'https://example.net:6080/vnc_auto.html',
-+ 'spice')
-+
-+ def _fake_getheader(self, header):
-+ if header == 'cookie':
-+ return 'token="123-456-789"'
-+ elif header == 'Origin':
-+ return 'https://example.net:6080'
-+ elif header == 'Host':
-+ return 'example.net:6080'
-+ else:
-+ return
-+
-+ def _fake_getheader_bad_token(self, header):
-+ if header == 'cookie':
-+ return 'token="XXX"'
-+ elif header == 'Origin':
-+ return 'https://example.net:6080'
-+ elif header == 'Host':
-+ return 'example.net:6080'
-+ else:
-+ return
-+
-+ def _fake_getheader_bad_origin(self, header):
-+ if header == 'cookie':
-+ return 'token="123-456-789"'
-+ elif header == 'Origin':
-+ return 'https://bad-origin-example.net:6080'
-+ elif header == 'Host':
-+ return 'example.net:6080'
-+ else:
-+ return
-+
-+ def _fake_getheader_blank_origin(self, header):
-+ if header == 'cookie':
-+ return 'token="123-456-789"'
-+ elif header == 'Origin':
-+ return ''
-+ elif header == 'Host':
-+ return 'example.net:6080'
-+ else:
-+ return
-+
-+ def _fake_getheader_no_origin(self, header):
-+ if header == 'cookie':
-+ return 'token="123-456-789"'
-+ elif header == 'Origin':
-+ return None
-+ elif header == 'Host':
-+ return 'any-example.net:6080'
-+ else:
-+ return
-+
-+ def _fake_getheader_http(self, header):
-+ if header == 'cookie':
-+ return 'token="123-456-789"'
-+ elif header == 'Origin':
-+ return 'http://example.net:6080'
-+ elif header == 'Host':
-+ return 'example.net:6080'
-+ else:
-+ return
-
- @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
- def test_new_websocket_client(self, check_token):
- check_token.return_value = {
- 'host': 'node1',
-- 'port': '10000'
-+ 'port': '10000',
-+ 'console_type': 'novnc'
- }
- self.wh.socket.return_value = '<socket>'
- self.wh.path = "ws://127.0.0.1/?token=123-456-789"
-+ self.wh.headers.getheader = self._fake_getheader
-
- self.wh.new_websocket_client()
-
-@@ -52,6 +123,7 @@ class NovaProxyRequestHandlerBaseTestCase(test.TestCase):
- check_token.return_value = False
-
- self.wh.path = "ws://127.0.0.1/?token=XXX"
-+ self.wh.headers.getheader = self._fake_getheader
-
- self.assertRaises(Exception, self.wh.new_websocket_client) # noqa
- check_token.assert_called_with(mock.ANY, token="XXX")
-@@ -60,11 +132,12 @@ class NovaProxyRequestHandlerBaseTestCase(test.TestCase):
- def test_new_websocket_client_novnc(self, check_token):
- check_token.return_value = {
- 'host': 'node1',
-- 'port': '10000'
-+ 'port': '10000',
-+ 'console_type': 'novnc'
- }
- self.wh.socket.return_value = '<socket>'
- self.wh.path = "http://127.0.0.1/"
-- self.wh.headers.getheader.return_value = "token=123-456-789"
-+ self.wh.headers.getheader = self._fake_getheader
-
- self.wh.new_websocket_client()
-
-@@ -77,7 +150,111 @@ class NovaProxyRequestHandlerBaseTestCase(test.TestCase):
- check_token.return_value = False
-
- self.wh.path = "http://127.0.0.1/"
-- self.wh.headers.getheader.return_value = "token=XXX"
-+ self.wh.headers.getheader = self._fake_getheader_bad_token
-
- self.assertRaises(Exception, self.wh.new_websocket_client) # noqa
- check_token.assert_called_with(mock.ANY, token="XXX")
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_bad_origin_header(self, check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'novnc'
-+ }
-+
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader_bad_origin
-+
-+ self.assertRaises(exception.ValidationError,
-+ self.wh.new_websocket_client)
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_blank_origin_header(self, check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'novnc'
-+ }
-+
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader_blank_origin
-+
-+ self.assertRaises(exception.ValidationError,
-+ self.wh.new_websocket_client)
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_no_origin_header(self, check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'novnc'
-+ }
-+ self.wh.socket.return_value = '<socket>'
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader_no_origin
-+
-+ self.wh.new_websocket_client()
-+
-+ check_token.assert_called_with(mock.ANY, token="123-456-789")
-+ self.wh.socket.assert_called_with('node1', 10000, connect=True)
-+ self.wh.do_proxy.assert_called_with('<socket>')
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_bad_origin_proto_vnc(self,
-+ check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'novnc'
-+ }
-+
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader_http
-+
-+ self.assertRaises(exception.ValidationError,
-+ self.wh.new_websocket_client)
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_bad_origin_proto_spice(self,
-+ check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'spice-html5'
-+ }
-+
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader_http
-+
-+ self.assertRaises(exception.ValidationError,
-+ self.wh.new_websocket_client)
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_bad_origin_proto_serial(self,
-+ check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'serial'
-+ }
-+
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader_http
-+
-+ self.assertRaises(exception.ValidationError,
-+ self.wh.new_websocket_client)
-+
-+ @mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
-+ def test_new_websocket_client_novnc_bad_console_type(self, check_token):
-+ check_token.return_value = {
-+ 'host': 'node1',
-+ 'port': '10000',
-+ 'console_type': 'bad-console-type'
-+ }
-+
-+ self.wh.path = "http://127.0.0.1/"
-+ self.wh.headers.getheader = self._fake_getheader
-+
-+ self.assertRaises(exception.ValidationError,
-+ self.wh.new_websocket_client)
---
-1.7.9.5
-