prometheanfire    15/03/11 05:56:18

  Added:                CVE-2015-0259-2014.2.2.patch
  Log:
  fixing CVE-2015-0259
  
  (Portage version: 2.2.14/cvs/Linux x86_64, signed Manifest commit with key 
0x33ED3FD25AFC78BA)

Revision  Changes    Path
1.1                  sys-cluster/nova/files/CVE-2015-0259-2014.2.2.patch

file : 
http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/CVE-2015-0259-2014.2.2.patch?rev=1.1&view=markup
plain: 
http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/CVE-2015-0259-2014.2.2.patch?rev=1.1&content-type=text/plain

Index: CVE-2015-0259-2014.2.2.patch
===================================================================
>From 8998627807f6512a8efd0902ed7c08b4273c7bcf 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.

Closes-Bug: 1409142
---
 nova/console/websocketproxy.py            |  37 ++++++++
 nova/tests/console/test_websocketproxy.py | 143 +++++++++++++++++++++++++++++-
 2 files changed, 176 insertions(+), 4 deletions(-)

diff --git a/nova/console/websocketproxy.py b/nova/console/websocketproxy.py
index ef684f5..ead557c 100644
--- a/nova/console/websocketproxy.py
+++ b/nova/console/websocketproxy.py
@@ -22,17 +22,37 @@ 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')
+
 
 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
+        else:
+            detail = _("Invalid Console Type for WebSocketProxy: '%s'") % \
+                        console_type
+            LOG.audit(detail)
+            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 +82,23 @@ class NovaProxyRequestHandlerBase(object):
         if not connect_info:
             raise Exception(_("Invalid Token"))
 
+        # Verify Origin
+        expected_origin_netloc = self.headers.getheader('Host')
+        origin_url = self.headers.getheader('Origin')
+        origin = urlparse.urlparse(origin_url)
+        origin_netloc = origin.netloc
+        origin_scheme = origin.scheme
+        if origin_netloc == '' or origin_scheme == '':
+            detail = _("Origin header not valid.")
+            raise exception.ValidationError(detail=detail)
+        if expected_origin_netloc != origin_netloc:
+            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..b125c54 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,72 @@ 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_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 +113,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 +122,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 +140,79 @@ 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_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_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.9.3 (Apple Git-50)





Reply via email to