VYPR
Moderate severityNVD Advisory· Published Oct 2, 2014· Updated May 6, 2026

CVE-2014-3621

CVE-2014-3621

Description

The catalog url replacement in OpenStack Identity (Keystone) before 2013.2.3 and 2014.1 before 2014.1.2.1 allows remote authenticated users to read sensitive configuration options via a crafted endpoint, as demonstrated by "$(admin_token)" in the publicurl endpoint field.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
keystonePyPI
< 8.0.0a08.0.0a0

Affected products

4
  • cpe:2.3:a:openstack:keystone:*:*:*:*:*:*:*:*
    Range: >=2013.2,<2013.2.3
  • Red Hat/Openstack2 versions
    cpe:2.3:a:redhat:openstack:4.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:redhat:openstack:4.0:*:*:*:*:*:*:*
    • cpe:2.3:a:redhat:openstack:5.0:*:*:*:*:*:*:*
  • cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*

Patches

2
52714633c9a4

Adds a whitelist for endpoint catalog substitution

https://github.com/openstack/keystoneDavid StanekAug 15, 2014via ghsa
7 files changed · +97 2
  • etc/keystone.conf.sample+7 0 modified
    @@ -190,6 +190,13 @@
     
     # template_file = default_catalog.templates
     
    +# (Deprecated) List of possible substitutions for use in
    +# formatting endpoints. Use caution when modifying this list.
    +# It will give users with permission to create endpoints the
    +# ability to see those values in your configuration file. This
    +# option will be removed in Juno. (list value)
    +#endpoint_substitution_whitelist=tenant_id,user_id,public_bind_host,admin_bind_hostompute_hostompute_port,admin_port,public_port,public_endpoint,admin_endpoint
    +
     [endpoint_filter]
     # extension for creating associations between project and endpoints in order to
     # provide a tailored catalog for project-scoped token requests.
    
  • keystone/catalog/core.py+4 0 modified
    @@ -19,6 +19,7 @@
     
     from keystone.common import dependency
     from keystone.common import manager
    +from keystone.common import utils
     from keystone import config
     from keystone import exception
     from keystone.openstack.common import log as logging
    @@ -30,6 +31,9 @@
     
     def format_url(url, data):
         """Safely string formats a user-defined URL with the given data."""
    +    data = utils.WhiteListedItemFilter(
    +        CONF.catalog.endpoint_substitution_whitelist,
    +        data)
         try:
             result = url.replace('$(', '%(') % data
         except AttributeError:
    
  • keystone/common/config.py+12 2 modified
    @@ -260,8 +260,18 @@
             cfg.StrOpt('template_file',
                        default='default_catalog.templates'),
             cfg.StrOpt('driver',
    -                   default='keystone.catalog.backends.sql.Catalog')]}
    -
    +                   default='keystone.catalog.backends.sql.Catalog'),
    +        cfg.ListOpt('endpoint_substitution_whitelist',
    +                    default=['tenant_id', 'user_id', 'public_bind_host',
    +                             'admin_bind_host', 'compute_host', 'compute_port',
    +                             'admin_port', 'public_port', 'public_endpoint',
    +                             'admin_endpoint'],
    +                    help='(Deprecated) List of possible substitutions for use '
    +                         'in formatting endpoints. Use caution when modifying '
    +                         'this list. It will give users with permission to '
    +                         'create endpoints the ability to see those values '
    +                         'in your configuration file. This option will be '
    +                         'removed in Juno.')]}
     
     CONF = cfg.CONF
     
    
  • keystone/common/utils.py+12 0 modified
    @@ -516,3 +516,15 @@ def make_dirs(path, mode=None, user=None, group=None, log=None):
                 raise EnvironmentError("makedirs('%s'): %s" % (path, exc.strerror))
     
         set_permissions(path, mode, user, group, log)
    +
    +
    +class WhiteListedItemFilter(object):
    +
    +    def __init__(self, whitelist, data):
    +        self._whitelist = set(whitelist or [])
    +        self._data = data
    +
    +    def __getitem__(self, name):
    +        if name not in self._whitelist:
    +            raise KeyError
    +        return self._data[name]
    
  • keystone/tests/unit/catalog/__init__.py+0 0 added
  • keystone/tests/unit/catalog/test_core.py+62 0 added
    @@ -0,0 +1,62 @@
    +# Licensed under the Apache License, Version 2.0 (the "License"); you may
    +# not use this file except in compliance with the License. You may obtain
    +# a copy of the License at
    +#
    +#      http://www.apache.org/licenses/LICENSE-2.0
    +#
    +# Unless required by applicable law or agreed to in writing, software
    +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    +# License for the specific language governing permissions and limitations
    +# under the License.
    +
    +from keystone.catalog import core
    +from keystone import config
    +from keystone import exception
    +from keystone import tests
    +
    +
    +CONF = config.CONF
    +
    +
    +class FormatUrlTests(tests.TestCase):
    +
    +    def setUp(self):
    +        super(FormatUrlTests, self).setUp()
    +        whitelist = ['host', 'port', 'part1', 'part2']
    +        self.opt_in_group('catalog', endpoint_substitution_whitelist=whitelist)
    +
    +    def test_successful_formatting(self):
    +        url_template = 'http://%(host)s:%(port)d/%(part1)s/%(part2)s'
    +        values = {'host': 'server', 'port': 9090, 'part1': 'A', 'part2': 'B'}
    +        actual_url = core.format_url(url_template, values)
    +
    +        expected_url = 'http://server:9090/A/B'
    +        self.assertEqual(actual_url, expected_url)
    +
    +    def test_raises_malformed_on_missing_key(self):
    +        self.assertRaises(exception.MalformedEndpoint,
    +                          core.format_url,
    +                          "http://%(foo)s/%(bar)s",
    +                          {"foo": "1"})
    +
    +    def test_raises_malformed_on_wrong_type(self):
    +        self.assertRaises(exception.MalformedEndpoint,
    +                          core.format_url,
    +                          "http://%foo%s",
    +                          {"foo": "1"})
    +
    +    def test_raises_malformed_on_incomplete_format(self):
    +        self.assertRaises(exception.MalformedEndpoint,
    +                          core.format_url,
    +                          "http://%(foo)",
    +                          {"foo": "1"})
    +
    +    def test_substitution_with_key_not_whitelisted(self):
    +        url_template = 'http://%(host)s:%(port)d/%(part1)s/%(part2)s/%(part3)s'
    +        values = {'host': 'server', 'port': 9090,
    +                  'part1': 'A', 'part2': 'B', 'part3': 'C'}
    +        self.assertRaises(exception.MalformedEndpoint,
    +                          core.format_url,
    +                          url_template,
    +                          values)
    
  • keystone/tests/unit/__init__.py+0 0 added
2989ff257e4f

Adds a whitelist for endpoint catalog substitution

https://github.com/openstack/keystoneDavid StanekAug 15, 2014via ghsa
5 files changed · +55 0
  • etc/keystone.conf.sample+7 0 modified
    @@ -555,6 +555,13 @@
     # catalog collection. (integer value)
     #list_limit=<None>
     
    +# (Deprecated) List of possible substitutions for use in
    +# formatting endpoints. Use caution when modifying this list.
    +# It will give users with permission to create endpoints the
    +# ability to see those values in your configuration file. This
    +# option will be removed in Juno. (list value)
    +#endpoint_substitution_whitelist=tenant_id,user_id,public_bind_host,admin_bind_host,compute_host,compute_port,admin_port,public_port,public_endpoint,admin_endpoint
    +
     
     [credential]
     
    
  • keystone/catalog/core.py+4 0 modified
    @@ -23,6 +23,7 @@
     from keystone.common import dependency
     from keystone.common import driver_hints
     from keystone.common import manager
    +from keystone.common import utils
     from keystone import config
     from keystone import exception
     from keystone.i18n import _
    @@ -46,6 +47,9 @@ def format_url(url, substitutions):
         :returns: a formatted URL
     
         """
    +    substitutions = utils.WhiteListedItemFilter(
    +        CONF.catalog.endpoint_substitution_whitelist,
    +        substitutions)
         try:
             result = url.replace('$(', '%(') % substitutions
         except AttributeError:
    
  • keystone/common/config.py+11 0 modified
    @@ -792,6 +792,17 @@
             cfg.IntOpt('list_limit',
                        help='Maximum number of entities that will be returned '
                             'in a catalog collection.'),
    +        cfg.ListOpt('endpoint_substitution_whitelist',
    +                    default=['tenant_id', 'user_id', 'public_bind_host',
    +                             'admin_bind_host', 'compute_host', 'compute_port',
    +                             'admin_port', 'public_port', 'public_endpoint',
    +                             'admin_endpoint'],
    +                    help='(Deprecated) List of possible substitutions for use '
    +                         'in formatting endpoints. Use caution when modifying '
    +                         'this list. It will give users with permission to '
    +                         'create endpoints the ability to see those values '
    +                         'in your configuration file. This option will be '
    +                         'removed in Juno.'),
         ],
         'kvs': [
             cfg.ListOpt('backends', default=[],
    
  • keystone/common/utils.py+12 0 modified
    @@ -526,3 +526,15 @@ def make_dirs(path, mode=None, user=None, group=None, log=None):
                 raise EnvironmentError("makedirs('%s'): %s" % (path, exc.strerror))
     
         set_permissions(path, mode, user, group, log)
    +
    +
    +class WhiteListedItemFilter(object):
    +
    +    def __init__(self, whitelist, data):
    +        self._whitelist = set(whitelist or [])
    +        self._data = data
    +
    +    def __getitem__(self, name):
    +        if name not in self._whitelist:
    +            raise KeyError
    +        return self._data[name]
    
  • keystone/tests/unit/catalog/test_core.py+21 0 modified
    @@ -10,14 +10,26 @@
     # License for the specific language governing permissions and limitations
     # under the License.
     
    +from oslo.config import fixture as config_fixture
     import testtools
     
     from keystone.catalog import core
    +from keystone import config
     from keystone import exception
     
     
    +CONF = config.CONF
    +
    +
     class FormatUrlTests(testtools.TestCase):
     
    +    def setUp(self):
    +        super(FormatUrlTests, self).setUp()
    +        fixture = self.useFixture(config_fixture.Config(CONF))
    +        fixture.config(
    +            group='catalog',
    +            endpoint_substitution_whitelist=['host', 'port', 'part1', 'part2'])
    +
         def test_successful_formatting(self):
             url_template = 'http://%(host)s:%(port)d/%(part1)s/%(part2)s'
             values = {'host': 'server', 'port': 9090, 'part1': 'A', 'part2': 'B'}
    @@ -53,3 +65,12 @@ def _test(url_template):
     
             _test(None)
             _test(object())
    +
    +    def test_substitution_with_key_not_whitelisted(self):
    +        url_template = 'http://%(host)s:%(port)d/%(part1)s/%(part2)s/%(part3)s'
    +        values = {'host': 'server', 'port': 9090,
    +                  'part1': 'A', 'part2': 'B', 'part3': 'C'}
    +        self.assertRaises(exception.MalformedEndpoint,
    +                          core.format_url,
    +                          url_template,
    +                          values)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

15

News mentions

0

No linked articles in our index yet.