[iOS] Deny mach lookup access to content filter service in the WebContent sandbox
[WebKit.git] / Source / WebCore / platform / cocoa / ParentalControlsContentFilter.mm
1 /*
2  * Copyright (C) 2015-2019 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 #import "config.h"
27 #import "ParentalControlsContentFilter.h"
28
29 #if HAVE(PARENTAL_CONTROLS)
30
31 #import "ContentFilterUnblockHandler.h"
32 #import "Logging.h"
33 #import "ResourceResponse.h"
34 #import "SharedBuffer.h"
35 #import <objc/runtime.h>
36 #import <pal/spi/cocoa/WebFilterEvaluatorSPI.h>
37 #import <wtf/SoftLinking.h>
38
39 SOFT_LINK_PRIVATE_FRAMEWORK(WebContentAnalysis);
40 SOFT_LINK_CLASS(WebContentAnalysis, WebFilterEvaluator);
41
42 namespace WebCore {
43
44 #if PLATFORM(IOS)
45 ParentalControlsContentFilter::SandboxExtensionState ParentalControlsContentFilter::m_sandboxExtensionState = SandboxExtensionState::NotSet;
46 #endif
47
48 bool ParentalControlsContentFilter::enabled()
49 {
50 #if PLATFORM(IOS)
51     bool enabled = false;
52     switch (m_sandboxExtensionState) {
53     case SandboxExtensionState::Consumed:
54         enabled = true;
55         break;
56     case SandboxExtensionState::NotConsumed:
57         enabled = false;
58         break;
59     case SandboxExtensionState::NotSet:
60         enabled = [getWebFilterEvaluatorClass() isManagedSession];
61         break;
62     }
63 #else
64     bool enabled = [getWebFilterEvaluatorClass() isManagedSession];
65 #endif
66     LOG(ContentFiltering, "ParentalControlsContentFilter is %s.\n", enabled ? "enabled" : "not enabled");
67     return enabled;
68 }
69
70 std::unique_ptr<ParentalControlsContentFilter> ParentalControlsContentFilter::create()
71 {
72     return makeUnique<ParentalControlsContentFilter>();
73 }
74
75 static inline bool canHandleResponse(const ResourceResponse& response)
76 {
77 #if PLATFORM(MAC) || PLATFORM(MACCATALYST)
78     return response.url().protocolIs("https");
79 #else
80     return response.url().protocolIsInHTTPFamily();
81 #endif
82 }
83
84 void ParentalControlsContentFilter::responseReceived(const ResourceResponse& response)
85 {
86     ASSERT(!m_webFilterEvaluator);
87
88     if (!canHandleResponse(response) || !enabled()) {
89         m_state = State::Allowed;
90         return;
91     }
92
93     m_webFilterEvaluator = adoptNS([allocWebFilterEvaluatorInstance() initWithResponse:response.nsURLResponse()]);
94     updateFilterState();
95 }
96
97 void ParentalControlsContentFilter::addData(const char* data, int length)
98 {
99     ASSERT(![m_replacementData.get() length]);
100     m_replacementData = [m_webFilterEvaluator addData:[NSData dataWithBytesNoCopy:(void*)data length:length freeWhenDone:NO]];
101     updateFilterState();
102     ASSERT(needsMoreData() || [m_replacementData.get() length]);
103 }
104
105 void ParentalControlsContentFilter::finishedAddingData()
106 {
107     ASSERT(![m_replacementData.get() length]);
108     m_replacementData = [m_webFilterEvaluator dataComplete];
109     updateFilterState();
110 }
111
112 Ref<SharedBuffer> ParentalControlsContentFilter::replacementData() const
113 {
114     ASSERT(didBlockData());
115     return SharedBuffer::create(m_replacementData.get());
116 }
117
118 #if ENABLE(CONTENT_FILTERING)
119 ContentFilterUnblockHandler ParentalControlsContentFilter::unblockHandler() const
120 {
121 #if HAVE(PARENTAL_CONTROLS_WITH_UNBLOCK_HANDLER)
122     return ContentFilterUnblockHandler { "unblock"_s, m_webFilterEvaluator };
123 #else
124     return { };
125 #endif
126 }
127 #endif
128
129 void ParentalControlsContentFilter::updateFilterState()
130 {
131     switch ([m_webFilterEvaluator filterState]) {
132     case kWFEStateAllowed:
133     case kWFEStateEvaluating:
134         m_state = State::Allowed;
135         break;
136     case kWFEStateBlocked:
137         m_state = State::Blocked;
138         break;
139     case kWFEStateBuffering:
140         m_state = State::Filtering;
141         break;
142     }
143
144 #if !LOG_DISABLED
145     if (!needsMoreData())
146         LOG(ContentFiltering, "ParentalControlsContentFilter stopped buffering with state %d and replacement data length %zu.\n", m_state, [m_replacementData length]);
147 #endif
148 }
149
150 #if PLATFORM(IOS)
151 void ParentalControlsContentFilter::setHasConsumedSandboxExtension(bool hasConsumedSandboxExtension)
152 {
153     m_sandboxExtensionState = (hasConsumedSandboxExtension ? SandboxExtensionState::Consumed : SandboxExtensionState::NotConsumed);
154 }
155 #endif
156
157 } // namespace WebCore
158
159 #endif // HAVE(PARENTAL_CONTROLS)