1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/execution_context/security_context_init.h"
6
7 #include "base/metrics/histogram_macros.h"
8 #include "services/network/public/cpp/web_sandbox_flags.h"
9 #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
10 #include "third_party/blink/renderer/core/dom/element.h"
11 #include "third_party/blink/renderer/core/execution_context/agent.h"
12 #include "third_party/blink/renderer/core/feature_policy/document_policy_parser.h"
13 #include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
14 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
15 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
16 #include "third_party/blink/renderer/core/frame/local_frame.h"
17 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
18 #include "third_party/blink/renderer/core/frame/sandbox_flags.h"
19 #include "third_party/blink/renderer/core/frame/settings.h"
20 #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h"
21 #include "third_party/blink/renderer/core/inspector/console_message.h"
22 #include "third_party/blink/renderer/core/loader/document_loader.h"
23 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
24 #include "third_party/blink/renderer/core/page/chrome_client.h"
25 #include "third_party/blink/renderer/core/page/page.h"
26 #include "third_party/blink/renderer/platform/heap/heap.h"
27
28 namespace blink {
29 namespace {
30
31 // Helper function to filter out features that are not in origin trial in
32 // ParsedDocumentPolicy.
FilterByOriginTrial(const DocumentPolicy::ParsedDocumentPolicy & parsed_policy,ExecutionContext * context)33 DocumentPolicy::ParsedDocumentPolicy FilterByOriginTrial(
34 const DocumentPolicy::ParsedDocumentPolicy& parsed_policy,
35 ExecutionContext* context) {
36 DocumentPolicy::ParsedDocumentPolicy filtered_policy;
37 for (auto i = parsed_policy.feature_state.begin(),
38 last = parsed_policy.feature_state.end();
39 i != last;) {
40 if (!DisabledByOriginTrial(i->first, context))
41 filtered_policy.feature_state.insert(*i);
42 ++i;
43 }
44 for (auto i = parsed_policy.endpoint_map.begin(),
45 last = parsed_policy.endpoint_map.end();
46 i != last;) {
47 if (!DisabledByOriginTrial(i->first, context))
48 filtered_policy.endpoint_map.insert(*i);
49 ++i;
50 }
51 return filtered_policy;
52 }
53
54 // Helper function: Merge the feature policy strings from HTTP headers and the
55 // origin policy (if any).
56 // Headers go first, which means that the per-page headers override the
57 // origin policy features.
58 //
59 // TODO(domenic): we want to treat origin policy feature policy as a single
60 // feature policy, not a header serialization, so it should be processed
61 // differently.
MergeFeaturesFromOriginPolicy(WTF::StringBuilder & feature_policy,const WebOriginPolicy & origin_policy)62 void MergeFeaturesFromOriginPolicy(WTF::StringBuilder& feature_policy,
63 const WebOriginPolicy& origin_policy) {
64 if (!origin_policy.feature_policy.IsNull()) {
65 if (!feature_policy.IsEmpty()) {
66 feature_policy.Append(',');
67 }
68 feature_policy.Append(origin_policy.feature_policy);
69 }
70 }
71
72 } // namespace
73
74 // A helper class that allows the security context be initialized in the
75 // process of constructing the document.
SecurityContextInit(ExecutionContext * context)76 SecurityContextInit::SecurityContextInit(ExecutionContext* context)
77 : execution_context_(context) {}
78
ApplyDocumentPolicy(DocumentPolicy::ParsedDocumentPolicy & document_policy,const String & report_only_document_policy_header)79 void SecurityContextInit::ApplyDocumentPolicy(
80 DocumentPolicy::ParsedDocumentPolicy& document_policy,
81 const String& report_only_document_policy_header) {
82 if (!RuntimeEnabledFeatures::DocumentPolicyEnabled(execution_context_))
83 return;
84
85 // Because Document-Policy http header is parsed in DocumentLoader,
86 // when origin trial context is not initialized yet.
87 // Needs to filter out features that are not in origin trial after
88 // we have origin trial information available.
89 document_policy = FilterByOriginTrial(document_policy, execution_context_);
90 if (!document_policy.feature_state.empty()) {
91 UseCounter::Count(execution_context_, WebFeature::kDocumentPolicyHeader);
92 for (const auto& policy_entry : document_policy.feature_state) {
93 UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.DocumentPolicy.Header",
94 policy_entry.first);
95 }
96 }
97 execution_context_->GetSecurityContext().SetDocumentPolicy(
98 DocumentPolicy::CreateWithHeaderPolicy(document_policy));
99
100 // Handle Report-Only-Document-Policy HTTP header.
101 // Console messages generated from logger are discarded, because currently
102 // there is no way to output them to console.
103 // Calling |Document::AddConsoleMessage| in
104 // |SecurityContextInit::ApplyPendingDataToDocument| will have no effect,
105 // because when the function is called, the document is not fully initialized
106 // yet (|document_| field in current frame is not yet initialized yet).
107 DocumentPolicy::ParsedDocumentPolicy report_only_document_policy;
108 PolicyParserMessageBuffer logger("%s", /* discard_message */ true);
109 base::Optional<DocumentPolicy::ParsedDocumentPolicy>
110 report_only_parsed_policy = DocumentPolicyParser::Parse(
111 report_only_document_policy_header, logger);
112 if (report_only_parsed_policy) {
113 report_only_document_policy =
114 FilterByOriginTrial(*report_only_parsed_policy, execution_context_);
115 if (!report_only_document_policy.feature_state.empty()) {
116 UseCounter::Count(execution_context_,
117 WebFeature::kDocumentPolicyReportOnlyHeader);
118 execution_context_->GetSecurityContext().SetReportOnlyDocumentPolicy(
119 DocumentPolicy::CreateWithHeaderPolicy(report_only_document_policy));
120 }
121 }
122 }
123
ApplyFeaturePolicy(LocalFrame * frame,const ResourceResponse & response,const base::Optional<WebOriginPolicy> & origin_policy,const FramePolicy & frame_policy)124 void SecurityContextInit::ApplyFeaturePolicy(
125 LocalFrame* frame,
126 const ResourceResponse& response,
127 const base::Optional<WebOriginPolicy>& origin_policy,
128 const FramePolicy& frame_policy) {
129 // If we are a HTMLViewSourceDocument we use container, header or
130 // inherited policies. https://crbug.com/898688.
131 if (frame->InViewSourceMode()) {
132 execution_context_->GetSecurityContext().SetFeaturePolicy(
133 FeaturePolicy::CreateFromParentPolicy(
134 nullptr, {},
135 execution_context_->GetSecurityOrigin()->ToUrlOrigin()));
136 return;
137 }
138
139 const String& permissions_policy_header =
140 RuntimeEnabledFeatures::PermissionsPolicyHeaderEnabled()
141 ? response.HttpHeaderField(http_names::kPermissionsPolicy)
142 : g_empty_string;
143 const String& report_only_permissions_policy_header =
144 RuntimeEnabledFeatures::PermissionsPolicyHeaderEnabled()
145 ? response.HttpHeaderField(http_names::kPermissionsPolicyReportOnly)
146 : g_empty_string;
147
148 PolicyParserMessageBuffer feature_policy_logger(
149 "Error with Feature-Policy header: ");
150 PolicyParserMessageBuffer report_only_feature_policy_logger(
151 "Error with Feature-Policy-Report-Only header: ");
152
153 PolicyParserMessageBuffer permissions_policy_logger(
154 "Error with Permissions-Policy header: ");
155 PolicyParserMessageBuffer report_only_permissions_policy_logger(
156 "Error with Permissions-Policy-Report-Only header: ");
157
158 WTF::StringBuilder policy_builder;
159 policy_builder.Append(response.HttpHeaderField(http_names::kFeaturePolicy));
160 if (origin_policy.has_value())
161 MergeFeaturesFromOriginPolicy(policy_builder, origin_policy.value());
162 String feature_policy_header = policy_builder.ToString();
163 if (!feature_policy_header.IsEmpty())
164 UseCounter::Count(execution_context_, WebFeature::kFeaturePolicyHeader);
165
166 feature_policy_header_ = FeaturePolicyParser::ParseHeader(
167 feature_policy_header, permissions_policy_header,
168 execution_context_->GetSecurityOrigin(), feature_policy_logger,
169 permissions_policy_logger, execution_context_);
170
171 ParsedFeaturePolicy report_only_feature_policy_header =
172 FeaturePolicyParser::ParseHeader(
173 response.HttpHeaderField(http_names::kFeaturePolicyReportOnly),
174 report_only_permissions_policy_header,
175 execution_context_->GetSecurityOrigin(),
176 report_only_feature_policy_logger,
177 report_only_permissions_policy_logger, execution_context_);
178
179 if (!report_only_feature_policy_header.empty()) {
180 UseCounter::Count(execution_context_,
181 WebFeature::kFeaturePolicyReportOnlyHeader);
182 }
183
184 auto messages = Vector<PolicyParserMessageBuffer::Message>();
185 messages.AppendVector(feature_policy_logger.GetMessages());
186 messages.AppendVector(report_only_feature_policy_logger.GetMessages());
187 messages.AppendVector(permissions_policy_logger.GetMessages());
188 messages.AppendVector(report_only_permissions_policy_logger.GetMessages());
189
190 for (const auto& message : messages) {
191 execution_context_->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
192 mojom::blink::ConsoleMessageSource::kSecurity, message.level,
193 message.content));
194 }
195
196 // DocumentLoader applied the sandbox flags before calling this function, so
197 // they are accessible here.
198 auto sandbox_flags = execution_context_->GetSandboxFlags();
199 if (sandbox_flags != network::mojom::blink::WebSandboxFlags::kNone &&
200 RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
201 // The sandbox flags might have come from CSP header or the browser; in
202 // such cases the sandbox is not part of the container policy. They are
203 // added to the header policy (which specifically makes sense in the case
204 // of CSP sandbox).
205 ApplySandboxFlagsToParsedFeaturePolicy(sandbox_flags,
206 feature_policy_header_);
207 }
208
209 ParsedFeaturePolicy container_policy;
210 if (frame && frame->Owner())
211 container_policy = frame_policy.container_policy;
212
213 // TODO(icelland): This is problematic querying sandbox flags before
214 // feature policy is initialized.
215 if (RuntimeEnabledFeatures::BlockingFocusWithoutUserActivationEnabled() &&
216 frame && frame->Tree().Parent() &&
217 (sandbox_flags & network::mojom::blink::WebSandboxFlags::kNavigation) !=
218 network::mojom::blink::WebSandboxFlags::kNone) {
219 // Enforcing the policy for sandbox frames (for context see
220 // https://crbug.com/954349).
221 DisallowFeatureIfNotPresent(
222 mojom::blink::FeaturePolicyFeature::kFocusWithoutUserActivation,
223 container_policy);
224 }
225
226 // Feature policy should either come from a parent in the case of an
227 // embedded child frame, or from an opener if any when a new window is
228 // created by an opener. A main frame without an opener would not have a
229 // parent policy nor an opener feature state.
230 // For a main frame, get inherited feature policy from the opener if any.
231 std::unique_ptr<FeaturePolicy> feature_policy;
232 if (!frame->IsMainFrame() || frame->OpenerFeatureState().empty() ||
233 !RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
234 auto* parent_feature_policy =
235 frame->Tree().Parent()
236 ? frame->Tree().Parent()->GetSecurityContext()->GetFeaturePolicy()
237 : nullptr;
238 feature_policy = FeaturePolicy::CreateFromParentPolicy(
239 parent_feature_policy, container_policy,
240 execution_context_->GetSecurityOrigin()->ToUrlOrigin());
241 } else {
242 feature_policy = FeaturePolicy::CreateWithOpenerPolicy(
243 frame->OpenerFeatureState(),
244 execution_context_->GetSecurityOrigin()->ToUrlOrigin());
245 }
246 feature_policy->SetHeaderPolicy(feature_policy_header_);
247 execution_context_->GetSecurityContext().SetFeaturePolicy(
248 std::move(feature_policy));
249
250 // Report-only feature policy only takes effect when it is stricter than
251 // enforced feature policy, i.e. when enforced feature policy allows a feature
252 // while report-only feature policy do not. In such scenario, a report-only
253 // policy violation report will be generated, but the feature is still allowed
254 // to be used. Since child frames cannot loosen enforced feature policy, there
255 // is no need to inherit parent policy and container policy for report-only
256 // feature policy. For inherited policies, the behavior is dominated by
257 // enforced feature policy.
258 if (!report_only_feature_policy_header.empty()) {
259 std::unique_ptr<FeaturePolicy> report_only_policy =
260 FeaturePolicy::CreateFromParentPolicy(
261 nullptr /* parent_policy */, {} /* container_policy */,
262 execution_context_->GetSecurityOrigin()->ToUrlOrigin());
263 report_only_policy->SetHeaderPolicy(report_only_feature_policy_header);
264 execution_context_->GetSecurityContext().SetReportOnlyFeaturePolicy(
265 std::move(report_only_policy));
266 }
267 }
268
269 } // namespace blink
270