1 // Copyright (c) 2012 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/chromeos/login/profile_auth_data.h"
6
7 #include "base/barrier_closure.h"
8 #include "base/bind.h"
9 #include "base/callback.h"
10 #include "components/google/core/common/google_util.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/storage_partition.h"
13 #include "net/cookies/canonical_cookie.h"
14 #include "net/cookies/cookie_access_result.h"
15 #include "net/cookies/cookie_util.h"
16 #include "services/network/public/mojom/cookie_manager.mojom.h"
17 #include "services/network/public/mojom/network_context.mojom.h"
18 #include "url/gurl.h"
19
20 using content::BrowserThread;
21
22 namespace chromeos {
23
24 namespace {
25
26 // Callback that receives the key for from_partition's saved http auth cache
27 // proxy entries.
OnTargetHttpAuthCacheProxyEntriesSaved(base::RepeatingClosure completion_callback,content::StoragePartition * to_partition,const base::UnguessableToken & cache_key)28 void OnTargetHttpAuthCacheProxyEntriesSaved(
29 base::RepeatingClosure completion_callback,
30 content::StoragePartition* to_partition,
31 const base::UnguessableToken& cache_key) {
32 to_partition->GetNetworkContext()->LoadHttpAuthCacheProxyEntries(
33 cache_key, completion_callback);
34 }
35
36 // Starts tranferring `from_partition`'s http auth cache's proxy entries into
37 // `to_partition`.
TransferHttpAuthCacheProxyEntries(base::RepeatingClosure completion_callback,content::StoragePartition * from_partition,content::StoragePartition * to_partition)38 void TransferHttpAuthCacheProxyEntries(
39 base::RepeatingClosure completion_callback,
40 content::StoragePartition* from_partition,
41 content::StoragePartition* to_partition) {
42 // `to_partition` will outlive the call to `completion_callback`.
43 // See ProfileAuthData::Transfer.
44 from_partition->GetNetworkContext()->SaveHttpAuthCacheProxyEntries(
45 base::BindOnce(&OnTargetHttpAuthCacheProxyEntriesSaved,
46 completion_callback, base::Unretained(to_partition)));
47 }
48
49 // Given a `cookie` set during login, returns true if the cookie may have been
50 // set by GAIA. The main criterion is the `cookie`'s domain. If the domain
51 // is *google.<TLD> or *youtube.<TLD>, the cookie is considered to have been set
52 // by GAIA as well.
IsGAIACookie(const net::CanonicalCookie & cookie)53 bool IsGAIACookie(const net::CanonicalCookie& cookie) {
54 GURL cookie_url =
55 net::cookie_util::CookieOriginToURL(cookie.Domain(), cookie.IsSecure());
56
57 return google_util::IsGoogleDomainUrl(
58 cookie_url, google_util::ALLOW_SUBDOMAIN,
59 google_util::ALLOW_NON_STANDARD_PORTS) ||
60 google_util::IsYoutubeDomainUrl(cookie_url,
61 google_util::ALLOW_SUBDOMAIN,
62 google_util::ALLOW_NON_STANDARD_PORTS);
63 }
64
OnCookieSet(base::RepeatingClosure completion_callback,net::CookieAccessResult result)65 void OnCookieSet(base::RepeatingClosure completion_callback,
66 net::CookieAccessResult result) {
67 completion_callback.Run();
68 }
69
70 // Imports `cookies` into `to_partition`'s cookie jar. `cookie.IsCanonical()`
71 // must be true for all cookies in `cookies`.
ImportCookies(base::RepeatingClosure completion_callback,content::StoragePartition * to_partition,const net::CookieList & cookies)72 void ImportCookies(base::RepeatingClosure completion_callback,
73 content::StoragePartition* to_partition,
74 const net::CookieList& cookies) {
75 if (cookies.empty()) {
76 completion_callback.Run();
77 return;
78 }
79
80 network::mojom::CookieManager* cookie_manager =
81 to_partition->GetCookieManagerForBrowserProcess();
82
83 base::RepeatingClosure cookie_completion_callback =
84 base::BarrierClosure(cookies.size(), completion_callback);
85 for (const auto& cookie : cookies) {
86 // Assume secure_source - since the cookies are being restored from
87 // another store, they have already gone through the strict secure check.
88 // Likewise for permitting same-site marked cookies.
89 DCHECK(cookie.IsCanonical());
90 net::CookieOptions options;
91 options.set_include_httponly();
92 options.set_same_site_cookie_context(
93 net::CookieOptions::SameSiteCookieContext::MakeInclusive());
94 cookie_manager->SetCanonicalCookie(
95 cookie, net::cookie_util::SimulatedCookieSource(cookie, "https"),
96 options, base::BindOnce(&OnCookieSet, cookie_completion_callback));
97 }
98 }
99
100 // Callback that receives the contents of `from_partition`'s cookie jar.
101 // Transfers the necessary cookies to `to_partition`'s cookie jar.
OnCookiesToTransferRetrieved(base::RepeatingClosure completion_callback,content::StoragePartition * to_partition,bool first_login,const net::CookieList & cookies)102 void OnCookiesToTransferRetrieved(base::RepeatingClosure completion_callback,
103 content::StoragePartition* to_partition,
104 bool first_login,
105 const net::CookieList& cookies) {
106 DCHECK_CURRENTLY_ON(BrowserThread::UI);
107
108 if (first_login) {
109 ImportCookies(completion_callback, to_partition, cookies);
110 } else {
111 net::CookieList non_gaia_cookies;
112 for (const auto& cookie : cookies) {
113 if (!IsGAIACookie(cookie))
114 non_gaia_cookies.push_back(cookie);
115 }
116 ImportCookies(completion_callback, to_partition, non_gaia_cookies);
117 }
118 }
119
120 // Callback that receives the content of `to_partition`'s cookie jar. Checks
121 // whether this is the user's first login, based on the state of the cookie
122 // jar, and starts retrieval of the data that should be transfered.
OnTargetCookieJarContentsRetrieved(base::RepeatingClosure completion_callback,content::StoragePartition * from_partition,content::StoragePartition * to_partition,bool transfer_auth_cookies_on_first_login,bool transfer_saml_auth_cookies_on_subsequent_login,const net::CookieList & target_cookies)123 void OnTargetCookieJarContentsRetrieved(
124 base::RepeatingClosure completion_callback,
125 content::StoragePartition* from_partition,
126 content::StoragePartition* to_partition,
127 bool transfer_auth_cookies_on_first_login,
128 bool transfer_saml_auth_cookies_on_subsequent_login,
129 const net::CookieList& target_cookies) {
130 DCHECK_CURRENTLY_ON(BrowserThread::UI);
131 bool transfer_auth_cookies;
132
133 bool first_login = target_cookies.empty();
134 if (first_login) {
135 // On first login, transfer all auth cookies if
136 // `transfer_auth_cookies_on_first_login` is true.
137 transfer_auth_cookies = transfer_auth_cookies_on_first_login;
138 } else {
139 // On subsequent login, transfer auth cookies set by the SAML IdP if
140 // `transfer_saml_auth_cookies_on_subsequent_login` is true.
141 transfer_auth_cookies = transfer_saml_auth_cookies_on_subsequent_login;
142 }
143
144 if (!transfer_auth_cookies) {
145 completion_callback.Run();
146 return;
147 }
148
149 // Retrieve the contents of `from_partition`'s cookie jar. When the retrieval
150 // finishes, OnCookiesToTransferRetrieved will be called with the result.
151 network::mojom::CookieManager* from_manager =
152 from_partition->GetCookieManagerForBrowserProcess();
153 from_manager->GetAllCookies(
154 base::BindOnce(&OnCookiesToTransferRetrieved, completion_callback,
155 base::Unretained(to_partition), first_login));
156 }
157
158 // Starts the process of transferring cookies from `from_partition` to
159 // `to_partition`.
TransferCookies(base::RepeatingClosure completion_callback,content::StoragePartition * from_partition,content::StoragePartition * to_partition,bool transfer_auth_cookies_on_first_login,bool transfer_saml_auth_cookies_on_subsequent_login)160 void TransferCookies(base::RepeatingClosure completion_callback,
161 content::StoragePartition* from_partition,
162 content::StoragePartition* to_partition,
163 bool transfer_auth_cookies_on_first_login,
164 bool transfer_saml_auth_cookies_on_subsequent_login) {
165 if (transfer_auth_cookies_on_first_login ||
166 transfer_saml_auth_cookies_on_subsequent_login) {
167 // Retrieve the contents of `to_partition_`'s cookie jar.
168 network::mojom::CookieManager* to_manager =
169 to_partition->GetCookieManagerForBrowserProcess();
170 to_manager->GetAllCookies(base::BindOnce(
171 &OnTargetCookieJarContentsRetrieved, completion_callback,
172 base::Unretained(from_partition), base::Unretained(to_partition),
173 transfer_auth_cookies_on_first_login,
174 transfer_saml_auth_cookies_on_subsequent_login));
175 } else {
176 completion_callback.Run();
177 }
178 }
179
180 } // namespace
181
Transfer(content::StoragePartition * from_partition,content::StoragePartition * to_partition,bool transfer_auth_cookies_on_first_login,bool transfer_saml_auth_cookies_on_subsequent_login,base::OnceClosure completion_callback)182 void ProfileAuthData::Transfer(
183 content::StoragePartition* from_partition,
184 content::StoragePartition* to_partition,
185 bool transfer_auth_cookies_on_first_login,
186 bool transfer_saml_auth_cookies_on_subsequent_login,
187 base::OnceClosure completion_callback) {
188 DCHECK_CURRENTLY_ON(BrowserThread::UI);
189
190 // The BarrierClosure will call `completion_callback` after the 2 async
191 // transfers have finished.
192 base::RepeatingClosure task_completion_callback =
193 base::BarrierClosure(2, std::move(completion_callback));
194
195 // Transfer the proxy auth cache entries from `from_context` to `to_context`.
196 // If the user was required to authenticate with a proxy during login, this
197 // authentication information will be transferred into the user's session.
198 TransferHttpAuthCacheProxyEntries(task_completion_callback, from_partition,
199 to_partition);
200
201 TransferCookies(task_completion_callback, from_partition, to_partition,
202 transfer_auth_cookies_on_first_login,
203 transfer_saml_auth_cookies_on_subsequent_login);
204 }
205
206 } // namespace chromeos
207