1 This proposed upstream patch addresses CVE-2014-0162 and is tracked |
|
2 under Launchpad bug 1298698. Although it's been addressed in Icehouse |
|
3 2014.1, the patch below is still not yet released for Havana. |
|
4 |
|
5 commit 13069a4017d36a549576a21ca3ec5b15c411effc |
|
6 Author: Zhi Yan Liu <[email protected]> |
|
7 Date: Sat Mar 29 03:35:35 2014 +0800 |
|
8 |
|
9 To prevent remote code injection on Sheepdog store |
|
10 |
|
11 Change-Id: Iae92eaf9eb023f36a1bab7c20ea41c985f2bf51b |
|
12 Signed-off-by: Zhi Yan Liu <[email protected]> |
|
13 |
|
14 diff --git a/glance/store/sheepdog.py b/glance/store/sheepdog.py |
|
15 index d10aea7..2f75441 100644 |
|
16 --- a/glance/store/sheepdog.py |
|
17 +++ b/glance/store/sheepdog.py |
|
18 @@ -25,6 +25,7 @@ from glance.common import exception |
|
19 from glance.openstack.common import excutils |
|
20 import glance.openstack.common.log as logging |
|
21 from glance.openstack.common import processutils |
|
22 +from glance.openstack.common import uuidutils |
|
23 import glance.store |
|
24 import glance.store.base |
|
25 import glance.store.location |
|
26 @@ -32,7 +33,7 @@ import glance.store.location |
|
27 |
|
28 LOG = logging.getLogger(__name__) |
|
29 |
|
30 -DEFAULT_ADDR = 'localhost' |
|
31 +DEFAULT_ADDR = '127.0.0.1' |
|
32 DEFAULT_PORT = '7000' |
|
33 DEFAULT_CHUNKSIZE = 64 # in MiB |
|
34 |
|
35 @@ -63,18 +64,14 @@ class SheepdogImage: |
|
36 self.chunk_size = chunk_size |
|
37 |
|
38 def _run_command(self, command, data, *params): |
|
39 - cmd = ("collie vdi %(command)s -a %(addr)s -p %(port)s %(name)s " |
|
40 - "%(params)s" % |
|
41 - {"command": command, |
|
42 - "addr": self.addr, |
|
43 - "port": self.port, |
|
44 - "name": self.name, |
|
45 - "params": " ".join(map(str, params))}) |
|
46 + cmd = ["collie", "vdi"] |
|
47 + cmd.extend(command) |
|
48 + cmd.extend(["-a", self.addr, "-p", self.port, self.name]) |
|
49 + cmd.extend(params) |
|
50 |
|
51 try: |
|
52 - return processutils.execute( |
|
53 - cmd, process_input=data, shell=True)[0] |
|
54 - except processutils.ProcessExecutionError as exc: |
|
55 + return processutils.execute(*cmd, process_input=data)[0] |
|
56 + except (processutils.ProcessExecutionError, OSError) as exc: |
|
57 LOG.error(exc) |
|
58 raise glance.store.BackendException(exc) |
|
59 |
|
60 @@ -84,7 +81,7 @@ class SheepdogImage: |
|
61 |
|
62 Sheepdog Usage: collie vdi list -r -a address -p port image |
|
63 """ |
|
64 - out = self._run_command("list -r", None) |
|
65 + out = self._run_command(["list", "-r"], None) |
|
66 return long(out.split(' ')[3]) |
|
67 |
|
68 def read(self, offset, count): |
|
69 @@ -94,7 +91,7 @@ class SheepdogImage: |
|
70 |
|
71 Sheepdog Usage: collie vdi read -a address -p port image offset len |
|
72 """ |
|
73 - return self._run_command("read", None, str(offset), str(count)) |
|
74 + return self._run_command(["read"], None, str(offset), str(count)) |
|
75 |
|
76 def write(self, data, offset, count): |
|
77 """ |
|
78 @@ -103,7 +100,7 @@ class SheepdogImage: |
|
79 |
|
80 Sheepdog Usage: collie vdi write -a address -p port image offset len |
|
81 """ |
|
82 - self._run_command("write", data, str(offset), str(count)) |
|
83 + self._run_command(["write"], data, str(offset), str(count)) |
|
84 |
|
85 def create(self, size): |
|
86 """ |
|
87 @@ -111,7 +108,7 @@ class SheepdogImage: |
|
88 |
|
89 Sheepdog Usage: collie vdi create -a address -p port image size |
|
90 """ |
|
91 - self._run_command("create", None, str(size)) |
|
92 + self._run_command(["create"], None, str(size)) |
|
93 |
|
94 def delete(self): |
|
95 """ |
|
96 @@ -119,7 +116,7 @@ class SheepdogImage: |
|
97 |
|
98 Sheepdog Usage: collie vdi delete -a address -p port image |
|
99 """ |
|
100 - self._run_command("delete", None) |
|
101 + self._run_command(["delete"], None) |
|
102 |
|
103 def exist(self): |
|
104 """ |
|
105 @@ -127,7 +124,7 @@ class SheepdogImage: |
|
106 |
|
107 Sheepdog Usage: collie vdi list -r -a address -p port image |
|
108 """ |
|
109 - out = self._run_command("list -r", None) |
|
110 + out = self._run_command(["list", "-r"], None) |
|
111 if not out: |
|
112 return False |
|
113 else: |
|
114 @@ -138,7 +135,7 @@ class StoreLocation(glance.store.location.StoreLocation): |
|
115 """ |
|
116 Class describing a Sheepdog URI. This is of the form: |
|
117 |
|
118 - sheepdog://image |
|
119 + sheepdog://image-id |
|
120 |
|
121 """ |
|
122 |
|
123 @@ -149,10 +146,14 @@ class StoreLocation(glance.store.location.StoreLocation): |
|
124 return "sheepdog://%s" % self.image |
|
125 |
|
126 def parse_uri(self, uri): |
|
127 - if not uri.startswith('sheepdog://'): |
|
128 - raise exception.BadStoreUri(uri, "URI must start with %s://" % |
|
129 - 'sheepdog') |
|
130 - self.image = uri[11:] |
|
131 + valid_schema = 'sheepdog://' |
|
132 + if not uri.startswith(valid_schema): |
|
133 + raise exception.BadStoreUri(_("URI must start with %s://") % |
|
134 + valid_schema) |
|
135 + self.image = uri[len(valid_schema):] |
|
136 + if not uuidutils.is_uuid_like(self.image): |
|
137 + raise exception.BadStoreUri(_("URI must contains well-formated " |
|
138 + "image id")) |
|
139 |
|
140 |
|
141 class ImageIterator(object): |
|
142 @@ -192,7 +193,7 @@ class Store(glance.store.base.Store): |
|
143 |
|
144 try: |
|
145 self.chunk_size = CONF.sheepdog_store_chunk_size * 1024 * 1024 |
|
146 - self.addr = CONF.sheepdog_store_address |
|
147 + self.addr = CONF.sheepdog_store_address.strip() |
|
148 self.port = CONF.sheepdog_store_port |
|
149 except cfg.ConfigFileValueError as e: |
|
150 reason = _("Error in store configuration: %s") % e |
|
151 @@ -200,10 +201,18 @@ class Store(glance.store.base.Store): |
|
152 raise exception.BadStoreConfiguration(store_name='sheepdog', |
|
153 reason=reason) |
|
154 |
|
155 + if ' ' in self.addr: |
|
156 + reason = (_("Invalid address configuration of sheepdog store: %s") |
|
157 + % self.addr) |
|
158 + LOG.error(reason) |
|
159 + raise exception.BadStoreConfiguration(store_name='sheepdog', |
|
160 + reason=reason) |
|
161 + |
|
162 try: |
|
163 - processutils.execute("collie", shell=True) |
|
164 - except processutils.ProcessExecutionError as exc: |
|
165 - reason = _("Error in store configuration: %s") % exc |
|
166 + cmd = ["collie", "vdi", "list", "-a", self.addr, "-p", self.port] |
|
167 + processutils.execute(*cmd) |
|
168 + except Exception as e: |
|
169 + reason = _("Error in store configuration: %s") % e |
|
170 LOG.error(reason) |
|
171 raise exception.BadStoreConfiguration(store_name='sheepdog', |
|
172 reason=reason) |
|
173 diff --git a/glance/tests/unit/test_sheepdog_store.py b/glance/tests/unit/test_sheepdog_store.py |
|
174 index 8eef86b..bea7e29 100644 |
|
175 --- a/glance/tests/unit/test_sheepdog_store.py |
|
176 +++ b/glance/tests/unit/test_sheepdog_store.py |
|
177 @@ -57,4 +57,5 @@ class TestStore(base.StoreClearingUnitTest): |
|
178 'fake_image_id', |
|
179 utils.LimitingReader(StringIO.StringIO('xx'), 1), |
|
180 2) |
|
181 - self.assertEqual(called_commands, ['list -r', 'create', 'delete']) |
|
182 + self.assertEqual([['list', '-r'], ['create'], ['delete']], |
|
183 + called_commands) |
|
184 diff --git a/glance/tests/unit/test_store_location.py b/glance/tests/unit/test_store_location.py |
|
185 index 7eec171..2464ebb 100644 |
|
186 --- a/glance/tests/unit/test_store_location.py |
|
187 +++ b/glance/tests/unit/test_store_location.py |
|
188 @@ -52,7 +52,7 @@ class TestStoreLocation(base.StoreClearingUnitTest): |
|
189 'rbd://imagename', |
|
190 'rbd://fsid/pool/image/snap', |
|
191 'rbd://%2F/%2F/%2F/%2F', |
|
192 - 'sheepdog://imagename', |
|
193 + 'sheepdog://244e75f1-9c69-4167-9db7-1aa7d1973f6c', |
|
194 'cinder://12345678-9012-3455-6789-012345678901', |
|
195 ] |
|
196 |
|
197 @@ -367,15 +367,18 @@ class TestStoreLocation(base.StoreClearingUnitTest): |
|
198 """ |
|
199 Test the specific StoreLocation for the Sheepdog store |
|
200 """ |
|
201 - uri = 'sheepdog://imagename' |
|
202 + uri = 'sheepdog://244e75f1-9c69-4167-9db7-1aa7d1973f6c' |
|
203 loc = glance.store.sheepdog.StoreLocation({}) |
|
204 loc.parse_uri(uri) |
|
205 - self.assertEqual('imagename', loc.image) |
|
206 + self.assertEqual('244e75f1-9c69-4167-9db7-1aa7d1973f6c', loc.image) |
|
207 |
|
208 - bad_uri = 'sheepdog:/image' |
|
209 + bad_uri = 'sheepdog:/244e75f1-9c69-4167-9db7-1aa7d1973f6c' |
|
210 self.assertRaises(exception.BadStoreUri, loc.parse_uri, bad_uri) |
|
211 |
|
212 - bad_uri = 'http://image' |
|
213 + bad_uri = 'http://244e75f1-9c69-4167-9db7-1aa7d1973f6c' |
|
214 + self.assertRaises(exception.BadStoreUri, loc.parse_uri, bad_uri) |
|
215 + |
|
216 + bad_uri = 'image; name' |
|
217 self.assertRaises(exception.BadStoreUri, loc.parse_uri, bad_uri) |
|
218 |
|
219 def test_cinder_store_good_location(self): |
|