ef3c46f231f1e05dd3855e622efa1334ac2f7ddc
[WebKit-https.git] / Source / WebCore / platform / network / cf / NetworkStorageSessionCFNet.cpp
1 /*
2  * Copyright (C) 2012-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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkStorageSession.h"
28
29 #include <wtf/MainThread.h>
30 #include <wtf/NeverDestroyed.h>
31 #include <wtf/ProcessID.h>
32
33 #if PLATFORM(COCOA)
34 #include "PublicSuffix.h"
35 #include "ResourceRequest.h"
36 #else
37 #include <WebKitSystemInterface/WebKitSystemInterface.h>
38 #endif
39
40 namespace WebCore {
41
42 static bool cookieStoragePartitioningEnabled;
43 static bool storageAccessAPIEnabled;
44
45 static RetainPtr<CFURLStorageSessionRef> createCFStorageSessionForIdentifier(CFStringRef identifier)
46 {
47     auto storageSession = adoptCF(_CFURLStorageSessionCreate(kCFAllocatorDefault, identifier, nullptr));
48
49     if (!storageSession)
50         return nullptr;
51
52     auto cache = adoptCF(_CFURLStorageSessionCopyCache(kCFAllocatorDefault, storageSession.get()));
53     if (!cache)
54         return nullptr;
55
56     CFURLCacheSetDiskCapacity(cache.get(), 0);
57
58     auto sharedCache = adoptCF(CFURLCacheCopySharedURLCache());
59     CFURLCacheSetMemoryCapacity(cache.get(), CFURLCacheMemoryCapacity(sharedCache.get()));
60
61     auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
62     if (!cookieStorage)
63         return nullptr;
64
65     auto sharedCookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
66     auto sharedPolicy = CFHTTPCookieStorageGetCookieAcceptPolicy(sharedCookieStorage);
67     CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage.get(), sharedPolicy);
68
69     return storageSession;
70 }
71
72 NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID, RetainPtr<CFURLStorageSessionRef>&& platformSession, RetainPtr<CFHTTPCookieStorageRef>&& platformCookieStorage)
73     : m_sessionID(sessionID)
74     , m_platformSession(WTFMove(platformSession))
75 {
76     m_platformCookieStorage = platformCookieStorage ? WTFMove(platformCookieStorage) : cookieStorage();
77 }
78
79 static std::unique_ptr<NetworkStorageSession>& defaultNetworkStorageSession()
80 {
81     ASSERT(isMainThread());
82     static NeverDestroyed<std::unique_ptr<NetworkStorageSession>> session;
83     return session;
84 }
85
86 void NetworkStorageSession::switchToNewTestingSession()
87 {
88     // Session name should be short enough for shared memory region name to be under the limit, otehrwise sandbox rules won't work (see <rdar://problem/13642852>).
89     String sessionName = String::format("WebKit Test-%u", static_cast<uint32_t>(getCurrentProcessID()));
90
91     RetainPtr<CFURLStorageSessionRef> session;
92 #if PLATFORM(COCOA)
93     session = adoptCF(createPrivateStorageSession(sessionName.createCFString().get()));
94 #else
95     session = adoptCF(wkCreatePrivateStorageSession(sessionName.createCFString().get(), defaultStorageSession().platformSession()));
96 #endif
97
98     RetainPtr<CFHTTPCookieStorageRef> cookieStorage;
99     if (session)
100         cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, session.get()));
101
102     defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(PAL::SessionID::defaultSessionID(), WTFMove(session), WTFMove(cookieStorage));
103 }
104
105 NetworkStorageSession& NetworkStorageSession::defaultStorageSession()
106 {
107     if (!defaultNetworkStorageSession())
108         defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(PAL::SessionID::defaultSessionID(), nullptr, nullptr);
109     return *defaultNetworkStorageSession();
110 }
111
112 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase, RetainPtr<CFHTTPCookieStorageRef>&& cookieStorage)
113 {
114     auto addResult = globalSessionMap().add(sessionID, nullptr);
115     if (!addResult.isNewEntry)
116         return;
117
118     RetainPtr<CFStringRef> cfIdentifier = String(identifierBase + ".PrivateBrowsing").createCFString();
119
120     RetainPtr<CFURLStorageSessionRef> storageSession;
121     if (sessionID.isEphemeral()) {
122 #if PLATFORM(COCOA)
123         storageSession = adoptCF(createPrivateStorageSession(cfIdentifier.get()));
124 #else
125         storageSession = adoptCF(wkCreatePrivateStorageSession(cfIdentifier.get(), defaultNetworkStorageSession()->platformSession()));
126 #endif
127     } else
128         storageSession = createCFStorageSessionForIdentifier(cfIdentifier.get());
129
130     if (!cookieStorage && storageSession)
131         cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
132
133     addResult.iterator->value = std::make_unique<NetworkStorageSession>(sessionID, WTFMove(storageSession), WTFMove(cookieStorage));
134 }
135
136 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase)
137 {
138     ensureSession(sessionID, identifierBase, nullptr);
139 }
140
141 RetainPtr<CFHTTPCookieStorageRef> NetworkStorageSession::cookieStorage() const
142 {
143     if (m_platformCookieStorage)
144         return m_platformCookieStorage;
145
146     if (m_platformSession)
147         return adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, m_platformSession.get()));
148
149 #if USE(CFURLCONNECTION)
150     return _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
151 #else
152     // When using NSURLConnection, we also use its shared cookie storage.
153     return nullptr;
154 #endif
155 }
156
157 void NetworkStorageSession::setCookieStoragePartitioningEnabled(bool enabled)
158 {
159     cookieStoragePartitioningEnabled = enabled;
160 }
161
162 void NetworkStorageSession::setStorageAccessAPIEnabled(bool enabled)
163 {
164     storageAccessAPIEnabled = enabled;
165 }
166
167 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
168
169 String NetworkStorageSession::cookieStoragePartition(const ResourceRequest& request, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) const
170 {
171     return cookieStoragePartition(request.firstPartyForCookies(), request.url(), frameID, pageID);
172 }
173
174 static inline String getPartitioningDomain(const URL& url) 
175 {
176 #if ENABLE(PUBLIC_SUFFIX_LIST)
177     auto domain = topPrivatelyControlledDomain(url.host());
178     if (domain.isEmpty())
179         domain = url.host();
180 #else
181     auto domain = url.host();
182 #endif
183     return domain;
184 }
185
186 String NetworkStorageSession::cookieStoragePartition(const URL& firstPartyForCookies, const URL& resource, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) const
187 {
188     if (!cookieStoragePartitioningEnabled)
189         return emptyString();
190     
191     auto resourceDomain = getPartitioningDomain(resource);
192     if (!shouldPartitionCookies(resourceDomain))
193         return emptyString();
194
195     auto firstPartyDomain = getPartitioningDomain(firstPartyForCookies);
196     if (firstPartyDomain == resourceDomain)
197         return emptyString();
198
199     if (frameID && pageID && hasStorageAccessForFrame(resourceDomain, firstPartyDomain, frameID.value(), pageID.value()))
200         return emptyString();
201
202     return firstPartyDomain;
203 }
204
205 bool NetworkStorageSession::shouldPartitionCookies(const String& topPrivatelyControlledDomain) const
206 {
207     if (topPrivatelyControlledDomain.isEmpty())
208         return false;
209
210     return m_topPrivatelyControlledDomainsToPartition.contains(topPrivatelyControlledDomain);
211 }
212
213 bool NetworkStorageSession::shouldBlockThirdPartyCookies(const String& topPrivatelyControlledDomain) const
214 {
215     if (topPrivatelyControlledDomain.isEmpty())
216         return false;
217
218     return m_topPrivatelyControlledDomainsToBlock.contains(topPrivatelyControlledDomain);
219 }
220
221 bool NetworkStorageSession::shouldBlockCookies(const ResourceRequest& request) const
222 {
223     if (!cookieStoragePartitioningEnabled)
224         return false;
225
226     return shouldBlockCookies(request.firstPartyForCookies(), request.url());
227 }
228     
229 bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies, const URL& resource) const
230 {
231     if (!cookieStoragePartitioningEnabled)
232         return false;
233     
234     auto firstPartyDomain = getPartitioningDomain(firstPartyForCookies);
235     if (firstPartyDomain.isEmpty())
236         return false;
237
238     auto resourceDomain = getPartitioningDomain(resource);
239     if (resourceDomain.isEmpty())
240         return false;
241
242     if (firstPartyDomain == resourceDomain)
243         return false;
244
245     return shouldBlockThirdPartyCookies(resourceDomain);
246 }
247
248 void NetworkStorageSession::setPrevalentDomainsToPartitionOrBlockCookies(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, bool clearFirst)
249 {
250     if (clearFirst) {
251         m_topPrivatelyControlledDomainsToPartition.clear();
252         m_topPrivatelyControlledDomainsToBlock.clear();
253         m_framesGrantedStorageAccess.clear();
254     }
255
256     for (auto& domain : domainsToPartition) {
257         m_topPrivatelyControlledDomainsToPartition.add(domain);
258         if (!clearFirst)
259             m_topPrivatelyControlledDomainsToBlock.remove(domain);
260     }
261
262     for (auto& domain : domainsToBlock) {
263         m_topPrivatelyControlledDomainsToBlock.add(domain);
264         if (!clearFirst)
265             m_topPrivatelyControlledDomainsToPartition.remove(domain);
266     }
267     
268     if (!clearFirst) {
269         for (auto& domain : domainsToNeitherPartitionNorBlock) {
270             m_topPrivatelyControlledDomainsToPartition.remove(domain);
271             m_topPrivatelyControlledDomainsToBlock.remove(domain);
272         }
273     }
274 }
275
276 void NetworkStorageSession::removePrevalentDomains(const Vector<String>& domains)
277 {
278     for (auto& domain : domains) {
279         m_topPrivatelyControlledDomainsToPartition.remove(domain);
280         m_topPrivatelyControlledDomainsToBlock.remove(domain);
281     }
282 }
283
284 bool NetworkStorageSession::hasStorageAccessForFrame(const String& resourceDomain, const String& firstPartyDomain, uint64_t frameID, uint64_t pageID) const
285 {
286     UNUSED_PARAM(firstPartyDomain);
287
288     auto it1 = m_framesGrantedStorageAccess.find(pageID);
289     if (it1 == m_framesGrantedStorageAccess.end())
290         return false;
291
292     auto it2 = it1->value.find(frameID);
293     if (it2 == it1->value.end())
294         return false;
295     
296     return it2->value == resourceDomain;
297 }
298
299 bool NetworkStorageSession::hasStorageAccessForFrame(const ResourceRequest& request, uint64_t frameID, uint64_t pageID) const
300 {
301     if (!cookieStoragePartitioningEnabled)
302         return false;
303
304     return hasStorageAccessForFrame(getPartitioningDomain(request.url()), getPartitioningDomain(request.firstPartyForCookies()), frameID, pageID);
305 }
306
307 Vector<String> NetworkStorageSession::getAllStorageAccessEntries() const
308 {
309     Vector<String> entries;
310     for (auto& pageID : m_framesGrantedStorageAccess.keys()) {
311         auto it1 = m_framesGrantedStorageAccess.find(pageID);
312         for (auto& frameID : it1->value.keys())
313             entries.append(it1->value.find(frameID)->value);
314     }
315     return entries;
316 }
317     
318 void NetworkStorageSession::grantStorageAccessForFrame(const String& resourceDomain, const String& firstPartyDomain, uint64_t frameID, uint64_t pageID)
319 {
320     UNUSED_PARAM(firstPartyDomain);
321
322     auto it1 = m_framesGrantedStorageAccess.find(pageID);
323     if (it1 == m_framesGrantedStorageAccess.end()) {
324         HashMap<uint64_t, String, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> entry;
325         entry.add(frameID, resourceDomain);
326         m_framesGrantedStorageAccess.add(pageID, entry);
327     } else {
328         auto it2 = it1->value.find(frameID);
329         it2->value = resourceDomain;
330     }
331 }
332
333 void NetworkStorageSession::removeStorageAccessForFrame(uint64_t frameID, uint64_t pageID)
334 {
335     auto iteration = m_framesGrantedStorageAccess.find(pageID);
336     if (iteration == m_framesGrantedStorageAccess.end())
337         return;
338
339     iteration->value.remove(frameID);
340 }
341
342 void NetworkStorageSession::removeStorageAccessForAllFramesOnPage(uint64_t pageID)
343 {
344     m_framesGrantedStorageAccess.remove(pageID);
345 }
346
347 #endif // HAVE(CFNETWORK_STORAGE_PARTITIONING)
348
349 #if !PLATFORM(COCOA)
350 void NetworkStorageSession::setCookies(const Vector<Cookie>&, const URL&, const URL&)
351 {
352     // FIXME: Implement this. <https://webkit.org/b/156298>
353 }
354 #endif
355
356 }