1 | /* |
2 | * Copyright (C) 2011 Google 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 GOOGLE, 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 | |
27 | #include "config.h" |
28 | #include "SecurityContext.h" |
29 | |
30 | #include "ContentSecurityPolicy.h" |
31 | #include "HTMLParserIdioms.h" |
32 | #include "SecurityOrigin.h" |
33 | #include "SecurityOriginPolicy.h" |
34 | #include <wtf/text/StringBuilder.h> |
35 | |
36 | namespace WebCore { |
37 | |
38 | SecurityContext::SecurityContext() = default; |
39 | |
40 | SecurityContext::~SecurityContext() = default; |
41 | |
42 | void SecurityContext::setSecurityOriginPolicy(RefPtr<SecurityOriginPolicy>&& securityOriginPolicy) |
43 | { |
44 | m_securityOriginPolicy = WTFMove(securityOriginPolicy); |
45 | m_haveInitializedSecurityOrigin = true; |
46 | } |
47 | |
48 | SecurityOrigin* SecurityContext::securityOrigin() const |
49 | { |
50 | if (!m_securityOriginPolicy) |
51 | return nullptr; |
52 | |
53 | return &m_securityOriginPolicy->origin(); |
54 | } |
55 | |
56 | void SecurityContext::setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy> contentSecurityPolicy) |
57 | { |
58 | m_contentSecurityPolicy = WTFMove(contentSecurityPolicy); |
59 | } |
60 | |
61 | bool SecurityContext::isSecureTransitionTo(const URL& url) const |
62 | { |
63 | // If we haven't initialized our security origin by now, this is probably |
64 | // a new window created via the API (i.e., that lacks an origin and lacks |
65 | // a place to inherit the origin from). |
66 | if (!haveInitializedSecurityOrigin()) |
67 | return true; |
68 | |
69 | return securityOriginPolicy()->origin().canAccess(SecurityOrigin::create(url).get()); |
70 | } |
71 | |
72 | void SecurityContext::enforceSandboxFlags(SandboxFlags mask) |
73 | { |
74 | m_sandboxFlags |= mask; |
75 | |
76 | // The SandboxOrigin is stored redundantly in the security origin. |
77 | if (isSandboxed(SandboxOrigin) && securityOriginPolicy() && !securityOriginPolicy()->origin().isUnique()) |
78 | setSecurityOriginPolicy(SecurityOriginPolicy::create(SecurityOrigin::createUnique())); |
79 | } |
80 | |
81 | bool SecurityContext::isSupportedSandboxPolicy(StringView policy) |
82 | { |
83 | static const char* const supportedPolicies[] = { |
84 | "allow-forms" , "allow-same-origin" , "allow-scripts" , "allow-top-navigation" , "allow-pointer-lock" , "allow-popups" , "allow-popups-to-escape-sandbox" , "allow-top-navigation-by-user-activation" , "allow-modals" , "allow-storage-access-by-user-activation" |
85 | }; |
86 | |
87 | for (auto* supportedPolicy : supportedPolicies) { |
88 | if (equalIgnoringASCIICase(policy, supportedPolicy)) |
89 | return true; |
90 | } |
91 | return false; |
92 | } |
93 | |
94 | // Keep SecurityContext::isSupportedSandboxPolicy() in sync when updating this function. |
95 | SandboxFlags SecurityContext::parseSandboxPolicy(const String& policy, String& invalidTokensErrorMessage) |
96 | { |
97 | // http://www.w3.org/TR/html5/the-iframe-element.html#attr-iframe-sandbox |
98 | // Parse the unordered set of unique space-separated tokens. |
99 | SandboxFlags flags = SandboxAll; |
100 | unsigned length = policy.length(); |
101 | unsigned start = 0; |
102 | unsigned numberOfTokenErrors = 0; |
103 | StringBuilder tokenErrors; |
104 | while (true) { |
105 | while (start < length && isHTMLSpace(policy[start])) |
106 | ++start; |
107 | if (start >= length) |
108 | break; |
109 | unsigned end = start + 1; |
110 | while (end < length && !isHTMLSpace(policy[end])) |
111 | ++end; |
112 | |
113 | // Turn off the corresponding sandbox flag if it's set as "allowed". |
114 | String sandboxToken = policy.substring(start, end - start); |
115 | if (equalLettersIgnoringASCIICase(sandboxToken, "allow-same-origin" )) |
116 | flags &= ~SandboxOrigin; |
117 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-forms" )) |
118 | flags &= ~SandboxForms; |
119 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-scripts" )) { |
120 | flags &= ~SandboxScripts; |
121 | flags &= ~SandboxAutomaticFeatures; |
122 | } else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-top-navigation" )) { |
123 | flags &= ~SandboxTopNavigation; |
124 | flags &= ~SandboxTopNavigationByUserActivation; |
125 | } else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-popups" )) |
126 | flags &= ~SandboxPopups; |
127 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-pointer-lock" )) |
128 | flags &= ~SandboxPointerLock; |
129 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-popups-to-escape-sandbox" )) |
130 | flags &= ~SandboxPropagatesToAuxiliaryBrowsingContexts; |
131 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-top-navigation-by-user-activation" )) |
132 | flags &= ~SandboxTopNavigationByUserActivation; |
133 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-modals" )) |
134 | flags &= ~SandboxModals; |
135 | else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-storage-access-by-user-activation" )) |
136 | flags &= ~SandboxStorageAccessByUserActivation; |
137 | else { |
138 | if (numberOfTokenErrors) |
139 | tokenErrors.appendLiteral(", '" ); |
140 | else |
141 | tokenErrors.append('\''); |
142 | tokenErrors.append(sandboxToken); |
143 | tokenErrors.append('\''); |
144 | numberOfTokenErrors++; |
145 | } |
146 | |
147 | start = end + 1; |
148 | } |
149 | |
150 | if (numberOfTokenErrors) { |
151 | if (numberOfTokenErrors > 1) |
152 | tokenErrors.appendLiteral(" are invalid sandbox flags." ); |
153 | else |
154 | tokenErrors.appendLiteral(" is an invalid sandbox flag." ); |
155 | invalidTokensErrorMessage = tokenErrors.toString(); |
156 | } |
157 | |
158 | return flags; |
159 | } |
160 | |
161 | } |
162 | |