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.
| Package | Affected versions | Patched versions |
|---|---|---|
keystonePyPI | < 8.0.0a0 | 8.0.0a0 |
Affected products
4- cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*
Patches
252714633c9a4Adds a whitelist for endpoint catalog substitution
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 addedkeystone/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
2989ff257e4fAdds a whitelist for endpoint catalog substitution
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- www.openwall.com/lists/oss-security/2014/09/16/10nvdMailing ListPatchThird Party AdvisoryWEB
- bugs.launchpad.net/keystone/+bug/1354208nvdExploitIssue TrackingThird Party AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2014-1688.htmlnvdThird Party AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2014-1789.htmlnvdThird Party AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2014-1790.htmlnvdThird Party AdvisoryWEB
- www.ubuntu.com/usn/USN-2406-1nvdThird Party AdvisoryWEB
- github.com/advisories/GHSA-8v8f-vc72-pmhcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2014-3621ghsaADVISORY
- access.redhat.com/errata/RHSA-2014:1688ghsaWEB
- access.redhat.com/errata/RHSA-2014:1789ghsaWEB
- access.redhat.com/errata/RHSA-2014:1790ghsaWEB
- access.redhat.com/security/cve/CVE-2014-3621ghsaWEB
- bugzilla.redhat.com/show_bug.cgighsaWEB
- github.com/openstack/keystone/commit/2989ff257e4fde6a168e25b926805e700406aa80ghsaWEB
- github.com/openstack/keystone/commit/52714633c9a4dae5e60279217090859aa6dbcb4fghsaWEB
News mentions
0No linked articles in our index yet.