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