components/openstack/neutron/patches/03-CVE-2014-0187.patch
changeset 4063 12e03e5492b8
parent 4062 f45bb9cec48c
parent 4061 5ac5027dc3e3
equal deleted inserted replaced
4062:f45bb9cec48c 4063:12e03e5492b8
     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'