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