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