1/*
2 * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "NetworkStorageSession.h"
28
29#include "RuntimeApplicationChecks.h"
30#include <pal/SessionID.h>
31#include <wtf/NeverDestroyed.h>
32#include <wtf/ProcessPrivilege.h>
33
34#if ENABLE(RESOURCE_LOAD_STATISTICS)
35#include "ResourceRequest.h"
36#if ENABLE(PUBLIC_SUFFIX_LIST)
37#include "PublicSuffix.h"
38#endif
39#endif
40
41namespace WebCore {
42
43bool NetworkStorageSession::m_processMayUseCookieAPI = false;
44
45bool NetworkStorageSession::processMayUseCookieAPI()
46{
47 return m_processMayUseCookieAPI;
48}
49
50void NetworkStorageSession::permitProcessToUseCookieAPI(bool value)
51{
52 m_processMayUseCookieAPI = value;
53 if (m_processMayUseCookieAPI)
54 addProcessPrivilege(ProcessPrivilege::CanAccessRawCookies);
55 else
56 removeProcessPrivilege(ProcessPrivilege::CanAccessRawCookies);
57}
58
59#if ENABLE(RESOURCE_LOAD_STATISTICS)
60
61bool NetworkStorageSession::shouldBlockThirdPartyCookies(const RegistrableDomain& registrableDomain) const
62{
63 if (registrableDomain.isEmpty())
64 return false;
65
66 return m_registrableDomainsToBlockCookieFor.contains(registrableDomain);
67}
68
69bool NetworkStorageSession::shouldBlockCookies(const ResourceRequest& request, Optional<uint64_t> frameID, Optional<uint64_t> pageID) const
70{
71 return shouldBlockCookies(request.firstPartyForCookies(), request.url(), frameID, pageID);
72}
73
74bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies, const URL& resource, Optional<uint64_t> frameID, Optional<uint64_t> pageID) const
75{
76 RegistrableDomain firstPartyDomain { firstPartyForCookies };
77 if (firstPartyDomain.isEmpty())
78 return false;
79
80 RegistrableDomain resourceDomain { resource };
81 if (resourceDomain.isEmpty())
82 return false;
83
84 if (firstPartyDomain == resourceDomain)
85 return false;
86
87 if (pageID && hasStorageAccess(resourceDomain, firstPartyDomain, frameID, pageID.value()))
88 return false;
89
90 return shouldBlockThirdPartyCookies(resourceDomain);
91}
92
93Optional<Seconds> NetworkStorageSession::maxAgeCacheCap(const ResourceRequest& request)
94{
95 if (m_cacheMaxAgeCapForPrevalentResources && shouldBlockCookies(request, WTF::nullopt, WTF::nullopt))
96 return m_cacheMaxAgeCapForPrevalentResources;
97 return WTF::nullopt;
98}
99
100void NetworkStorageSession::setAgeCapForClientSideCookies(Optional<Seconds> seconds)
101{
102 m_ageCapForClientSideCookies = seconds;
103 m_ageCapForClientSideCookiesShort = seconds ? Seconds { seconds->seconds() / 7. } : seconds;
104}
105
106void NetworkStorageSession::setPrevalentDomainsToBlockCookiesFor(const Vector<RegistrableDomain>& domains)
107{
108 m_registrableDomainsToBlockCookieFor.clear();
109 m_registrableDomainsToBlockCookieFor.add(domains.begin(), domains.end());
110}
111
112void NetworkStorageSession::removePrevalentDomains(const Vector<RegistrableDomain>& domains)
113{
114 for (auto& domain : domains)
115 m_registrableDomainsToBlockCookieFor.remove(domain);
116}
117
118bool NetworkStorageSession::hasStorageAccess(const RegistrableDomain& resourceDomain, const RegistrableDomain& firstPartyDomain, Optional<uint64_t> frameID, uint64_t pageID) const
119{
120 if (frameID) {
121 auto framesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
122 if (framesGrantedIterator != m_framesGrantedStorageAccess.end()) {
123 auto it = framesGrantedIterator->value.find(frameID.value());
124 if (it != framesGrantedIterator->value.end() && it->value == resourceDomain)
125 return true;
126 }
127 }
128
129 if (!firstPartyDomain.isEmpty()) {
130 auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
131 if (pagesGrantedIterator != m_pagesGrantedStorageAccess.end()) {
132 auto it = pagesGrantedIterator->value.find(firstPartyDomain);
133 if (it != pagesGrantedIterator->value.end() && it->value == resourceDomain)
134 return true;
135 }
136 }
137
138 return false;
139}
140
141Vector<String> NetworkStorageSession::getAllStorageAccessEntries() const
142{
143 Vector<String> entries;
144 for (auto& innerMap : m_framesGrantedStorageAccess.values()) {
145 for (auto& value : innerMap.values())
146 entries.append(value.string());
147 }
148 return entries;
149}
150
151void NetworkStorageSession::grantStorageAccess(const RegistrableDomain& resourceDomain, const RegistrableDomain& firstPartyDomain, Optional<uint64_t> frameID, uint64_t pageID)
152{
153 if (!frameID) {
154 if (firstPartyDomain.isEmpty())
155 return;
156 auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
157 if (pagesGrantedIterator == m_pagesGrantedStorageAccess.end()) {
158 HashMap<RegistrableDomain, RegistrableDomain> entry;
159 entry.add(firstPartyDomain, resourceDomain);
160 m_pagesGrantedStorageAccess.add(pageID, entry);
161 } else {
162 auto firstPartyDomainIterator = pagesGrantedIterator->value.find(firstPartyDomain);
163 if (firstPartyDomainIterator == pagesGrantedIterator->value.end())
164 pagesGrantedIterator->value.add(firstPartyDomain, resourceDomain);
165 else
166 firstPartyDomainIterator->value = resourceDomain;
167 }
168 return;
169 }
170
171 auto pagesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
172 if (pagesGrantedIterator == m_framesGrantedStorageAccess.end()) {
173 HashMap<uint64_t, RegistrableDomain, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> entry;
174 entry.add(frameID.value(), resourceDomain);
175 m_framesGrantedStorageAccess.add(pageID, entry);
176 } else {
177 auto framesGrantedIterator = pagesGrantedIterator->value.find(frameID.value());
178 if (framesGrantedIterator == pagesGrantedIterator->value.end())
179 pagesGrantedIterator->value.add(frameID.value(), resourceDomain);
180 else
181 framesGrantedIterator->value = resourceDomain;
182 }
183}
184
185void NetworkStorageSession::removeStorageAccessForFrame(uint64_t frameID, uint64_t pageID)
186{
187 auto iteration = m_framesGrantedStorageAccess.find(pageID);
188 if (iteration == m_framesGrantedStorageAccess.end())
189 return;
190
191 iteration->value.remove(frameID);
192}
193
194void NetworkStorageSession::clearPageSpecificDataForResourceLoadStatistics(uint64_t pageID)
195{
196 m_pagesGrantedStorageAccess.remove(pageID);
197 m_framesGrantedStorageAccess.remove(pageID);
198 if (!m_navigationWithLinkDecorationTestMode)
199 m_navigatedToWithLinkDecorationByPrevalentResource.remove(pageID);
200}
201
202void NetworkStorageSession::removeAllStorageAccess()
203{
204 m_pagesGrantedStorageAccess.clear();
205 m_framesGrantedStorageAccess.clear();
206}
207
208void NetworkStorageSession::setCacheMaxAgeCapForPrevalentResources(Seconds seconds)
209{
210 m_cacheMaxAgeCapForPrevalentResources = seconds;
211}
212
213void NetworkStorageSession::resetCacheMaxAgeCapForPrevalentResources()
214{
215 m_cacheMaxAgeCapForPrevalentResources = WTF::nullopt;
216}
217
218void NetworkStorageSession::committedCrossSiteLoadWithLinkDecoration(const RegistrableDomain& fromDomain, const RegistrableDomain& toDomain, uint64_t pageID)
219{
220 if (shouldBlockThirdPartyCookies(fromDomain))
221 m_navigatedToWithLinkDecorationByPrevalentResource.add(pageID, toDomain);
222}
223
224void NetworkStorageSession::resetCrossSiteLoadsWithLinkDecorationForTesting()
225{
226 m_navigatedToWithLinkDecorationByPrevalentResource.clear();
227 m_navigationWithLinkDecorationTestMode = true;
228}
229
230Optional<Seconds> NetworkStorageSession::clientSideCookieCap(const RegistrableDomain& firstParty, Optional<uint64_t> pageID) const
231{
232 if (!m_ageCapForClientSideCookies || !pageID || m_navigatedToWithLinkDecorationByPrevalentResource.isEmpty())
233 return m_ageCapForClientSideCookies;
234
235 auto domainIterator = m_navigatedToWithLinkDecorationByPrevalentResource.find(*pageID);
236 if (domainIterator == m_navigatedToWithLinkDecorationByPrevalentResource.end())
237 return m_ageCapForClientSideCookies;
238
239 if (domainIterator->value == firstParty)
240 return m_ageCapForClientSideCookiesShort;
241
242 return m_ageCapForClientSideCookies;
243}
244#endif // ENABLE(RESOURCE_LOAD_STATISTICS)
245
246}
247