• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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