|
1 This upstream patch addresses CVE-2014-0187 and is tracked under |
|
2 Launchpad bug 1300785. It is addressed in Icehouse 2014.1.2 and Havana |
|
3 2013.2.4. |
|
4 |
|
5 commit 03eed8cd34cd4fb043c11fc99f6bb0b4fbd5728d |
|
6 Author: marios <[email protected]> |
|
7 Date: Fri Nov 29 18:23:54 2013 +0200 |
|
8 |
|
9 Validate CIDR given as ip-prefix in security-group-rule-create |
|
10 |
|
11 There was no validation for the provided ip prefix. This just adds |
|
12 a simple parse using netaddr and explodes with appropriate message. |
|
13 Also makes sure ip prefix _is_ cidr (192.168.1.1-->192.168.1.1/32). |
|
14 |
|
15 Validation occurs at the attribute level (API model) as well as at |
|
16 the db level, where the ethertype is validated against the ip_prefix |
|
17 address type. |
|
18 |
|
19 Unit test cases added - bad prefix, unmasked prefix and incorrect |
|
20 ethertype. Also adds attribute test cases for the added |
|
21 convert_ip_prefix_to_cidr method |
|
22 |
|
23 Closes-Bug: 1255338 |
|
24 |
|
25 Conflicts: |
|
26 neutron/tests/unit/test_security_groups_rpc.py |
|
27 neutron/tests/unit/test_extension_security_group.py |
|
28 |
|
29 Change-Id: I71fb8c887963a122a5bd8cfdda800026c1cd3954 |
|
30 (cherry picked from commit 65aa92b0348b7ab8413f359b00825610cdf66607) |
|
31 |
|
32 diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py |
|
33 index 88fa6e4..80a75d1 100644 |
|
34 --- a/neutron/common/exceptions.py |
|
35 +++ b/neutron/common/exceptions.py |
|
36 @@ -306,3 +306,7 @@ class NetworkVxlanPortRangeError(object): |
|
37 class DeviceIDNotOwnedByTenant(Conflict): |
|
38 message = _("The following device_id %(device_id)s is not owned by your " |
|
39 "tenant or matches another tenants router.") |
|
40 + |
|
41 + |
|
42 +class InvalidCIDR(BadRequest): |
|
43 + message = _("Invalid CIDR %(input)s given as IP prefix") |
|
44 diff --git a/neutron/db/securitygroups_db.py b/neutron/db/securitygroups_db.py |
|
45 index 2a7d2ef..8868546 100644 |
|
46 --- a/neutron/db/securitygroups_db.py |
|
47 +++ b/neutron/db/securitygroups_db.py |
|
48 @@ -16,6 +16,7 @@ |
|
49 # |
|
50 # @author: Aaron Rosen, Nicira, Inc |
|
51 |
|
52 +import netaddr |
|
53 import sqlalchemy as sa |
|
54 from sqlalchemy import orm |
|
55 from sqlalchemy.orm import exc |
|
56 @@ -331,6 +332,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): |
|
57 new_rules.add(rule['security_group_id']) |
|
58 |
|
59 self._validate_port_range(rule) |
|
60 + self._validate_ip_prefix(rule) |
|
61 |
|
62 if rule['remote_ip_prefix'] and rule['remote_group_id']: |
|
63 raise ext_sg.SecurityGroupRemoteGroupAndRemoteIpPrefix() |
|
64 @@ -411,6 +413,24 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): |
|
65 if (i['security_group_rule'] == db_rule): |
|
66 raise ext_sg.SecurityGroupRuleExists(id=id) |
|
67 |
|
68 + def _validate_ip_prefix(self, rule): |
|
69 + """Check that a valid cidr was specified as remote_ip_prefix |
|
70 + |
|
71 + No need to check that it is in fact an IP address as this is already |
|
72 + validated by attribute validators. |
|
73 + Check that rule ethertype is consistent with remote_ip_prefix ip type. |
|
74 + Add mask to ip_prefix if absent (192.168.1.10 -> 192.168.1.10/32). |
|
75 + """ |
|
76 + input_prefix = rule['remote_ip_prefix'] |
|
77 + if input_prefix: |
|
78 + addr = netaddr.IPNetwork(input_prefix) |
|
79 + # set input_prefix to always include the netmask: |
|
80 + rule['remote_ip_prefix'] = str(addr) |
|
81 + # check consistency of ethertype with addr version |
|
82 + if rule['ethertype'] != "IPv%d" % (addr.version): |
|
83 + raise ext_sg.SecurityGroupRuleParameterConflict( |
|
84 + ethertype=rule['ethertype'], cidr=input_prefix) |
|
85 + |
|
86 def get_security_group_rules(self, context, filters=None, fields=None, |
|
87 sorts=None, limit=None, marker=None, |
|
88 page_reverse=False): |
|
89 diff --git a/neutron/extensions/securitygroup.py b/neutron/extensions/securitygroup.py |
|
90 index 85d499a..3d10b5a 100644 |
|
91 --- a/neutron/extensions/securitygroup.py |
|
92 +++ b/neutron/extensions/securitygroup.py |
|
93 @@ -17,6 +17,7 @@ |
|
94 |
|
95 from abc import ABCMeta |
|
96 from abc import abstractmethod |
|
97 +import netaddr |
|
98 |
|
99 from oslo.config import cfg |
|
100 |
|
101 @@ -102,6 +103,10 @@ class SecurityGroupRuleExists(qexception.InUse): |
|
102 message = _("Security group rule already exists. Group id is %(id)s.") |
|
103 |
|
104 |
|
105 +class SecurityGroupRuleParameterConflict(qexception.InvalidInput): |
|
106 + message = _("Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s") |
|
107 + |
|
108 + |
|
109 def convert_protocol(value): |
|
110 if value is None: |
|
111 return |
|
112 @@ -152,6 +157,16 @@ def convert_to_uuid_list_or_none(value_list): |
|
113 return value_list |
|
114 |
|
115 |
|
116 +def convert_ip_prefix_to_cidr(ip_prefix): |
|
117 + if not ip_prefix: |
|
118 + return |
|
119 + try: |
|
120 + cidr = netaddr.IPNetwork(ip_prefix) |
|
121 + return str(cidr) |
|
122 + except (TypeError, netaddr.AddrFormatError): |
|
123 + raise qexception.InvalidCIDR(input=ip_prefix) |
|
124 + |
|
125 + |
|
126 def _validate_name_not_default(data, valid_values=None): |
|
127 if data == "default": |
|
128 raise SecurityGroupDefaultAlreadyExists() |
|
129 @@ -207,7 +222,8 @@ RESOURCE_ATTRIBUTE_MAP = { |
|
130 'convert_to': convert_ethertype_to_case_insensitive, |
|
131 'validate': {'type:values': sg_supported_ethertypes}}, |
|
132 'remote_ip_prefix': {'allow_post': True, 'allow_put': False, |
|
133 - 'default': None, 'is_visible': True}, |
|
134 + 'default': None, 'is_visible': True, |
|
135 + 'convert_to': convert_ip_prefix_to_cidr}, |
|
136 'tenant_id': {'allow_post': True, 'allow_put': False, |
|
137 'required_by_policy': True, |
|
138 'is_visible': True}, |
|
139 diff --git a/neutron/tests/unit/test_extension_security_group.py b/neutron/tests/unit/test_extension_security_group.py |
|
140 index d53e140..f0b1636 100644 |
|
141 --- a/neutron/tests/unit/test_extension_security_group.py |
|
142 +++ b/neutron/tests/unit/test_extension_security_group.py |
|
143 @@ -21,11 +21,13 @@ import webob.exc |
|
144 |
|
145 from neutron.api.v2 import attributes as attr |
|
146 from neutron.common import constants as const |
|
147 +from neutron.common import exceptions as n_exc |
|
148 from neutron.common.test_lib import test_config |
|
149 from neutron import context |
|
150 from neutron.db import db_base_plugin_v2 |
|
151 from neutron.db import securitygroups_db |
|
152 from neutron.extensions import securitygroup as ext_sg |
|
153 +from neutron.tests import base |
|
154 from neutron.tests.unit import test_db_plugin |
|
155 |
|
156 DB_PLUGIN_KLASS = ('neutron.tests.unit.test_extension_security_group.' |
|
157 @@ -413,6 +415,70 @@ class TestSecurityGroups(SecurityGroupDBTestCase): |
|
158 self.deserialize(self.fmt, res) |
|
159 self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) |
|
160 |
|
161 + def test_create_security_group_rule_invalid_ip_prefix(self): |
|
162 + name = 'webservers' |
|
163 + description = 'my webservers' |
|
164 + for bad_prefix in ['bad_ip', 256, "2001:db8:a::123/129", '172.30./24']: |
|
165 + with self.security_group(name, description) as sg: |
|
166 + sg_id = sg['security_group']['id'] |
|
167 + remote_ip_prefix = bad_prefix |
|
168 + rule = self._build_security_group_rule( |
|
169 + sg_id, |
|
170 + 'ingress', |
|
171 + const.PROTO_NAME_TCP, |
|
172 + '22', '22', |
|
173 + remote_ip_prefix) |
|
174 + res = self._create_security_group_rule(self.fmt, rule) |
|
175 + self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) |
|
176 + |
|
177 + def test_create_security_group_rule_invalid_ethertype_for_prefix(self): |
|
178 + name = 'webservers' |
|
179 + description = 'my webservers' |
|
180 + test_addr = {'192.168.1.1/24': 'ipv4', '192.168.1.1/24': 'IPv6', |
|
181 + '2001:db8:1234::/48': 'ipv6', |
|
182 + '2001:db8:1234::/48': 'IPv4'} |
|
183 + for prefix, ether in test_addr.iteritems(): |
|
184 + with self.security_group(name, description) as sg: |
|
185 + sg_id = sg['security_group']['id'] |
|
186 + ethertype = ether |
|
187 + remote_ip_prefix = prefix |
|
188 + rule = self._build_security_group_rule( |
|
189 + sg_id, |
|
190 + 'ingress', |
|
191 + const.PROTO_NAME_TCP, |
|
192 + '22', '22', |
|
193 + remote_ip_prefix, |
|
194 + None, |
|
195 + None, |
|
196 + ethertype) |
|
197 + res = self._create_security_group_rule(self.fmt, rule) |
|
198 + self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) |
|
199 + |
|
200 + def test_create_security_group_rule_with_unmasked_prefix(self): |
|
201 + name = 'webservers' |
|
202 + description = 'my webservers' |
|
203 + addr = {'10.1.2.3': {'mask': '32', 'ethertype': 'IPv4'}, |
|
204 + 'fe80::2677:3ff:fe7d:4c': {'mask': '128', 'ethertype': 'IPv6'}} |
|
205 + for ip in addr: |
|
206 + with self.security_group(name, description) as sg: |
|
207 + sg_id = sg['security_group']['id'] |
|
208 + ethertype = addr[ip]['ethertype'] |
|
209 + remote_ip_prefix = ip |
|
210 + rule = self._build_security_group_rule( |
|
211 + sg_id, |
|
212 + 'ingress', |
|
213 + const.PROTO_NAME_TCP, |
|
214 + '22', '22', |
|
215 + remote_ip_prefix, |
|
216 + None, |
|
217 + None, |
|
218 + ethertype) |
|
219 + res = self._create_security_group_rule(self.fmt, rule) |
|
220 + self.assertEqual(res.status_int, 201) |
|
221 + res_sg = self.deserialize(self.fmt, res) |
|
222 + prefix = res_sg['security_group_rule']['remote_ip_prefix'] |
|
223 + self.assertEqual(prefix, '%s/%s' % (ip, addr[ip]['mask'])) |
|
224 + |
|
225 def test_create_security_group_rule_tcp_protocol_as_number(self): |
|
226 name = 'webservers' |
|
227 description = 'my webservers' |
|
228 @@ -1348,5 +1414,25 @@ class TestSecurityGroups(SecurityGroupDBTestCase): |
|
229 self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) |
|
230 |
|
231 |
|
232 +class TestConvertIPPrefixToCIDR(base.BaseTestCase): |
|
233 + |
|
234 + def test_convert_bad_ip_prefix_to_cidr(self): |
|
235 + for val in ['bad_ip', 256, "2001:db8:a::123/129"]: |
|
236 + self.assertRaises(n_exc.InvalidCIDR, |
|
237 + ext_sg.convert_ip_prefix_to_cidr, val) |
|
238 + self.assertIsNone(ext_sg.convert_ip_prefix_to_cidr(None)) |
|
239 + |
|
240 + def test_convert_ip_prefix_no_netmask_to_cidr(self): |
|
241 + addr = {'10.1.2.3': '32', 'fe80::2677:3ff:fe7d:4c': '128'} |
|
242 + for k, v in addr.iteritems(): |
|
243 + self.assertEqual(ext_sg.convert_ip_prefix_to_cidr(k), |
|
244 + '%s/%s' % (k, v)) |
|
245 + |
|
246 + def test_convert_ip_prefix_with_netmask_to_cidr(self): |
|
247 + addresses = ['10.1.0.0/16', '10.1.2.3/32', '2001:db8:1234::/48'] |
|
248 + for addr in addresses: |
|
249 + self.assertEqual(ext_sg.convert_ip_prefix_to_cidr(addr), addr) |
|
250 + |
|
251 + |
|
252 class TestSecurityGroupsXML(TestSecurityGroups): |
|
253 fmt = 'xml' |