// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/web/model/certificate_policy_app_agent.h"
#import "base/task/cancelable_task_tracker.h"
#import "base/task/single_thread_task_runner.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_list.h"
#import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/profile/profile_manager_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/web/public/security/certificate_policy_cache.h"
#import "ios/web/public/session/session_certificate_policy_cache.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
#import "ios/web/public/web_state.h"
namespace {
// Updates the BrowserState's policy cache from the `web_state` session policy
// cache.
void UpdateCertificatePolicyCacheFromWebState(const web::WebState* web_state) {
DCHECK(web_state);
DCHECK_CURRENTLY_ON(web::WebThread::UI);
// The WebState install its certificate policy cache upon realization, so
// unrealized WebState can be skipped (to avoid forcing their realization).
if (!web_state->IsRealized())
return;
web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache();
}
// Populates the certificate policy cache based on all of the WebStates in
// the `incognito` browsers of `weak_browser_state`. Because this is called
// asynchronously, it needs to be resilient to shutdown having happened before
// it is invoked.
void RestoreCertificatePolicyCacheFromBrowsers(
base::WeakPtr<ChromeBrowserState> weak_browser_state,
bool incognito) {
DCHECK_CURRENTLY_ON(web::WebThread::UI);
// If the ChromeBrowserState is destroyed, it's too late to do anything.
ChromeBrowserState* browser_state = weak_browser_state.get();
if (!browser_state) {
return;
}
BrowserList* browser_list =
BrowserListFactory::GetForBrowserState(browser_state);
const BrowserList::BrowserType browser_types =
incognito ? BrowserList::BrowserType::kIncognito
: BrowserList::BrowserType::kRegularAndInactive;
std::set<Browser*> browsers = browser_list->BrowsersOfType(browser_types);
for (Browser* browser : browsers) {
WebStateList* web_state_list = browser->GetWebStateList();
for (int index = 0; index < web_state_list->count(); ++index) {
UpdateCertificatePolicyCacheFromWebState(
web_state_list->GetWebStateAt(index));
}
}
}
// Scrubs the certificate policy cache of all certificates policies except
// those for the current `incognito` browsers in `weak_browser_state`.
// Clearing the cache is done on the IO thread, and then cache repopulation is
// done on the UI thread.
void CleanCertificatePolicyCache(
base::CancelableTaskTracker* task_tracker,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
base::WeakPtr<ChromeBrowserState> weak_browser_state,
bool incognito) {
DCHECK(policy_cache);
DCHECK_CURRENTLY_ON(web::WebThread::UI);
task_tracker->PostTaskAndReply(
task_runner.get(), FROM_HERE,
base::BindOnce(&web::CertificatePolicyCache::ClearCertificatePolicies,
policy_cache),
base::BindOnce(&RestoreCertificatePolicyCacheFromBrowsers,
std::move(weak_browser_state), incognito));
}
} // anonymous namespace
@implementation CertificatePolicyAppAgent {
// Used to ensure thread-safety of the certificate policy management code.
base::CancelableTaskTracker _clearPoliciesTaskTracker;
}
- (void)dealloc {
// Clean up any remaining tasks.
_clearPoliciesTaskTracker.TryCancelAll();
}
- (BOOL)isWorking {
return static_cast<BOOL>(_clearPoliciesTaskTracker.HasTrackedTasks());
}
- (void)appDidEnterBackground {
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
// Evict all the certificate policies except for the current entries of the
// active sessions, for the regular and incognito browsers.
CleanCertificatePolicyCache(
&_clearPoliciesTaskTracker, web::GetIOThreadTaskRunner({}),
web::BrowserState::GetCertificatePolicyCache(browserState),
browserState->AsWeakPtr(),
/*incognito=*/false);
if (browserState->HasOffTheRecordChromeBrowserState()) {
ChromeBrowserState* incognitoBrowserState =
browserState->GetOffTheRecordChromeBrowserState();
CleanCertificatePolicyCache(
&_clearPoliciesTaskTracker, web::GetIOThreadTaskRunner({}),
web::BrowserState::GetCertificatePolicyCache(incognitoBrowserState),
browserState->AsWeakPtr(), /*incognito=*/true);
}
}
}
@end