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