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