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