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 <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/callback.h" 12 #include "base/location.h" 13 #include "base/logging.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/time/time.h" 17 #include "content/public/browser/browser_context.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "net/cookies/canonical_cookie.h" 20 #include "net/cookies/cookie_monster.h" 21 #include "net/cookies/cookie_store.h" 22 #include "net/http/http_auth_cache.h" 23 #include "net/http/http_network_session.h" 24 #include "net/http/http_transaction_factory.h" 25 #include "net/ssl/channel_id_service.h" 26 #include "net/ssl/channel_id_store.h" 27 #include "net/url_request/url_request_context.h" 28 #include "net/url_request/url_request_context_getter.h" 29 #include "url/gurl.h" 30 31 using content::BrowserThread; 32 33 namespace chromeos { 34 35 namespace { 36 37 const char kSAMLStartCookie[] = "google-accounts-saml-start"; 38 const char kSAMLEndCookie[] = "google-accounts-saml-end"; 39 40 class ProfileAuthDataTransferer { 41 public: 42 ProfileAuthDataTransferer( 43 content::BrowserContext* from_context, 44 content::BrowserContext* to_context, 45 bool transfer_auth_cookies_and_channel_ids_on_first_login, 46 bool transfer_saml_auth_cookies_on_subsequent_login, 47 const base::Closure& completion_callback); 48 49 void BeginTransfer(); 50 51 private: 52 void BeginTransferOnIOThread(); 53 54 // Transfer the proxy auth cache from |from_context_| to |to_context_|. If 55 // the user was required to authenticate with a proxy during login, this 56 // authentication information will be transferred into the user's session. 57 void TransferProxyAuthCache(); 58 59 // Callback that receives the content of |to_context_|'s cookie jar. Checks 60 // whether this is the user's first login, based on the state of the cookie 61 // jar, and starts retrieval of the data that should be transfered. Calls 62 // Finish() if there is no data to transfer. 63 void OnTargetCookieJarContentsRetrieved( 64 const net::CookieList& target_cookies); 65 66 // Retrieve the contents of |from_context_|'s cookie jar. When the retrieval 67 // finishes, OnCookiesToTransferRetrieved will be called with the result. 68 void RetrieveCookiesToTransfer(); 69 70 // Callback that receives the contents of |from_context_|'s cookie jar. Calls 71 // MaybeTransferCookiesAndChannelIDs() to try and perform the transfer. 72 void OnCookiesToTransferRetrieved(const net::CookieList& cookies_to_transfer); 73 74 // Retrieve |from_context_|'s channel IDs. When the retrieval finishes, 75 // OnChannelIDsToTransferRetrieved will be called with the result. 76 void RetrieveChannelIDsToTransfer(); 77 78 // Callback that receives |from_context_|'s channel IDs. Calls 79 // MaybeTransferCookiesAndChannelIDs() to try and perform the transfer. 80 void OnChannelIDsToTransferRetrieved( 81 const net::ChannelIDStore::ChannelIDList& channel_ids_to_transfer); 82 83 // Given a |cookie| set during login, returns true if the cookie may have been 84 // set by GAIA. The main criterion is the |cookie|'s creation date. The points 85 // in time at which redirects from GAIA to SAML IdP and back occur are stored 86 // in |saml_start_time_| and |saml_end_time_|. If the cookie was set between 87 // these two times, it was created by the SAML IdP. Otherwise, it was created 88 // by GAIA. 89 // As an additional precaution, the cookie's domain is checked. If the domain 90 // contains "google" or "youtube", the cookie is considered to have been set 91 // by GAIA as well. 92 bool IsGAIACookie(const net::CanonicalCookie& cookie); 93 94 // If all data to be transferred has been retrieved already, transfer it to 95 // |to_context_| and call Finish(). 96 void MaybeTransferCookiesAndChannelIDs(); 97 98 // Post the |completion_callback_| to the UI thread and schedule destruction 99 // of |this|. 100 void Finish(); 101 102 scoped_refptr<net::URLRequestContextGetter> from_context_; 103 scoped_refptr<net::URLRequestContextGetter> to_context_; 104 bool transfer_auth_cookies_and_channel_ids_on_first_login_; 105 bool transfer_saml_auth_cookies_on_subsequent_login_; 106 base::Closure completion_callback_; 107 108 net::CookieList cookies_to_transfer_; 109 net::ChannelIDStore::ChannelIDList channel_ids_to_transfer_; 110 111 // The time at which a redirect from GAIA to a SAML IdP occurred. 112 base::Time saml_start_time_; 113 // The time at which a redirect from a SAML IdP back to GAIA occurred. 114 base::Time saml_end_time_; 115 116 bool first_login_; 117 bool waiting_for_auth_cookies_; 118 bool waiting_for_channel_ids_; 119 }; 120 121 ProfileAuthDataTransferer::ProfileAuthDataTransferer( 122 content::BrowserContext* from_context, 123 content::BrowserContext* to_context, 124 bool transfer_auth_cookies_and_channel_ids_on_first_login, 125 bool transfer_saml_auth_cookies_on_subsequent_login, 126 const base::Closure& completion_callback) 127 : from_context_(from_context->GetRequestContext()), 128 to_context_(to_context->GetRequestContext()), 129 transfer_auth_cookies_and_channel_ids_on_first_login_( 130 transfer_auth_cookies_and_channel_ids_on_first_login), 131 transfer_saml_auth_cookies_on_subsequent_login_( 132 transfer_saml_auth_cookies_on_subsequent_login), 133 completion_callback_(completion_callback), 134 first_login_(false), 135 waiting_for_auth_cookies_(false), 136 waiting_for_channel_ids_(false) { 137 } 138 139 void ProfileAuthDataTransferer::BeginTransfer() { 140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 141 // If we aren't transferring auth cookies or channel IDs, post the completion 142 // callback immediately. Otherwise, it will be called when the transfer 143 // finishes. 144 if (!transfer_auth_cookies_and_channel_ids_on_first_login_ && 145 !transfer_saml_auth_cookies_on_subsequent_login_) { 146 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_); 147 // Null the callback so that when Finish is called, the callback won't be 148 // called again. 149 completion_callback_.Reset(); 150 } 151 BrowserThread::PostTask( 152 BrowserThread::IO, FROM_HERE, 153 base::Bind(&ProfileAuthDataTransferer::BeginTransferOnIOThread, 154 base::Unretained(this))); 155 } 156 157 void ProfileAuthDataTransferer::BeginTransferOnIOThread() { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 159 TransferProxyAuthCache(); 160 if (transfer_auth_cookies_and_channel_ids_on_first_login_ || 161 transfer_saml_auth_cookies_on_subsequent_login_) { 162 // Retrieve the contents of |to_context_|'s cookie jar. 163 net::CookieStore* to_store = 164 to_context_->GetURLRequestContext()->cookie_store(); 165 net::CookieMonster* to_monster = to_store->GetCookieMonster(); 166 to_monster->GetAllCookiesAsync( 167 base::Bind( 168 &ProfileAuthDataTransferer::OnTargetCookieJarContentsRetrieved, 169 base::Unretained(this))); 170 } else { 171 Finish(); 172 } 173 } 174 175 void ProfileAuthDataTransferer::TransferProxyAuthCache() { 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 177 net::HttpAuthCache* new_cache = to_context_->GetURLRequestContext()-> 178 http_transaction_factory()->GetSession()->http_auth_cache(); 179 new_cache->UpdateAllFrom(*from_context_->GetURLRequestContext()-> 180 http_transaction_factory()->GetSession()->http_auth_cache()); 181 } 182 183 void ProfileAuthDataTransferer::OnTargetCookieJarContentsRetrieved( 184 const net::CookieList& target_cookies) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 186 first_login_ = target_cookies.empty(); 187 if (first_login_) { 188 // On first login, transfer all auth cookies and channel IDs if 189 // |transfer_auth_cookies_and_channel_ids_on_first_login_| is true. 190 waiting_for_auth_cookies_ = 191 transfer_auth_cookies_and_channel_ids_on_first_login_; 192 waiting_for_channel_ids_ = 193 transfer_auth_cookies_and_channel_ids_on_first_login_; 194 } else { 195 // On subsequent login, transfer auth cookies set by the SAML IdP if 196 // |transfer_saml_auth_cookies_on_subsequent_login_| is true. 197 waiting_for_auth_cookies_ = transfer_saml_auth_cookies_on_subsequent_login_; 198 } 199 200 if (!waiting_for_auth_cookies_ && !waiting_for_channel_ids_) { 201 Finish(); 202 return; 203 } 204 205 if (waiting_for_auth_cookies_) 206 RetrieveCookiesToTransfer(); 207 if (waiting_for_channel_ids_) 208 RetrieveChannelIDsToTransfer(); 209 } 210 211 void ProfileAuthDataTransferer::RetrieveCookiesToTransfer() { 212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 213 net::CookieStore* from_store = 214 from_context_->GetURLRequestContext()->cookie_store(); 215 net::CookieMonster* from_monster = from_store->GetCookieMonster(); 216 from_monster->SetKeepExpiredCookies(); 217 from_monster->GetAllCookiesAsync( 218 base::Bind(&ProfileAuthDataTransferer::OnCookiesToTransferRetrieved, 219 base::Unretained(this))); 220 } 221 222 void ProfileAuthDataTransferer::OnCookiesToTransferRetrieved( 223 const net::CookieList& cookies_to_transfer) { 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 225 waiting_for_auth_cookies_ = false; 226 cookies_to_transfer_ = cookies_to_transfer; 227 228 // Look for cookies indicating the points in time at which redirects from GAIA 229 // to SAML IdP and back occurred. These cookies are synthesized by 230 // chrome/browser/resources/gaia_auth/background.js. If the cookies are found, 231 // their creation times are stored in |saml_start_time_| and 232 // |cookies_to_transfer_| and the cookies are deleted. 233 for (net::CookieList::iterator it = cookies_to_transfer_.begin(); 234 it != cookies_to_transfer_.end(); ) { 235 if (it->Name() == kSAMLStartCookie) { 236 saml_start_time_ = it->CreationDate(); 237 it = cookies_to_transfer_.erase(it); 238 } else if (it->Name() == kSAMLEndCookie) { 239 saml_end_time_ = it->CreationDate(); 240 it = cookies_to_transfer_.erase(it); 241 } else { 242 ++it; 243 } 244 } 245 246 MaybeTransferCookiesAndChannelIDs(); 247 } 248 249 void ProfileAuthDataTransferer::RetrieveChannelIDsToTransfer() { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 251 net::ChannelIDService* from_service = 252 from_context_->GetURLRequestContext()->channel_id_service(); 253 from_service->GetChannelIDStore()->GetAllChannelIDs( 254 base::Bind( 255 &ProfileAuthDataTransferer::OnChannelIDsToTransferRetrieved, 256 base::Unretained(this))); 257 } 258 259 void ProfileAuthDataTransferer::OnChannelIDsToTransferRetrieved( 260 const net::ChannelIDStore::ChannelIDList& channel_ids_to_transfer) { 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 262 channel_ids_to_transfer_ = channel_ids_to_transfer; 263 waiting_for_channel_ids_ = false; 264 MaybeTransferCookiesAndChannelIDs(); 265 } 266 267 bool ProfileAuthDataTransferer::IsGAIACookie( 268 const net::CanonicalCookie& cookie) { 269 const base::Time& creation_date = cookie.CreationDate(); 270 if (creation_date < saml_start_time_) 271 return true; 272 if (!saml_end_time_.is_null() && creation_date > saml_end_time_) 273 return true; 274 275 const std::string& domain = cookie.Domain(); 276 return domain.find("google") != std::string::npos || 277 domain.find("youtube") != std::string::npos; 278 } 279 280 void ProfileAuthDataTransferer::MaybeTransferCookiesAndChannelIDs() { 281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 282 if (waiting_for_auth_cookies_ || waiting_for_channel_ids_) 283 return; 284 285 net::CookieStore* to_store = 286 to_context_->GetURLRequestContext()->cookie_store(); 287 net::CookieMonster* to_monster = to_store->GetCookieMonster(); 288 if (first_login_) { 289 to_monster->ImportCookies(cookies_to_transfer_); 290 net::ChannelIDService* to_cert_service = 291 to_context_->GetURLRequestContext()->channel_id_service(); 292 to_cert_service->GetChannelIDStore()->InitializeFrom( 293 channel_ids_to_transfer_); 294 } else { 295 net::CookieList non_gaia_cookies; 296 for (net::CookieList::const_iterator it = cookies_to_transfer_.begin(); 297 it != cookies_to_transfer_.end(); ++it) { 298 if (!IsGAIACookie(*it)) 299 non_gaia_cookies.push_back(*it); 300 } 301 to_monster->ImportCookies(non_gaia_cookies); 302 } 303 304 Finish(); 305 } 306 307 void ProfileAuthDataTransferer::Finish() { 308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 309 if (!completion_callback_.is_null()) 310 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_); 311 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 312 } 313 314 } // namespace 315 316 void ProfileAuthData::Transfer( 317 content::BrowserContext* from_context, 318 content::BrowserContext* to_context, 319 bool transfer_auth_cookies_and_channel_ids_on_first_login, 320 bool transfer_saml_auth_cookies_on_subsequent_login, 321 const base::Closure& completion_callback) { 322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 323 (new ProfileAuthDataTransferer( 324 from_context, 325 to_context, 326 transfer_auth_cookies_and_channel_ids_on_first_login, 327 transfer_saml_auth_cookies_on_subsequent_login, 328 completion_callback))->BeginTransfer(); 329 } 330 331 } // namespace chromeos 332