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 "extensions/browser/content_verifier.h" 6 7 #include <algorithm> 8 9 #include "base/command_line.h" 10 #include "base/files/file_path.h" 11 #include "base/metrics/field_trial.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "content/public/common/content_switches.h" 14 #include "extensions/browser/content_hash_fetcher.h" 15 #include "extensions/browser/content_hash_reader.h" 16 #include "extensions/browser/content_verifier_delegate.h" 17 #include "extensions/browser/extension_registry.h" 18 #include "extensions/common/constants.h" 19 #include "extensions/common/extension_l10n_util.h" 20 #include "extensions/common/switches.h" 21 22 namespace { 23 24 const char kExperimentName[] = "ExtensionContentVerification"; 25 26 } // namespace 27 28 namespace extensions { 29 30 ContentVerifier::ContentVerifier(content::BrowserContext* context, 31 ContentVerifierDelegate* delegate) 32 : mode_(GetMode()), 33 context_(context), 34 delegate_(delegate), 35 fetcher_(new ContentHashFetcher( 36 context, 37 delegate, 38 base::Bind(&ContentVerifier::OnFetchComplete, this))) { 39 } 40 41 ContentVerifier::~ContentVerifier() { 42 } 43 44 void ContentVerifier::Start() { 45 if (mode_ >= BOOTSTRAP) 46 fetcher_->Start(); 47 } 48 49 void ContentVerifier::Shutdown() { 50 fetcher_.reset(); 51 delegate_.reset(); 52 } 53 54 ContentVerifyJob* ContentVerifier::CreateJobFor( 55 const std::string& extension_id, 56 const base::FilePath& extension_root, 57 const base::FilePath& relative_path) { 58 if (mode_ < BOOTSTRAP || !delegate_) 59 return NULL; 60 61 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); 62 const Extension* extension = 63 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); 64 65 std::set<base::FilePath> paths; 66 paths.insert(relative_path); 67 if (!ShouldVerifyAnyPaths(extension, paths)) 68 return NULL; 69 70 // TODO(asargent) - we can probably get some good performance wins by having 71 // a cache of ContentHashReader's that we hold onto past the end of each job. 72 return new ContentVerifyJob( 73 new ContentHashReader(extension_id, 74 *extension->version(), 75 extension_root, 76 relative_path, 77 delegate_->PublicKey()), 78 base::Bind(&ContentVerifier::VerifyFailed, this, extension->id())); 79 } 80 81 void ContentVerifier::VerifyFailed(const std::string& extension_id, 82 ContentVerifyJob::FailureReason reason) { 83 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { 84 content::BrowserThread::PostTask( 85 content::BrowserThread::UI, 86 FROM_HERE, 87 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason)); 88 return; 89 } 90 91 VLOG(1) << "VerifyFailed " << extension_id << " reason:" << reason; 92 93 if (!delegate_ || !fetcher_.get() || mode_ < ENFORCE) 94 return; 95 96 if (reason == ContentVerifyJob::MISSING_ALL_HASHES) { 97 // If we failed because there were no hashes yet for this extension, just 98 // request some. 99 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); 100 const Extension* extension = 101 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); 102 if (extension) 103 fetcher_->DoFetch(extension, true /* force */); 104 } else { 105 delegate_->VerifyFailed(extension_id); 106 } 107 } 108 109 void ContentVerifier::OnFetchComplete( 110 const std::string& extension_id, 111 bool success, 112 bool was_force_check, 113 const std::set<base::FilePath>& hash_mismatch_paths) { 114 VLOG(1) << "OnFetchComplete " << extension_id << " success:" << success; 115 116 if (!delegate_ || mode_ < ENFORCE) 117 return; 118 119 if (!success && mode_ < ENFORCE_STRICT) 120 return; 121 122 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); 123 const Extension* extension = 124 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); 125 if (!extension) 126 return; 127 128 if ((was_force_check && !success) || 129 ShouldVerifyAnyPaths(extension, hash_mismatch_paths)) 130 delegate_->VerifyFailed(extension_id); 131 } 132 133 bool ContentVerifier::ShouldVerifyAnyPaths( 134 const Extension* extension, 135 const std::set<base::FilePath>& relative_paths) { 136 if (!extension || !extension->version() || 137 !delegate_->ShouldBeVerified(*extension)) 138 return false; 139 140 // Images used in the browser get transcoded during install, so skip 141 // checking them for now. TODO(asargent) - see if we can cache this list 142 // for a given extension id/version pair. 143 std::set<base::FilePath> browser_images = 144 delegate_->GetBrowserImagePaths(extension); 145 146 base::FilePath locales_dir = extension->path().Append(kLocaleFolder); 147 scoped_ptr<std::set<std::string> > all_locales; 148 149 for (std::set<base::FilePath>::const_iterator i = relative_paths.begin(); 150 i != relative_paths.end(); 151 ++i) { 152 const base::FilePath& relative_path = *i; 153 154 if (relative_path == base::FilePath(kManifestFilename)) 155 continue; 156 157 if (ContainsKey(browser_images, relative_path)) 158 continue; 159 160 base::FilePath full_path = extension->path().Append(relative_path); 161 if (locales_dir.IsParent(full_path)) { 162 if (!all_locales) { 163 // TODO(asargent) - see if we can cache this list longer to avoid 164 // having to fetch it more than once for a given run of the 165 // browser. Maybe it can never change at runtime? (Or if it can, maybe 166 // there is an event we can listen for to know to drop our cache). 167 all_locales.reset(new std::set<std::string>); 168 extension_l10n_util::GetAllLocales(all_locales.get()); 169 } 170 171 // Since message catalogs get transcoded during installation, we want 172 // to skip those paths. 173 if (full_path.DirName().DirName() == locales_dir && 174 !extension_l10n_util::ShouldSkipValidation( 175 locales_dir, full_path.DirName(), *all_locales)) 176 continue; 177 } 178 return true; 179 } 180 return false; 181 } 182 183 // static 184 ContentVerifier::Mode ContentVerifier::GetMode() { 185 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); 186 187 Mode experiment_value = NONE; 188 const std::string group = base::FieldTrialList::FindFullName(kExperimentName); 189 if (group == "EnforceStrict") 190 experiment_value = ENFORCE_STRICT; 191 else if (group == "Enforce") 192 experiment_value = ENFORCE; 193 else if (group == "Bootstrap") 194 experiment_value = BOOTSTRAP; 195 196 // The field trial value that normally comes from the server can be 197 // overridden on the command line, which we don't want to allow since malware 198 // can set chrome command line flags. There isn't currently a way to find out 199 // what the server-provided value is in this case, so we conservatively 200 // default to the strictest mode if we detect our experiment name being 201 // overridden. 202 if (command_line->HasSwitch(::switches::kForceFieldTrials)) { 203 std::string forced_trials = 204 command_line->GetSwitchValueASCII(::switches::kForceFieldTrials); 205 if (forced_trials.find(kExperimentName) != std::string::npos) 206 experiment_value = ENFORCE_STRICT; 207 } 208 209 Mode cmdline_value = NONE; 210 if (command_line->HasSwitch(switches::kExtensionContentVerification)) { 211 std::string switch_value = command_line->GetSwitchValueASCII( 212 switches::kExtensionContentVerification); 213 if (switch_value == switches::kExtensionContentVerificationBootstrap) 214 cmdline_value = BOOTSTRAP; 215 else if (switch_value == switches::kExtensionContentVerificationEnforce) 216 cmdline_value = ENFORCE; 217 else if (switch_value == 218 switches::kExtensionContentVerificationEnforceStrict) 219 cmdline_value = ENFORCE_STRICT; 220 else 221 // If no value was provided (or the wrong one), just default to enforce. 222 cmdline_value = ENFORCE; 223 } 224 225 // We don't want to allow the command-line flags to eg disable enforcement if 226 // the experiment group says it should be on, or malware may just modify the 227 // command line flags. So return the more restrictive of the 2 values. 228 return std::max(experiment_value, cmdline_value); 229 } 230 231 } // namespace extensions 232