CVE-2015-8749
Description
The volume_utils._parse_volume_info function in OpenStack Compute (Nova) before 2015.1.3 (kilo) and 12.0.x before 12.0.1 (liberty) includes the connection_info dictionary in the StorageError message when using the Xen backend, which might allow attackers to obtain sensitive password information by reading log files or other unspecified vectors.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
novaPyPI | >= 12.0.0, < 12.0.1 | 12.0.1 |
Affected products
1Patches
4b2acc9fa864bAdd security fixes to the release notes for 12.0.1
1 file changed · +14 −0
releasenotes/notes/12.0.1-cve-bugs-7b04b2e34a3e9a70.yaml+14 −0 added@@ -0,0 +1,14 @@ +--- +prelude: | + The 12.0.1 release contains fixes for two security issues. +security: + - | + [OSSA 2016-001] Nova host data leak through snapshot (CVE-2015-7548) + + * `Bug 1524274 <https://bugs.launchpad.net/nova/+bug/1524274>`_ + * `Announcement <http://lists.openstack.org/pipermail/openstack-announce/2016-January/000911.html>`_ + + [OSSA 2016-002] Xen connection password leak in logs via StorageError (CVE-2015-8749) + + * `Bug 1516765 <https://bugs.launchpad.net/nova/+bug/1516765>`_ + * `Announcement <http://lists.openstack.org/pipermail/openstack-announce/2016-January/000916.html>`_
ef1ccdaca951xen: mask passwords in volume connection_data dict
4 files changed · +38 −4
nova/tests/unit/virt/xenapi/test_volumeops.py+16 −0 modified@@ -381,6 +381,22 @@ def test_connect_hypervisor_to_volume_lun(self, mock_intro): mock_intro.assert_called_once_with(self.session, "sr", target_lun="lun") + @mock.patch.object(volume_utils, "introduce_vdi") + @mock.patch.object(volumeops.LOG, 'debug') + def test_connect_hypervisor_to_volume_mask_password(self, mock_debug, + mock_intro): + # Tests that the connection_data is scrubbed before logging. + data = {'auth_password': 'verybadpass'} + self.ops._connect_hypervisor_to_volume("sr", data) + self.assertTrue(mock_debug.called, 'LOG.debug was not called') + password_logged = False + for call in mock_debug.call_args_list: + # The call object is a tuple of (args, kwargs) + if 'verybadpass' in call[0]: + password_logged = True + break + self.assertFalse(password_logged, 'connection_data was not scrubbed') + @mock.patch.object(vm_utils, "is_vm_shutdown") @mock.patch.object(vm_utils, "create_vbd") def test_attach_volume_to_vm_plug(self, mock_vbd, mock_shutdown):
nova/tests/unit/virt/xenapi/test_volume_utils.py+15 −2 modified@@ -15,6 +15,7 @@ from eventlet import greenthread import mock +import six from nova import exception from nova import test @@ -168,14 +169,26 @@ def _make_connection_info(cls): 'target_lun': None, 'auth_method': 'CHAP', 'auth_username': 'username', - 'auth_password': 'password'}} + 'auth_password': 'verybadpass'}} def test_parse_volume_info_parsing_auth_details(self): conn_info = self._make_connection_info() result = volume_utils._parse_volume_info(conn_info['data']) self.assertEqual('username', result['chapuser']) - self.assertEqual('password', result['chappassword']) + self.assertEqual('verybadpass', result['chappassword']) + + def test_parse_volume_info_missing_details(self): + # Tests that a StorageError is raised if volume_id, target_host, or + # target_ign is missing from connection_data. Also ensures that the + # auth_password value is not present in the StorageError message. + for data_key_to_null in ('volume_id', 'target_portal', 'target_iqn'): + conn_info = self._make_connection_info() + conn_info['data'][data_key_to_null] = None + ex = self.assertRaises(exception.StorageError, + volume_utils._parse_volume_info, + conn_info['data']) + self.assertNotIn('verybadpass', six.text_type(ex)) def test_get_device_number_raise_exception_on_wrong_mountpoint(self): self.assertRaises(
nova/virt/xenapi/volumeops.py+5 −1 modified@@ -19,6 +19,7 @@ from oslo_log import log as logging from oslo_utils import excutils +from oslo_utils import strutils from nova import exception from nova.i18n import _LI, _LW @@ -91,7 +92,10 @@ def _connect_to_volume_provider(self, connection_data, instance_name): return (sr_ref, sr_uuid) def _connect_hypervisor_to_volume(self, sr_ref, connection_data): - LOG.debug("Connect volume to hypervisor: %s", connection_data) + # connection_data can have credentials in it so make sure to scrub + # those before logging. + LOG.debug("Connect volume to hypervisor: %s", + strutils.mask_password(connection_data)) if 'vdi_uuid' in connection_data: vdi_ref = volume_utils.introduce_vdi( self._session, sr_ref,
nova/virt/xenapi/volume_utils.py+2 −1 modified@@ -24,6 +24,7 @@ from eventlet import greenthread from oslo_config import cfg from oslo_log import log as logging +from oslo_utils import strutils from nova import exception from nova.i18n import _, _LE, _LW @@ -84,7 +85,7 @@ def _parse_volume_info(connection_data): target_iqn is None): raise exception.StorageError( reason=_('Unable to obtain target information %s') % - connection_data) + strutils.mask_password(connection_data)) volume_info = {} volume_info['id'] = volume_id volume_info['target'] = target_host
8b289237ed6dxen: mask passwords in volume connection_data dict
4 files changed · +37 −4
nova/tests/unit/virt/xenapi/test_volumeops.py+16 −0 modified@@ -381,6 +381,22 @@ def test_connect_hypervisor_to_volume_lun(self, mock_intro): mock_intro.assert_called_once_with(self.session, "sr", target_lun="lun") + @mock.patch.object(volume_utils, "introduce_vdi") + @mock.patch.object(volumeops.LOG, 'debug') + def test_connect_hypervisor_to_volume_mask_password(self, mock_debug, + mock_intro): + # Tests that the connection_data is scrubbed before logging. + data = {'auth_password': 'verybadpass'} + self.ops._connect_hypervisor_to_volume("sr", data) + self.assertTrue(mock_debug.called, 'LOG.debug was not called') + password_logged = False + for call in mock_debug.call_args_list: + # The call object is a tuple of (args, kwargs) + if 'verybadpass' in call[0]: + password_logged = True + break + self.assertFalse(password_logged, 'connection_data was not scrubbed') + @mock.patch.object(vm_utils, "is_vm_shutdown") @mock.patch.object(vm_utils, "create_vbd") def test_attach_volume_to_vm_plug(self, mock_vbd, mock_shutdown):
nova/tests/unit/virt/xenapi/test_volume_utils.py+14 −2 modified@@ -165,14 +165,26 @@ def _make_connection_info(cls): 'target_lun': None, 'auth_method': 'CHAP', 'auth_username': 'username', - 'auth_password': 'password'}} + 'auth_password': 'verybadpass'}} def test_parse_volume_info_parsing_auth_details(self): conn_info = self._make_connection_info() result = volume_utils._parse_volume_info(conn_info['data']) self.assertEqual('username', result['chapuser']) - self.assertEqual('password', result['chappassword']) + self.assertEqual('verybadpass', result['chappassword']) + + def test_parse_volume_info_missing_details(self): + # Tests that a StorageError is raised if volume_id, target_host, or + # target_ign is missing from connection_data. Also ensures that the + # auth_password value is not present in the StorageError message. + for data_key_to_null in ('volume_id', 'target_portal', 'target_iqn'): + conn_info = self._make_connection_info() + conn_info['data'][data_key_to_null] = None + ex = self.assertRaises(exception.StorageError, + volume_utils._parse_volume_info, + conn_info['data']) + self.assertNotIn('verybadpass', six.text_type(ex)) def test_get_device_number_raise_exception_on_wrong_mountpoint(self): self.assertRaises(
nova/virt/xenapi/volumeops.py+5 −1 modified@@ -19,6 +19,7 @@ from oslo_log import log as logging from oslo_utils import excutils +from oslo_utils import strutils from nova import exception from nova.i18n import _LI, _LW @@ -91,7 +92,10 @@ def _connect_to_volume_provider(self, connection_data, instance_name): return (sr_ref, sr_uuid) def _connect_hypervisor_to_volume(self, sr_ref, connection_data): - LOG.debug("Connect volume to hypervisor: %s", connection_data) + # connection_data can have credentials in it so make sure to scrub + # those before logging. + LOG.debug("Connect volume to hypervisor: %s", + strutils.mask_password(connection_data)) if 'vdi_uuid' in connection_data: vdi_ref = volume_utils.introduce_vdi( self._session, sr_ref,
nova/virt/xenapi/volume_utils.py+2 −1 modified@@ -24,6 +24,7 @@ from eventlet import greenthread from oslo_config import cfg from oslo_log import log as logging +from oslo_utils import strutils from nova import exception from nova.i18n import _, _LE, _LW @@ -84,7 +85,7 @@ def _parse_volume_info(connection_data): target_iqn is None): raise exception.StorageError( reason=_('Unable to obtain target information %s') % - connection_data) + strutils.mask_password(connection_data)) volume_info = {} volume_info['id'] = volume_id volume_info['target'] = target_host
cf197ec2d682xen: mask passwords in volume connection_data dict
4 files changed · +37 −4
nova/tests/unit/virt/xenapi/test_volumeops.py+16 −0 modified@@ -381,6 +381,22 @@ def test_connect_hypervisor_to_volume_lun(self, mock_intro): mock_intro.assert_called_once_with(self.session, "sr", target_lun="lun") + @mock.patch.object(volume_utils, "introduce_vdi") + @mock.patch.object(volumeops.LOG, 'debug') + def test_connect_hypervisor_to_volume_mask_password(self, mock_debug, + mock_intro): + # Tests that the connection_data is scrubbed before logging. + data = {'auth_password': 'verybadpass'} + self.ops._connect_hypervisor_to_volume("sr", data) + self.assertTrue(mock_debug.called, 'LOG.debug was not called') + password_logged = False + for call in mock_debug.call_args_list: + # The call object is a tuple of (args, kwargs) + if 'verybadpass' in call[0]: + password_logged = True + break + self.assertFalse(password_logged, 'connection_data was not scrubbed') + @mock.patch.object(vm_utils, "is_vm_shutdown") @mock.patch.object(vm_utils, "create_vbd") def test_attach_volume_to_vm_plug(self, mock_vbd, mock_shutdown):
nova/tests/unit/virt/xenapi/test_volume_utils.py+14 −2 modified@@ -165,14 +165,26 @@ def _make_connection_info(cls): 'target_lun': None, 'auth_method': 'CHAP', 'auth_username': 'username', - 'auth_password': 'password'}} + 'auth_password': 'verybadpass'}} def test_parse_volume_info_parsing_auth_details(self): conn_info = self._make_connection_info() result = volume_utils._parse_volume_info(conn_info['data']) self.assertEqual('username', result['chapuser']) - self.assertEqual('password', result['chappassword']) + self.assertEqual('verybadpass', result['chappassword']) + + def test_parse_volume_info_missing_details(self): + # Tests that a StorageError is raised if volume_id, target_host, or + # target_ign is missing from connection_data. Also ensures that the + # auth_password value is not present in the StorageError message. + for data_key_to_null in ('volume_id', 'target_portal', 'target_iqn'): + conn_info = self._make_connection_info() + conn_info['data'][data_key_to_null] = None + ex = self.assertRaises(exception.StorageError, + volume_utils._parse_volume_info, + conn_info['data']) + self.assertNotIn('verybadpass', six.text_type(ex)) def test_get_device_number_raise_exception_on_wrong_mountpoint(self): self.assertRaises(
nova/virt/xenapi/volumeops.py+5 −1 modified@@ -19,6 +19,7 @@ from oslo_log import log as logging from oslo_utils import excutils +from oslo_utils import strutils from nova import exception from nova.i18n import _LI, _LW @@ -91,7 +92,10 @@ def _connect_to_volume_provider(self, connection_data, instance_name): return (sr_ref, sr_uuid) def _connect_hypervisor_to_volume(self, sr_ref, connection_data): - LOG.debug("Connect volume to hypervisor: %s", connection_data) + # connection_data can have credentials in it so make sure to scrub + # those before logging. + LOG.debug("Connect volume to hypervisor: %s", + strutils.mask_password(connection_data)) if 'vdi_uuid' in connection_data: vdi_ref = volume_utils.introduce_vdi( self._session, sr_ref,
nova/virt/xenapi/volume_utils.py+2 −1 modified@@ -24,6 +24,7 @@ from eventlet import greenthread from oslo_config import cfg from oslo_log import log as logging +from oslo_utils import strutils from nova import exception from nova.i18n import _, _LE, _LW @@ -84,7 +85,7 @@ def _parse_volume_info(connection_data): target_iqn is None): raise exception.StorageError( reason=_('Unable to obtain target information %s') % - connection_data) + strutils.mask_password(connection_data)) volume_info = {} volume_info['id'] = volume_id volume_info['target'] = target_host
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
11- security.openstack.org/ossa/OSSA-2016-002.htmlnvdPatchVendor AdvisoryWEB
- www.openwall.com/lists/oss-security/2016/01/07/8nvdMailing ListThird Party AdvisoryWEB
- www.openwall.com/lists/oss-security/2016/01/07/9nvdMailing ListThird Party AdvisoryWEB
- www.securityfocus.com/bid/80189nvdThird Party AdvisoryVDB EntryWEB
- bugs.launchpad.net/nova/+bug/1516765nvdThird Party AdvisoryWEB
- github.com/advisories/GHSA-c36r-g737-9qp8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2015-8749ghsaADVISORY
- github.com/openstack/nova/commit/8b289237ed6d53738c22878decf0c429301cf3d0ghsaWEB
- github.com/openstack/nova/commit/b2acc9fa864b6fe10bc0c5f3786b976b472b1b27ghsaWEB
- github.com/openstack/nova/commit/cf197ec2d682fb4da777df2291ca7ef101f73b77ghsaWEB
- github.com/openstack/nova/commit/ef1ccdaca9512b88878155f7d8c2c77853d91252ghsaWEB
News mentions
0No linked articles in our index yet.