1 |
prometheanfire 14/03/27 22:34:02 |
2 |
|
3 |
Added: 2013.2.2-CVE-2014-0056.patch |
4 |
Log: |
5 |
fix for bug 505980 CVE-2014-0056 |
6 |
|
7 |
(Portage version: 2.2.8-r1/cvs/Linux x86_64, signed Manifest commit with key 0x2471eb3e40ac5ac3) |
8 |
|
9 |
Revision Changes Path |
10 |
1.1 sys-cluster/neutron/files/2013.2.2-CVE-2014-0056.patch |
11 |
|
12 |
file : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/neutron/files/2013.2.2-CVE-2014-0056.patch?rev=1.1&view=markup |
13 |
plain: http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/neutron/files/2013.2.2-CVE-2014-0056.patch?rev=1.1&content-type=text/plain |
14 |
|
15 |
Index: 2013.2.2-CVE-2014-0056.patch |
16 |
=================================================================== |
17 |
From 1faec8354a0fab953524eaeb6042ad38461a58bc Mon Sep 17 00:00:00 2001 |
18 |
From: Aaron Rosen <aaronorosen@×××××.com> |
19 |
Date: Wed, 26 Mar 2014 16:36:56 -0700 |
20 |
Subject: [PATCH] Prevent cross plugging router ports from other tenants |
21 |
|
22 |
Previously, a tenant could plug an interface into another tenant's |
23 |
router if he knew their router_id by creating a port with the correct |
24 |
device_id and device_owner. This patch prevents this from occuring |
25 |
by preventing non-admin users from creating ports with device_owner |
26 |
network:router_interface with a device_id that matches another tenants router. |
27 |
In addition, it prevents one from updating a ports device_owner and device_id |
28 |
so that the device_id won't match another tenants router with device_owner |
29 |
being network:router_interface. |
30 |
|
31 |
NOTE: with this change it does open up the possiblity for a tenant to discover |
32 |
router_id's of another tenant's by guessing them and updating a port till |
33 |
a conflict occurs. That said, randomly guessing the router id would be hard |
34 |
and in theory should not matter if exposed. We also need to allow a tenant |
35 |
to update the device_id on network:router_interface ports as this would be |
36 |
used for by anyone using a vm as a service router. This issue will be fixed in |
37 |
another patch upstream as a db migration is required but since this needs |
38 |
to be backported to all stable branches this is not possible. |
39 |
|
40 |
NOTE: The only plugins affect by this are the ones that use the l3-agent. |
41 |
|
42 |
NOTE: **One should perform and audit of the ports that are already |
43 |
attached to routers after applying this patch and remove ports |
44 |
that a tenant may have cross plugged.** |
45 |
|
46 |
Closes-bug: #1243327 |
47 |
|
48 |
Conflicts: |
49 |
neutron/common/exceptions.py |
50 |
neutron/db/db_base_plugin_v2.py |
51 |
|
52 |
Change-Id: I8bc6241f537d937e5729072dcc76871bf407cdb3 |
53 |
--- |
54 |
neutron/common/exceptions.py | 5 +++ |
55 |
neutron/db/db_base_plugin_v2.py | 62 +++++++++++++++++++++++++++++++++++ |
56 |
neutron/tests/unit/test_l3_plugin.py | 63 +++++++++++++++++++++++++++++++++++- |
57 |
3 files changed, 129 insertions(+), 1 deletion(-) |
58 |
|
59 |
diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py |
60 |
index 7b02647..88fa6e4 100644 |
61 |
--- a/neutron/common/exceptions.py |
62 |
+++ b/neutron/common/exceptions.py |
63 |
@@ -301,3 +301,8 @@ def __init__(self, **kwargs): |
64 |
|
65 |
class NetworkVxlanPortRangeError(object): |
66 |
message = _("Invalid network VXLAN port range: '%(vxlan_range)s'") |
67 |
+ |
68 |
+ |
69 |
+class DeviceIDNotOwnedByTenant(Conflict): |
70 |
+ message = _("The following device_id %(device_id)s is not owned by your " |
71 |
+ "tenant or matches another tenants router.") |
72 |
diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py |
73 |
index 2afbac5..872463f 100644 |
74 |
--- a/neutron/db/db_base_plugin_v2.py |
75 |
+++ b/neutron/db/db_base_plugin_v2.py |
76 |
@@ -27,14 +27,18 @@ |
77 |
from neutron.api.v2 import attributes |
78 |
from neutron.common import constants |
79 |
from neutron.common import exceptions as q_exc |
80 |
+from neutron import context as ctx |
81 |
from neutron.db import api as db |
82 |
from neutron.db import models_v2 |
83 |
from neutron.db import sqlalchemyutils |
84 |
+from neutron.extensions import l3 |
85 |
+from neutron import manager |
86 |
from neutron import neutron_plugin_base_v2 |
87 |
from neutron.openstack.common import excutils |
88 |
from neutron.openstack.common import log as logging |
89 |
from neutron.openstack.common import timeutils |
90 |
from neutron.openstack.common import uuidutils |
91 |
+from neutron.plugins.common import constants as service_constants |
92 |
|
93 |
|
94 |
LOG = logging.getLogger(__name__) |
95 |
@@ -1311,6 +1315,9 @@ def create_port(self, context, port): |
96 |
# NOTE(jkoelker) Get the tenant_id outside of the session to avoid |
97 |
# unneeded db action if the operation raises |
98 |
tenant_id = self._get_tenant_id_for_create(context, p) |
99 |
+ if p.get('device_owner') == constants.DEVICE_OWNER_ROUTER_INTF: |
100 |
+ self._enforce_device_owner_not_router_intf_or_device_id(context, p, |
101 |
+ tenant_id) |
102 |
|
103 |
with context.session.begin(subtransactions=True): |
104 |
network = self._get_network(context, network_id) |
105 |
@@ -1374,6 +1381,23 @@ def update_port(self, context, id, port): |
106 |
changed_ips = False |
107 |
with context.session.begin(subtransactions=True): |
108 |
port = self._get_port(context, id) |
109 |
+ if 'device_owner' in p: |
110 |
+ current_device_owner = p['device_owner'] |
111 |
+ changed_device_owner = True |
112 |
+ else: |
113 |
+ current_device_owner = port['device_owner'] |
114 |
+ changed_device_owner = False |
115 |
+ if p.get('device_id') != port['device_id']: |
116 |
+ changed_device_id = True |
117 |
+ |
118 |
+ # if the current device_owner is ROUTER_INF and the device_id or |
119 |
+ # device_owner changed check device_id is not another tenants |
120 |
+ # router |
121 |
+ if ((current_device_owner == constants.DEVICE_OWNER_ROUTER_INTF) |
122 |
+ and (changed_device_id or changed_device_owner)): |
123 |
+ self._enforce_device_owner_not_router_intf_or_device_id( |
124 |
+ context, p, port['tenant_id'], port) |
125 |
+ |
126 |
# Check if the IPs need to be updated |
127 |
if 'fixed_ips' in p: |
128 |
changed_ips = True |
129 |
@@ -1483,3 +1507,41 @@ def get_ports(self, context, filters=None, fields=None, |
130 |
|
131 |
def get_ports_count(self, context, filters=None): |
132 |
return self._get_ports_query(context, filters).count() |
133 |
+ |
134 |
+ def _enforce_device_owner_not_router_intf_or_device_id(self, context, |
135 |
+ port_request, |
136 |
+ tenant_id, |
137 |
+ db_port=None): |
138 |
+ if not context.is_admin: |
139 |
+ # find the device_id. If the call was update_port and the |
140 |
+ # device_id was not passed in we use the device_id from the |
141 |
+ # db. |
142 |
+ device_id = port_request.get('device_id') |
143 |
+ if not device_id and db_port: |
144 |
+ device_id = db_port.get('device_id') |
145 |
+ # check to make sure device_id does not match another tenants |
146 |
+ # router. |
147 |
+ if device_id: |
148 |
+ if hasattr(self, 'get_router'): |
149 |
+ try: |
150 |
+ ctx_admin = ctx.get_admin_context() |
151 |
+ router = self.get_router(ctx_admin, device_id) |
152 |
+ except l3.RouterNotFound: |
153 |
+ return |
154 |
+ else: |
155 |
+ l3plugin = ( |
156 |
+ manager.NeutronManager.get_service_plugins().get( |
157 |
+ service_constants.L3_ROUTER_NAT)) |
158 |
+ if l3plugin: |
159 |
+ try: |
160 |
+ ctx_admin = ctx.get_admin_context() |
161 |
+ router = l3plugin.get_router(ctx_admin, |
162 |
+ device_id) |
163 |
+ except l3.RouterNotFound: |
164 |
+ return |
165 |
+ else: |
166 |
+ # raise as extension doesn't support L3 anyways. |
167 |
+ raise q_exc.DeviceIDNotOwnedByTenant( |
168 |
+ device_id=device_id) |
169 |
+ if tenant_id != router['tenant_id']: |
170 |
+ raise q_exc.DeviceIDNotOwnedByTenant(device_id=device_id) |
171 |
diff --git a/neutron/tests/unit/test_l3_plugin.py b/neutron/tests/unit/test_l3_plugin.py |
172 |
index 4f75b57..9cc5cf9 100644 |
173 |
--- a/neutron/tests/unit/test_l3_plugin.py |
174 |
+++ b/neutron/tests/unit/test_l3_plugin.py |
175 |
@@ -379,7 +379,8 @@ def _remove_external_gateway_from_router(self, router_id, network_id, |
176 |
|
177 |
def _router_interface_action(self, action, router_id, subnet_id, port_id, |
178 |
expected_code=exc.HTTPOk.code, |
179 |
- expected_body=None): |
180 |
+ expected_body=None, |
181 |
+ tenant_id=None): |
182 |
interface_data = {} |
183 |
if subnet_id: |
184 |
interface_data.update({'subnet_id': subnet_id}) |
185 |
@@ -388,6 +389,10 @@ def _router_interface_action(self, action, router_id, subnet_id, port_id, |
186 |
|
187 |
req = self.new_action_request('routers', interface_data, router_id, |
188 |
"%s_router_interface" % action) |
189 |
+ # if tenant_id was specified, create a tenant context for this request |
190 |
+ if tenant_id: |
191 |
+ req.environ['neutron.context'] = context.Context( |
192 |
+ '', tenant_id) |
193 |
res = req.get_response(self.ext_api) |
194 |
self.assertEqual(res.status_int, expected_code) |
195 |
response = self.deserialize(self.fmt, res) |
196 |
@@ -968,6 +973,62 @@ def test_router_add_gateway_tenant_ctx(self): |
197 |
gw_info = body['router']['external_gateway_info'] |
198 |
self.assertEqual(gw_info, None) |
199 |
|
200 |
+ def test_create_router_port_with_device_id_of_other_teants_router(self): |
201 |
+ with self.router() as admin_router: |
202 |
+ with self.network(tenant_id='tenant_a', |
203 |
+ set_context=True) as n: |
204 |
+ with self.subnet(network=n): |
205 |
+ self._create_port( |
206 |
+ self.fmt, n['network']['id'], |
207 |
+ tenant_id='tenant_a', |
208 |
+ device_id=admin_router['router']['id'], |
209 |
+ device_owner='network:router_interface', |
210 |
+ set_context=True, |
211 |
+ expected_res_status=exc.HTTPConflict.code) |
212 |
+ |
213 |
+ def test_create_non_router_port_device_id_of_other_teants_router_update( |
214 |
+ self): |
215 |
+ # This tests that HTTPConflict is raised if we create a non-router |
216 |
+ # port that matches the device_id of another tenants router and then |
217 |
+ # we change the device_owner to be network:router_interface. |
218 |
+ with self.router() as admin_router: |
219 |
+ with self.network(tenant_id='tenant_a', |
220 |
+ set_context=True) as n: |
221 |
+ with self.subnet(network=n): |
222 |
+ port_res = self._create_port( |
223 |
+ self.fmt, n['network']['id'], |
224 |
+ tenant_id='tenant_a', |
225 |
+ device_id=admin_router['router']['id'], |
226 |
+ set_context=True) |
227 |
+ port = self.deserialize(self.fmt, port_res) |
228 |
+ neutron_context = context.Context('', 'tenant_a') |
229 |
+ data = {'port': {'device_owner': |
230 |
+ 'network:router_interface'}} |
231 |
+ self._update('ports', port['port']['id'], data, |
232 |
+ neutron_context=neutron_context, |
233 |
+ expected_code=exc.HTTPConflict.code) |
234 |
+ self._delete('ports', port['port']['id']) |
235 |
+ |
236 |
+ def test_update_port_device_id_to_different_tenants_router(self): |
237 |
+ with self.router() as admin_router: |
238 |
+ with self.router(tenant_id='tenant_a', |
239 |
+ set_context=True) as tenant_router: |
240 |
+ with self.network(tenant_id='tenant_a', |
241 |
+ set_context=True) as n: |
242 |
+ with self.subnet(network=n) as s: |
243 |
+ port = self._router_interface_action( |
244 |
+ 'add', tenant_router['router']['id'], |
245 |
+ s['subnet']['id'], None, tenant_id='tenant_a') |
246 |
+ neutron_context = context.Context('', 'tenant_a') |
247 |
+ data = {'port': |
248 |
+ {'device_id': admin_router['router']['id']}} |
249 |
+ self._update('ports', port['port_id'], data, |
250 |
+ neutron_context=neutron_context, |
251 |
+ expected_code=exc.HTTPConflict.code) |
252 |
+ self._router_interface_action( |
253 |
+ 'remove', tenant_router['router']['id'], |
254 |
+ s['subnet']['id'], None, tenant_id='tenant_a') |
255 |
+ |
256 |
def test_router_add_gateway_invalid_network_returns_404(self): |
257 |
with self.router() as r: |
258 |
self._add_external_gateway_to_router( |
259 |
-- |
260 |
1.8.5.5 |