OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/nacl/renderer/json_manifest.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/macros.h" | |
11 #include "components/nacl/renderer/nexe_load_manager.h" | |
12 #include "third_party/jsoncpp/source/include/json/reader.h" | |
13 #include "third_party/jsoncpp/source/include/json/value.h" | |
14 #include "url/gurl.h" | |
15 | |
16 namespace nacl { | |
17 | |
18 namespace { | |
19 // Top-level section name keys | |
20 const char* const kProgramKey = "program"; | |
21 const char* const kInterpreterKey = "interpreter"; | |
22 const char* const kFilesKey = "files"; | |
23 | |
24 // ISA Dictionary keys | |
25 const char* const kX8632Key = "x86-32"; | |
26 const char* const kX8632NonSFIKey = "x86-32-nonsfi"; | |
27 const char* const kX8664Key = "x86-64"; | |
28 const char* const kX8664NonSFIKey = "x86-64-nonsfi"; | |
29 const char* const kArmKey = "arm"; | |
30 const char* const kArmNonSFIKey = "arm-nonsfi"; | |
31 const char* const kPortableKey = "portable"; | |
32 | |
33 // Url Resolution keys | |
34 const char* const kPnaclDebugKey = "pnacl-debug"; | |
35 const char* const kPnaclTranslateKey = "pnacl-translate"; | |
36 const char* const kUrlKey = "url"; | |
37 | |
38 // PNaCl keys | |
39 const char* const kOptLevelKey = "optlevel"; | |
40 | |
41 // Sample NaCl manifest file: | |
42 // { | |
43 // "program": { | |
44 // "x86-32": {"url": "myprogram_x86-32.nexe"}, | |
45 // "x86-64": {"url": "myprogram_x86-64.nexe"}, | |
46 // "arm": {"url": "myprogram_arm.nexe"} | |
47 // }, | |
48 // "interpreter": { | |
49 // "x86-32": {"url": "interpreter_x86-32.nexe"}, | |
50 // "x86-64": {"url": "interpreter_x86-64.nexe"}, | |
51 // "arm": {"url": "interpreter_arm.nexe"} | |
52 // }, | |
53 // "files": { | |
54 // "foo.txt": { | |
55 // "portable": {"url": "foo.txt"} | |
56 // }, | |
57 // "bar.txt": { | |
58 // "x86-32": {"url": "x86-32/bar.txt"}, | |
59 // "portable": {"url": "bar.txt"} | |
60 // }, | |
61 // "libfoo.so": { | |
62 // "x86-64" : { "url": "..." } | |
63 // } | |
64 // } | |
65 // } | |
66 | |
67 // Sample PNaCl manifest file: | |
68 // { | |
69 // "program": { | |
70 // "portable": { | |
71 // "pnacl-translate": { | |
72 // "url": "myprogram.pexe" | |
73 // }, | |
74 // "pnacl-debug": { | |
75 // "url": "myprogram.debug.pexe", | |
76 // "opt_level": 0 | |
77 // } | |
78 // } | |
79 // }, | |
80 // "files": { | |
81 // "foo.txt": { | |
82 // "portable": {"url": "foo.txt"} | |
83 // }, | |
84 // "bar.txt": { | |
85 // "portable": {"url": "bar.txt"} | |
86 // } | |
87 // } | |
88 // } | |
89 | |
90 // Returns the key for the architecture in non-SFI mode. | |
91 std::string GetNonSFIKey(const std::string& sandbox_isa) { | |
92 return sandbox_isa + "-nonsfi"; | |
93 } | |
94 | |
95 // Looks up |property_name| in the vector |valid_names| with length | |
96 // |valid_name_count|. Returns true if |property_name| is found. | |
97 bool FindMatchingProperty(const std::string& property_name, | |
98 const char** valid_names, | |
99 size_t valid_name_count) { | |
100 for (size_t i = 0; i < valid_name_count; ++i) { | |
101 if (property_name == valid_names[i]) { | |
102 return true; | |
103 } | |
104 } | |
105 return false; | |
106 } | |
107 | |
108 // Return true if this is a valid dictionary. Having only keys present in | |
109 // |valid_keys| and having at least the keys in |required_keys|. | |
110 // Error messages will be placed in |error_string|, given that the dictionary | |
111 // was the property value of |container_key|. | |
112 // E.g., "container_key" : dictionary | |
113 bool IsValidDictionary(const Json::Value& dictionary, | |
114 const std::string& container_key, | |
115 const std::string& parent_key, | |
116 const char** valid_keys, | |
117 size_t valid_key_count, | |
118 const char** required_keys, | |
119 size_t required_key_count, | |
120 std::string* error_string) { | |
121 if (!dictionary.isObject()) { | |
122 std::stringstream error_stream; | |
123 error_stream << parent_key << " property '" << container_key | |
124 << "' is non-dictionary value '" | |
125 << dictionary.toStyledString() << "'."; | |
126 *error_string = error_stream.str(); | |
127 return false; | |
128 } | |
129 // Check for unknown dictionary members. | |
130 Json::Value::Members members = dictionary.getMemberNames(); | |
131 for (size_t i = 0; i < members.size(); ++i) { | |
132 std::string property_name = members[i]; | |
133 if (!FindMatchingProperty(property_name, | |
134 valid_keys, | |
135 valid_key_count)) { | |
136 // For forward compatibility, we do not prohibit other keys being in | |
137 // the dictionary. | |
138 VLOG(1) << "WARNING: '" << parent_key << "' property '" | |
139 << container_key << "' has unknown key '" | |
140 << property_name << "'."; | |
141 } | |
142 } | |
143 // Check for required members. | |
144 for (size_t i = 0; i < required_key_count; ++i) { | |
145 if (!dictionary.isMember(required_keys[i])) { | |
146 std::stringstream error_stream; | |
147 error_stream << parent_key << " property '" << container_key | |
148 << "' does not have required key: '" | |
149 << required_keys[i] << "'."; | |
150 *error_string = error_stream.str(); | |
151 return false; | |
152 } | |
153 } | |
154 return true; | |
155 } | |
156 | |
157 // Validate a "url" dictionary assuming it was resolved from container_key. | |
158 // E.g., "container_key" : { "url": "foo.txt" } | |
159 bool IsValidUrlSpec(const Json::Value& url_spec, | |
160 const std::string& container_key, | |
161 const std::string& parent_key, | |
162 const std::string& sandbox_isa, | |
163 std::string* error_string) { | |
164 static const char* kManifestUrlSpecRequired[] = { | |
165 kUrlKey | |
166 }; | |
167 const char** urlSpecPlusOptional; | |
168 size_t urlSpecPlusOptionalLength; | |
169 if (sandbox_isa == kPortableKey) { | |
170 static const char* kPnaclUrlSpecPlusOptional[] = { | |
171 kUrlKey, | |
172 kOptLevelKey, | |
173 }; | |
174 urlSpecPlusOptional = kPnaclUrlSpecPlusOptional; | |
175 urlSpecPlusOptionalLength = arraysize(kPnaclUrlSpecPlusOptional); | |
176 } else { | |
177 // URL specifications must not contain "pnacl-translate" keys. | |
178 // This prohibits NaCl clients from invoking PNaCl. | |
179 if (url_spec.isMember(kPnaclTranslateKey)) { | |
180 std::stringstream error_stream; | |
181 error_stream << "PNaCl-like NMF with application/x-nacl mimetype instead " | |
182 << "of x-pnacl mimetype (has " << kPnaclTranslateKey << ")."; | |
183 *error_string = error_stream.str(); | |
184 return false; | |
185 } | |
186 urlSpecPlusOptional = kManifestUrlSpecRequired; | |
187 urlSpecPlusOptionalLength = arraysize(kManifestUrlSpecRequired); | |
188 } | |
189 if (!IsValidDictionary(url_spec, container_key, parent_key, | |
190 urlSpecPlusOptional, | |
191 urlSpecPlusOptionalLength, | |
192 kManifestUrlSpecRequired, | |
193 arraysize(kManifestUrlSpecRequired), | |
194 error_string)) { | |
195 return false; | |
196 } | |
197 // Verify the correct types of the fields if they exist. | |
198 Json::Value url = url_spec[kUrlKey]; | |
199 if (!url.isString()) { | |
200 std::stringstream error_stream; | |
201 error_stream << parent_key << " property '" << container_key << | |
202 "' has non-string value '" << url.toStyledString() << | |
203 "' for key '" << kUrlKey << "'."; | |
204 *error_string = error_stream.str(); | |
205 return false; | |
206 } | |
207 Json::Value opt_level = url_spec[kOptLevelKey]; | |
208 if (!opt_level.empty() && !opt_level.isNumeric()) { | |
209 std::stringstream error_stream; | |
210 error_stream << parent_key << " property '" << container_key << | |
211 "' has non-numeric value '" << opt_level.toStyledString() << | |
212 "' for key '" << kOptLevelKey << "'."; | |
213 *error_string = error_stream.str(); | |
214 return false; | |
215 } | |
216 return true; | |
217 } | |
218 | |
219 // Validate a "pnacl-translate" or "pnacl-debug" dictionary, assuming | |
220 // it was resolved from container_key. | |
221 // E.g., "container_key" : { "pnacl-translate" : URLSpec } | |
222 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec, | |
223 const std::string& container_key, | |
224 const std::string& parent_key, | |
225 const std::string& sandbox_isa, | |
226 std::string* error_string) { | |
227 static const char* kManifestPnaclSpecValid[] = { | |
228 kPnaclDebugKey, | |
229 kPnaclTranslateKey | |
230 }; | |
231 static const char* kManifestPnaclSpecRequired[] = { kPnaclTranslateKey }; | |
232 if (!IsValidDictionary(pnacl_spec, container_key, parent_key, | |
233 kManifestPnaclSpecValid, | |
234 arraysize(kManifestPnaclSpecValid), | |
235 kManifestPnaclSpecRequired, | |
236 arraysize(kManifestPnaclSpecRequired), | |
237 error_string)) { | |
238 return false; | |
239 } | |
240 Json::Value url_spec = pnacl_spec[kPnaclTranslateKey]; | |
241 return IsValidUrlSpec(url_spec, kPnaclTranslateKey, | |
242 container_key, sandbox_isa, error_string); | |
243 } | |
244 | |
245 // Validates that |dictionary| is a valid ISA dictionary. An ISA dictionary | |
246 // is validated to have keys from within the set of recognized ISAs. Unknown | |
247 // ISAs are allowed, but ignored and warnings are produced. It is also | |
248 // validated | |
249 // that it must have an entry to match the ISA specified in |sandbox_isa| or | |
250 // have a fallback 'portable' entry if there is no match. Returns true if | |
251 // |dictionary| is an ISA to URL map. Sets |error_info| to something | |
252 // descriptive if it fails. | |
253 bool IsValidISADictionary(const Json::Value& dictionary, | |
254 const std::string& parent_key, | |
255 const std::string& sandbox_isa, | |
256 bool must_find_matching_entry, | |
257 bool nonsfi_enabled, | |
258 JsonManifest::ErrorInfo* error_info) { | |
259 // An ISA to URL dictionary has to be an object. | |
260 if (!dictionary.isObject()) { | |
261 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE; | |
262 error_info->string = std::string("manifest: ") + parent_key + | |
263 " property is not an ISA to URL dictionary"; | |
264 return false; | |
265 } | |
266 // Build the set of reserved ISA dictionary keys. | |
267 const char** isaProperties; | |
268 size_t isaPropertiesLength; | |
269 if (sandbox_isa == kPortableKey) { | |
270 // The known values for PNaCl ISA dictionaries in the manifest. | |
271 static const char* kPnaclManifestISAProperties[] = { | |
272 kPortableKey | |
273 }; | |
274 isaProperties = kPnaclManifestISAProperties; | |
275 isaPropertiesLength = arraysize(kPnaclManifestISAProperties); | |
276 } else { | |
277 // The known values for NaCl ISA dictionaries in the manifest. | |
278 static const char* kNaClManifestISAProperties[] = { | |
279 kX8632Key, | |
280 kX8632NonSFIKey, | |
281 kX8664Key, | |
282 kX8664NonSFIKey, | |
283 kArmKey, | |
284 kArmNonSFIKey, | |
285 // "portable" is here to allow checking that, if present, it can | |
286 // only refer to an URL, such as for a data file, and not to | |
287 // "pnacl-translate", which would cause the creation of a nexe. | |
288 kPortableKey | |
289 }; | |
290 isaProperties = kNaClManifestISAProperties; | |
291 isaPropertiesLength = arraysize(kNaClManifestISAProperties); | |
292 } | |
293 // Check that entries in the dictionary are structurally correct. | |
294 Json::Value::Members members = dictionary.getMemberNames(); | |
295 for (size_t i = 0; i < members.size(); ++i) { | |
296 std::string property_name = members[i]; | |
297 Json::Value property_value = dictionary[property_name]; | |
298 std::string error_string; | |
299 if (FindMatchingProperty(property_name, | |
300 isaProperties, | |
301 isaPropertiesLength)) { | |
302 // For NaCl, arch entries can only be | |
303 // "arch/portable" : URLSpec | |
304 // For PNaCl arch in "program" dictionary entries can be | |
305 // "portable" : { "pnacl-translate": URLSpec } | |
306 // or "portable" : { "pnacl-debug": URLSpec } | |
307 // For PNaCl arch elsewhere, dictionary entries can only be | |
308 // "portable" : URLSpec | |
309 if ((sandbox_isa != kPortableKey && | |
310 !IsValidUrlSpec(property_value, property_name, parent_key, | |
311 sandbox_isa, &error_string)) || | |
312 (sandbox_isa == kPortableKey && | |
313 parent_key == kProgramKey && | |
314 !IsValidPnaclTranslateSpec(property_value, property_name, parent_key, | |
315 sandbox_isa, &error_string)) || | |
316 (sandbox_isa == kPortableKey && | |
317 parent_key != kProgramKey && | |
318 !IsValidUrlSpec(property_value, property_name, parent_key, | |
319 sandbox_isa, &error_string))) { | |
320 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE; | |
321 error_info->string = "manifest: " + error_string; | |
322 return false; | |
323 } | |
324 } else { | |
325 // For forward compatibility, we do not prohibit other keys being in | |
326 // the dictionary, as they may be architectures supported in later | |
327 // versions. However, the value of these entries must be an URLSpec. | |
328 VLOG(1) << "IsValidISADictionary: unrecognized key '" | |
329 << property_name << "'."; | |
330 if (!IsValidUrlSpec(property_value, property_name, parent_key, | |
331 sandbox_isa, &error_string)) { | |
332 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE; | |
333 error_info->string = "manifest: " + error_string; | |
334 return false; | |
335 } | |
336 } | |
337 } | |
338 | |
339 if (sandbox_isa == kPortableKey) { | |
340 if (!dictionary.isMember(kPortableKey)) { | |
341 error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH; | |
342 error_info->string = "manifest: no version of " + parent_key + | |
343 " given for portable."; | |
344 return false; | |
345 } | |
346 } else if (must_find_matching_entry) { | |
347 // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include | |
348 // micro-architectures that can resolve to multiple valid sandboxes. | |
349 bool has_isa = dictionary.isMember(sandbox_isa); | |
350 bool has_nonsfi_isa = | |
351 nonsfi_enabled && dictionary.isMember(GetNonSFIKey(sandbox_isa)); | |
352 bool has_portable = dictionary.isMember(kPortableKey); | |
353 | |
354 if (!has_isa && !has_nonsfi_isa && !has_portable) { | |
355 error_info->error = PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH; | |
356 error_info->string = "manifest: no version of " + parent_key + | |
357 " given for current arch and no portable version found."; | |
358 return false; | |
359 } | |
360 } | |
361 return true; | |
362 } | |
363 | |
364 void GrabUrlAndPnaclOptions(const Json::Value& url_spec, | |
365 std::string* url, | |
366 PP_PNaClOptions* pnacl_options) { | |
367 *url = url_spec[kUrlKey].asString(); | |
368 pnacl_options->translate = PP_TRUE; | |
369 if (url_spec.isMember(kOptLevelKey)) { | |
370 int32_t opt_raw = url_spec[kOptLevelKey].asInt(); | |
371 // Currently only allow 0 or 2, since that is what we test. | |
372 if (opt_raw <= 0) | |
373 pnacl_options->opt_level = 0; | |
374 else | |
375 pnacl_options->opt_level = 2; | |
376 } | |
377 } | |
378 | |
379 } // namespace | |
380 | |
381 JsonManifest::JsonManifest(const std::string& manifest_base_url, | |
382 const std::string& sandbox_isa, | |
383 bool nonsfi_enabled, | |
384 bool pnacl_debug) | |
385 : manifest_base_url_(manifest_base_url), | |
386 sandbox_isa_(sandbox_isa), | |
387 nonsfi_enabled_(nonsfi_enabled), | |
388 pnacl_debug_(pnacl_debug) { } | |
389 | |
390 bool JsonManifest::Init(const std::string& manifest_json, | |
391 ErrorInfo* error_info) { | |
392 CHECK(error_info); | |
393 | |
394 Json::Reader reader; | |
395 if (!reader.parse(manifest_json, dictionary_)) { | |
396 std::string json_error = reader.getFormattedErrorMessages(); | |
397 error_info->error = PP_NACL_ERROR_MANIFEST_PARSING; | |
398 error_info->string = "manifest JSON parsing failed: " + json_error; | |
399 return false; | |
400 } | |
401 // Parse has ensured the string was valid JSON. Check that it matches the | |
402 // manifest schema. | |
403 return MatchesSchema(error_info); | |
404 } | |
405 | |
406 bool JsonManifest::GetProgramURL(std::string* full_url, | |
407 PP_PNaClOptions* pnacl_options, | |
408 bool* uses_nonsfi_mode, | |
409 ErrorInfo* error_info) const { | |
410 if (!full_url) | |
411 return false; | |
412 CHECK(pnacl_options); | |
413 CHECK(uses_nonsfi_mode); | |
414 CHECK(error_info); | |
415 | |
416 const Json::Value& program = dictionary_[kProgramKey]; | |
417 std::string nexe_url; | |
418 if (!GetURLFromISADictionary(program, | |
419 kProgramKey, | |
420 &nexe_url, | |
421 pnacl_options, | |
422 uses_nonsfi_mode, | |
423 error_info)) { | |
424 return false; | |
425 } | |
426 | |
427 // The contents of the manifest are resolved relative to the manifest URL. | |
428 GURL base_gurl(manifest_base_url_); | |
429 if (!base_gurl.is_valid()) | |
430 return false; | |
431 | |
432 GURL resolved_gurl = base_gurl.Resolve(nexe_url); | |
433 if (!resolved_gurl.is_valid()) { | |
434 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL; | |
435 error_info->string = | |
436 "could not resolve url '" + nexe_url + | |
437 "' relative to manifest base url '" + manifest_base_url_.c_str() + | |
438 "'."; | |
439 return false; | |
440 } | |
441 *full_url = resolved_gurl.possibly_invalid_spec(); | |
442 return true; | |
443 } | |
444 | |
445 bool JsonManifest::ResolveKey(const std::string& key, | |
446 std::string* full_url, | |
447 PP_PNaClOptions* pnacl_options) const { | |
448 // key must be one of kProgramKey or kFileKey '/' file-section-key | |
449 if (full_url == NULL || pnacl_options == NULL) | |
450 return false; | |
451 | |
452 if (key == kProgramKey) | |
453 return GetKeyUrl(dictionary_, key, full_url, pnacl_options); | |
454 | |
455 std::string::const_iterator p = find(key.begin(), key.end(), '/'); | |
456 if (p == key.end()) { | |
457 VLOG(1) << "ResolveKey failed: invalid key, no slash: " << key; | |
458 return false; | |
459 } | |
460 | |
461 // generalize to permit other sections? | |
462 std::string prefix(key.begin(), p); | |
463 if (prefix != kFilesKey) { | |
464 VLOG(1) << "ResolveKey failed: invalid key, no \"files\" prefix: " << key; | |
465 return false; | |
466 } | |
467 | |
468 const Json::Value& files = dictionary_[kFilesKey]; | |
469 if (!files.isObject()) { | |
470 VLOG(1) << "ResolveKey failed: no \"files\" dictionary"; | |
471 return false; | |
472 } | |
473 | |
474 std::string rest(p + 1, key.end()); | |
475 if (!files.isMember(rest)) { | |
476 VLOG(1) << "ResolveKey failed: no such \"files\" entry: " << key; | |
477 return false; | |
478 } | |
479 return GetKeyUrl(files, rest, full_url, pnacl_options); | |
480 } | |
481 | |
482 bool JsonManifest::MatchesSchema(ErrorInfo* error_info) { | |
483 if (!dictionary_.isObject()) { | |
484 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE; | |
485 error_info->string = "manifest: is not a json dictionary."; | |
486 return false; | |
487 } | |
488 Json::Value::Members members = dictionary_.getMemberNames(); | |
489 for (size_t i = 0; i < members.size(); ++i) { | |
490 // The top level dictionary entries valid in the manifest file. | |
491 static const char* kManifestTopLevelProperties[] = { kProgramKey, | |
492 kInterpreterKey, | |
493 kFilesKey }; | |
494 std::string property_name = members[i]; | |
495 if (!FindMatchingProperty(property_name, | |
496 kManifestTopLevelProperties, | |
497 arraysize(kManifestTopLevelProperties))) { | |
498 VLOG(1) << "JsonManifest::MatchesSchema: WARNING: unknown top-level " | |
499 << "section '" << property_name << "' in manifest."; | |
500 } | |
501 } | |
502 | |
503 // A manifest file must have a program section. | |
504 if (!dictionary_.isMember(kProgramKey)) { | |
505 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE; | |
506 error_info->string = std::string("manifest: missing '") + kProgramKey + | |
507 "' section."; | |
508 return false; | |
509 } | |
510 | |
511 // Validate the program section. | |
512 // There must be a matching (portable or sandbox_isa_) entry for program for | |
513 // NaCl. | |
514 if (!IsValidISADictionary(dictionary_[kProgramKey], | |
515 kProgramKey, | |
516 sandbox_isa_, | |
517 true, | |
518 nonsfi_enabled_, | |
519 error_info)) { | |
520 return false; | |
521 } | |
522 | |
523 // Validate the interpreter section (if given). | |
524 // There must be a matching (portable or sandbox_isa_) entry for interpreter | |
525 // for NaCl. | |
526 if (dictionary_.isMember(kInterpreterKey)) { | |
527 if (!IsValidISADictionary(dictionary_[kInterpreterKey], | |
528 kInterpreterKey, | |
529 sandbox_isa_, | |
530 true, | |
531 nonsfi_enabled_, | |
532 error_info)) { | |
533 return false; | |
534 } | |
535 } | |
536 | |
537 // Validate the file dictionary (if given). | |
538 // The "files" key does not require a matching (portable or sandbox_isa_) | |
539 // entry at schema validation time for NaCl. This allows manifests to | |
540 // specify resources that are only loaded for a particular sandbox_isa. | |
541 if (dictionary_.isMember(kFilesKey)) { | |
542 const Json::Value& files = dictionary_[kFilesKey]; | |
543 if (!files.isObject()) { | |
544 error_info->error = PP_NACL_ERROR_MANIFEST_SCHEMA_VALIDATE; | |
545 error_info->string = std::string("manifest: '") + kFilesKey + | |
546 "' is not a dictionary."; | |
547 } | |
548 Json::Value::Members members = files.getMemberNames(); | |
549 for (size_t i = 0; i < members.size(); ++i) { | |
550 std::string file_name = members[i]; | |
551 if (!IsValidISADictionary(files[file_name], | |
552 file_name, | |
553 sandbox_isa_, | |
554 false, | |
555 nonsfi_enabled_, | |
556 error_info)) { | |
557 return false; | |
558 } | |
559 } | |
560 } | |
561 return true; | |
562 } | |
563 | |
564 bool JsonManifest::GetKeyUrl(const Json::Value& dictionary, | |
565 const std::string& key, | |
566 std::string* full_url, | |
567 PP_PNaClOptions* pnacl_options) const { | |
568 DCHECK(full_url && pnacl_options); | |
569 if (!dictionary.isMember(key)) { | |
570 VLOG(1) << "GetKeyUrl failed: file " << key << " not found in manifest."; | |
571 return false; | |
572 } | |
573 const Json::Value& isa_dict = dictionary[key]; | |
574 std::string relative_url; | |
575 bool uses_nonsfi_mode; | |
576 ErrorInfo ignored_error_info; | |
577 if (!GetURLFromISADictionary(isa_dict, key, &relative_url, | |
578 pnacl_options, &uses_nonsfi_mode, | |
579 &ignored_error_info)) | |
580 return false; | |
581 | |
582 // The contents of the manifest are resolved relative to the manifest URL. | |
583 GURL base_gurl(manifest_base_url_); | |
584 if (!base_gurl.is_valid()) | |
585 return false; | |
586 GURL resolved_gurl = base_gurl.Resolve(relative_url); | |
587 if (!resolved_gurl.is_valid()) | |
588 return false; | |
589 *full_url = resolved_gurl.possibly_invalid_spec(); | |
590 return true; | |
591 } | |
592 | |
593 bool JsonManifest::GetURLFromISADictionary(const Json::Value& dictionary, | |
594 const std::string& parent_key, | |
595 std::string* url, | |
596 PP_PNaClOptions* pnacl_options, | |
597 bool* uses_nonsfi_mode, | |
598 ErrorInfo* error_info) const { | |
599 DCHECK(url && pnacl_options && error_info); | |
600 | |
601 // When the application actually requests a resolved URL, we must have | |
602 // a matching entry (sandbox_isa_ or portable) for NaCl. | |
603 ErrorInfo ignored_error_info; | |
604 if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa_, true, | |
605 nonsfi_enabled_, &ignored_error_info)) { | |
606 error_info->error = PP_NACL_ERROR_MANIFEST_RESOLVE_URL; | |
607 error_info->string = "architecture " + sandbox_isa_ + | |
608 " is not found for file " + parent_key; | |
609 return false; | |
610 } | |
611 | |
612 // The call to IsValidISADictionary() above guarantees that either | |
613 // sandbox_isa_, its nonsfi mode, or kPortableKey is present in the | |
614 // dictionary. | |
615 *uses_nonsfi_mode = false; | |
616 std::string chosen_isa; | |
617 if (sandbox_isa_ == kPortableKey) { | |
618 chosen_isa = kPortableKey; | |
619 } else { | |
620 std::string nonsfi_isa = GetNonSFIKey(sandbox_isa_); | |
621 if (nonsfi_enabled_ && dictionary.isMember(nonsfi_isa)) { | |
622 chosen_isa = nonsfi_isa; | |
623 *uses_nonsfi_mode = true; | |
624 } else if (dictionary.isMember(sandbox_isa_)) { | |
625 chosen_isa = sandbox_isa_; | |
626 } else if (dictionary.isMember(kPortableKey)) { | |
627 chosen_isa = kPortableKey; | |
628 } else { | |
629 // Should not reach here, because the earlier IsValidISADictionary() | |
630 // call checked that the manifest covers the current architecture. | |
631 DCHECK(false); | |
632 return false; | |
633 } | |
634 } | |
635 | |
636 const Json::Value& isa_spec = dictionary[chosen_isa]; | |
637 // If the PNaCl debug flag is turned on, look for pnacl-debug entries first. | |
638 // If found, mark that it is a debug URL. Otherwise, fall back to | |
639 // checking for pnacl-translate URLs, etc. and don't mark it as a debug URL. | |
640 if (pnacl_debug_ && isa_spec.isMember(kPnaclDebugKey)) { | |
641 GrabUrlAndPnaclOptions(isa_spec[kPnaclDebugKey], url, pnacl_options); | |
642 pnacl_options->is_debug = PP_TRUE; | |
643 } else if (isa_spec.isMember(kPnaclTranslateKey)) { | |
644 GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options); | |
645 } else { | |
646 // NaCl | |
647 *url = isa_spec[kUrlKey].asString(); | |
648 pnacl_options->translate = PP_FALSE; | |
649 } | |
650 | |
651 return true; | |
652 } | |
653 | |
654 } // namespace nacl | |
OLD | NEW |