Home | History | Annotate | Download | only in login
      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