File: CVE-2013-0335_VNC-proxy-can-connect-to-the-wrong-VM.patch

package info (click to toggle)
nova 2012.1.1-18
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 18,900 kB
  • sloc: python: 102,511; sql: 3,318; sh: 2,488; xml: 1,131; makefile: 146
file content (195 lines) | stat: -rw-r--r-- 8,818 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
Description: Flush tokens on instance delete
 Force console auth service to flush all tokens associated with an instance
 when it is deleted. This will fix bug 1125378, where the console for the wrong
 instance can be connected to via theconsole if the correct circumstances
 occur. This change also adds a call to validate the token when it is used.
 This check will ensure that all tokens are valid for their target instances.
 Tokens can become scrambled when a compute node is restarted, because the virt
 driver may not assign ports in the same way.
Bug-Debian: http://bugs.debian.org/701773
Bug-Ubuntu: https://launchpad.net/bugs/1125378
Author: Loganathan Parthipan <parthipan@hp.com>
Date: Sat, 23 Feb 2013 05:42:10 +0000

diff --git a/nova/compute/api.py b/nova/compute/api.py
index 633e282..a317c44 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1554,12 +1554,22 @@ class API(BaseAPI):
                            'console_type': console_type,
                            'host': connect_info['host'],
                            'port': connect_info['port'],
+                           'instance_id': instance['id'],
                            'internal_access_path':
                                    connect_info['internal_access_path']}})
 
         return {'url': connect_info['access_url']}
 
     @wrap_check_policy
+    def validate_vnc_console(self, context, instance_id, host, port):
+        """Validate VNC Console for an instance."""
+        instance = self.get(context, instance_id)
+        output = self._call_compute_message('get_vnc_console',
+                                            context,
+                                            instance)
+        return (port == output['port'] and host == output['host'])
+
+    @wrap_check_policy
     def get_console_output(self, context, instance, tail_length=None):
         """Get console output for an an instance."""
         params = {'tail_length': tail_length}
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index ca51fc5..67c6bb1 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -749,6 +749,10 @@ class ComputeManager(manager.SchedulerDependentManager):
                               terminated_at=utils.utcnow())
 
         self.db.instance_destroy(context, instance_id)
+        if FLAGS.vnc_enabled:
+            rpc.cast(context, '%s' % FLAGS.consoleauth_topic,
+                 {'method': 'delete_tokens_for_instance',
+                  'args': {'instance_id': instance['id']}})
         self._notify_about_instance_usage(instance, "delete.end")
 
     @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py
index f43dbfb..ae929c4 100644
--- a/nova/consoleauth/manager.py
+++ b/nova/consoleauth/manager.py
@@ -27,6 +27,8 @@ from nova import log as logging
 from nova import manager
 from nova.openstack.common import cfg
 from nova import utils
+from nova import exception
+from nova import compute
 
 
 LOG = logging.getLogger(__name__)
@@ -49,6 +51,7 @@ class ConsoleAuthManager(manager.Manager):
 
     def __init__(self, scheduler_driver=None, *args, **kwargs):
         super(ConsoleAuthManager, self).__init__(*args, **kwargs)
+        self.compute_api = compute.API()
         self.tokens = {}
         utils.LoopingCall(self._delete_expired_tokens).start(1)
 
@@ -64,18 +67,43 @@ class ConsoleAuthManager(manager.Manager):
             del self.tokens[k]
 
     def authorize_console(self, context, token, console_type, host, port,
-                          internal_access_path):
+                          instance_id, internal_access_path):
         self.tokens[token] = {'token': token,
                               'console_type': console_type,
                               'host': host,
                               'port': port,
+                              'instance_id': instance_id,
                               'internal_access_path': internal_access_path,
                               'last_activity_at': time.time()}
         token_dict = self.tokens[token]
+        if instance_id is not None:
+            tokens = self.tokens[instance_id]
+            tokens.append(token)
+            self.tokens[instance_id] = tokens
+
         LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals())
 
+    def _validate_console(self, context, token):
+        console_valid = False
+        token_dict = self.tokens[token]
+        try:
+            console_valid = self.compute_api.validate_vnc_console(context,
+                                                token_dict['instance_id'],
+                                                token_dict['host'],
+                                                token_dict['port'])
+        except exception.InstanceNotFound:
+            pass
+        return console_valid
+
     def check_token(self, context, token):
         token_valid = token in self.tokens
         LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals())
-        if token_valid:
+        if token_valid and _validate_console(token):
             return self.tokens[token]
+
+    def delete_tokens_for_instance(self, context, instance_id):
+        for token in self.tokens[instance_id]:
+            token_dict = self.tokens[token]
+            token_dict['last_activity_at'] = 0
+            self.tokens[token] = token_dict
+        del self.tokens[instance_id]
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 2e7e7de..b0a8b79 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -754,6 +754,47 @@ class ComputeTestCase(BaseTestCase):
         self.assert_(console)
         self.compute.terminate_instance(self.context, instance['uuid'])
 
+    def test_validate_vnc_console(self):
+        """Check if a vnc console is really for the instance"""
+        def fake(*args, **kwargs):
+            vnc = {'host': 'myhost', 'port': '5900'}
+            return vnc
+        self.stubs.Set(self.compute_api, '_call_compute_message', fake)
+        instance = self._create_instance_full()
+        self.compute.run_instance(self.context, instance['id'])
+
+        console_valid = self.compute_api.validate_vnc_console(self.context,
+                                               instance['id'],
+                                               'myhost',
+                                               '5900')
+        self.assertTrue(console_valid)
+        self.compute.terminate_instance(self.context, instance['id'])
+
+    def test_validate_vnc_console_wrong_port(self):
+        """Check if a vnc console is really for the instance"""
+        def fake(*args, **kwargs):
+            vnc = {'host': 'myhost', 'port': '5901'}
+            return vnc
+        self.stubs.Set(self.compute_api, '_call_compute_message', fake)
+        instance = self._create_instance_full()
+        self.compute.run_instance(self.context, instance['id'])
+
+        console_valid = self.compute_api.validate_vnc_console(self.context,
+                                               instance['id'],
+                                               'myhost',
+                                               '5900')
+        self.assertFalse(console_valid)
+        self.compute.terminate_instance(self.context, instance['id'])
+
+    def test_validate_vnc_console_deleted_instance(self):
+        """Check if a vnc console is really for the instance"""
+        instance = self._create_instance_full()
+        self.compute.run_instance(self.context, instance['id'])
+        self.assertRaises(exception.InstanceNotFound,
+                            self.compute_api.validate_vnc_console,
+                            self.context, 5555, 'myhost', '5900')
+        self.compute.terminate_instance(self.context, instance['id'])
+
     def test_xvpvnc_vnc_console(self):
         """Make sure we can a vnc console for an instance."""
         instance = self._create_fake_instance()
diff --git a/nova/tests/test_consoleauth.py b/nova/tests/test_consoleauth.py
index 6370a4f..13d0e74 100644
--- a/nova/tests/test_consoleauth.py
+++ b/nova/tests/test_consoleauth.py
@@ -47,6 +47,13 @@ class ConsoleauthTestCase(test.TestCase):
         """Test that tokens expire correctly."""
         token = 'mytok'
         self.flags(console_token_ttl=1)
+
+        def fake_validate_console(*args, **kwargs):
+            return True
+        self.stubs.Set(self.manager,
+                       "_validate_console",
+                       fake_validate_console)
+
         self.manager.authorize_console(self.context, token, 'novnc',
                                        '127.0.0.1', 'host', '')
         self.assertTrue(self.manager.check_token(self.context, token))
-- 
1.7.9.5