extensions/cookie/nsCookiePermission.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsCookiePermission.h"
     9 #include "mozIThirdPartyUtil.h"
    10 #include "nsICookie2.h"
    11 #include "nsIServiceManager.h"
    12 #include "nsICookiePromptService.h"
    13 #include "nsICookieManager2.h"
    14 #include "nsNetUtil.h"
    15 #include "nsIURI.h"
    16 #include "nsIPrefService.h"
    17 #include "nsIPrefBranch.h"
    18 #include "nsIChannel.h"
    19 #include "nsIHttpChannelInternal.h"
    20 #include "nsIDOMWindow.h"
    21 #include "nsIPrincipal.h"
    22 #include "nsString.h"
    23 #include "nsCRT.h"
    24 #include "nsILoadContext.h"
    25 #include "nsIScriptObjectPrincipal.h"
    26 #include "nsNetCID.h"
    28 /****************************************************************
    29  ************************ nsCookiePermission ********************
    30  ****************************************************************/
    32 // values for mCookiesLifetimePolicy
    33 // 0 == accept normally
    34 // 1 == ask before accepting
    35 // 2 == downgrade to session
    36 // 3 == limit lifetime to N days
    37 static const uint32_t ACCEPT_NORMALLY = 0;
    38 static const uint32_t ASK_BEFORE_ACCEPT = 1;
    39 static const uint32_t ACCEPT_SESSION = 2;
    40 static const uint32_t ACCEPT_FOR_N_DAYS = 3;
    42 static const bool kDefaultPolicy = true;
    43 static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
    44 static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days";
    45 static const char kCookiesAlwaysAcceptSession[] = "network.cookie.alwaysAcceptSessionCookies";
    47 static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated";
    48 // obsolete pref names for migration
    49 static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
    50 static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
    51 static const char kCookiesAskPermission[] = "network.cookie.warnAboutCookies";
    53 static const char kPermissionType[] = "cookie";
    55 NS_IMPL_ISUPPORTS(nsCookiePermission,
    56                   nsICookiePermission,
    57                   nsIObserver)
    59 bool
    60 nsCookiePermission::Init()
    61 {
    62   // Initialize nsIPermissionManager and fetch relevant prefs. This is only
    63   // required for some methods on nsICookiePermission, so it should be done
    64   // lazily.
    65   nsresult rv;
    66   mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    67   if (NS_FAILED(rv)) return false;
    68   mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
    69   if (NS_FAILED(rv)) return false;
    71   // failure to access the pref service is non-fatal...
    72   nsCOMPtr<nsIPrefBranch> prefBranch =
    73       do_GetService(NS_PREFSERVICE_CONTRACTID);
    74   if (prefBranch) {
    75     prefBranch->AddObserver(kCookiesLifetimePolicy, this, false);
    76     prefBranch->AddObserver(kCookiesLifetimeDays, this, false);
    77     prefBranch->AddObserver(kCookiesAlwaysAcceptSession, this, false);
    78     PrefChanged(prefBranch, nullptr);
    80     // migration code for original cookie prefs
    81     bool migrated;
    82     rv = prefBranch->GetBoolPref(kCookiesPrefsMigrated, &migrated);
    83     if (NS_FAILED(rv) || !migrated) {
    84       bool warnAboutCookies = false;
    85       prefBranch->GetBoolPref(kCookiesAskPermission, &warnAboutCookies);
    87       // if the user is using ask before accepting, we'll use that
    88       if (warnAboutCookies)
    89         prefBranch->SetIntPref(kCookiesLifetimePolicy, ASK_BEFORE_ACCEPT);
    91       bool lifetimeEnabled = false;
    92       prefBranch->GetBoolPref(kCookiesLifetimeEnabled, &lifetimeEnabled);
    94       // if they're limiting lifetime and not using the prompts, use the 
    95       // appropriate limited lifetime pref
    96       if (lifetimeEnabled && !warnAboutCookies) {
    97         int32_t lifetimeBehavior;
    98         prefBranch->GetIntPref(kCookiesLifetimeBehavior, &lifetimeBehavior);
    99         if (lifetimeBehavior)
   100           prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_FOR_N_DAYS);
   101         else
   102           prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_SESSION);
   103       }
   104       prefBranch->SetBoolPref(kCookiesPrefsMigrated, true);
   105     }
   106   }
   108   return true;
   109 }
   111 void
   112 nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch,
   113                                 const char    *aPref)
   114 {
   115   int32_t val;
   117 #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
   119   if (PREF_CHANGED(kCookiesLifetimePolicy) &&
   120       NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val)))
   121     mCookiesLifetimePolicy = val;
   123   if (PREF_CHANGED(kCookiesLifetimeDays) &&
   124       NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val)))
   125     // save cookie lifetime in seconds instead of days
   126     mCookiesLifetimeSec = val * 24 * 60 * 60;
   128   bool bval;
   129   if (PREF_CHANGED(kCookiesAlwaysAcceptSession) &&
   130       NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookiesAlwaysAcceptSession, &bval)))
   131     mCookiesAlwaysAcceptSession = bval;
   132 }
   134 NS_IMETHODIMP
   135 nsCookiePermission::SetAccess(nsIURI         *aURI,
   136                               nsCookieAccess  aAccess)
   137 {
   138   // Lazily initialize ourselves
   139   if (!EnsureInitialized())
   140     return NS_ERROR_UNEXPECTED;
   142   //
   143   // NOTE: nsCookieAccess values conveniently match up with
   144   //       the permission codes used by nsIPermissionManager.
   145   //       this is nice because it avoids conversion code.
   146   //
   147   return mPermMgr->Add(aURI, kPermissionType, aAccess,
   148                        nsIPermissionManager::EXPIRE_NEVER, 0);
   149 }
   151 NS_IMETHODIMP
   152 nsCookiePermission::CanAccess(nsIURI         *aURI,
   153                               nsIChannel     *aChannel,
   154                               nsCookieAccess *aResult)
   155 {
   156   // Check this protocol doesn't allow cookies
   157   bool hasFlags;
   158   nsresult rv =
   159     NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS,
   160                         &hasFlags);
   161   if (NS_FAILED(rv) || hasFlags) {
   162     *aResult = ACCESS_DENY;
   163     return NS_OK;
   164   }
   166   // Lazily initialize ourselves
   167   if (!EnsureInitialized())
   168     return NS_ERROR_UNEXPECTED;
   170   // finally, check with permission manager...
   171   rv = mPermMgr->TestPermission(aURI, kPermissionType, (uint32_t *) aResult);
   172   if (NS_SUCCEEDED(rv)) {
   173     if (*aResult == nsICookiePermission::ACCESS_SESSION) {
   174       *aResult = nsICookiePermission::ACCESS_ALLOW;
   175     }
   176   }
   178   return rv;
   179 }
   181 NS_IMETHODIMP
   182 nsCookiePermission::CanSetCookie(nsIURI     *aURI,
   183                                  nsIChannel *aChannel,
   184                                  nsICookie2 *aCookie,
   185                                  bool       *aIsSession,
   186                                  int64_t    *aExpiry,
   187                                  bool       *aResult)
   188 {
   189   NS_ASSERTION(aURI, "null uri");
   191   *aResult = kDefaultPolicy;
   193   // Lazily initialize ourselves
   194   if (!EnsureInitialized())
   195     return NS_ERROR_UNEXPECTED;
   197   uint32_t perm;
   198   mPermMgr->TestPermission(aURI, kPermissionType, &perm);
   199   bool isThirdParty = false;
   200   switch (perm) {
   201   case nsICookiePermission::ACCESS_SESSION:
   202     *aIsSession = true;
   204   case nsICookiePermission::ACCESS_ALLOW:
   205     *aResult = true;
   206     break;
   208   case nsICookiePermission::ACCESS_DENY:
   209     *aResult = false;
   210     break;
   212   case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
   213     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
   214     // If it's third party, we can't set the cookie
   215     if (isThirdParty)
   216       *aResult = false;
   217     break;
   219   case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
   220     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
   221     // If it's third party, check whether cookies are already set
   222     if (isThirdParty) {
   223       nsresult rv;
   224       nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
   225       if (NS_FAILED(rv)) {
   226         *aResult = false;
   227         break;
   228       }
   229       uint32_t priorCookieCount = 0;
   230       nsAutoCString hostFromURI;
   231       aURI->GetHost(hostFromURI);
   232       cookieManager->CountCookiesFromHost(hostFromURI, &priorCookieCount);
   233       *aResult = priorCookieCount != 0;
   234     }
   235     break;
   237   default:
   238     // the permission manager has nothing to say about this cookie -
   239     // so, we apply the default prefs to it.
   240     NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
   242     // now we need to figure out what type of accept policy we're dealing with
   243     // if we accept cookies normally, just bail and return
   244     if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
   245       *aResult = true;
   246       return NS_OK;
   247     }
   249     // declare this here since it'll be used in all of the remaining cases
   250     int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
   251     int64_t delta = *aExpiry - currentTime;
   253     // check whether the user wants to be prompted
   254     if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) {
   255       // if it's a session cookie and the user wants to accept these 
   256       // without asking, or if we are in private browsing mode, just
   257       // accept the cookie and return
   258       if ((*aIsSession && mCookiesAlwaysAcceptSession) ||
   259           (aChannel && NS_UsePrivateBrowsing(aChannel))) {
   260         *aResult = true;
   261         return NS_OK;
   262       }
   264       // default to rejecting, in case the prompting process fails
   265       *aResult = false;
   267       nsAutoCString hostPort;
   268       aURI->GetHostPort(hostPort);
   270       if (!aCookie) {
   271          return NS_ERROR_UNEXPECTED;
   272       }
   273       // If there is no host, use the scheme, and append "://",
   274       // to make sure it isn't a host or something.
   275       // This is done to make the dialog appear for javascript cookies from
   276       // file:// urls, and make the text on it not too weird. (bug 209689)
   277       if (hostPort.IsEmpty()) {
   278         aURI->GetScheme(hostPort);
   279         if (hostPort.IsEmpty()) {
   280           // still empty. Just return the default.
   281           return NS_OK;
   282         }
   283         hostPort = hostPort + NS_LITERAL_CSTRING("://");
   284       }
   286       // we don't cache the cookiePromptService - it's not used often, so not
   287       // worth the memory.
   288       nsresult rv;
   289       nsCOMPtr<nsICookiePromptService> cookiePromptService =
   290           do_GetService(NS_COOKIEPROMPTSERVICE_CONTRACTID, &rv);
   291       if (NS_FAILED(rv)) return rv;
   293       // get some useful information to present to the user:
   294       // whether a previous cookie already exists, and how many cookies this host
   295       // has set
   296       bool foundCookie = false;
   297       uint32_t countFromHost;
   298       nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
   299       if (NS_SUCCEEDED(rv)) {
   300         nsAutoCString rawHost;
   301         aCookie->GetRawHost(rawHost);
   302         rv = cookieManager->CountCookiesFromHost(rawHost, &countFromHost);
   304         if (NS_SUCCEEDED(rv) && countFromHost > 0)
   305           rv = cookieManager->CookieExists(aCookie, &foundCookie);
   306       }
   307       if (NS_FAILED(rv)) return rv;
   309       // check if the cookie we're trying to set is already expired, and return;
   310       // but only if there's no previous cookie, because then we need to delete the previous
   311       // cookie. we need this check to avoid prompting the user for already-expired cookies.
   312       if (!foundCookie && !*aIsSession && delta <= 0) {
   313         // the cookie has already expired. accept it, and let the backend figure
   314         // out it's expired, so that we get correct logging & notifications.
   315         *aResult = true;
   316         return rv;
   317       }
   319       bool rememberDecision = false;
   320       int32_t dialogRes = nsICookiePromptService::DENY_COOKIE;
   321       rv = cookiePromptService->CookieDialog(nullptr, aCookie, hostPort, 
   322                                              countFromHost, foundCookie,
   323                                              &rememberDecision, &dialogRes);
   324       if (NS_FAILED(rv)) return rv;
   326       *aResult = !!dialogRes;
   327       if (dialogRes == nsICookiePromptService::ACCEPT_SESSION_COOKIE)
   328         *aIsSession = true;
   330       if (rememberDecision) {
   331         switch (dialogRes) {
   332           case nsICookiePromptService::DENY_COOKIE:
   333             mPermMgr->Add(aURI, kPermissionType, (uint32_t) nsIPermissionManager::DENY_ACTION,
   334                           nsIPermissionManager::EXPIRE_NEVER, 0);
   335             break;
   336           case nsICookiePromptService::ACCEPT_COOKIE:
   337             mPermMgr->Add(aURI, kPermissionType, (uint32_t) nsIPermissionManager::ALLOW_ACTION,
   338                           nsIPermissionManager::EXPIRE_NEVER, 0);
   339             break;
   340           case nsICookiePromptService::ACCEPT_SESSION_COOKIE:
   341             mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION,
   342                           nsIPermissionManager::EXPIRE_NEVER, 0);
   343             break;
   344           default:
   345             break;
   346         }
   347       }
   348     } else {
   349       // we're not prompting, so we must be limiting the lifetime somehow
   350       // if it's a session cookie, we do nothing
   351       if (!*aIsSession && delta > 0) {
   352         if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
   353           // limit lifetime to session
   354           *aIsSession = true;
   355         } else if (delta > mCookiesLifetimeSec) {
   356           // limit lifetime to specified time
   357           *aExpiry = currentTime + mCookiesLifetimeSec;
   358         }
   359       }
   360     }
   362     // TODO: Why don't we just use this here:
   363     // httpChannelInternal->GetDocumentURI(aURI);
   364   }
   366   return NS_OK;
   367 }
   369 NS_IMETHODIMP
   370 nsCookiePermission::Observe(nsISupports     *aSubject,
   371                             const char      *aTopic,
   372                             const char16_t *aData)
   373 {
   374   nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
   375   NS_ASSERTION(!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
   376                "unexpected topic - we only deal with pref changes!");
   378   if (prefBranch)
   379     PrefChanged(prefBranch, NS_LossyConvertUTF16toASCII(aData).get());
   380   return NS_OK;
   381 }

mercurial