extensions/cookie/nsCookiePermission.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial