Gentoo Archives: gentoo-commits

From: "Matt Thode (prometheanfire)" <prometheanfire@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-x86 commit in sys-cluster/neutron/files: 2013.2.2-CVE-2014-0056.patch
Date: Thu, 27 Mar 2014 22:34:05
Message-Id: 20140327223402.A930D2004F@flycatcher.gentoo.org
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