Line data Source code
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/. */
6 :
7 : #include "nsCookiePermission.h"
8 :
9 : #include "mozIThirdPartyUtil.h"
10 : #include "nsICookie2.h"
11 : #include "nsIServiceManager.h"
12 : #include "nsICookieManager2.h"
13 : #include "nsNetUtil.h"
14 : #include "nsIInterfaceRequestorUtils.h"
15 : #include "nsIProtocolHandler.h"
16 : #include "nsIURI.h"
17 : #include "nsIPrefService.h"
18 : #include "nsIPrefBranch.h"
19 : #include "nsIChannel.h"
20 : #include "nsIHttpChannelInternal.h"
21 : #include "nsIDOMWindow.h"
22 : #include "nsIPrincipal.h"
23 : #include "nsString.h"
24 : #include "nsCRT.h"
25 : #include "nsILoadContext.h"
26 : #include "nsIScriptObjectPrincipal.h"
27 : #include "nsNetCID.h"
28 : #include "prtime.h"
29 :
30 : /****************************************************************
31 : ************************ nsCookiePermission ********************
32 : ****************************************************************/
33 :
34 : // values for mCookiesLifetimePolicy
35 : // 0 == accept normally
36 : // 1 == ask before accepting, no more supported, treated like ACCEPT_NORMALLY (Bug 606655).
37 : // 2 == downgrade to session
38 : // 3 == limit lifetime to N days
39 : static const uint32_t ACCEPT_NORMALLY = 0;
40 : static const uint32_t ASK_BEFORE_ACCEPT = 1;
41 : static const uint32_t ACCEPT_SESSION = 2;
42 : static const uint32_t ACCEPT_FOR_N_DAYS = 3;
43 :
44 : static const bool kDefaultPolicy = true;
45 : static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
46 : static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days";
47 :
48 : static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated";
49 : // obsolete pref names for migration
50 : static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
51 : static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
52 :
53 : static const char kPermissionType[] = "cookie";
54 :
55 21 : NS_IMPL_ISUPPORTS(nsCookiePermission,
56 : nsICookiePermission,
57 : nsIObserver)
58 :
59 : bool
60 1 : 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 1 : mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
67 1 : if (NS_FAILED(rv)) return false;
68 1 : mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
69 1 : if (NS_FAILED(rv)) return false;
70 :
71 : // failure to access the pref service is non-fatal...
72 : nsCOMPtr<nsIPrefBranch> prefBranch =
73 2 : do_GetService(NS_PREFSERVICE_CONTRACTID);
74 1 : if (prefBranch) {
75 1 : prefBranch->AddObserver(kCookiesLifetimePolicy, this, false);
76 1 : prefBranch->AddObserver(kCookiesLifetimeDays, this, false);
77 1 : PrefChanged(prefBranch, nullptr);
78 :
79 : // migration code for original cookie prefs
80 : bool migrated;
81 1 : rv = prefBranch->GetBoolPref(kCookiesPrefsMigrated, &migrated);
82 1 : if (NS_FAILED(rv) || !migrated) {
83 0 : bool lifetimeEnabled = false;
84 0 : prefBranch->GetBoolPref(kCookiesLifetimeEnabled, &lifetimeEnabled);
85 :
86 : // if they're limiting lifetime, use the appropriate limited lifetime pref
87 0 : if (lifetimeEnabled) {
88 : int32_t lifetimeBehavior;
89 0 : prefBranch->GetIntPref(kCookiesLifetimeBehavior, &lifetimeBehavior);
90 0 : if (lifetimeBehavior)
91 0 : prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_FOR_N_DAYS);
92 : else
93 0 : prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_SESSION);
94 : }
95 0 : prefBranch->SetBoolPref(kCookiesPrefsMigrated, true);
96 : }
97 : }
98 :
99 1 : return true;
100 : }
101 :
102 : void
103 1 : nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch,
104 : const char *aPref)
105 : {
106 : int32_t val;
107 :
108 : #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
109 :
110 2 : if (PREF_CHANGED(kCookiesLifetimePolicy) &&
111 1 : NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val))) {
112 1 : if (val != static_cast<int32_t>(ACCEPT_SESSION) && val != static_cast<int32_t>(ACCEPT_FOR_N_DAYS)) {
113 1 : val = ACCEPT_NORMALLY;
114 : }
115 1 : mCookiesLifetimePolicy = val;
116 : }
117 :
118 2 : if (PREF_CHANGED(kCookiesLifetimeDays) &&
119 1 : NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val)))
120 : // save cookie lifetime in seconds instead of days
121 1 : mCookiesLifetimeSec = (int64_t)val * 24 * 60 * 60;
122 1 : }
123 :
124 : NS_IMETHODIMP
125 0 : nsCookiePermission::SetAccess(nsIURI *aURI,
126 : nsCookieAccess aAccess)
127 : {
128 : // Lazily initialize ourselves
129 0 : if (!EnsureInitialized())
130 0 : return NS_ERROR_UNEXPECTED;
131 :
132 : //
133 : // NOTE: nsCookieAccess values conveniently match up with
134 : // the permission codes used by nsIPermissionManager.
135 : // this is nice because it avoids conversion code.
136 : //
137 0 : return mPermMgr->Add(aURI, kPermissionType, aAccess,
138 0 : nsIPermissionManager::EXPIRE_NEVER, 0);
139 : }
140 :
141 : NS_IMETHODIMP
142 6 : nsCookiePermission::CanAccess(nsIURI *aURI,
143 : nsIChannel *aChannel,
144 : nsCookieAccess *aResult)
145 : {
146 : // Check this protocol doesn't allow cookies
147 : bool hasFlags;
148 : nsresult rv =
149 : NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FORBIDS_COOKIE_ACCESS,
150 6 : &hasFlags);
151 6 : if (NS_FAILED(rv) || hasFlags) {
152 0 : *aResult = ACCESS_DENY;
153 0 : return NS_OK;
154 : }
155 :
156 : // Lazily initialize ourselves
157 6 : if (!EnsureInitialized())
158 0 : return NS_ERROR_UNEXPECTED;
159 :
160 : // finally, check with permission manager...
161 6 : rv = mPermMgr->TestPermission(aURI, kPermissionType, (uint32_t *) aResult);
162 6 : if (NS_SUCCEEDED(rv)) {
163 6 : if (*aResult == nsICookiePermission::ACCESS_SESSION) {
164 0 : *aResult = nsICookiePermission::ACCESS_ALLOW;
165 : }
166 : }
167 :
168 6 : return rv;
169 : }
170 :
171 : NS_IMETHODIMP
172 0 : nsCookiePermission::CanSetCookie(nsIURI *aURI,
173 : nsIChannel *aChannel,
174 : nsICookie2 *aCookie,
175 : bool *aIsSession,
176 : int64_t *aExpiry,
177 : bool *aResult)
178 : {
179 0 : NS_ASSERTION(aURI, "null uri");
180 :
181 0 : *aResult = kDefaultPolicy;
182 :
183 : // Lazily initialize ourselves
184 0 : if (!EnsureInitialized())
185 0 : return NS_ERROR_UNEXPECTED;
186 :
187 : uint32_t perm;
188 0 : mPermMgr->TestPermission(aURI, kPermissionType, &perm);
189 0 : bool isThirdParty = false;
190 0 : switch (perm) {
191 : case nsICookiePermission::ACCESS_SESSION:
192 0 : *aIsSession = true;
193 : MOZ_FALLTHROUGH;
194 :
195 : case nsICookiePermission::ACCESS_ALLOW:
196 0 : *aResult = true;
197 0 : break;
198 :
199 : case nsICookiePermission::ACCESS_DENY:
200 0 : *aResult = false;
201 0 : break;
202 :
203 : case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
204 0 : mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
205 : // If it's third party, we can't set the cookie
206 0 : if (isThirdParty)
207 0 : *aResult = false;
208 0 : break;
209 :
210 : case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
211 0 : mThirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &isThirdParty);
212 : // If it's third party, check whether cookies are already set
213 0 : if (isThirdParty) {
214 : nsresult rv;
215 0 : nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
216 0 : if (NS_FAILED(rv)) {
217 0 : *aResult = false;
218 0 : break;
219 : }
220 0 : uint32_t priorCookieCount = 0;
221 0 : nsAutoCString hostFromURI;
222 0 : aURI->GetHost(hostFromURI);
223 0 : cookieManager->CountCookiesFromHost(hostFromURI, &priorCookieCount);
224 0 : *aResult = priorCookieCount != 0;
225 : }
226 0 : break;
227 :
228 : default:
229 : // the permission manager has nothing to say about this cookie -
230 : // so, we apply the default prefs to it.
231 0 : NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
232 :
233 : // now we need to figure out what type of accept policy we're dealing with
234 : // if we accept cookies normally, just bail and return
235 0 : if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
236 0 : *aResult = true;
237 0 : return NS_OK;
238 : }
239 :
240 : // declare this here since it'll be used in all of the remaining cases
241 0 : int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
242 0 : int64_t delta = *aExpiry - currentTime;
243 :
244 : // We are accepting the cookie, but,
245 : // if it's not a session cookie, we may have to limit its lifetime.
246 0 : if (!*aIsSession && delta > 0) {
247 0 : if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
248 : // limit lifetime to session
249 0 : *aIsSession = true;
250 0 : } else if (delta > mCookiesLifetimeSec) {
251 : // limit lifetime to specified time
252 0 : *aExpiry = currentTime + mCookiesLifetimeSec;
253 : }
254 : }
255 : }
256 :
257 0 : return NS_OK;
258 : }
259 :
260 : NS_IMETHODIMP
261 0 : nsCookiePermission::Observe(nsISupports *aSubject,
262 : const char *aTopic,
263 : const char16_t *aData)
264 : {
265 0 : nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
266 0 : NS_ASSERTION(!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
267 : "unexpected topic - we only deal with pref changes!");
268 :
269 0 : if (prefBranch)
270 0 : PrefChanged(prefBranch, NS_LossyConvertUTF16toASCII(aData).get());
271 0 : return NS_OK;
272 : }
|