| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 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 "chrome/browser/component_updater/sth_set_component_installer.h" |
| 6 |
| 7 #include <utility> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/files/file_enumerator.h" |
| 11 #include "base/files/file_path.h" |
| 12 #include "base/files/file_util.h" |
| 13 #include "base/logging.h" |
| 14 #include "base/path_service.h" |
| 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/values.h" |
| 17 #include "base/version.h" |
| 18 #include "components/component_updater/component_updater_paths.h" |
| 19 #include "components/safe_json/safe_json_parser.h" |
| 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "crypto/sha2.h" |
| 22 #include "net/cert/ct_known_logs_static.h" |
| 23 #include "net/cert/ct_log_response_parser.h" |
| 24 #include "net/cert/signed_tree_head.h" |
| 25 #include "net/cert/sth_distributor.h" |
| 26 #include "net/cert/sth_observer.h" |
| 27 |
| 28 using component_updater::ComponentUpdateService; |
| 29 |
| 30 namespace { |
| 31 const base::FilePath::CharType kSTHsDirName[] = FILE_PATH_LITERAL("sths"); |
| 32 |
| 33 base::FilePath GetInstalledPath(const base::FilePath& base) { |
| 34 return base.Append(FILE_PATH_LITERAL("_platform_specific")) |
| 35 .Append(FILE_PATH_LITERAL("all")) |
| 36 .Append(kSTHsDirName); |
| 37 } |
| 38 |
| 39 } // namespace |
| 40 |
| 41 namespace component_updater { |
| 42 |
| 43 // The SHA256 of the SubjectPublicKeyInfo used to sign the extension. |
| 44 // The extension id is: ojjgnpkioondelmggbekfhllhdaimnho |
| 45 const uint8_t kPublicKeySHA256[32] = { |
| 46 0xe9, 0x96, 0xdf, 0xa8, 0xee, 0xd3, 0x4b, 0xc6, 0x61, 0x4a, 0x57, |
| 47 0xbb, 0x73, 0x08, 0xcd, 0x7e, 0x51, 0x9b, 0xcc, 0x69, 0x08, 0x41, |
| 48 0xe1, 0x96, 0x9f, 0x7c, 0xb1, 0x73, 0xef, 0x16, 0x80, 0x0a}; |
| 49 |
| 50 const char kSTHSetFetcherManifestName[] = "Signed Tree Heads"; |
| 51 |
| 52 STHSetComponentInstallerTraits::STHSetComponentInstallerTraits( |
| 53 scoped_ptr<net::ct::STHObserver> sth_observer) |
| 54 : sth_observer_(std::move(sth_observer)) {} |
| 55 |
| 56 STHSetComponentInstallerTraits::~STHSetComponentInstallerTraits() {} |
| 57 |
| 58 bool STHSetComponentInstallerTraits::CanAutoUpdate() const { |
| 59 return true; |
| 60 } |
| 61 |
| 62 // Public data is delivered via this component, no need for encryption. |
| 63 bool STHSetComponentInstallerTraits::RequiresNetworkEncryption() const { |
| 64 return false; |
| 65 } |
| 66 |
| 67 bool STHSetComponentInstallerTraits::OnCustomInstall( |
| 68 const base::DictionaryValue& manifest, |
| 69 const base::FilePath& install_dir) { |
| 70 return true; // Nothing custom here. |
| 71 } |
| 72 |
| 73 void STHSetComponentInstallerTraits::ComponentReady( |
| 74 const base::Version& version, |
| 75 const base::FilePath& install_dir, |
| 76 scoped_ptr<base::DictionaryValue> manifest) { |
| 77 if (!content::BrowserThread::PostBlockingPoolTask( |
| 78 FROM_HERE, |
| 79 base::Bind(&STHSetComponentInstallerTraits::LoadSTHsFromDisk, |
| 80 base::Unretained(this), GetInstalledPath(install_dir), |
| 81 version))) { |
| 82 NOTREACHED(); |
| 83 } |
| 84 } |
| 85 |
| 86 // Called during startup and installation before ComponentReady(). |
| 87 bool STHSetComponentInstallerTraits::VerifyInstallation( |
| 88 const base::DictionaryValue& manifest, |
| 89 const base::FilePath& install_dir) const { |
| 90 return base::PathExists(GetInstalledPath(install_dir)); |
| 91 } |
| 92 |
| 93 base::FilePath STHSetComponentInstallerTraits::GetBaseDirectory() const { |
| 94 base::FilePath result; |
| 95 PathService::Get(DIR_CERT_TRANS_TREE_STATES, &result); |
| 96 return result; |
| 97 } |
| 98 |
| 99 void STHSetComponentInstallerTraits::GetHash(std::vector<uint8_t>* hash) const { |
| 100 hash->assign(std::begin(kPublicKeySHA256), std::end(kPublicKeySHA256)); |
| 101 } |
| 102 |
| 103 std::string STHSetComponentInstallerTraits::GetName() const { |
| 104 return kSTHSetFetcherManifestName; |
| 105 } |
| 106 |
| 107 void STHSetComponentInstallerTraits::LoadSTHsFromDisk( |
| 108 const base::FilePath& sths_path, |
| 109 const base::Version& version) { |
| 110 if (sths_path.empty()) |
| 111 return; |
| 112 |
| 113 base::FileEnumerator sth_file_enumerator(sths_path, false, |
| 114 base::FileEnumerator::FILES, |
| 115 FILE_PATH_LITERAL("*.sth")); |
| 116 base::FilePath sth_file_path; |
| 117 |
| 118 while (!(sth_file_path = sth_file_enumerator.Next()).empty()) { |
| 119 DVLOG(1) << "Reading STH from file: " << sth_file_path.value(); |
| 120 |
| 121 const std::string log_id_hex = |
| 122 sth_file_path.BaseName().RemoveExtension().MaybeAsASCII(); |
| 123 if (log_id_hex.empty()) { |
| 124 DVLOG(1) << "Error extracting log_id from: " |
| 125 << sth_file_path.BaseName().LossyDisplayName(); |
| 126 continue; |
| 127 } |
| 128 |
| 129 std::vector<uint8_t> decoding_output; |
| 130 if (!base::HexStringToBytes(log_id_hex, &decoding_output)) { |
| 131 DVLOG(1) << "Failed to decode Log ID: " << log_id_hex; |
| 132 continue; |
| 133 } |
| 134 |
| 135 const std::string log_id(reinterpret_cast<const char*>(&decoding_output[0]), |
| 136 decoding_output.size()); |
| 137 |
| 138 std::string json_sth; |
| 139 if (!base::ReadFileToString(sth_file_path, &json_sth)) { |
| 140 DVLOG(1) << "Failed reading from " << sth_file_path.value(); |
| 141 continue; |
| 142 } |
| 143 |
| 144 DVLOG(1) << "STH: Successfully read: " << json_sth; |
| 145 safe_json::SafeJsonParser::Parse( |
| 146 json_sth, |
| 147 base::Bind(&STHSetComponentInstallerTraits::OnJsonParseSuccess, |
| 148 base::Unretained(this), log_id), |
| 149 base::Bind(&STHSetComponentInstallerTraits::OnJsonParseError, |
| 150 base::Unretained(this), log_id)); |
| 151 } |
| 152 } |
| 153 |
| 154 void STHSetComponentInstallerTraits::OnJsonParseSuccess( |
| 155 const std::string& log_id, |
| 156 scoped_ptr<base::Value> parsed_json) { |
| 157 net::ct::SignedTreeHead signed_tree_head; |
| 158 DVLOG(1) << "STH parsing success for log: " |
| 159 << base::HexEncode(log_id.data(), log_id.length()); |
| 160 if (!net::ct::FillSignedTreeHead(*(parsed_json.get()), &signed_tree_head)) { |
| 161 LOG(ERROR) << "Failed to fill in signed tree head."; |
| 162 return; |
| 163 } |
| 164 |
| 165 // The log id is not a part of the response, fill in manually. |
| 166 signed_tree_head.log_id = log_id; |
| 167 content::BrowserThread::PostTask( |
| 168 content::BrowserThread::IO, FROM_HERE, |
| 169 base::Bind(&net::ct::STHObserver::NewSTHObserved, |
| 170 base::Unretained(sth_observer_.get()), signed_tree_head)); |
| 171 } |
| 172 |
| 173 void STHSetComponentInstallerTraits::OnJsonParseError( |
| 174 const std::string& log_id, |
| 175 const std::string& error) { |
| 176 DVLOG(1) << "STH loading failed: " << error |
| 177 << " for log: " << base::HexEncode(log_id.data(), log_id.length()); |
| 178 } |
| 179 |
| 180 void RegisterSTHSetComponent(ComponentUpdateService* cus, |
| 181 const base::FilePath& user_data_dir) { |
| 182 DVLOG(1) << "Registering STH Set fetcher component."; |
| 183 |
| 184 // TODO(eranm): The next step in auditing CT logs (crbug.com/506227) is to |
| 185 // pass the distributor to the IOThread so it can be used in a per-profile |
| 186 // context for checking inclusion of SCTs. |
| 187 scoped_ptr<net::ct::STHDistributor> distributor( |
| 188 new net::ct::STHDistributor()); |
| 189 |
| 190 scoped_ptr<ComponentInstallerTraits> traits( |
| 191 new STHSetComponentInstallerTraits(std::move(distributor))); |
| 192 // |cus| will take ownership of |installer| during installer->Register(cus). |
| 193 DefaultComponentInstaller* installer = |
| 194 new DefaultComponentInstaller(std::move(traits)); |
| 195 installer->Register(cus, base::Closure()); |
| 196 } |
| 197 |
| 198 } // namespace component_updater |
| OLD | NEW |