aw_apps_package_names_allowlist_component_loader_policy.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // Copyright 2021 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. #include "android_webview/browser/component_updater/loader_policies/aw_apps_package_names_allowlist_component_loader_policy.h"
  5. #include <stdint.h>
  6. #include <stdio.h>
  7. #include <cstring>
  8. #include <memory>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12. #include "android_webview/browser/metrics/aw_metrics_service_client.h"
  13. #include "android_webview/common/aw_switches.h"
  14. #include "android_webview/common/components/aw_apps_package_names_allowlist_component_utils.h"
  15. #include "android_webview/common/metrics/app_package_name_logging_rule.h"
  16. #include "base/bind.h"
  17. #include "base/callback.h"
  18. #include "base/command_line.h"
  19. #include "base/containers/flat_map.h"
  20. #include "base/feature_list.h"
  21. #include "base/files/file_util.h"
  22. #include "base/files/scoped_file.h"
  23. #include "base/logging.h"
  24. #include "base/metrics/histogram_macros.h"
  25. #include "base/strings/string_util.h"
  26. #include "base/task/thread_pool.h"
  27. #include "base/time/time.h"
  28. #include "base/values.h"
  29. #include "base/version.h"
  30. #include "components/component_updater/android/component_loader_policy.h"
  31. #include "components/optimization_guide/core/bloom_filter.h"
  32. #include "third_party/abseil-cpp/absl/types/optional.h"
  33. namespace android_webview {
  34. const base::TimeDelta kWebViewAppsMinAllowlistThrottleTimeDelta =
  35. base::Hours(1);
  36. const base::TimeDelta kWebViewAppsMaxAllowlistThrottleTimeDelta = base::Days(2);
  37. namespace {
  38. // Persisted to logs, should never change.
  39. constexpr char kAppsPackageNamesAllowlistMetricsSuffix[] =
  40. "WebViewAppsPackageNamesAllowlist";
  41. constexpr int kBitsPerByte = 8;
  42. using AllowlistPraseStatus =
  43. AwAppsPackageNamesAllowlistComponentLoaderPolicy::AllowlistPraseStatus;
  44. struct AllowListLookupResult {
  45. AllowlistPraseStatus parse_status;
  46. absl::optional<AppPackageNameLoggingRule> record_rule;
  47. };
  48. void RecordAndReportResult(AllowListLookupCallback lookup_callback,
  49. AllowListLookupResult result) {
  50. DCHECK(lookup_callback);
  51. UMA_HISTOGRAM_ENUMERATION(
  52. "Android.WebView.Metrics.PackagesAllowList.ParseStatus",
  53. result.parse_status);
  54. std::move(lookup_callback).Run(result.record_rule);
  55. }
  56. // Lookup the `package_name` in `allowlist_fd`, returns a null
  57. // an `AllowListLookupResult` containing an `AppPackageNameLoggingRule` if it
  58. // fails.
  59. //
  60. // `allowlist_fd` the fd of a file the contain a bloomfilter that represents an
  61. // allowlist of apps package names.
  62. // `num_hash` the number of hash functions to use in the
  63. // `optimization_guide::BloomFilter`.
  64. // `num_bits` the number of bits in the `optimization_guide::BloomFilter`.
  65. // `package_name` the app package name to look up in the allowlist.
  66. // `version` the allowlist version.
  67. // `expiry_date` the expiry date of the allowlist, i.e the date after which
  68. // this allowlist shouldn't be used.
  69. AllowListLookupResult GetAppPackageNameLoggingRule(
  70. base::ScopedFD allowlist_fd,
  71. int num_hash,
  72. int num_bits,
  73. const std::string& package_name,
  74. const base::Version& version,
  75. const base::Time& expiry_date) {
  76. // Transfer the ownership of the file from `allowlist_fd` to `file_stream`.
  77. base::ScopedFILE file_stream(fdopen(allowlist_fd.release(), "r"));
  78. if (!file_stream.get()) {
  79. return {AllowlistPraseStatus::kIOError};
  80. }
  81. // TODO(https://crbug.com/1219496): use mmap instead of reading the whole
  82. // file.
  83. std::string bloom_filter_data;
  84. if (!base::ReadStreamToString(file_stream.get(), &bloom_filter_data) ||
  85. bloom_filter_data.empty()) {
  86. return {AllowlistPraseStatus::kIOError};
  87. }
  88. // Make sure the bloomfilter binary data is of the correct length.
  89. if (bloom_filter_data.size() !=
  90. size_t((num_bits + kBitsPerByte - 1) / kBitsPerByte)) {
  91. return {AllowlistPraseStatus::kMalformedBloomFilter};
  92. }
  93. if (optimization_guide::BloomFilter(num_hash, num_bits, bloom_filter_data)
  94. .Contains(package_name)) {
  95. return {AllowlistPraseStatus::kSuccess,
  96. AppPackageNameLoggingRule(version, expiry_date)};
  97. } else {
  98. return {AllowlistPraseStatus::kSuccess,
  99. AppPackageNameLoggingRule(version, base::Time::Min())};
  100. }
  101. }
  102. void SetAppPackageNameLoggingRule(
  103. absl::optional<AppPackageNameLoggingRule> record) {
  104. auto* metrics_service_client = AwMetricsServiceClient::GetInstance();
  105. DCHECK(metrics_service_client);
  106. metrics_service_client->SetAppPackageNameLoggingRule(record);
  107. metrics_service_client->SetAppPackageNameLoggingRuleLastUpdateTime(
  108. base::Time::Now());
  109. if (!record.has_value()) {
  110. VLOG(2) << "Failed to load WebView apps package allowlist";
  111. return;
  112. }
  113. VLOG(2) << "WebView apps package allowlist version "
  114. << record.value().GetVersion() << " is loaded";
  115. if (record.value().IsAppPackageNameAllowed()) {
  116. VLOG(2) << "App package name should be recorded until "
  117. << record.value().GetExpiryDate();
  118. } else {
  119. VLOG(2) << "App package name shouldn't be recorded";
  120. }
  121. }
  122. bool ShouldThrottleAppPackageNamesAllowlistComponent(
  123. base::Time last_update,
  124. absl::optional<AppPackageNameLoggingRule> cached_record) {
  125. if (base::CommandLine::ForCurrentProcess()->HasSwitch(
  126. switches::kWebViewDisablePackageAllowlistThrottling) ||
  127. last_update.is_null()) {
  128. return false;
  129. }
  130. base::TimeDelta throttle_time_delta(
  131. kWebViewAppsMinAllowlistThrottleTimeDelta);
  132. if (cached_record.has_value()) {
  133. base::Time expiry_date = cached_record.value().GetExpiryDate();
  134. bool in_the_allowlist = !expiry_date.is_min();
  135. bool is_allowlist_expired = expiry_date <= base::Time::Now();
  136. if (!in_the_allowlist || !is_allowlist_expired) {
  137. throttle_time_delta = kWebViewAppsMaxAllowlistThrottleTimeDelta;
  138. }
  139. }
  140. return base::Time::Now() - last_update <= throttle_time_delta;
  141. }
  142. } // namespace
  143. AwAppsPackageNamesAllowlistComponentLoaderPolicy::
  144. AwAppsPackageNamesAllowlistComponentLoaderPolicy(
  145. std::string app_package_name,
  146. absl::optional<AppPackageNameLoggingRule> cached_record,
  147. AllowListLookupCallback lookup_callback)
  148. : app_package_name_(std::move(app_package_name)),
  149. cached_record_(cached_record),
  150. lookup_callback_(std::move(lookup_callback)) {
  151. DCHECK(!app_package_name_.empty());
  152. DCHECK(lookup_callback_);
  153. }
  154. AwAppsPackageNamesAllowlistComponentLoaderPolicy::
  155. ~AwAppsPackageNamesAllowlistComponentLoaderPolicy() = default;
  156. // `manifest` represents a JSON object that looks like this:
  157. // {
  158. // "name": "WebViewAppsPackageNamesAllowlist",
  159. //
  160. // /* The component version string, matches the param `version`. */
  161. // "version": "xxxx.xx.xx.xx",
  162. //
  163. // /* Bloomfilter parameters, set by the server side */
  164. // /* int32: number of hash functions used by the bloomfilter */
  165. // "bloomfilter_num_hash": xx
  166. // /* int32: number of bits in the bloomfilter binary array */
  167. // "bloomfilter_num_bits": xx
  168. //
  169. // /* The allowlist expiry date after which the allowlist shouldn't be used.
  170. // Its format is an int64 number representing the number of milliseconds
  171. // after UNIX Epoch. */
  172. // "expiry_date": 12345678910
  173. // }
  174. void AwAppsPackageNamesAllowlistComponentLoaderPolicy::ComponentLoaded(
  175. const base::Version& version,
  176. base::flat_map<std::string, base::ScopedFD>& fd_map,
  177. std::unique_ptr<base::DictionaryValue> manifest) {
  178. DCHECK(version.IsValid());
  179. // Have to use double because base::DictionaryValue doesn't support int64
  180. // values.
  181. absl::optional<double> expiry_date_ms =
  182. manifest->FindDoublePath(kExpiryDateKey);
  183. absl::optional<int> num_hash = manifest->FindIntPath(kBloomFilterNumHashKey);
  184. absl::optional<int> num_bits = manifest->FindIntPath(kBloomFilterNumBitsKey);
  185. // Being conservative and consider the allowlist expired when a valid expiry
  186. // date is absent.
  187. if (!expiry_date_ms.has_value() || !num_hash.has_value() ||
  188. !num_bits.has_value() || num_hash.value() <= 0 || num_bits.value() <= 0) {
  189. RecordAndReportResult(std::move(lookup_callback_),
  190. {AllowlistPraseStatus::kMissingFields});
  191. return;
  192. }
  193. base::Time expiry_date = base::Time::UnixEpoch() +
  194. base::Milliseconds(expiry_date_ms.value_or(0.0));
  195. if (expiry_date <= base::Time::Now()) {
  196. RecordAndReportResult(std::move(lookup_callback_),
  197. {AllowlistPraseStatus::kExpiredAllowlist});
  198. return;
  199. }
  200. if (cached_record_.has_value() &&
  201. cached_record_.value().GetVersion() == version) {
  202. RecordAndReportResult(std::move(lookup_callback_),
  203. {AllowlistPraseStatus::kUsingCache, cached_record_});
  204. return;
  205. }
  206. auto allowlist_iterator = fd_map.find(kAllowlistBloomFilterFileName);
  207. if (allowlist_iterator == fd_map.end()) {
  208. RecordAndReportResult(std::move(lookup_callback_),
  209. {AllowlistPraseStatus::kMissingAllowlistFile});
  210. return;
  211. }
  212. base::ThreadPool::PostTaskAndReplyWithResult(
  213. FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
  214. base::BindOnce(&GetAppPackageNameLoggingRule,
  215. std::move(allowlist_iterator->second), num_hash.value(),
  216. num_bits.value(), std::move(app_package_name_), version,
  217. expiry_date),
  218. base::BindOnce(&RecordAndReportResult, std::move(lookup_callback_)));
  219. }
  220. void AwAppsPackageNamesAllowlistComponentLoaderPolicy::ComponentLoadFailed(
  221. component_updater::ComponentLoadResult /*error*/) {
  222. DCHECK(lookup_callback_);
  223. // TODO(crbug.com/1216200): Record the error in a histogram in the
  224. // ComponentLoader for each component.
  225. std::move(lookup_callback_).Run(absl::optional<AppPackageNameLoggingRule>());
  226. }
  227. void AwAppsPackageNamesAllowlistComponentLoaderPolicy::GetHash(
  228. std::vector<uint8_t>* hash) const {
  229. GetWebViewAppsPackageNamesAllowlistPublicKeyHash(hash);
  230. }
  231. std::string AwAppsPackageNamesAllowlistComponentLoaderPolicy::GetMetricsSuffix()
  232. const {
  233. return kAppsPackageNamesAllowlistMetricsSuffix;
  234. }
  235. void LoadPackageNamesAllowlistComponent(
  236. component_updater::ComponentLoaderPolicyVector& policies,
  237. AwMetricsServiceClient* metrics_service_client) {
  238. DCHECK(metrics_service_client);
  239. absl::optional<AppPackageNameLoggingRule> cached_record =
  240. metrics_service_client->GetCachedAppPackageNameLoggingRule();
  241. base::Time last_update =
  242. metrics_service_client->GetAppPackageNameLoggingRuleLastUpdateTime();
  243. bool should_throttle = ShouldThrottleAppPackageNamesAllowlistComponent(
  244. last_update, cached_record);
  245. UMA_HISTOGRAM_BOOLEAN(
  246. "Android.WebView.Metrics.PackagesAllowList.ThrottleStatus",
  247. should_throttle);
  248. if (should_throttle) {
  249. return;
  250. }
  251. policies.push_back(
  252. std::make_unique<AwAppsPackageNamesAllowlistComponentLoaderPolicy>(
  253. metrics_service_client->GetAppPackageName(), std::move(cached_record),
  254. base::BindOnce(&SetAppPackageNameLoggingRule)));
  255. }
  256. } // namespace android_webview