extensions/cookie/nsCookiePermission.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/extensions/cookie/nsCookiePermission.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,381 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsCookiePermission.h"
    1.11 +
    1.12 +#include "mozIThirdPartyUtil.h"
    1.13 +#include "nsICookie2.h"
    1.14 +#include "nsIServiceManager.h"
    1.15 +#include "nsICookiePromptService.h"
    1.16 +#include "nsICookieManager2.h"
    1.17 +#include "nsNetUtil.h"
    1.18 +#include "nsIURI.h"
    1.19 +#include "nsIPrefService.h"
    1.20 +#include "nsIPrefBranch.h"
    1.21 +#include "nsIChannel.h"
    1.22 +#include "nsIHttpChannelInternal.h"
    1.23 +#include "nsIDOMWindow.h"
    1.24 +#include "nsIPrincipal.h"
    1.25 +#include "nsString.h"
    1.26 +#include "nsCRT.h"
    1.27 +#include "nsILoadContext.h"
    1.28 +#include "nsIScriptObjectPrincipal.h"
    1.29 +#include "nsNetCID.h"
    1.30 +
    1.31 +/****************************************************************
    1.32 + ************************ nsCookiePermission ********************
    1.33 + ****************************************************************/
    1.34 +
    1.35 +// values for mCookiesLifetimePolicy
    1.36 +// 0 == accept normally
    1.37 +// 1 == ask before accepting
    1.38 +// 2 == downgrade to session
    1.39 +// 3 == limit lifetime to N days
    1.40 +static const uint32_t ACCEPT_NORMALLY = 0;
    1.41 +static const uint32_t ASK_BEFORE_ACCEPT = 1;
    1.42 +static const uint32_t ACCEPT_SESSION = 2;
    1.43 +static const uint32_t ACCEPT_FOR_N_DAYS = 3;
    1.44 +
    1.45 +static const bool kDefaultPolicy = true;
    1.46 +static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
    1.47 +static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days";
    1.48 +static const char kCookiesAlwaysAcceptSession[] = "network.cookie.alwaysAcceptSessionCookies";
    1.49 +
    1.50 +static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated";
    1.51 +// obsolete pref names for migration
    1.52 +static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
    1.53 +static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
    1.54 +static const char kCookiesAskPermission[] = "network.cookie.warnAboutCookies";
    1.55 +
    1.56 +static const char kPermissionType[] = "cookie";
    1.57 +
    1.58 +NS_IMPL_ISUPPORTS(nsCookiePermission,
    1.59 +                  nsICookiePermission,
    1.60 +                  nsIObserver)
    1.61 +
    1.62 +bool
    1.63 +nsCookiePermission::Init()
    1.64 +{
    1.65 +  // Initialize nsIPermissionManager and fetch relevant prefs. This is only
    1.66 +  // required for some methods on nsICookiePermission, so it should be done
    1.67 +  // lazily.
    1.68 +  nsresult rv;
    1.69 +  mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    1.70 +  if (NS_FAILED(rv)) return false;
    1.71 +  mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
    1.72 +  if (NS_FAILED(rv)) return false;
    1.73 +
    1.74 +  // failure to access the pref service is non-fatal...
    1.75 +  nsCOMPtr<nsIPrefBranch> prefBranch =
    1.76 +      do_GetService(NS_PREFSERVICE_CONTRACTID);
    1.77 +  if (prefBranch) {
    1.78 +    prefBranch->AddObserver(kCookiesLifetimePolicy, this, false);
    1.79 +    prefBranch->AddObserver(kCookiesLifetimeDays, this, false);
    1.80 +    prefBranch->AddObserver(kCookiesAlwaysAcceptSession, this, false);
    1.81 +    PrefChanged(prefBranch, nullptr);
    1.82 +
    1.83 +    // migration code for original cookie prefs
    1.84 +    bool migrated;
    1.85 +    rv = prefBranch->GetBoolPref(kCookiesPrefsMigrated, &migrated);
    1.86 +    if (NS_FAILED(rv) || !migrated) {
    1.87 +      bool warnAboutCookies = false;
    1.88 +      prefBranch->GetBoolPref(kCookiesAskPermission, &warnAboutCookies);
    1.89 +
    1.90 +      // if the user is using ask before accepting, we'll use that
    1.91 +      if (warnAboutCookies)
    1.92 +        prefBranch->SetIntPref(kCookiesLifetimePolicy, ASK_BEFORE_ACCEPT);
    1.93 +        
    1.94 +      bool lifetimeEnabled = false;
    1.95 +      prefBranch->GetBoolPref(kCookiesLifetimeEnabled, &lifetimeEnabled);
    1.96 +      
    1.97 +      // if they're limiting lifetime and not using the prompts, use the 
    1.98 +      // appropriate limited lifetime pref
    1.99 +      if (lifetimeEnabled && !warnAboutCookies) {
   1.100 +        int32_t lifetimeBehavior;
   1.101 +        prefBranch->GetIntPref(kCookiesLifetimeBehavior, &lifetimeBehavior);
   1.102 +        if (lifetimeBehavior)
   1.103 +          prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_FOR_N_DAYS);
   1.104 +        else
   1.105 +          prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_SESSION);
   1.106 +      }
   1.107 +      prefBranch->SetBoolPref(kCookiesPrefsMigrated, true);
   1.108 +    }
   1.109 +  }
   1.110 +
   1.111 +  return true;
   1.112 +}
   1.113 +
   1.114 +void
   1.115 +nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch,
   1.116 +                                const char    *aPref)
   1.117 +{
   1.118 +  int32_t val;
   1.119 +
   1.120 +#define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
   1.121 +
   1.122 +  if (PREF_CHANGED(kCookiesLifetimePolicy) &&
   1.123 +      NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val)))
   1.124 +    mCookiesLifetimePolicy = val;
   1.125 +
   1.126 +  if (PREF_CHANGED(kCookiesLifetimeDays) &&
   1.127 +      NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val)))
   1.128 +    // save cookie lifetime in seconds instead of days
   1.129 +    mCookiesLifetimeSec = val * 24 * 60 * 60;
   1.130 +
   1.131 +  bool bval;
   1.132 +  if (PREF_CHANGED(kCookiesAlwaysAcceptSession) &&
   1.133 +      NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookiesAlwaysAcceptSession, &bval)))
   1.134 +    mCookiesAlwaysAcceptSession = bval;
   1.135 +}
   1.136 +
   1.137 +NS_IMETHODIMP
   1.138 +nsCookiePermission::SetAccess(nsIURI         *aURI,
   1.139 +                              nsCookieAccess  aAccess)
   1.140 +{
   1.141 +  // Lazily initialize ourselves
   1.142 +  if (!EnsureInitialized())
   1.143 +    return NS_ERROR_UNEXPECTED;
   1.144 +
   1.145 +  //
   1.146 +  // NOTE: nsCookieAccess values conveniently match up with
   1.147 +  //       the permission codes used by nsIPermissionManager.
   1.148 +  //       this is nice because it avoids conversion code.
   1.149 +  //
   1.150 +  return mPermMgr->Add(aURI, kPermissionType, aAccess,
   1.151 +                       nsIPermissionManager::EXPIRE_NEVER, 0);
   1.152 +}
   1.153 +
   1.154 +NS_IMETHODIMP
   1.155 +nsCookiePermission::CanAccess(nsIURI         *aURI,
   1.156 +                              nsIChannel     *aChannel,
   1.157 +                              nsCookieAccess *aResult)
   1.158 +{
   1.159 +  // Check this protocol doesn't allow cookies
   1.160 +  bool hasFlags;
   1.161 +  nsresult rv =
   1.162 +    NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS,
   1.163 +                        &hasFlags);
   1.164 +  if (NS_FAILED(rv) || hasFlags) {
   1.165 +    *aResult = ACCESS_DENY;
   1.166 +    return NS_OK;
   1.167 +  }
   1.168 +
   1.169 +  // Lazily initialize ourselves
   1.170 +  if (!EnsureInitialized())
   1.171 +    return NS_ERROR_UNEXPECTED;
   1.172 +
   1.173 +  // finally, check with permission manager...
   1.174 +  rv = mPermMgr->TestPermission(aURI, kPermissionType, (uint32_t *) aResult);
   1.175 +  if (NS_SUCCEEDED(rv)) {
   1.176 +    if (*aResult == nsICookiePermission::ACCESS_SESSION) {
   1.177 +      *aResult = nsICookiePermission::ACCESS_ALLOW;
   1.178 +    }
   1.179 +  }
   1.180 +
   1.181 +  return rv;
   1.182 +}
   1.183 +
   1.184 +NS_IMETHODIMP
   1.185 +nsCookiePermission::CanSetCookie(nsIURI     *aURI,
   1.186 +                                 nsIChannel *aChannel,
   1.187 +                                 nsICookie2 *aCookie,
   1.188 +                                 bool       *aIsSession,
   1.189 +                                 int64_t    *aExpiry,
   1.190 +                                 bool       *aResult)
   1.191 +{
   1.192 +  NS_ASSERTION(aURI, "null uri");
   1.193 +
   1.194 +  *aResult = kDefaultPolicy;
   1.195 +
   1.196 +  // Lazily initialize ourselves
   1.197 +  if (!EnsureInitialized())
   1.198 +    return NS_ERROR_UNEXPECTED;
   1.199 +
   1.200 +  uint32_t perm;
   1.201 +  mPermMgr->TestPermission(aURI, kPermissionType, &perm);
   1.202 +  bool isThirdParty = false;
   1.203 +  switch (perm) {
   1.204 +  case nsICookiePermission::ACCESS_SESSION:
   1.205 +    *aIsSession = true;
   1.206 +
   1.207 +  case nsICookiePermission::ACCESS_ALLOW:
   1.208 +    *aResult = true;
   1.209 +    break;
   1.210 +
   1.211 +  case nsICookiePermission::ACCESS_DENY:
   1.212 +    *aResult = false;
   1.213 +    break;
   1.214 +
   1.215 +  case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
   1.216 +    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
   1.217 +    // If it's third party, we can't set the cookie
   1.218 +    if (isThirdParty)
   1.219 +      *aResult = false;
   1.220 +    break;
   1.221 +
   1.222 +  case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
   1.223 +    mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
   1.224 +    // If it's third party, check whether cookies are already set
   1.225 +    if (isThirdParty) {
   1.226 +      nsresult rv;
   1.227 +      nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
   1.228 +      if (NS_FAILED(rv)) {
   1.229 +        *aResult = false;
   1.230 +        break;
   1.231 +      }
   1.232 +      uint32_t priorCookieCount = 0;
   1.233 +      nsAutoCString hostFromURI;
   1.234 +      aURI->GetHost(hostFromURI);
   1.235 +      cookieManager->CountCookiesFromHost(hostFromURI, &priorCookieCount);
   1.236 +      *aResult = priorCookieCount != 0;
   1.237 +    }
   1.238 +    break;
   1.239 +
   1.240 +  default:
   1.241 +    // the permission manager has nothing to say about this cookie -
   1.242 +    // so, we apply the default prefs to it.
   1.243 +    NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
   1.244 +
   1.245 +    // now we need to figure out what type of accept policy we're dealing with
   1.246 +    // if we accept cookies normally, just bail and return
   1.247 +    if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
   1.248 +      *aResult = true;
   1.249 +      return NS_OK;
   1.250 +    }
   1.251 +
   1.252 +    // declare this here since it'll be used in all of the remaining cases
   1.253 +    int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
   1.254 +    int64_t delta = *aExpiry - currentTime;
   1.255 +
   1.256 +    // check whether the user wants to be prompted
   1.257 +    if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) {
   1.258 +      // if it's a session cookie and the user wants to accept these 
   1.259 +      // without asking, or if we are in private browsing mode, just
   1.260 +      // accept the cookie and return
   1.261 +      if ((*aIsSession && mCookiesAlwaysAcceptSession) ||
   1.262 +          (aChannel && NS_UsePrivateBrowsing(aChannel))) {
   1.263 +        *aResult = true;
   1.264 +        return NS_OK;
   1.265 +      }
   1.266 +
   1.267 +      // default to rejecting, in case the prompting process fails
   1.268 +      *aResult = false;
   1.269 +
   1.270 +      nsAutoCString hostPort;
   1.271 +      aURI->GetHostPort(hostPort);
   1.272 +
   1.273 +      if (!aCookie) {
   1.274 +         return NS_ERROR_UNEXPECTED;
   1.275 +      }
   1.276 +      // If there is no host, use the scheme, and append "://",
   1.277 +      // to make sure it isn't a host or something.
   1.278 +      // This is done to make the dialog appear for javascript cookies from
   1.279 +      // file:// urls, and make the text on it not too weird. (bug 209689)
   1.280 +      if (hostPort.IsEmpty()) {
   1.281 +        aURI->GetScheme(hostPort);
   1.282 +        if (hostPort.IsEmpty()) {
   1.283 +          // still empty. Just return the default.
   1.284 +          return NS_OK;
   1.285 +        }
   1.286 +        hostPort = hostPort + NS_LITERAL_CSTRING("://");
   1.287 +      }
   1.288 +
   1.289 +      // we don't cache the cookiePromptService - it's not used often, so not
   1.290 +      // worth the memory.
   1.291 +      nsresult rv;
   1.292 +      nsCOMPtr<nsICookiePromptService> cookiePromptService =
   1.293 +          do_GetService(NS_COOKIEPROMPTSERVICE_CONTRACTID, &rv);
   1.294 +      if (NS_FAILED(rv)) return rv;
   1.295 +
   1.296 +      // get some useful information to present to the user:
   1.297 +      // whether a previous cookie already exists, and how many cookies this host
   1.298 +      // has set
   1.299 +      bool foundCookie = false;
   1.300 +      uint32_t countFromHost;
   1.301 +      nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
   1.302 +      if (NS_SUCCEEDED(rv)) {
   1.303 +        nsAutoCString rawHost;
   1.304 +        aCookie->GetRawHost(rawHost);
   1.305 +        rv = cookieManager->CountCookiesFromHost(rawHost, &countFromHost);
   1.306 +
   1.307 +        if (NS_SUCCEEDED(rv) && countFromHost > 0)
   1.308 +          rv = cookieManager->CookieExists(aCookie, &foundCookie);
   1.309 +      }
   1.310 +      if (NS_FAILED(rv)) return rv;
   1.311 +
   1.312 +      // check if the cookie we're trying to set is already expired, and return;
   1.313 +      // but only if there's no previous cookie, because then we need to delete the previous
   1.314 +      // cookie. we need this check to avoid prompting the user for already-expired cookies.
   1.315 +      if (!foundCookie && !*aIsSession && delta <= 0) {
   1.316 +        // the cookie has already expired. accept it, and let the backend figure
   1.317 +        // out it's expired, so that we get correct logging & notifications.
   1.318 +        *aResult = true;
   1.319 +        return rv;
   1.320 +      }
   1.321 +
   1.322 +      bool rememberDecision = false;
   1.323 +      int32_t dialogRes = nsICookiePromptService::DENY_COOKIE;
   1.324 +      rv = cookiePromptService->CookieDialog(nullptr, aCookie, hostPort, 
   1.325 +                                             countFromHost, foundCookie,
   1.326 +                                             &rememberDecision, &dialogRes);
   1.327 +      if (NS_FAILED(rv)) return rv;
   1.328 +
   1.329 +      *aResult = !!dialogRes;
   1.330 +      if (dialogRes == nsICookiePromptService::ACCEPT_SESSION_COOKIE)
   1.331 +        *aIsSession = true;
   1.332 +
   1.333 +      if (rememberDecision) {
   1.334 +        switch (dialogRes) {
   1.335 +          case nsICookiePromptService::DENY_COOKIE:
   1.336 +            mPermMgr->Add(aURI, kPermissionType, (uint32_t) nsIPermissionManager::DENY_ACTION,
   1.337 +                          nsIPermissionManager::EXPIRE_NEVER, 0);
   1.338 +            break;
   1.339 +          case nsICookiePromptService::ACCEPT_COOKIE:
   1.340 +            mPermMgr->Add(aURI, kPermissionType, (uint32_t) nsIPermissionManager::ALLOW_ACTION,
   1.341 +                          nsIPermissionManager::EXPIRE_NEVER, 0);
   1.342 +            break;
   1.343 +          case nsICookiePromptService::ACCEPT_SESSION_COOKIE:
   1.344 +            mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION,
   1.345 +                          nsIPermissionManager::EXPIRE_NEVER, 0);
   1.346 +            break;
   1.347 +          default:
   1.348 +            break;
   1.349 +        }
   1.350 +      }
   1.351 +    } else {
   1.352 +      // we're not prompting, so we must be limiting the lifetime somehow
   1.353 +      // if it's a session cookie, we do nothing
   1.354 +      if (!*aIsSession && delta > 0) {
   1.355 +        if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
   1.356 +          // limit lifetime to session
   1.357 +          *aIsSession = true;
   1.358 +        } else if (delta > mCookiesLifetimeSec) {
   1.359 +          // limit lifetime to specified time
   1.360 +          *aExpiry = currentTime + mCookiesLifetimeSec;
   1.361 +        }
   1.362 +      }
   1.363 +    }
   1.364 +
   1.365 +    // TODO: Why don't we just use this here:
   1.366 +    // httpChannelInternal->GetDocumentURI(aURI);
   1.367 +  }
   1.368 +
   1.369 +  return NS_OK;
   1.370 +}
   1.371 +
   1.372 +NS_IMETHODIMP
   1.373 +nsCookiePermission::Observe(nsISupports     *aSubject,
   1.374 +                            const char      *aTopic,
   1.375 +                            const char16_t *aData)
   1.376 +{
   1.377 +  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
   1.378 +  NS_ASSERTION(!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
   1.379 +               "unexpected topic - we only deal with pref changes!");
   1.380 +
   1.381 +  if (prefBranch)
   1.382 +    PrefChanged(prefBranch, NS_LossyConvertUTF16toASCII(aData).get());
   1.383 +  return NS_OK;
   1.384 +}

mercurial