| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "extensions/browser/content_verifier.h" | 5 #include "extensions/browser/content_verifier.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 } // namespace | 26 } // namespace |
| 27 | 27 |
| 28 namespace extensions { | 28 namespace extensions { |
| 29 | 29 |
| 30 ContentVerifier::ContentVerifier(content::BrowserContext* context, | 30 ContentVerifier::ContentVerifier(content::BrowserContext* context, |
| 31 ContentVerifierDelegate* delegate) | 31 ContentVerifierDelegate* delegate) |
| 32 : mode_(GetMode()), | 32 : mode_(GetMode()), |
| 33 context_(context), | 33 context_(context), |
| 34 delegate_(delegate), | 34 delegate_(delegate), |
| 35 fetcher_(new ContentHashFetcher(context, delegate)) { | 35 fetcher_(new ContentHashFetcher( |
| 36 context, |
| 37 delegate, |
| 38 base::Bind(&ContentVerifier::OnFetchComplete, this))) { |
| 36 } | 39 } |
| 37 | 40 |
| 38 ContentVerifier::~ContentVerifier() { | 41 ContentVerifier::~ContentVerifier() { |
| 39 } | 42 } |
| 40 | 43 |
| 41 void ContentVerifier::Start() { | 44 void ContentVerifier::Start() { |
| 42 if (mode_ >= BOOTSTRAP) | 45 if (mode_ >= BOOTSTRAP) |
| 43 fetcher_->Start(); | 46 fetcher_->Start(); |
| 44 } | 47 } |
| 45 | 48 |
| 46 void ContentVerifier::Shutdown() { | 49 void ContentVerifier::Shutdown() { |
| 47 fetcher_.reset(); | 50 fetcher_.reset(); |
| 48 delegate_.reset(); | 51 delegate_.reset(); |
| 49 } | 52 } |
| 50 | 53 |
| 51 ContentVerifyJob* ContentVerifier::CreateJobFor( | 54 ContentVerifyJob* ContentVerifier::CreateJobFor( |
| 52 const std::string& extension_id, | 55 const std::string& extension_id, |
| 53 const base::FilePath& extension_root, | 56 const base::FilePath& extension_root, |
| 54 const base::FilePath& relative_path) { | 57 const base::FilePath& relative_path) { |
| 55 if (mode_ < BOOTSTRAP || !delegate_) | 58 if (mode_ < BOOTSTRAP || !delegate_) |
| 56 return NULL; | 59 return NULL; |
| 57 | 60 |
| 58 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); | 61 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); |
| 59 const Extension* extension = | 62 const Extension* extension = |
| 60 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); | 63 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); |
| 61 | 64 |
| 62 if (!extension || !extension->version() || | 65 std::set<base::FilePath> paths; |
| 63 !delegate_->ShouldBeVerified(*extension)) | 66 paths.insert(relative_path); |
| 67 if (!ShouldVerifyAnyPaths(extension, paths)) |
| 64 return NULL; | 68 return NULL; |
| 65 | 69 |
| 66 // Images used in the browser get transcoded during install, so skip checking | |
| 67 // them for now. TODO(asargent) - see if we can cache this list for a given | |
| 68 // extension id/version pair. | |
| 69 std::set<base::FilePath> browser_images = | |
| 70 delegate_->GetBrowserImagePaths(extension); | |
| 71 if (ContainsKey(browser_images, relative_path)) | |
| 72 return NULL; | |
| 73 | |
| 74 base::FilePath locales_dir = extension_root.Append(kLocaleFolder); | |
| 75 base::FilePath full_path = extension_root.Append(relative_path); | |
| 76 if (locales_dir.IsParent(full_path)) { | |
| 77 // TODO(asargent) - see if we can cache this list to avoid having to fetch | |
| 78 // it every time. Maybe it can never change at runtime? (Or if it can, | |
| 79 // maybe there is an event we can listen for to know to drop our cache). | |
| 80 std::set<std::string> all_locales; | |
| 81 extension_l10n_util::GetAllLocales(&all_locales); | |
| 82 // Since message catalogs get transcoded during installation, we want to | |
| 83 // ignore only those paths that the localization transcoding *did* ignore. | |
| 84 if (!extension_l10n_util::ShouldSkipValidation( | |
| 85 locales_dir, full_path, all_locales)) | |
| 86 return NULL; | |
| 87 } | |
| 88 | |
| 89 // TODO(asargent) - we can probably get some good performance wins by having | 70 // TODO(asargent) - we can probably get some good performance wins by having |
| 90 // a cache of ContentHashReader's that we hold onto past the end of each job. | 71 // a cache of ContentHashReader's that we hold onto past the end of each job. |
| 91 return new ContentVerifyJob( | 72 return new ContentVerifyJob( |
| 92 new ContentHashReader(extension_id, | 73 new ContentHashReader(extension_id, |
| 93 *extension->version(), | 74 *extension->version(), |
| 94 extension_root, | 75 extension_root, |
| 95 relative_path, | 76 relative_path, |
| 96 delegate_->PublicKey()), | 77 delegate_->PublicKey()), |
| 97 base::Bind(&ContentVerifier::VerifyFailed, this, extension->id())); | 78 base::Bind(&ContentVerifier::VerifyFailed, this, extension->id())); |
| 98 } | 79 } |
| 99 | 80 |
| 100 void ContentVerifier::VerifyFailed(const std::string& extension_id, | 81 void ContentVerifier::VerifyFailed(const std::string& extension_id, |
| 101 ContentVerifyJob::FailureReason reason) { | 82 ContentVerifyJob::FailureReason reason) { |
| 102 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { | 83 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { |
| 103 content::BrowserThread::PostTask( | 84 content::BrowserThread::PostTask( |
| 104 content::BrowserThread::UI, | 85 content::BrowserThread::UI, |
| 105 FROM_HERE, | 86 FROM_HERE, |
| 106 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason)); | 87 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason)); |
| 107 return; | 88 return; |
| 108 } | 89 } |
| 109 | 90 |
| 110 if (!delegate_ || mode_ < ENFORCE) | 91 VLOG(1) << "VerifyFailed " << extension_id << " reason:" << reason; |
| 92 |
| 93 if (!delegate_ || !fetcher_.get() || mode_ < ENFORCE) |
| 111 return; | 94 return; |
| 112 | 95 |
| 113 if (reason == ContentVerifyJob::NO_HASHES && mode_ < ENFORCE_STRICT && | 96 if (reason == ContentVerifyJob::MISSING_ALL_HASHES) { |
| 114 fetcher_.get()) { | |
| 115 // If we failed because there were no hashes yet for this extension, just | 97 // If we failed because there were no hashes yet for this extension, just |
| 116 // request some. | 98 // request some. |
| 117 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); | 99 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); |
| 118 const Extension* extension = | 100 const Extension* extension = |
| 119 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); | 101 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); |
| 120 if (extension) | 102 if (extension) |
| 121 fetcher_->DoFetch(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) |
| 122 return; | 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; |
| 123 } | 179 } |
| 124 delegate_->VerifyFailed(extension_id); | 180 return false; |
| 125 } | 181 } |
| 126 | 182 |
| 127 // static | 183 // static |
| 128 ContentVerifier::Mode ContentVerifier::GetMode() { | 184 ContentVerifier::Mode ContentVerifier::GetMode() { |
| 129 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | 185 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| 130 | 186 |
| 131 Mode experiment_value = NONE; | 187 Mode experiment_value = NONE; |
| 132 const std::string group = base::FieldTrialList::FindFullName(kExperimentName); | 188 const std::string group = base::FieldTrialList::FindFullName(kExperimentName); |
| 133 if (group == "EnforceStrict") | 189 if (group == "EnforceStrict") |
| 134 experiment_value = ENFORCE_STRICT; | 190 experiment_value = ENFORCE_STRICT; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 cmdline_value = ENFORCE; | 222 cmdline_value = ENFORCE; |
| 167 } | 223 } |
| 168 | 224 |
| 169 // We don't want to allow the command-line flags to eg disable enforcement if | 225 // We don't want to allow the command-line flags to eg disable enforcement if |
| 170 // the experiment group says it should be on, or malware may just modify the | 226 // the experiment group says it should be on, or malware may just modify the |
| 171 // command line flags. So return the more restrictive of the 2 values. | 227 // command line flags. So return the more restrictive of the 2 values. |
| 172 return std::max(experiment_value, cmdline_value); | 228 return std::max(experiment_value, cmdline_value); |
| 173 } | 229 } |
| 174 | 230 |
| 175 } // namespace extensions | 231 } // namespace extensions |
| OLD | NEW |